Roundup Tracker

This is a combination of a reactor that creates a static template file including all the users and an example how to make use of the created template. It is designed to speed up display of user and other dropdowns. Another way to handle dropdowns is to use filter_sql in TAL to select a subset of users for display.

The solution was initially developed by Marcus Priesch -- I'm just documenting it, since the mailinglist archives::

* http://sourceforge.net/mailarchive/message.php?msg_id=12295188

* http://roundup.1086182.n5.nabble.com/Practical-Roundup-Limitations-td3708.html

* http://sourceforge.net/mailarchive/forum.php?thread_name=1110888492.20233.212.camel%40tttmexx.vie.at.tttech.ttt&forum_name=roundup-users

are unavailable from time to time::

 import os
 import shutil
 pjoin = os.path.join
 from roundup.cgi.TranslationService import get_translation
 from tempfile                       import mkstemp
 from roundup.date                   import Date
 from roundup.exceptions             import Reject
 USER_SINGLE = """
 <tal:block metal:define-macro="%(macro_name)s">
  <tal:block tal:condition="python:not context [name].is_edit_ok ()"
   tal:replace="python: context [name]"/>
  <select tal:attributes="name name"
   tal:condition="python: context [name].is_edit_ok ()">
   <option value=""
           tal:content="dont_care"></option>
   <!-- autogenerated -->
 %(option_list)s
   <!-- autogenerated -->
 </select>
 <disabled script language="javascript"
         tal:content="structure string:
 <!--
 select_box = document.${form}.${name};
 for (i = 0; i < select_box.length; i++)
   {
     if (select_box.options [i].value == ${selected})
       select_box.options [i].selected = true;
     else
       select_box.options [i].selected = false;
   }
 -->
 "><disabled /script>;
 </tal:block>
 """
 
 USER_MULTI = """
 <tal:block metal:define-macro="%(macro_name)s">
  <tal:block tal:condition="python:not %(condition)s"
   tal:replace="python: context [name]"/>
  <select multiple tal:condition="python: %(condition)s"
          tal:attributes="size size;
                          name name">
   <option value=""
           tal:content="dont_care"></option>
   <!-- autogenerated -->
 %(option_list)s
   <!-- autogenerated -->
 </select>
 <disabled script language="javascript"
         tal:content="structure string:
 <!--
 select_box = document.${form}.${name};
 selected   = new Array (${selected});
 for (i = 0; i < select_box.length; i++)
   {
     for (j = 0; j < selected.length; j++)
       {
         if (select_box.options [i].value == selected [j])
           select_box.options [i].selected = true;
       }
   }
 -->
 "><disabled /script>
 </tal:block>
 """
 
 OPTION_FMT = """  <option value="%s">%s</option>"""
 
 def update_userlist_html (db, cl, nodeid, old_values) :
     """newly create user_list.html macro page
     """
     root       = pjoin (db.config.TRACKER_HOME, "html")
     userlist   = "userlist.html"
     changed    = False
     for i in 'username', 'status', 'roles' :
         if  (  not old_values
             or i not in old_values
             or old_values [i] != cl.get (nodeid, i)
             ) :
             changed = True
     if not changed :
         return
     f, tmpname = mkstemp (".html", "userlist", root)
     f          = os.fdopen (f, "w")
     # all 'real' users
     spec = {}
     if 'status' in cl.properties :
         valid = db.user_status.lookup ('valid')
         spec  = { 'status' : [valid] }
     users      = cl.filter ( None # full text search
                            , filterspec = spec
                            , sort       = ("+", "username")
                            )
     if users :
         options  = [OPTION_FMT % (id, cl.get (id, "username")) for id in users] 
 
         f.write (USER_SINGLE % { "macro_name"  : "user"
                                , "option_list" : "\n".join (options)
                                }
                 )
         f.write (USER_MULTI  % { "macro_name"  : "user_multi"
                                , "option_list" : "\n".join (options)
                                , "condition"   : "context [name].is_edit_ok ()"
                                }
                 )
         f.write (USER_MULTI  % { "macro_name"  : "user_multi_read"
                                , "option_list" : "\n".join (options)
                                , "condition"   : "True"
                                }
                 )
 
     # all users (incl. mail alias users)
     # RSC: now there are no mail alias users -- and we don't want
     # invalid users here, so use the same filterspec.
     spec = {}
     if 'status' in cl.properties :
         status = [db.user_status.lookup (s) for s in 'valid', 'system']
         spec   = {"status" : status}
     users    = cl.filter ( None # full text search
                          , filterspec = spec
                          , sort       = ("+", "username")
                          )
     if users :
         options  = [OPTION_FMT % (id, cl.get (id, "username")) for id in users]

         f.write (USER_MULTI  % { "macro_name"  : "nosy_multi"
                                , "option_list" : "\n".join (options)
                                , "condition"   : "True"
                                }
                 )

     f.close ()
     shutil.move (tmpname, pjoin (root, userlist))
 # end def update_userlist_html

The following example is a roundup link-attribute, the "supervisor" field of the user class (the utils.fieldname utility function creates the localised field name with a link to a documentation popup in our implementation)::

 <tr>
  <th tal:content="structure python:utils.fieldname('user', 'supervisor')"/>
  <td>
   <tal:block tal:define="name      string:supervisor;
                          form      string:user_form;
                          dont_care string:don't care;
                          selected  context/supervisor/id | string:0">
    <tal:block metal:use-macro="templates/userlist/macros/user"></tal:block>
   </tal:block>
  </td>
 </tr>

The following example is for a multilink-property called 'authors'::

    <tr>
     <th class="required"
      tal:content="structure python:utils.fieldname (classname, 'authors')" />
     <td colspan="3">
      <tal:block tal:define="size      string:3;
                             name      string:authors;
                             form      string:itemSynopsis;
                             dont_care string:don't care;
                             selected  python:str([u.id for u in context.authors.reverse ()] or [''])[1:-1]">
       <tal:block metal:use-macro="templates/userlist/macros/user_multi"></tal:block>
      </tal:block>
     </td>
    </tr>


John Rouillard 2013-03-26 added links to relevent mailing list messages, removed encoding of > and < signs from code blocks. Formatted code blocks as pre sections to make reading easier.