[Zope3-checkins] CVS: Products3/z3checkins - bookmark.pt:1.1 README:1.2 TODO:1.5 configure.zcml:1.5 container.pt:1.6 interfaces.py:1.4 message.py:1.7

Marius Gedminas mgedmin@codeworks.lt
Wed, 16 Apr 2003 18:02:18 -0400


Update of /cvs-repository/Products3/z3checkins
In directory cvs.zope.org:/tmp/cvs-serv21560

Modified Files:
	README TODO configure.zcml container.pt interfaces.py 
	message.py 
Added Files:
	bookmark.pt 
Log Message:
z3checkins:
- updated README
- added three new TODO items
- store last 5 visits in a cookie and display them as bookmarks between
  messages


=== Added File Products3/z3checkins/bookmark.pt ===
<hr />


=== Products3/z3checkins/README 1.1.1.1 => 1.2 ===
--- Products3/z3checkins/README:1.1.1.1	Thu Apr  3 03:19:59 2003
+++ Products3/z3checkins/README	Wed Apr 16 18:01:48 2003
@@ -40,7 +40,9 @@
 Replace username:password with the username and password of a Zope 3 user that
 has zope.ManageContent permission in the 'zope3-checkins' folder.
 
-This script may be used to import archives in Maildir format.
+This script may be used to import archives in Maildir format.  Or you can
+use formail or some other tool to split mbox folders available at
+http://mail.zope.org/pipermail/zope3-checkins/
 
 
 Procmail
@@ -56,14 +58,6 @@
 Replace username:password with the username and password of a Zope 3 user that
 has zope.ManageContent permission in the 'zope3-checkins' folder.
 
-
-Mozilla sidebar
----------------
-
-Type the following in Mozilla address bar (as a single line):
-
-  javascript:window.sidebar.addPanel('Zope 3 Checkins',
-             'http://localhost:8080/zope3-checkins/@@checkins.html', '')
 
 RSS feed
 --------


=== Products3/z3checkins/TODO 1.4 => 1.5 ===
--- Products3/z3checkins/TODO:1.4	Wed Apr 16 07:46:06 2003
+++ Products3/z3checkins/TODO	Wed Apr 16 18:01:48 2003
@@ -2,16 +2,21 @@
 
 - Highlight branch tags in message body
 - Detect URLs in checkin comments and make those into <a> elements
-- Remember the last-seen message in a cookie and display a <hr> there.  Perhaps
-  keep a FIFO of, say, last 5 boundaries
 - Detect several checkins with the same checkin comment and group them in some
   way (e.g. display <em>(same checkin)</em> instead of repeating the comment)
 - Include all messages sent to zope3-checkins, not just checkin messages
 
 I'm not sure I'll find time for these:
 
+- Replace newlines with <br/> elements in checkin messages in message_part.pt
+- Shorten descriptions in message_part.pt to first 100 chars (words? sentences?
+  lines?)
 - Add links to the specific versions of the files mentioned in the CVS web
   interface
 - Filtering (by branch, by author, etc)
 - Verify how all this works in other browsers beside Mozilla
 - Is storing all the messages in a single folder scalable enough?
+- Create an interface ICheckinsFolder extending IFolder and write a factory
+  to create folders marked with this interface.  Register z3checkins container
+  views only for ICheckinsFolders.  Perhaps wait until the interfacegheddon
+  before starting this.


=== Products3/z3checkins/configure.zcml 1.4 => 1.5 ===
--- Products3/z3checkins/configure.zcml:1.4	Wed Apr 16 07:32:58 2003
+++ Products3/z3checkins/configure.zcml	Wed Apr 16 18:01:48 2003
@@ -78,6 +78,19 @@
     permission="zope.View" />
 
   <browser:page
+    for=".interfaces.ICheckinBookmark"
+    name="html"
+    template="bookmark.pt"
+    permission="zope.View" />
+
+  <browser:page
+    for=".interfaces.ICheckinBookmark"
+    name="html-sidebar"
+    template="bookmark.pt"
+    usage="sidebar"
+    permission="zope.View" />
+
+  <browser:page
     for=".interfaces.ICheckinMessage"
     name="index.html"
     template="message.pt"


=== Products3/z3checkins/container.pt 1.5 => 1.6 ===
--- Products3/z3checkins/container.pt:1.5	Wed Apr 16 07:46:06 2003
+++ Products3/z3checkins/container.pt	Wed Apr 16 18:01:48 2003
@@ -14,11 +14,14 @@
   a.title { display: block; width: 100%; }
   a:hover { background: #e0e6ff; }
   img.icon { float: right; padding: 0ex; margin: 2px; border: none; }
+  hr { width: 60%; border: none; background: gray; height: 1px;
+       margin-top: 1ex; margin-bottom: 0ex; }
 </style>
 </head>
 <body tal:define="sidebar usage/sidebar;
                   target python: sidebar and '_content' or None;
-                  msg_view python: sidebar and '@@html-sidebar' or '@@html'">
+                  msg_view python: sidebar and '@@html-sidebar' or '@@html';
+                  dummy view/placeBookmark">
 <h1>Zope 3 Checkins</h1>
 
 <div class="toolbar"


=== Products3/z3checkins/interfaces.py 1.3 => 1.4 ===
--- Products3/z3checkins/interfaces.py:1.3	Tue Apr  8 13:35:10 2003
+++ Products3/z3checkins/interfaces.py	Wed Apr 16 18:01:48 2003
@@ -22,6 +22,10 @@
     # Maybe store full headers as well?
 
 
+class ICheckinBookmark(Interface):
+    """Bookmark placed between checkin messages."""
+
+
 class FormatError(Exception):
     """Ill-formed checkin message exception"""
 


=== Products3/z3checkins/message.py 1.6 => 1.7 ===
--- Products3/z3checkins/message.py:1.6	Wed Apr 16 07:32:58 2003
+++ Products3/z3checkins/message.py	Wed Apr 16 18:01:48 2003
@@ -18,9 +18,10 @@
 from zope.app.interfaces.container import IReadContainer
 from zope.proxy.introspection import removeAllProxies
 from zope.proxy.context import getWrapperContainer
+from zope.app.datetimeutils import parseDatetimetz, DateTimeError
 
 from interfaces import ICheckinMessage, ICheckinMessageParser, FormatError
-from interfaces import ICheckinMessageArchive
+from interfaces import ICheckinMessageArchive, ICheckinBookmark
 
 __metaclass__ = type
 
@@ -217,6 +218,10 @@
         return self.messages.index(message)
 
 
+class CheckinBookmark:
+
+    __implements__ = ICheckinBookmark
+
 
 #
 # Browser views
@@ -239,6 +244,41 @@
 class ContainerView:
     """View mixin for locating checkin messages in a container."""
 
+    max_bookmarks = 5
+
+    def bookmarks(self):
+        """Returns a list of bookmarks from a cookie.  Each bookmark is
+        expressed as a datetime object.
+        """
+        bookmarks = []
+        cookie = self.request.get('bookmarks', '')
+        for item in cookie.split():
+            try:
+                bookmarks.append(parseDatetimetz(item))
+            except (DateTimeError, IndexError):
+                pass
+        return bookmarks
+
+    def placeBookmark(self):
+        """Place a new bookmark after the latest checkin message in a
+        cookie."""
+        if not hasattr(self, '_archive'):
+            self._archive = getAdapter(self.context, ICheckinMessageArchive)
+        if not self._archive:
+            return # No messages -- no bookmarks
+        bookmarks = self.bookmarks()
+        bookmarks.sort()
+        # Do not insert a bookmark if there were no checkins since the last
+        # bookmark
+        if (bookmarks and bookmarks[-1] >= self._archive[-1].date):
+            return
+        bookmarks.append(self._archive[-1].date)
+        if len(bookmarks) > self.max_bookmarks:
+            del bookmarks[:-self.max_bookmarks]
+        cookie = " ".join([dt.isoformat() for dt in bookmarks])
+        self.request.response.setCookie('bookmarks', cookie,
+                                        max_age=365*24*60*60) # 1 year
+
     def checkins(self, start=None, size=None):
         """Returns a list of the last 'size' checkin messages in
         self.context, newest first, skipping the first 'start' messages.
@@ -249,7 +289,29 @@
             self._archive = getAdapter(self.context, ICheckinMessageArchive)
         idx = len(self._archive) - start
         items = self._archive[max(0, idx-size):idx]
-        items = removeAllProxies(items) # needed for reverse
+        items = removeAllProxies(items)
+        # insert bookmarks
+        def bookmarkBetween(msg1, msg2, bookmarks=self.bookmarks()):
+            for b in bookmarks:
+                if msg1.date <= b < msg2.date:
+                    return True
+            return False
+        n = 1
+        while n < len(items):
+            if bookmarkBetween(items[n-1], items[n]):
+                items.insert(n, CheckinBookmark())
+                n += 2
+            else:
+                n += 1
+        # insert bookmarks before the first/after the last batch item
+        if items:
+            before = self._archive[max(0, idx-size-1):max(0, idx-size)]
+            if before and bookmarkBetween(before[0], items[0]):
+                items.insert(0, CheckinBookmark())
+            after = self._archive[idx:idx+1]
+            if after and bookmarkBetween(items[-1], after[0]):
+                items.insert(len(items), CheckinBookmark())
+        # reverse order to present newest checkins first
         items.reverse()
         return items