moin-2.0 / MoinMoin / items /

# Copyright: 2012 MoinMoin:CheerXiao
# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.

MoinMoin - Ticket itemtype

from __future__ import absolute_import, division

import time

from flask import request, abort, redirect, url_for
from flask import g as flaskg

from jinja2 import Markup

from whoosh.query import Term

from MoinMoin.i18n import L_
from MoinMoin.themes import render_template
from MoinMoin.forms import (Form, OptionalText, OptionalMultilineText, SmallNatural, Tags,
                            Reference, BackReference, SelectSubmit)
from import AccessDenied
from MoinMoin.constants.keys import ITEMTYPE, CONTENTTYPE, ITEMID, CURRENT
from MoinMoin.constants.contenttypes import CONTENTTYPE_USER
from MoinMoin.items import Item, Contentful, register, BaseModifyForm
from MoinMoin.items.content import NonExistentContent



Rating = SmallNatural.using(optional=True).with_properties(lower=1, upper=5)
OptionalTicketReference =
OptionalUserReference ='(Nobody)')

class TicketMetaForm(Form):
    summary = OptionalText.using(label=L_("Summary")).with_properties(placeholder=L_("One-line summary of the item"))
    effort = Rating.using(label=L_("Effort"))
    difficulty = Rating.using(label=L_("Difficulty"))
    severity = Rating.using(label=L_("Severity"))
    priority = Rating.using(label=L_("Priority"))
    tags = Tags.using(optional=True)
    assigned_to = OptionalUserReference.using(label=L_("Assigned To"))
    superseded_by = OptionalTicketReference.using(label=L_("Superseded By"))
    depends_on = OptionalTicketReference.using(label=L_("Depends On"))

class TicketBackRefForm(Form):
    supersedes = BackReference.using(label=L_("Supersedes"))
    required_by = BackReference.using(label=L_("Required By"))
    subscribers = BackReference.using(label=L_("Subscribers"))

    def _load(self, item):
        id_ = item.meta[ITEMID]
        self['supersedes'].set(Term('superseded_by', id_))
        self['required_by'].set(Term('depends_on', id_))
        self['subscribers'].set(Term('subscribed_items', id_))

class TicketForm(BaseModifyForm):
    meta = TicketMetaForm
    backrefs = TicketBackRefForm
    message = OptionalMultilineText.using(label=L_("Message")).with_properties(rows=8, cols=80)

    def _load(self, item):
        meta = item.prepare_meta_for_modify(item.meta)
        self['meta'].set(meta, 'duck')
        # XXX need a more explicit way to test for item creation/modification
        if ITEMID in item.meta:

class TicketSubmitForm(TicketForm):
    submit_label = L_("Submit ticket")

    def _dump(self, item):
        # initial metadata for Ticket-itemtyped item
        meta = {
            ITEMTYPE: item.itemtype,
            # XXX support other markups
            CONTENTTYPE: 'text/;charset=utf-8',
            'closed': False,
        return meta, message_markup(self['message'].value)

class TicketUpdateForm(TicketForm):
    submit = SelectSubmit.valued('update', 'update_negate_status')

    def _load(self, item):
        super(TicketUpdateForm, self)._load(item)
        self['submit'].properties['labels'] = {
            'update': L_('Update ticket'),
                L_('Update & reopen ticket') if item.meta.get('closed') else
                L_('Update & close ticket')

    def _dump(self, item):
        # Since the metadata form for tickets is an incomplete one, we load the
        # original meta and update it with those from the metadata editor
        meta = item.meta_filter(item.prepare_meta_for_modify(item.meta))
        if self['submit'].value == 'update_negate_status':
            meta['closed'] = not meta.get('closed')

        data = item.content.data_storage_to_internal(
        message = self['message'].value
        if message:
            data += message_markup(message)

        return meta, data

# XXX Ideally we should generate DOM instead of moin wiki source. But
# currently this is not very useful, since
# * DOM cannot be stored directly, it has to be converted to some markup first
# * DOM -> markup conversion is only available for moinwiki

# XXX How to do i18n on this?

def message_markup(message):
    return u'''{{{{{{#!wiki tip
%(author)s wrote on <<DateTime(%(timestamp)d)>>:

''' % dict(, timestamp=time.time(), message=message)

class Ticket(Contentful):
    itemtype = ITEMTYPE_TICKET
    display_name = L_('Ticket')
    description = L_('Ticket item')
    modify_template = 'ticket.html'

    def do_show(self, revid):
        if revid != CURRENT:
            # TODO When requesting a historical version, show a readonly view
            return self.do_modify()

    def do_modify(self):
        is_new = isinstance(self.content, NonExistentContent)
        closed = self.meta.get('closed')

        Form = TicketSubmitForm if is_new else TicketUpdateForm

        if request.method in ['GET', 'HEAD']:
            form = Form.from_item(self)
        elif request.method == 'POST':
            form = Form.from_request(request)
            if form.validate():
                meta, data = form._dump(self)
                    self.modify(meta, data)
                except AccessDenied:
                    return redirect(url_for('.show_item',

        # XXX When creating new item, suppress the "foo doesn't exist. Create it?" dummy content
        data_rendered = None if is_new else Markup(self.content._render_data())

        return render_template(self.modify_template,