Commits

ana-balica committed 2ffec8c

user subscription functionality

Comments (0)

Files changed (2)

MoinMoin/_tests/test_user.py

 from flask import g as flaskg
 
 from MoinMoin import user
+from MoinMoin.items import Item
+from MoinMoin.constants.keys import (ITEMID, NAME, NAMEPREFIX, NAMERE, NAMESPACE, TAGS)
 
 
 class TestSimple(object):
 
     # Subscriptions ---------------------------------------------------
 
-    def testSubscriptionSubscribedPage(self):
-        """ user: tests is_subscribed_to  """
-        pagename = u'HelpMiscellaneous'
-        name = u'__Jürgen Herman__'
+    def test_subscriptions(self):
+        pagename = u"Foo:foo 123"
+        tagname = u"xxx"
+        regexp = r"\d+"
+        item = Item.create(pagename)
+        item._save({NAMESPACE: u"", TAGS: [tagname]})
+        item = Item.create(pagename)
+        meta = item.meta
+
+        name = u'bar'
         password = name
-        self.createUser(name, password)
-        # Login - this should replace the old password in the user file
-        theUser = user.User(name=name, password=password)
-        theUser.subscribe(pagename)
-        assert theUser.is_subscribed_to([pagename])  # list(!) of pages to check
+        email = "bar@example.org"
+        user.create_user(name, password, email)
+        the_user = user.User(name=name, password=password)
+        assert not the_user.is_subscribed_to(item)
+        the_user.subscribe(NAME, u"SomeOtherPageName", u"")
+        result = the_user.unsubscribe(NAME, u"OneMorePageName", u"")
+        assert result is False
 
-    def testSubscriptionSubPage(self):
-        """ user: tests is_subscribed_to on a subpage """
-        pagename = u'HelpMiscellaneous'
-        testPagename = u'HelpMiscellaneous/FrequentlyAskedQuestions'
-        name = u'__Jürgen Herman__'
-        password = name
-        self.createUser(name, password)
-        # Login - this should replace the old password in the user file
-        theUser = user.User(name=name, password=password)
-        theUser.subscribe(pagename)
-        assert not theUser.is_subscribed_to([testPagename])  # list(!) of pages to check
+        subscriptions = [(ITEMID, meta[ITEMID], None),
+                         (NAME, pagename, meta[NAMESPACE]),
+                         (TAGS, tagname, meta[NAMESPACE]),
+                         (NAMEPREFIX, pagename[:4], meta[NAMESPACE]),
+                         (NAMERE, regexp, meta[NAMESPACE])]
+        for subscription in subscriptions:
+            keyword, value, namespace = subscription
+            the_user.subscribe(keyword, value, namespace)
+            assert the_user.is_subscribed_to(item)
+            the_user.unsubscribe(keyword, value, namespace, item)
+            assert not the_user.is_subscribed_to(item)
 
     # Bookmarks -------------------------------------------------------
 
 from MoinMoin.mail import sendmail
 from MoinMoin.util.interwiki import getInterwikiHome, getInterwikiName, is_local_wiki
 from MoinMoin.util.crypto import generate_token, valid_token, make_uuid
+from MoinMoin.util.subscriptions import get_matched_subscription_patterns
 from MoinMoin.storage.error import NoSuchItemError, ItemAlreadyExistsError, NoSuchRevisionError
 
 
     return (name == normalized) and not wikiutil.isGroupItem(name)
 
 
+def assemble_subscription(keyword, value, namespace=None):
+    """ Create a valid subscription string
+
+    :param keyword: the keyword (itemid, name, tags, nameprefix, namere) by which
+                    the type of the subscription is determined
+    :param value: the subscription value (itemid, name, tag, regexp or nameprefix value)
+    :param namespace: the namespace of the subscription
+    :return: subscription string
+    """
+    if keyword == ITEMID:
+        subscription = "{0}:{1}".format(ITEMID, value)
+    elif keyword in [NAME, TAGS, NAMERE, NAMEPREFIX, ]:
+        if namespace is not None:
+            subscription = "{0}:{1}:{2}".format(keyword, namespace, value)
+        else:
+            raise ValueError("The subscription by {0} keyword requires a namespace".format(keyword))
+    else:
+        raise ValueError("Invalid keyword string: {0}".format(keyword))
+    return subscription
+
+
 class UserProfile(object):
     """ A User Profile"""
 
 
     # Subscribed Items -------------------------------------------------------
 
-    def is_subscribed_to(self, pagelist):
-        """ Check if user subscription matches any page in pagelist.
+    def is_subscribed_to(self, item):
+        """ Check if user is subscribed to the following item
 
-        The subscription contains interwiki page names. e.g 'WikiName:Page_Name'
-
-        TODO: check if it's fast enough when getting called for many
-              users from page.getSubscribersList()
-
-        :param pagelist: list of pages to check for subscription
+        :param item: Item object
         :rtype: bool
-        :returns: if user is subscribed any page in pagelist
+        :returns: if user is subscribed to the item
         """
         if not self.valid:
             return False
 
-        # Create a new list with interwiki names.
-        pages = [getInterwikiName(pagename) for pagename in pagelist]
-        # Create text for regular expression search
-        text = '\n'.join(pages)
+        meta = item.meta
+        try:
+            item_namespace = meta[NAMESPACE]
+        except KeyError:
+            return False
+        subscriptions = {"{0}:{1}".format(ITEMID, meta[ITEMID])}
+        subscriptions.update("{0}:{1}:{2}".format(NAME, item_namespace, name)
+                             for name in meta[NAME])
+        subscriptions.update("{0}:{1}:{2}".format(TAGS, item_namespace, tag)
+                             for tag in meta[TAGS])
+        if subscriptions & set(self.subscriptions):
+            return True
 
-        for pattern in self.subscribed_items:
-            # Try simple match first
-            if pattern in pages:
-                return True
-            # Try regular expression search, skipping bad patterns
-            try:
-                pattern = re.compile(r'^{0}$'.format(pattern), re.M)
-            except re.error:
-                continue
-            if pattern.search(text):
-                return True
-
-        return False
-
-    def subscribe(self, pagename):
-        """ Subscribe to a wiki page.
-
-        Page names are saved as interwiki names.
-
-        :param pagename: name of the page to subscribe
-        :type pagename: unicode
-        :rtype: bool
-        :returns: if page was subscribed
-        """
-        pagename = getInterwikiName(pagename)
-        subscribed_items = self.subscribed_items
-        if pagename not in subscribed_items:
-            subscribed_items.append(pagename)
-            self.save(force=True)
-            # XXX SubscribedToPageEvent
+        if get_matched_subscription_patterns(item, self.subscriptions):
             return True
         return False
 
-    def unsubscribe(self, pagename):
-        """ Unsubscribe a wiki page.
+    def subscribe(self, keyword, value, namespace=None):
+        """ Subscribe to a wiki page.
 
-        Try to unsubscribe by removing interwiki name from the subscription
-        list.
+        The user can subscribe in 5 different ways:
+        * by itemid - ITEMID:<itemid value>
+        * by item name - NAME:<namespace>:<name value>
+        * by a tagname - TAGS:<namespace>:<tag value>
+        * by a prefix name - NAMEPREFIX:<namespace>:<name prefix>
+        * by a regular expression - NAMERE:<namespace>:<name regexp>
 
-        Its possible that the user will be subscribed to a page by more
-        than one pattern. It can be both interwiki name and a regex pattern that
-        both match the page. Therefore, we must check if the user is
-        still subscribed to the page after we try to remove names from the list.
+:       :param keyword: the keyword (itemid, name, tags, nameprefix, namere) by which
+                        the type of the subscription is determined
+        :param value: the subscription value (itemid, name, tag, regexp or nameprefix value)
+        :param namespace: the namespace of the subscription; itemid keyword doesn't
+                            require a namespace
+        :rtype: bool
+        :returns: if user was subscribed
+        """
+        subscription = assemble_subscription(keyword, value, namespace)
+        subscriptions = self.subscriptions
+        if subscription not in subscriptions:
+            subscriptions.append(subscription)
+            self.save(force=True)
+            return True
+        return False
 
-        :param pagename: name of the page to subscribe
-        :type pagename: unicode
+    def unsubscribe(self, keyword, value, namespace=None, item=None):
+        """ Unsubscribe from a wiki page.
+
+        Same as for subscribing, user can also unsubscribe in 5 ways.
+        The unsubscribe action doesn't guarantee that user will not receive any
+        notification for this item, since user can be subscribed by some other
+        patterns that match current item.
+
+        :param keyword: the keyword (itemid, name, tags, nameprefix, namere) by which
+                        the type of the subscription is determined
+        :param value: the subscription value (itemid, name, tag, regexp or nameprefix value)
+        :param namespace: the namespace of the subscription; itemid keyword doesn't
+                            require a namespace
+        :param item: Item object to check if the user is still subscribed
         :rtype: bool
-        :returns: if unsubscribe was successful. If the user has a
-            regular expression that matches, unsubscribe will always fail.
+        :returns: if user was unsubscribed
         """
-        interWikiName = getInterwikiName(pagename)
-        subscribed_items = self.profile[SUBSCRIBED_ITEMS]
-        if interWikiName and interWikiName in subscribed_items:
-            subscribed_items.remove(interWikiName)
+        subscription = assemble_subscription(keyword, value, namespace)
+        subscriptions = self.subscriptions
+        if subscription in subscriptions:
+            subscriptions.remove(subscription)
             self.save(force=True)
-        return not self.is_subscribed_to([pagename])
+            return not self.is_subscribed_to(item) if item else True
+        return False
 
     # Quicklinks -------------------------------------------------------------