changeset 2924:60116a78e932

fix #414 traceback after entering invalid subscription in user settings
author RogerHaase <haaserd@gmail.com>
date Sat, 21 Feb 2015 11:09:16 -0700
parents f09bf7d9578e
children da84a2c42a92
files MoinMoin/apps/frontend/views.py MoinMoin/static/css/common.css MoinMoin/templates/usersettings_forms.html MoinMoin/util/subscriptions.py
diffstat 4 files changed, 69 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/apps/frontend/views.py	Sat Feb 21 08:15:07 2015 -0700
+++ b/MoinMoin/apps/frontend/views.py	Sat Feb 21 11:09:16 2015 -0700
@@ -1666,11 +1666,50 @@
     submit_label = L_('Save')
 
 
+class ValidSubscriptions(Validator):
+    """Validator for a subscriptions change
+    """
+
+    def validate(self, element, state):
+        # TODO: is additional validation for namespaces, itemids, names, or name prefixes needed?
+        invalid_subscription_msg = L_('Invalid subscription syntax: ')
+        invalid_keyword = L_('Invalid keyword: ')
+        invalid_re_expression = L_('Invalid RE syntax: ')
+        errors = []
+        for subscription in element.value['subscriptions']:
+            try:
+                keyword, value = subscription.split(":", 1)
+            except ValueError:
+                errors.append(invalid_subscription_msg + subscription)
+                continue
+            if keyword == ITEMID:
+                continue
+            if keyword not in (NAME, NAMEPREFIX, TAGS, NAMERE, ):
+                errors.append(invalid_keyword + subscription)
+                continue
+            try:
+                namespace, pattern = value.split(":", 1)
+            except ValueError:
+                errors.append(invalid_subscription_msg + subscription)
+                continue
+            if keyword == NAMERE:
+                try:
+                    pattern = re.compile(pattern, re.U)
+                except re.error:
+                    errors.append(invalid_re_expression + subscription)
+                    continue
+        if errors:
+            return self.note_error(element, state, message=', '.join(errors))
+        return True
+
+
 class UserSettingsSubscriptionsForm(Form):
     name = 'usersettings_subscriptions'
     subscriptions = Subscriptions
     submit_label = L_('Save')
 
+    validators = [ValidSubscriptions()]
+
 
 @frontend.route('/+usersettings', methods=['GET', 'POST'])
 def usersettings():
@@ -1792,7 +1831,9 @@
                                                           "error"))
                         else:
                             flaskg.user.save()
-
+            else:
+                # validation failed
+                response['flash'].append((_("Nothing saved."), "error"))
             if not response['flash']:
                 # if no flash message was added until here, we add a generic success message
                 response['flash'].append((_("Your changes have been saved."), "info"))
--- a/MoinMoin/static/css/common.css	Sat Feb 21 08:15:07 2015 -0700
+++ b/MoinMoin/static/css/common.css	Sat Feb 21 11:09:16 2015 -0700
@@ -365,4 +365,5 @@
 
 #options dd { width: 10%; }
 #options dt { width: 60%; max-width: 40em; }
-#subscriptions textarea { width: 80%; max-width: 40em; }
+#subscriptions textarea { width: 80%; max-width: 40em; margin-bottom: 1em; }
+#subscriptions div.tip { width: 80%; max-width: 40em; margin: auto; }
--- a/MoinMoin/templates/usersettings_forms.html	Sat Feb 21 08:15:07 2015 -0700
+++ b/MoinMoin/templates/usersettings_forms.html	Sat Feb 21 11:09:16 2015 -0700
@@ -77,11 +77,26 @@
 {% endmacro %}
 
 {% macro subscriptions(form) %}
+    {% set myitemname =  _('MyItemName')  %}
+    {% set mytagname =  _('MyTagName')  %}
+    {% set mynamespace =  _('MyNameSpace')  %}
+    {% set my =  _('My')  %}
+    {% set itemid =  _('item id')  %}
     {{ gen.form.open(form, method="post", action=url_for('frontend.usersettings')) }}
         {{ forms.render_errors(form) }}
         <dl>
             {{ forms.render(form['subscriptions']) }}
         </dl>
+        <div class="tip">
+            {{ _('Formats for entering subscriptions:') }}
+            <ul>
+                <li>name::{{ myitemname }} or name:{{ mynamespace }}:{{ myitemname }}</li>
+                <li>nameprefix::{{ my }}</li>
+                <li>tags::{{ mytagname }}</li>
+                <li>namere::.*</li>
+                <li>itemid:&lt;{{ itemid }}&gt; {{ _('is created/removed by clicking a Subscribe/Unsubscribe link') }}</li>
+            </ul>
+        </div>
         {{ forms.render_submit(form, 'part', 'subscriptions') }}
     {{ gen.form.close() }}
 {% endmacro %}
--- a/MoinMoin/util/subscriptions.py	Sat Feb 21 08:15:07 2015 -0700
+++ b/MoinMoin/util/subscriptions.py	Sat Feb 21 11:09:16 2015 -0700
@@ -75,9 +75,17 @@
     item_namespace = meta.get(NAMESPACE)
     matched_subscriptions = []
     for subscription in subscription_patterns:
-        keyword, value = subscription.split(":", 1)
+        try:
+            keyword, value = subscription.split(":", 1)
+        except ValueError:
+            logging.exception("User {0} has invalid subscription entry: {1}".format(flaskg.user.name[0], subscription))
+            continue
         if keyword in (NAMEPREFIX, NAMERE, ) and item_namespace is not None and item_names:
-            namespace, pattern = value.split(":", 1)
+            try:
+                namespace, pattern = value.split(":", 1)
+            except ValueError:
+                logging.exception("User {0} has invalid subscription entry: {1}".format(flaskg.user.name[0], subscription))
+                continue
             if item_namespace == namespace:
                 if keyword == NAMEPREFIX:
                     if any(name.startswith(pattern) for name in item_names):