← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wallyworld/launchpad/irc-nickname-formatter into lp:launchpad

 

Ian Booth has proposed merging lp:~wallyworld/launchpad/irc-nickname-formatter into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #787650 in Launchpad itself: "Add an IRC nick formatter"
  https://bugs.launchpad.net/launchpad/+bug/787650

For more details, see:
https://code.launchpad.net/~wallyworld/launchpad/irc-nickname-formatter/+merge/62237

Add a tales irc nick formatter

== Implementation ==

Pretty straightforward. Add a new IRCNicknameFormatterAPI class. It supports the following traversals:
/fmt:displayname
/fmt:formatted_displayname

The displayname returns straight text "fred on irc.freenode.net" and is what will be required for rendering on the picker later.

The formatted_displayname returns html markup, and is equivalent to what is rendered on a user's home page now, except that it says "... on ..." instead of "... on network...". The person contact details portlet has been updated to use the new formatter.

== Tests ==

Add new TestIRCNicknameFormatterAPI test case to test_tales.py

== Lint ==

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/app/browser/configure.zcml
  lib/lp/app/browser/tales.py
  lib/lp/app/tests/test_tales.py
  lib/lp/registry/templates/person-portlet-contact-details.pt

-- 
https://code.launchpad.net/~wallyworld/launchpad/irc-nickname-formatter/+merge/62237
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wallyworld/launchpad/irc-nickname-formatter into lp:launchpad.
=== modified file 'lib/lp/app/browser/configure.zcml'
--- lib/lp/app/browser/configure.zcml	2011-03-09 20:39:15 +0000
+++ lib/lp/app/browser/configure.zcml	2011-05-25 05:15:57 +0000
@@ -273,6 +273,13 @@
       />
 
   <adapter
+      for="lp.registry.interfaces.irc.IIrcID"
+      provides="zope.traversing.interfaces.IPathAdapter"
+      factory="lp.app.browser.tales.IRCNicknameFormatterAPI"
+      name="fmt"
+      />
+
+  <adapter
       for="datetime.timedelta"
       provides="zope.traversing.interfaces.IPathAdapter"
       factory="lp.app.browser.tales.DurationFormatterAPI"

=== modified file 'lib/lp/app/browser/tales.py'
--- lib/lp/app/browser/tales.py	2011-03-27 22:53:50 +0000
+++ lib/lp/app/browser/tales.py	2011-05-25 05:15:57 +0000
@@ -4,6 +4,7 @@
 # pylint: disable-msg=C0103,W0613,R0911,F0401
 #
 """Implementation of the lp: htmlform: fmt: namespaces in TALES."""
+from textwrap import dedent
 
 __metaclass__ = type
 
@@ -497,7 +498,7 @@
         # We are interested in the default value (param2).
         result = ''
         for nm in self.allowed_names:
-            if name.startswith(nm+":"):
+            if name.startswith(nm + ":"):
                 name_parts = name.split(":")
                 name = name_parts[0]
                 if len(name_parts) > 2:
@@ -2051,7 +2052,7 @@
         delta = abs(delta)
         days = delta.days
         hours = delta.seconds / 3600
-        minutes = (delta.seconds - (3600*hours)) / 60
+        minutes = (delta.seconds - (3600 * hours)) / 60
         seconds = delta.seconds % 60
         result = ''
         if future:
@@ -2129,7 +2130,7 @@
         parts = []
         minutes, seconds = divmod(self._duration.seconds, 60)
         hours, minutes = divmod(minutes, 60)
-        seconds = seconds + (float(self._duration.microseconds) / 10**6)
+        seconds = seconds + (float(self._duration.microseconds) / 10 ** 6)
         if self._duration.days > 0:
             if self._duration.days == 1:
                 parts.append('%d day' % self._duration.days)
@@ -2175,7 +2176,7 @@
         # including the decimal part.
         seconds = self._duration.days * (3600 * 24)
         seconds += self._duration.seconds
-        seconds += (float(self._duration.microseconds) / 10**6)
+        seconds += (float(self._duration.microseconds) / 10 ** 6)
 
         # First we'll try to calculate an approximate number of
         # seconds up to a minute. We'll start by defining a sorted
@@ -2655,3 +2656,24 @@
             return getattr(self, name)(furtherPath)
         except AttributeError:
             raise TraversalError(name)
+
+
+class IRCNicknameFormatterAPI(ObjectFormatterAPI):
+    """Adapter from IrcID objects to a formatted string."""
+
+    implements(ITraversable)
+
+    traversable_names = {
+        'displayname': 'displayname',
+        'formatted_displayname': 'formatted_displayname',
+    }
+
+    def displayname(self, view_name=None):
+        return "%s on %s" % (self._context.nickname, self._context.network)
+
+    def formatted_displayname(self, view_name=None):
+        return dedent("""\
+            <strong>%s</strong>
+            <span class="discreet"> on </span>
+            <strong>%s</strong>
+        """ % (self._context.nickname, self._context.network))

=== modified file 'lib/lp/app/tests/test_tales.py'
--- lib/lp/app/tests/test_tales.py	2011-04-21 01:44:06 +0000
+++ lib/lp/app/tests/test_tales.py	2011-05-25 05:15:57 +0000
@@ -5,7 +5,10 @@
 
 from lxml import html
 
-from zope.component import getAdapter
+from zope.component import (
+    getAdapter,
+    getUtility
+    )
 from zope.traversing.interfaces import (
     IPathAdapter,
     TraversalError,
@@ -19,6 +22,7 @@
     format_link,
     PersonFormatterAPI,
     )
+from lp.registry.interfaces.irc import IIrcIDSet
 from lp.testing import (
     test_tales,
     TestCaseWithFactory,
@@ -141,7 +145,8 @@
         # The rendering of an object's link ignores any specified default
         # value which would be used in the case where the object were None.
         person = self.factory.makePerson()
-        person_link = test_tales('person/fmt:link::default value', person=person)
+        person_link = test_tales(
+            'person/fmt:link::default value', person=person)
         self.assertEqual(PersonFormatterAPI(person).link(None), person_link)
         person_link = test_tales(
             'person/fmt:link:bugs:default value', person=person)
@@ -264,3 +269,29 @@
         extra = ['1', '2']
         self.assertEqual('', traverse('shorten', extra))
         self.assertEqual(['1'], extra)
+
+
+class TestIRCNicknameFormatterAPI(TestCaseWithFactory):
+    """Tests for IRCNicknameFormatterAPI"""
+
+    layer = DatabaseFunctionalLayer
+
+    def test_nick_displayname(self):
+        person = self.factory.makePerson(name='fred')
+        ircset = getUtility(IIrcIDSet)
+        ircID = ircset.new(person, "irc.canonical.com", "fred")
+        self.assertEqual(
+            'fred on irc.canonical.com',
+            test_tales('nick/fmt:displayname', nick=ircID))
+
+    def test_nick_formatted_displayname(self):
+        person = self.factory.makePerson(name='fred')
+        ircset = getUtility(IIrcIDSet)
+        ircID = ircset.new(person, "irc.canonical.com", "fred")
+        expected_html = test_tales(
+            'nick/fmt:formatted_displayname', nick=ircID)
+        self.assertEquals(
+            u'<strong>fred</strong>\n'
+            '<span class="discreet"> on </span>\n'
+            '<strong>irc.canonical.com</strong>\n',
+            expected_html)

=== modified file 'lib/lp/registry/templates/person-portlet-contact-details.pt'
--- lib/lp/registry/templates/person-portlet-contact-details.pt	2010-12-01 22:05:51 +0000
+++ lib/lp/registry/templates/person-portlet-contact-details.pt	2011-05-25 05:15:57 +0000
@@ -134,9 +134,7 @@
         <a tal:replace="structure overview_menu/editircnicknames/fmt:icon" />
       </dt>
       <dd tal:repeat="ircnick context/ircnicknames">
-        <strong tal:content="ircnick/nickname"/>
-        <span class="discreet">on network</span>
-        <strong tal:content="ircnick/network" />
+        <span tal:replace="structure ircnick/fmt:formatted_displayname"/>
       </dd>
       <dd tal:condition="not: context/ircnicknames">
         No IRC nicknames registered.