Commits

Thomas Waldmann committed a8bf7c1 Merge

merged

Comments (0)

Files changed (10)

MoinMoin/apps/frontend/views.py

 @frontend.route('/<itemname:item_name>', defaults=dict(rev=CURRENT), methods=['GET', 'POST'])
 @frontend.route('/+show/+<rev>/<itemname:item_name>', methods=['GET'])
 def show_item(item_name, rev):
+    fqname = split_fqname(item_name)
     item_displayed.send(app._get_current_object(),
-                        item_name=item_name)
-    fqname = split_fqname(item_name)
+                        fqname=fqname)
     if not fqname.value and fqname.field == NAME_EXACT:
         fqname = fqname.get_root_fqname()
         return redirect(url_for_item(fqname))
 @frontend.route('/+content/<itemname:item_name>', defaults=dict(rev=CURRENT))
 def content_item(item_name, rev):
     """ same as show_item, but we only show the content """
-    item_displayed.send(app, item_name=item_name)
+    fqname = split_fqname(item_name)
+    item_displayed.send(app, fqname=fqname)
     try:
         item = Item.create(item_name, rev_id=rev)
     except AccessDenied:
         item = Item.create(item_name)
         revid, size = item.modify({}, data, contenttype_guessed=contenttype)
         item_modified.send(app._get_current_object(),
-                           item_name=item_name, action=ACTION_SAVE)
+                           fqname=item.fqname, action=ACTION_SAVE)
         return jsonify(name=subitem_name,
                        size=size,
                        url=url_for('.show_item', item_name=item_name, rev=revid),

MoinMoin/forms.py

     @classmethod
     def _get_choice_specs(cls):
         revs = flaskg.storage.search(cls._query, **cls._query_args)
-        choices = [(rev.meta[ITEMID], rev.meta[NAME][0]) for rev in revs]
+        label_getter = cls.properties['label_getter']
+        choices = [(rev.meta[ITEMID], label_getter(rev)) for rev in revs]
         if cls.optional:
             choices.append((u'', cls.properties['empty_label']))
         return choices

MoinMoin/items/__init__.py

 
     def destroy(self, comment=u'', destroy_item=False):
         action = DESTROY_ALL if destroy_item else DESTROY_REV
-        item_modified.send(app, item_name=self.name, action=action, meta=self.meta,
+        item_modified.send(app, fqname=self.fqname, action=action, meta=self.meta,
                            content=self.rev.data, comment=comment)
         # called from destroy UI/POST
         if destroy_item:
                                              contenttype_guessed=contenttype_guessed,
                                              return_rev=True,
                                              )
-        # XXX TODO name might be None here (we have a failing unit test)
-        # maybe this needs to be changed so a fqname is used instead of
-        # a simple name
-        assert name is not None  # fail early
-        item_modified.send(app, item_name=name, action=action)
+        item_modified.send(app, fqname=self.fqname, action=action)
         return newrev.revid, newrev.meta[SIZE]
 
     @property

MoinMoin/items/ticket.py

                             Reference, BackReference, SelectSubmit)
 from MoinMoin.storage.middleware.protecting import AccessDenied
 from MoinMoin.constants.keys import (ITEMTYPE, CONTENTTYPE, ITEMID, CURRENT,
-                                     SUPERSEDED_BY, SUBSCRIPTIONS, DEPENDS_ON)
+                                     SUPERSEDED_BY, SUBSCRIPTIONS, DEPENDS_ON, NAME, SUMMARY)
 from MoinMoin.constants.contenttypes import CONTENTTYPE_USER
 from MoinMoin.items import Item, Contentful, register, BaseModifyForm
 from MoinMoin.items.content import NonExistentContent
 TICKET_QUERY = Term(ITEMTYPE, ITEMTYPE_TICKET)
 
 Rating = SmallNatural.using(optional=True).with_properties(lower=1, upper=5)
-OptionalTicketReference = Reference.to(TICKET_QUERY).using(optional=True)
-OptionalUserReference = Reference.to(USER_QUERY).using(optional=True).with_properties(empty_label='(Nobody)')
+
+
+def get_itemid_short_summary(rev):
+    return '{itemid} ({summary})'.format(itemid=rev.meta[ITEMID][:4], summary=rev.meta[SUMMARY][:50])
+
+
+def get_name(rev):
+    return rev.meta[NAME][0]
+
+
+OptionalTicketReference = Reference.to(
+    TICKET_QUERY,
+).using(
+    optional=True,
+).with_properties(
+    label_getter=get_itemid_short_summary,
+)
+OptionalUserReference = Reference.to(
+    USER_QUERY,
+).using(
+    optional=True,
+).with_properties(
+    empty_label='(Nobody)',
+    label_getter=get_name,
+)
 
 
 class TicketMetaForm(Form):

MoinMoin/signalling/log.py

 
 
 @item_displayed.connect_via(ANY)
-def log_item_displayed(app, item_name):
+def log_item_displayed(app, fqname):
     wiki_name = app.cfg.interwikiname
-    logging.info(u"item {0}:{1} displayed".format(wiki_name, item_name))
+    logging.info(u"item {0}:{1} displayed".format(wiki_name, unicode(fqname)))
 
 
 @item_modified.connect_via(ANY)
-def log_item_modified(app, item_name, **kwargs):
+def log_item_modified(app, fqname, **kwargs):
     wiki_name = app.cfg.interwikiname
-    logging.info(u"item {0}:{1} modified".format(wiki_name, item_name))
+    logging.info(u"item {0}:{1} modified".format(wiki_name, unicode(fqname)))
 
 
 @got_request_exception.connect_via(ANY)

MoinMoin/themes/basic/static/css/basic.css

 .moin-thead {
   background-color: silver;
 }
+.moin-viewoptions, .moin-itemactions, .moin-useractions {
+  float: right;
+}
+.list-group-item {
+  padding: 5px 15px;
+}
+@media (min-width: 768px) {
+  .container {
+    width: auto;
+  }
+}
+@media (min-width: 992px) {
+  .container {
+    width: auto;
+  }
+}
+@media (min-width: 1200px) {
+  .container {
+    width: auto;
+  }
+}

MoinMoin/themes/basic/static/custom-less/basic.less

 .moin-thead{
     background-color: silver;
 }
+// set min-width to prevent search box from floating off window to right
+@media (min-width: 768px) {
+  .container {
+    width: auto;
+  }
+}
+@media (min-width: 992px) {
+  .container {
+    width: auto;
+  }
+}
+@media (min-width: 1200px) {
+  .container {
+    width: auto;
+  }
+}

MoinMoin/util/_tests/test_notifications.py

 from MoinMoin.items import Item
 from MoinMoin.util.diff_datastruct import diff as dict_diff
 from MoinMoin.util.notifications import Notification, get_item_last_revisions, DESTROY_REV, DESTROY_ALL
+from MoinMoin.util.interwiki import split_fqname
 
 
 class TestNotifications(object):
     def setup_method(self, method):
         self.imw = flaskg.unprotected_storage
         self.item_name = u"foo"
+        self.fqname = split_fqname(self.item_name)
 
     def test_get_last_item_revisions(self):
-        assert get_item_last_revisions(app, self.item_name) == []
+        assert get_item_last_revisions(app, self.fqname) == []
         item = self.imw[self.item_name]
         rev1 = item.store_revision(dict(name=[self.item_name, ]),
                                    StringIO(u'x'), trusted=True, return_rev=True)
-        assert get_item_last_revisions(app, self.item_name) == [rev1]
+        assert get_item_last_revisions(app, self.fqname) == [rev1]
         rev2 = item.store_revision(dict(name=[self.item_name, ]),
                                    StringIO(u'xx'), trusted=True, return_rev=True)
-        assert get_item_last_revisions(app, self.item_name) == [rev2, rev1]
+        assert get_item_last_revisions(app, self.fqname) == [rev2, rev1]
         rev3 = item.store_revision(dict(name=[self.item_name, ]),
                                    StringIO(u'xxx'), trusted=True, return_rev=True)
-        assert get_item_last_revisions(app, self.item_name) == [rev3, rev2]
+        assert get_item_last_revisions(app, self.fqname) == [rev3, rev2]
 
     def test_get_content_diff(self):
         item = self.imw[self.item_name]
         rev1 = item.store_revision(dict(name=[self.item_name, ], contenttype='text/plain'),
                                    StringIO(u'x'), trusted=True, return_rev=True)
-        notification = Notification(app, self.item_name, [rev1], action=ACTION_SAVE)
+        notification = Notification(app, self.fqname, [rev1], action=ACTION_SAVE)
         assert notification.get_content_diff() == ["+ x"]
         rev1.data.seek(0, 0)
 
         rev2 = item.store_revision(dict(name=[self.item_name, ], contenttype='text/plain'),
                                    StringIO(u'xx'), trusted=True, return_rev=True)
-        notification = Notification(app, self.item_name, [rev2, rev1], action=ACTION_SAVE)
+        notification = Notification(app, self.fqname, [rev2, rev1], action=ACTION_SAVE)
         assert notification.get_content_diff() == ['- x', '+ xx']
         rev2.data.seek(0, 0)
 
-        notification = Notification(app, self.item_name, [rev2, rev1], action=ACTION_TRASH)
+        notification = Notification(app, self.fqname, [rev2, rev1], action=ACTION_TRASH)
         assert notification.get_content_diff() == ['- xx']
         rev2.data.seek(0, 0)
 
         item = Item.create(self.item_name)
-        notification = Notification(app, self.item_name, [], content=item.rev.data,
+        notification = Notification(app, self.fqname, [], content=item.rev.data,
                                     meta=rev2.meta, action=DESTROY_REV)
         assert notification.get_content_diff() == ['- xx']
         rev2.data.seek(0, 0)
 
         item = Item.create(self.item_name)
-        notification = Notification(app, self.item_name, [], content=item.rev.data,
+        notification = Notification(app, self.fqname, [], content=item.rev.data,
                                     meta=rev2.meta, action=DESTROY_ALL)
         assert notification.get_content_diff() == ['- xx']
 
         item = self.imw[self.item_name]
         rev1 = item.store_revision(dict(name=[self.item_name, ]), StringIO(u'x'),
                                    trusted=True, return_rev=True)
-        notification = Notification(app, self.item_name, [rev1], action=ACTION_SAVE)
+        notification = Notification(app, self.fqname, [rev1], action=ACTION_SAVE)
         assert notification.get_meta_diff() == dict_diff(dict(), rev1.meta._meta)
 
         rev2 = item.store_revision(dict(name=[self.item_name, ]), StringIO(u'xx'),
                                    trusted=True, return_rev=True)
-        notification = Notification(app, self.item_name, [rev2, rev1], action=ACTION_SAVE)
+        notification = Notification(app, self.fqname, [rev2, rev1], action=ACTION_SAVE)
         assert notification.get_meta_diff() == dict_diff(rev1.meta._meta, rev2.meta._meta)
 
         actions = [DESTROY_REV, DESTROY_ALL, ACTION_TRASH, ]
         for action in actions:
-            notification = Notification(app, self.item_name, [rev2, rev1], meta=rev2.meta, action=action)
+            notification = Notification(app, self.fqname, [rev2, rev1], meta=rev2.meta, action=action)
             assert notification.get_meta_diff() == dict_diff(rev2.meta._meta, dict())
 
     def test_generate_diff_url(self):
         domain = "http://test.com"
-        notification = Notification(app, self.item_name, [], action=DESTROY_REV)
+        notification = Notification(app, self.fqname, [], action=DESTROY_REV)
         assert notification.generate_diff_url(domain) == u""
 
         item = self.imw[self.item_name]

MoinMoin/util/notifications.py

 from MoinMoin.signalling.signals import item_modified
 from MoinMoin.util.subscriptions import get_subscribers
 from MoinMoin.util.diff_datastruct import make_text_diff, diff as dict_diff
+from MoinMoin.util.interwiki import url_for_item
 
 from MoinMoin import log
 logging = log.getLogger(__name__)
     """
     _ = lambda x: x
     messages = {
-        ACTION_CREATE: _("The '%(item_name)s' item on '%(wiki_name)s' has been created by %(user_name)s:"),
-        ACTION_MODIFY: _("The '%(item_name)s' item on '%(wiki_name)s' has been modified by %(user_name)s:"),
-        ACTION_RENAME: _("The '%(item_name)s' item on '%(wiki_name)s' has been renamed by %(user_name)s:"),
-        ACTION_COPY: _("The '%(item_name)s' item on '%(wiki_name)s' has been copied by %(user_name)s:"),
-        ACTION_REVERT: _("The '%(item_name)s' item on '%(wiki_name)s' has been reverted by %(user_name)s:"),
-        ACTION_TRASH: _("The '%(item_name)s' item on '%(wiki_name)s' has been deleted by %(user_name)s:"),
-        DESTROY_REV: _("The '%(item_name)s' item on '%(wiki_name)s' has one revision destroyed by %(user_name)s:"),
-        DESTROY_ALL: _("The '%(item_name)s' item on '%(wiki_name)s' has been destroyed by %(user_name)s:"),
+        ACTION_CREATE: _("The '%(fqname)s' ('%(item_url)s') item on '%(wiki_name)s' has been created by %(user_name)s:"),
+        ACTION_MODIFY: _("The '%(fqname)s' ('%(item_url)s') item on '%(wiki_name)s' has been modified by %(user_name)s:"),
+        ACTION_RENAME: _("The '%(fqname)s' ('%(item_url)s') item on '%(wiki_name)s' has been renamed by %(user_name)s:"),
+        ACTION_COPY: _("The '%(fqname)s' ('%(item_url)s') item on '%(wiki_name)s' has been copied by %(user_name)s:"),
+        ACTION_REVERT: _("The '%(fqname)s' ('%(item_url)s') item on '%(wiki_name)s' has been reverted by %(user_name)s:"),
+        ACTION_TRASH: _("The '%(fqname)s' ('%(item_url)s') item on '%(wiki_name)s' has been deleted by %(user_name)s:"),
+        DESTROY_REV: _("The '%(fqname)s' ('%(item_url)s') item on '%(wiki_name)s' has one revision destroyed by %(user_name)s:"),
+        DESTROY_ALL: _("The '%(fqname)s' ('%(item_url)s') item on '%(wiki_name)s' has been destroyed by %(user_name)s:"),
     }
     return messages
 
     txt_template = "mail/notification.txt"
     html_template = "mail/notification_main.html"
 
-    def __init__(self, app, item_name, revs, **kwargs):
+    def __init__(self, app, fqname, revs, **kwargs):
         self.app = app
-        self.item_name = item_name
+        self.fqname = fqname
         self.revs = revs
         self.action = kwargs.get('action', None)
         self.content = kwargs.get('content', None)
         if self.action == ACTION_TRASH:
             self.meta = self.revs[0].meta
 
-        kw = dict(item_name=self.item_name, wiki_name=self.wiki_name, user_name=flaskg.user.name0)
+        kw = dict(fqname=unicode(fqname), wiki_name=self.wiki_name, user_name=flaskg.user.name0, item_url=url_for_item(self.fqname))
         self.notification_sentence = L_(MESSAGES[self.action], **kw)
 
     def get_content_diff(self):
         else:
             revid1 = self.revs[1].revid
             revid2 = self.revs[0].revid
-        diff_rel_url = url_for('frontend.diff', item_name=self.item_name, rev1=revid1, rev2=revid2)
+        diff_rel_url = url_for('frontend.diff', item_name=self.fqname, rev1=revid1, rev2=revid2)
         return urljoin(domain, diff_rel_url)
 
     def render_templates(self, content_diff, meta_diff):
         meta_diff_txt = list(make_text_diff(meta_diff))
         domain = self.app.cfg.interwiki_map[self.app.cfg.interwikiname]
         unsubscribe_url = urljoin(domain, url_for('frontend.subscribe_item',
-                                                  item_name=self.item_name))
+                                                  item_name=self.fqname))
         diff_url = self.generate_diff_url(domain)
-        item_url = urljoin(domain, url_for('frontend.show_item', item_name=self.item_name))
+        item_url = urljoin(domain, url_for('frontend.show_item', item_name=self.fqname))
         if self.comment is not None:
             comment = self.meta["comment"]
         else:
         return txt_template, html_template
 
 
-def get_item_last_revisions(app, item_name):
+def get_item_last_revisions(app, fqname):
     """ Get 2 or less most recent item revisions from the index
 
     :param app: local proxy app
-    :param item_name: the name of the item
+    :param fqname: the fqname of the item
     :return: a list of revisions
     """
-    terms = [Term(WIKINAME, app.cfg.interwikiname), Term(NAME_EXACT, item_name), ]
+    terms = [Term(WIKINAME, app.cfg.interwikiname), Term(fqname.field, fqname.value), ]
     query = And(terms)
     return list(
         flaskg.storage.search(query, idx_name=ALL_REVS, sortedby=[MTIME],
 
 
 @item_modified.connect_via(ANY)
-def send_notifications(app, item_name, **kwargs):
+def send_notifications(app, fqname, **kwargs):
     """ Send mail notifications to subscribers on item change
 
     :param app: local proxy app
-    :param item_name: name of the changed item
+    :param fqname: fqname of the changed item
     :param kwargs: key/value pairs that contain extra information about the item
                    required in order to create a notification
     """
     action = kwargs.get('action')
-    revs = get_item_last_revisions(app, item_name) if action not in [
+    revs = get_item_last_revisions(app, fqname) if action not in [
         DESTROY_REV, DESTROY_ALL, ] else []
-    notification = Notification(app, item_name, revs, **kwargs)
+    notification = Notification(app, fqname, revs, **kwargs)
     content_diff = notification.get_content_diff()
     meta_diff = notification.get_meta_diff()
 
     for locale in subscribers_locale:
         with force_locale(locale):
             txt_msg, html_msg = notification.render_templates(content_diff, meta_diff)
-            subject = L_('[%(moin_name)s] Update of "%(item_name)s" by %(user_name)s',
-                         moin_name=app.cfg.interwikiname, item_name=item_name, user_name=u.name0)
+            subject = L_('[%(moin_name)s] Update of "%(fqname)s" by %(user_name)s',
+                         moin_name=app.cfg.interwikiname, fqname=unicode(fqname), user_name=u.name0)
             subscribers_emails = [subscriber.email for subscriber in subscribers
                                   if subscriber.locale == locale]
             sendmail(subject, txt_msg, to=subscribers_emails, html=html_msg)

MoinMoin/util/subscriptions.py

                                      NAMESPACE, SUBSCRIPTION_IDS, SUBSCRIPTION_PATTERNS, TAGS)
 from MoinMoin import log
 logging = log.getLogger(__name__)
+from MoinMoin.util.interwiki import CompositeName
 
 
 Subscriber = namedtuple('Subscriber', [ITEMID, NAME, EMAIL, LOCALE])
     itemid = meta.get(ITEMID)
     name = meta.get(NAME)
     namespace = meta.get(NAMESPACE)
+    fqname = CompositeName(namespace, ITEMID, itemid)
     tags = meta.get(TAGS)
     terms = []
     if itemid is not None:
             if email:
                 from MoinMoin.user import User
                 u = User(uid=user.get(ITEMID))
-                if u.may.read(name):
+                if u.may.read(fqname):
                     locale = user.get(LOCALE, DEFAULT_LOCALE)
                     subscribers.add(Subscriber(user[ITEMID], user[NAME][0], email, locale))
     return subscribers