changeset 2020:56e38b884e87

merged
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sat, 02 Mar 2013 18:23:08 +0100
parents 615ca978863f (current diff) ecd902b614bd (diff)
children 36d51bad06b3 606a6b22d82b
files
diffstat 6 files changed, 119 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/apps/frontend/views.py	Sat Mar 02 18:15:47 2013 +0100
+++ b/MoinMoin/apps/frontend/views.py	Sat Mar 02 18:23:08 2013 +0100
@@ -30,7 +30,7 @@
 from flask.ext.babel import format_date
 from flask.ext.themes import get_themes_list
 
-from flatland import Form, Enum, List
+from flatland import Form, List
 from flatland.validation import Validator
 
 from jinja2 import Markup
@@ -47,7 +47,7 @@
 from MoinMoin.themes import render_template, contenttype_to_class
 from MoinMoin.apps.frontend import frontend
 from MoinMoin.forms import (OptionalText, RequiredText, URL, YourOpenID, YourEmail, RequiredPassword, Checkbox,
-                            InlineCheckbox, Select, Names, Tags, Natural, Hidden, MultiSelect)
+                            InlineCheckbox, Select, Names, Tags, Natural, Hidden, MultiSelect, Enum)
 from MoinMoin.items import BaseChangeForm, Item, NonExistent
 from MoinMoin.items.content import content_registry
 from MoinMoin import user, util
@@ -726,14 +726,13 @@
         abort(403)
 
 
-contenttype_groups = content_registry.group_names[:]
-contenttype_group_descriptions = {}
-for g in contenttype_groups:
-    contenttype_group_descriptions[g] = ', '.join([e.display_name for e in content_registry.groups[g]])
-contenttype_groups.append('unknown items')
+def contenttype_selects_gen():
+    for g in content_registry.group_names:
+        description = u', '.join([e.display_name for e in content_registry.groups[g]])
+        yield g, None, description
+    yield u'unknown items', None, u'Items of contenttype unknown to MoinMoin'
 
-ContenttypeGroup = MultiSelect.of(Enum.using(valid_values=contenttype_groups).with_properties(
-                                  descriptions=contenttype_group_descriptions)).using(optional=True)
+ContenttypeGroup = MultiSelect.of(Enum.out_of(contenttype_selects_gen())).using(optional=True)
 
 
 class IndexForm(Form):
@@ -756,7 +755,7 @@
     # more.
     form = IndexForm.from_flat(request.args.items(multi=True))
     if not form['contenttype']:
-        form['contenttype'].set(contenttype_groups)
+        form['contenttype'].set(ContenttypeGroup.member_schema.valid_values)
 
     selected_groups = form['contenttype'].value
     startswith = request.values.get("startswith")
@@ -1401,23 +1400,18 @@
         display_name = OptionalText.using(label=L_('Display-Name')).with_properties(
             placeholder=L_("Your display name (informational)"))
         openid = YourOpenID.using(optional=True)
-        #timezones_keys = sorted(Locale('en').time_zones.keys())
-        timezones_keys = [unicode(tz) for tz in pytz.common_timezones]
-        timezone = Select.using(label=L_('Timezone')).valued(*timezones_keys)
-        supported_locales = [Locale('en')] + app.babel_instance.list_translations()
-        locales_available = sorted([(unicode(l), l.display_name) for l in supported_locales],
-                                   key=lambda x: x[1])
-        locales_keys = [l[0] for l in locales_available]
-        locale = Select.using(label=L_('Locale')).with_properties(labels=dict(locales_available)).valued(*locales_keys)
+        #_timezones_keys = sorted(Locale('en').time_zones.keys())
+        _timezones_keys = [unicode(tz) for tz in pytz.common_timezones]
+        timezone = Select.using(label=L_('Timezone')).out_of((e, e) for e in _timezones_keys)
+        _supported_locales = [Locale('en')] + app.babel_instance.list_translations()
+        locale = Select.using(label=L_('Locale')).out_of(
+            ((unicode(l), l.display_name) for l in _supported_locales), sort_by=1)
         submit_label = L_('Save')
 
     class UserSettingsUIForm(Form):
         name = 'usersettings_ui'
-        themes_available = sorted([(unicode(t.identifier), t.name) for t in get_themes_list()],
-                                  key=lambda x: x[1])
-        themes_keys = [t[0] for t in themes_available]
-        theme_name = Select.using(label=L_('Theme name')).with_properties(
-            labels=dict(themes_available)).valued(*themes_keys)
+        theme_name = Select.using(label=L_('Theme name')).out_of(
+            ((unicode(t.identifier), t.name) for t in get_themes_list()), sort_by=1)
         css_url = URL.using(label=L_('User CSS URL'), optional=True).with_properties(
             placeholder=L_("Give the URL of your custom CSS (optional)"))
         edit_rows = Natural.using(label=L_('Editor size')).with_properties(
--- a/MoinMoin/forms.py	Sat Mar 02 18:15:47 2013 +0100
+++ b/MoinMoin/forms.py	Sat Mar 02 18:23:08 2013 +0100
@@ -12,8 +12,9 @@
 import re
 import datetime
 import json
+from operator import itemgetter
 
-from flatland import (Element, Form, String, Integer, Boolean, Enum, Dict, JoinedString, List, Array,
+from flatland import (Element, Form, String, Integer, Boolean, Enum as BaseEnum, Dict, JoinedString, List, Array,
                       DateTime as _DateTime)
 from flatland.util import class_cloner, Unspecified
 from flatland.validation import Validator, Present, IsEmail, ValueBetween, URLValidator, Converted, ValueAtLeast
@@ -27,6 +28,32 @@
 from MoinMoin.util.forms import FileStorage
 
 
+class Enum(BaseEnum):
+    """
+    An Enum with a convenience class method out_of.
+    """
+    @classmethod
+    def out_of(cls, choice_specs, sort_by=None):
+        """
+        A convenience class method to build Enum with extra data attached to
+        each valid value.
+
+        :param choice_specs: An iterable of tuples. The elements are collected
+                             into the choice_specs property; the tuples' first
+                             elements become the valid values of the Enum. e.g.
+                             for choice_specs = [(v1, ...), (v2, ...), ... ],
+                             the valid values are v1, v2, ...
+
+        :param sort_by: If not None, sort choice_specs by the sort_by'th
+                        element.
+        """
+        if sort_by is not None:
+            choice_specs = sorted(choice_specs, key=itemgetter(sort_by))
+        else:
+            choice_specs = list(choice_specs)
+        return cls.valued(*[e[0] for e in choice_specs]).with_properties(choice_specs=choice_specs)
+
+
 Text = String.with_properties(widget=WIDGET_TEXT)
 
 MultilineText = String.with_properties(widget=WIDGET_MULTILINE_TEXT)
--- a/MoinMoin/items/ticket.py	Sat Mar 02 18:15:47 2013 +0100
+++ b/MoinMoin/items/ticket.py	Sat Mar 02 18:23:08 2013 +0100
@@ -20,7 +20,7 @@
 from MoinMoin.i18n import L_
 from MoinMoin.themes import render_template
 from MoinMoin.forms import (Form, OptionalText, OptionalMultilineText, SmallNatural, Tags,
-                            Reference, BackReference)
+                            Reference, BackReference, SelectSubmit)
 from MoinMoin.storage.middleware.protecting import AccessDenied
 from MoinMoin.constants.keys import ITEMTYPE, CONTENTTYPE, ITEMID, CURRENT
 from MoinMoin.constants.contenttypes import CONTENTTYPE_USER
@@ -66,7 +66,6 @@
     meta = TicketMetaForm
     backrefs = TicketBackRefForm
     message = OptionalMultilineText.using(label=L_("Message")).with_properties(rows=8, cols=80)
-    submit_label = L_("Update ticket")
 
     def _load(self, item):
         meta = item.prepare_meta_for_modify(item.meta)
@@ -76,6 +75,48 @@
             self['backrefs']._load(item)
 
 
+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/x.moin.wiki;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'),
+            'update_negate_status':
+                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))
+        meta.update(self['meta'].value)
+        if self['submit'].value == 'update_negate_status':
+            meta['closed'] = not meta.get('closed')
+
+        data = item.content.data_storage_to_internal(item.content.data)
+        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
@@ -108,39 +149,29 @@
 
     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 = TicketForm.from_item(self)
+            form = Form.from_item(self)
         elif request.method == 'POST':
-            form = TicketForm.from_request(request)
+            form = Form.from_request(request)
             if form.validate():
-                meta = form['meta'].value
-                meta.update({
-                    ITEMTYPE: self.itemtype,
-                    # XXX support other markups
-                    CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8',
-                })
-
-                data = u'' if is_new else self.content.data_storage_to_internal(self.content.data)
-                message = form['message'].value
-                if message:
-                    data += message_markup(message)
-
+                meta, data = form._dump(self)
                 try:
                     self.modify(meta, data)
                 except AccessDenied:
                     abort(403)
                 else:
                     return redirect(url_for('.show_item', item_name=self.name))
-        if is_new:
-            # XXX suppress the "foo doesn't exist. Create it?" dummy content
-            data_rendered = None
-            form.submit_label = L_('Submit ticket')
-        else:
-            data_rendered = Markup(self.content._render_data())
+
+        # 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,
                                is_new=is_new,
+                               closed=closed,
                                item_name=self.name,
                                data_rendered=data_rendered,
                                form=form,
--- a/MoinMoin/storage/middleware/indexing.py	Sat Mar 02 18:15:47 2013 +0100
+++ b/MoinMoin/storage/middleware/indexing.py	Sat Mar 02 18:23:08 2013 +0100
@@ -322,10 +322,10 @@
             'difficulty': NUMERIC(stored=True),
             'severity': NUMERIC(stored=True),
             'priority': NUMERIC(stored=True),
-            'status': ID(stored=True),
             'assigned_to': ID(stored=True),
             'superseded_by': ID(stored=True),
             'depends_on': ID(stored=True),
+            'closed': BOOLEAN(stored=True),
         }
         latest_revs_fields.update(**ticket_fields)
 
--- a/MoinMoin/templates/forms.html	Sat Mar 02 18:15:47 2013 +0100
+++ b/MoinMoin/templates/forms.html	Sat Mar 02 18:23:08 2013 +0100
@@ -156,9 +156,8 @@
   </dt>
   <dd>
     {{ gen.select.open(field) }}
-    {% set labels = field.properties.get('labels', {}) %}
-    {% for value in field.valid_values %}
-      {{ gen.option(field, value=value, contents=labels.get(value, value)) }}
+    {% for value, label in field.properties['choice_specs'] %}
+      {{ gen.option(field, value=value, contents=label or value) }}
     {% endfor %}
     {{ gen.select.close() }}
     {{ render_errors(field) }}
@@ -173,16 +172,13 @@
 {% endmacro %}
 
 {% macro multi_select(field) %}
-  {% set valid_values = field.member_schema.valid_values %}
-  {% set labels = field.member_schema.properties.get('labels', {}) %}
-  {% set descriptions = field.member_schema.properties.get('descriptions', {}) %}
-  {% for value in valid_values %}
+  {% for value, label, description in field.member_schema.properties['choice_specs'] %}
     <li>
       {{ raw_input(field, 'checkbox', value=value) }}
-      {{ _valued_label(field, value, labels.get(value, value), class='moin-inline-label') }}
-      {% if descriptions[value] is defined %}
+      {{ _valued_label(field, value, label or value, class='moin-inline-label') }}
+      {% if description %}
         <span class="helper-text">
-          {{ descriptions[value] }}
+          {{ description }}
         </span>
       {% endif %}
     </li>
--- a/MoinMoin/templates/ticket.html	Sat Mar 02 18:15:47 2013 +0100
+++ b/MoinMoin/templates/ticket.html	Sat Mar 02 18:23:08 2013 +0100
@@ -13,7 +13,13 @@
 {% endblock %}
 
 {% block content %}
-<h1>{{ title }}</h1>
+<h1>
+    {{ title }}
+    {% if closed %}
+        {# TODO style .moin-ticket-closed #}
+        <span class="moin-ticket-closed">{{ _("(Closed)") }}</span>
+    {% endif %}
+</h1>
 <div class="moin-form" id="moin-ticket-form">
     {{ gen.form.open(form, method='post', enctype='multipart/form-data') }}
 
@@ -49,7 +55,13 @@
         {{ forms.render(form['meta'][e]) }}
     {% endfor %}
     </dl>
-    {{ forms.render_submit(form) }}
+
+    {# see comments concerning the submit button within TicketForm in items/ticket.py #}
+    {% if is_new %}
+        {{ forms.render_submit(form) }}
+    {% else %}
+        {{ forms.render(form['submit']) }}
+    {% endif %}
 
     <h2>Back references</h2>
     <dl>