Allow for multiple list objects
authorSverre Rabbelier <srabbelier@gmail.com>
Fri, 21 Nov 2008 23:45:37 +0000
changeset 539 e30462354e26
parent 538 4d209757c835
child 540 e14e9cf23097
Allow for multiple list objects At the moment there's not really a practical use for it, since there is no way to make soc.views.models.base.View.list() show more than one list, but it shouldn't be too hard to allow for that. Patch by: Sverre Rabbelier
app/soc/logic/lists.py
app/soc/templates/soc/document/list/docs_row.html
app/soc/templates/soc/group/list/group_row.html
app/soc/templates/soc/host/list/host_row.html
app/soc/templates/soc/list/list_main.html
app/soc/templates/soc/list/list_pagination.html
app/soc/templates/soc/list/list_row.html
app/soc/templates/soc/models/list.html
app/soc/templates/soc/presence/list/home_row.html
app/soc/templates/soc/request/list/request_row.html
app/soc/templates/soc/site/list/site_row.html
app/soc/templates/soc/user/list/user_row.html
app/soc/views/helper/lists.py
app/soc/views/models/base.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/logic/lists.py	Fri Nov 21 23:45:37 2008 +0000
@@ -0,0 +1,134 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2008 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""List generation logic
+"""
+
+__authors__ = [
+  '"Sverre Rabbelier" <sverre@rabbelier.nl>',
+  ]
+
+
+class Lists(object):
+  """List array suitable for enumerating over with just 'for in'
+  """
+
+  DEF_PASSTHROUGH_FIELDS = [
+      'pagination',
+      'description',
+      'heading',
+      'row',
+      'limit',
+      'newest',
+      'prev',
+      'next',
+      'first',
+      'last',
+      ]
+
+  def __init__(self, contents):
+    """Constructs a new Lists object with the specified contents
+    """
+
+    # All the contents of all the lists
+    self.contents = contents
+    self.content = {}
+
+    # For iterating over all the lists
+    self.lists = range(len(contents))
+    self.list_data = []
+
+    # For iterating over the rows
+    self.rows = []
+    self.row_data = []
+
+  def __getattr__(self, attr):
+    """Delegate field lookup to the current list if appropriate
+
+    If, and only if, a lookup is done on one of the fields defined in
+    DEF_PASSTHROUGH_FIELDS, and the current list defines this field,
+    the value from the current list is returned.
+    """
+
+    if attr not in self.DEF_PASSTHROUGH_FIELDS:
+      raise AttributeError()
+
+    if attr not in self.content:
+      raise AttributeError()
+
+    return self.get(attr)
+
+  def get(self, item):
+    """Returns the item for the current list data
+    """
+
+    return self.content[item]
+
+  def next_list(self):
+    """Shifts out the current list content
+
+    The main content of the next list is returned for further processing.
+    """
+
+    # Advance the list data once
+    self.content = self.contents[0]
+    self.contents = self.contents[1:]
+
+    # Update internal 'iterators'
+    self.list_data = self.get('data')
+    self.rows = range(len(self.list_data))
+
+    return self.get('main')
+
+  def next_row(self):
+    """Returns the next list row for the current list
+
+    Before calling this method, next_list should be called at least once.
+    """
+
+    # Update internal 'iterators'
+    self.row_data =  self.list_data[0]
+
+    # Advance the row data once
+    self.list_data = self.list_data[1:]
+
+    return self.get('row')
+
+  def lists(self):
+    """Returns a list of numbers the size of the amount of lists.
+
+    This method can be used to iterate over all lists with shift,
+    without using a while loop.
+    """
+
+    return self.lists
+
+  def rows(self):
+    """Returns a list of numbers the size of the amount of items.
+
+    This method can be used to iterate over all items with next for
+    the current list, without using a while loop.
+    """
+
+    return self.rows
+
+  def item(self):
+    """Returns the current row item for the current list
+
+    Before calling this method, next_row should be called at least once.
+    """
+
+    return self.row_data
--- a/app/soc/templates/soc/document/list/docs_row.html	Fri Nov 21 23:45:15 2008 +0000
+++ b/app/soc/templates/soc/document/list/docs_row.html	Fri Nov 21 23:45:37 2008 +0000
@@ -1,15 +1,15 @@
 <tr class="off" onmouseover="this.className='on'" onmouseout="this.className='off'" 
-onclick="document.location.href='/document/edit/{{ data_element.scope_path }}/{{ data_element.link_id }}'" name="name">
+onclick="document.location.href='/document/edit/{{ list.item.scope_path }}/{{ list.item.link_id }}'" name="name">
   <td align="right">
    <div class="title">
     <a class="noul"
-     href="/document/edit/{{ data_element.scope_path }}/{{ data_element.link_id }}">{{ data_element.scope_path}}/{{ data_element.link_id }}</a>
+     href="/document/edit/{{ list.item.scope_path }}/{{ list.item.link_id }}">{{ list.item.scope_path}}/{{ list.item.link_id }}</a>
    </div>
   </td>
-  <td><div class="title">{{ data_element.title }}</div></td>
-  <td><div class="link_id">{{ data_element.link_id }}</div></td>
-  <td><div class="featured">{{ data_element.is_featured }}</div></td>
-  <td><div class="author">{{ data_element.author.link_id }}</div></td>
-  <td><div class="created">{{ data_element.created }}</div></td>
-  <td><div class="modified">{{ data_element.modified }}</div></td>
+  <td><div class="title">{{ list.item.title }}</div></td>
+  <td><div class="link_id">{{ list.item.link_id }}</div></td>
+  <td><div class="featured">{{ list.item.is_featured }}</div></td>
+  <td><div class="author">{{ list.item.author.link_id }}</div></td>
+  <td><div class="created">{{ list.item.created }}</div></td>
+  <td><div class="modified">{{ list.item.modified }}</div></td>
 </tr>
--- a/app/soc/templates/soc/group/list/group_row.html	Fri Nov 21 23:45:15 2008 +0000
+++ b/app/soc/templates/soc/group/list/group_row.html	Fri Nov 21 23:45:37 2008 +0000
@@ -1,9 +1,9 @@
 <tr class="off" onmouseover="this.className='on'" onmouseout="this.className='off'" 
-onclick="document.location.href='/{{ entity_type|lower }}/edit/{{ data_element.link_id }}'" name="name">
+onclick="document.location.href='/{{ entity_type|lower }}/edit/{{ list.item.link_id }}'" name="name">
   <td align="right"><div class="name"><a class="noul"
-         href="/{{ entity_type|lower }}/edit/{{ data_element.link_id }}">{{ data_element.name }}</a>
+         href="/{{ entity_type|lower }}/edit/{{ list.item.link_id }}">{{ list.item.name }}</a>
      </div>
   </td>
-  <td><div class="link_id">{{ data_element.link_id }}</a></div></td>
-  <td><div class="short_name">{{ data_element.short_name }}</div></td>
+  <td><div class="link_id">{{ list.item.link_id }}</a></div></td>
+  <td><div class="short_name">{{ list.item.short_name }}</div></td>
 </tr>
--- a/app/soc/templates/soc/host/list/host_row.html	Fri Nov 21 23:45:15 2008 +0000
+++ b/app/soc/templates/soc/host/list/host_row.html	Fri Nov 21 23:45:37 2008 +0000
@@ -1,10 +1,10 @@
 <tr class="off" onmouseover="this.className='on'" onmouseout="this.className='off'" 
-onclick="document.location.href='/{{ entity_type|lower }}/edit/{{ data_element.sponsor.link_id }}/{{ data_element.user.link_id }}'" name="name">
+onclick="document.location.href='/{{ entity_type|lower }}/edit/{{ list.item.sponsor.link_id }}/{{ list.item.user.link_id }}'" name="name">
   <td align="right"><div class="user_link_id"><a class="noul"
-         href="/{{ entity_type|lower }}/edit/{{ data_element.sponsor.link_id }}/{{ data_element.user.link_id }}">{{ data_element.user.link_id }}</a>
+         href="/{{ entity_type|lower }}/edit/{{ list.item.sponsor.link_id }}/{{ list.item.user.link_id }}">{{ list.item.user.link_id }}</a>
      </div>
   </td>
-  <td><div class="sponsor_name">{{ data_element.sponsor.name }}</div></td>
-  <td><div class="given_name">{{ data_element.given_name }}</div></td>
-  <td><div class="surname">{{ data_element.surname }}</div></td>
+  <td><div class="sponsor_name">{{ list.item.sponsor.name }}</div></td>
+  <td><div class="given_name">{{ list.item.given_name }}</div></td>
+  <td><div class="surname">{{ list.item.surname }}</div></td>
 </tr>
--- a/app/soc/templates/soc/list/list_main.html	Fri Nov 21 23:45:15 2008 +0000
+++ b/app/soc/templates/soc/list/list_main.html	Fri Nov 21 23:45:37 2008 +0000
@@ -1,17 +1,19 @@
+{{ list.description }}
+
 <div class="list">
-  {% include list_pagination %}
+  {% include list.pagination %}
 
   <table id="queues">
-    {% if not list_data %}
+    {% if not list.rows %}
       <tr><td colspan="9"><span class="disabled">(None)</span></td></tr>
     {% else %}
-      {% include list_heading %}
-      {% for data_element in list_data %}
-        {% include list_row %}
+      {% include list.heading %}
+      {% for _ in list.rows %}
+        {% include list.next_row %}
       {% endfor %}
     {% endif %}
   </table>
 
-  {% include list_pagination %}
+  {% include list.pagination %}
 
 </div>
\ No newline at end of file
--- a/app/soc/templates/soc/list/list_pagination.html	Fri Nov 21 23:45:15 2008 +0000
+++ b/app/soc/templates/soc/list/list_pagination.html	Fri Nov 21 23:45:37 2008 +0000
@@ -2,13 +2,13 @@
 
   {{ pagination_form.as_table }}
  
-  {% if newest %}
-    <a class="novisit" href="{{newest}}">&laquo; First</a>
+  {% if list.newest %}
+    <a class="novisit" href="{{ list.newest }}">&laquo; First</a>
   {% endif %}
-  {% if prev %}
-    <a class="novisit" href="{{prev}}">&lsaquo; Previous</a>
+  {% if list.prev %}
+    <a class="novisit" href="{{ list.prev }}">&lsaquo; Previous</a>
   {% endif %}
-  <b>{{ first }}{% if last %} - {{ last }}{% endif %}</b>
-  {% if next %}<a class="novisit" href="{{next}}">Next &rsaquo;</a>
+  <b>{{ list.first }}{% if list.last %} - {{ list.last }}{% endif %}</b>
+  {% if list.next %}<a class="novisit" href="{{ list.next }}">Next &rsaquo;</a>
   {% endif %}
 </div>
--- a/app/soc/templates/soc/list/list_row.html	Fri Nov 21 23:45:15 2008 +0000
+++ b/app/soc/templates/soc/list/list_row.html	Fri Nov 21 23:45:37 2008 +0000
@@ -1,7 +1,7 @@
 <tr class="off" onmouseover="this.className='on'" onmouseout="this.className='off'" 
 onclick="document.location.href=''" name="name">
   <td align="right"><div class="id"><a class="noul"
-         href="">{{ data_element }}</a>
+         href="">{{ list.item }}</a>
      </div>
   </td>
 </tr>
--- a/app/soc/templates/soc/models/list.html	Fri Nov 21 23:45:15 2008 +0000
+++ b/app/soc/templates/soc/models/list.html	Fri Nov 21 23:45:37 2008 +0000
@@ -14,16 +14,10 @@
 {% endcomment %}
 {% load forms_helpers %}
 {% block body %}
-<p>
+{% for _ in list.lists %}
 <p>
-{% block instructions %}
-{% if instruction_text %}
-{{ instruction_text }}
-{% else %}
-List of {{ entity_type_plural }} in Google Open Source Programs.
-{% endif %}
+{% include list.next_list %}
+</p>
+{% endfor %}
+
 {% endblock %}
-</p>
-{% include list_main %}
-</p>
-{% endblock %}
--- a/app/soc/templates/soc/presence/list/home_row.html	Fri Nov 21 23:45:15 2008 +0000
+++ b/app/soc/templates/soc/presence/list/home_row.html	Fri Nov 21 23:45:37 2008 +0000
@@ -1,10 +1,10 @@
 <tr class="off" onmouseover="this.className='on'" onmouseout="this.className='off'" 
-onclick="document.location.href='/home/settings/edit/{{ data_element.scope_path }}/{{ data_element.link_id }}'" name="name">
+onclick="document.location.href='/home/settings/edit/{{ list.item.scope_path }}/{{ list.item.link_id }}'" name="name">
   <td align="right">
    <div class="title">
     <a class="noul"
-     href="/home/settings/edit/{{ data_element.scope_path }}/{{ data_element.link_id }}">{{ data_element.scope_path}}/{{ data_element.link_id }}</a>
+     href="/home/settings/edit/{{ list.item.scope_path }}/{{ list.item.link_id }}">{{ list.item.scope_path}}/{{ list.item.link_id }}</a>
    </div>
   </td>
-  <td><div class="link_id">{{ data_element.home.title }}</div></td>
+  <td><div class="link_id">{{ list.item.home.title }}</div></td>
 </tr>
--- a/app/soc/templates/soc/request/list/request_row.html	Fri Nov 21 23:45:15 2008 +0000
+++ b/app/soc/templates/soc/request/list/request_row.html	Fri Nov 21 23:45:37 2008 +0000
@@ -1,11 +1,11 @@
 <tr class="off" onmouseover="this.className='on'" onmouseout="this.className='off'" 
-onclick="document.location.href='/{{ entity_type|lower }}/edit/{{ data_element.role|lower }}/{{ data_element.to.link_id }}/{{ data_element.requester.link_id }}'" name="name">
+onclick="document.location.href='/{{ entity_type|lower }}/edit/{{ list.item.role|lower }}/{{ list.item.to.link_id }}/{{ list.item.requester.link_id }}'" name="name">
   <td align="right"><div class="user_link_id"><a class="noul"
-         href="/{{ entity_type|lower }}/edit/{{ data_element.role|lower }}/{{ data_element.to.link_id }}/{{ data_element.requester.link_id }}">{{ data_element.requester.link_id }}</a>
+         href="/{{ entity_type|lower }}/edit/{{ list.item.role|lower }}/{{ list.item.to.link_id }}/{{ list.item.requester.link_id }}">{{ list.item.requester.link_id }}</a>
      </div>
   </td>
-  <td><div class="role">{{ data_element.role }}</div></td>
-  <td><div class="to">{{ data_element.to.link_id }}</div></td>
-  <td><div class="accepted">{{ data_element.accepted }}</div></td>
-  <td><div class="declined">{{ data_element.declined }}</div></td>
+  <td><div class="role">{{ list.item.role }}</div></td>
+  <td><div class="to">{{ list.item.to.link_id }}</div></td>
+  <td><div class="accepted">{{ list.item.accepted }}</div></td>
+  <td><div class="declined">{{ list.item.declined }}</div></td>
 </tr>
--- a/app/soc/templates/soc/site/list/site_row.html	Fri Nov 21 23:45:15 2008 +0000
+++ b/app/soc/templates/soc/site/list/site_row.html	Fri Nov 21 23:45:37 2008 +0000
@@ -1,10 +1,10 @@
 <tr class="off" onmouseover="this.className='on'" onmouseout="this.className='off'" 
-onclick="document.location.href='/site/settings/edit/{{ data_element.scope_path }}/{{ data_element.link_id }}'" name="name">
+onclick="document.location.href='/site/settings/edit/{{ list.item.scope_path }}/{{ list.item.link_id }}'" name="name">
   <td align="right">
    <div class="title">
     <a class="noul"
-     href="/site/settings/edit/{{ data_element.scope_path }}/{{ data_element.link_id }}">{{ data_element.scope_path}}/{{ data_element.link_id }}</a>
+     href="/site/settings/edit/{{ list.item.scope_path }}/{{ list.item.link_id }}">{{ list.item.scope_path}}/{{ list.item.link_id }}</a>
    </div>
   </td>
-  <td><div class="link_id">{{ data_element.home.title }}</div></td>
+  <td><div class="link_id">{{ list.item.home.title }}</div></td>
 </tr>
--- a/app/soc/templates/soc/user/list/user_row.html	Fri Nov 21 23:45:15 2008 +0000
+++ b/app/soc/templates/soc/user/list/user_row.html	Fri Nov 21 23:45:37 2008 +0000
@@ -1,10 +1,10 @@
 <tr class="off" onmouseover="this.className='on'" onmouseout="this.className='off'" 
-onclick="document.location.href='{{redirect_action|lower}}/{{ data_element.link_id }}'" name="name">
+onclick="document.location.href='{{redirect_action|lower}}/{{ list.item.link_id }}'" name="name">
   <td align="right"><div class="account"><a class="noul"
-         href="{{redirect_action|lower}}/{{ data_element.link_id }}">{{ data_element.account }}</a>
+         href="{{redirect_action|lower}}/{{ list.item.link_id }}">{{ list.item.account }}</a>
      </div>
   </td>
-  <td><div class="email">{{ data_element.account.email }}</a></div></td>
-  <td><div class="public_name">{{ data_element.public_name }}</div></td>
-  <td><div class="link_id">{{ data_element.link_id }}</div></td>
+  <td><div class="email">{{ list.item.account.email }}</a></div></td>
+  <td><div class="public_name">{{ list.item.public_name }}</div></td>
+  <td><div class="link_id">{{ list.item.link_id }}</div></td>
 </tr>
--- a/app/soc/views/helper/lists.py	Fri Nov 21 23:45:15 2008 +0000
+++ b/app/soc/views/helper/lists.py	Fri Nov 21 23:45:37 2008 +0000
@@ -78,36 +78,25 @@
   return max(0, offset), max(1, min(limit, MAX_PAGINATION))
 
 
-DEF_LIST_TEMPLATES = {'list_main': 'soc/list/list_main.html',
-                      'list_pagination': 'soc/list/list_pagination.html',
-                      'list_row': 'soc/list/list_row.html',
-                      'list_heading': 'soc/list/list_heading.html'}
-
-def setList(request, context, list_data,
-            offset=0, limit=0, list_templates=DEF_LIST_TEMPLATES):
-  """Updates template context dict with variables used for rendering lists.
+def getList(request, list_data, list_templates, description, offset=0, limit=0):
+  """Returns a dict with fields used for rendering lists.
 
   Args:
     request: the Django HTTP request object
-    context: the template context dict to be updated in-place (pass in a copy
-      if the original must not be modified), or None if a new one is to be
-      created; any existing fields already present in the context dict passed
-      in by the caller are left unaltered 
     list_data: array of data to be displayed in the list
     offset: offset in list which defines first item to return
     limit: max amount of items per page
     list_templates: templates that are used when rendering list
 
   Returns:
-    updated template context dict supplied by the caller or a new context
-    dict if the caller supplied None.
+    A a dictionary with the following values set:
 
     {
-      'list_data': list data to be displayed 
-      'list_main': url to list main template
-      'list_pagination': url to list pagination template
-      'list_row': url to list row template
-      'list_heading': url to list heading template
+      'data': list data to be displayed
+      'main': url to list main template
+      'pagination': url to list pagination template
+      'row': url to list row template
+      'heading': url to list heading template
       'limit': max amount of items per page,
       'newest': url to first page of the list 
       'prev': url to previous page 
@@ -115,42 +104,44 @@
       'first': offset of the first item in the list
       'last': offest of the lst item in the list
     }
-  """  
+  """
+
   if not list_data:
     list_data = []
-  
+
   more = bool(list_data[limit:])
   if more:
     del list_data[limit:]
+
+  newest = ''
+  next = ''
+  prev = ''
+
   if more:
     next = request.path + '?offset=%d&limit=%d' % (offset+limit, limit)
-  else:
-    next = ''
+
   if offset > 0:
     prev = request.path + '?offset=%d&limit=%d' % (max(0, offset-limit), limit)
-  else:
-    prev = ''
-  newest = ''
+
   if offset > limit:
     newest = request.path + '?limit=%d' % limit
-  
-  if not context:
-    context = {}
-  
-  context.update(
-    {'list_data': list_data, 
-     'list_main': list_templates['list_main'],
-     'list_pagination': list_templates['list_pagination'],
-     'list_row': list_templates['list_row'],
-     'list_heading': list_templates['list_heading'],
+
+  content = {
+      'data': list_data,
+      'description': description,
+     'main': list_templates['list_main'],
+     'pagination': list_templates['list_pagination'],
+     'row': list_templates['list_row'],
+     'heading': list_templates['list_heading'],
      'limit': limit,
      'newest': newest, 
      'prev': prev, 
      'next': next,
      'first': offset+1,
-     'last': len(list_data) > 1 and offset+len(list_data) or None})
-  
-  return context
+     'last': len(list_data) > 1 and offset+len(list_data) or None
+     }
+
+  return content
 
 
 def makePaginationForm(
--- a/app/soc/views/models/base.py	Fri Nov 21 23:45:15 2008 +0000
+++ b/app/soc/views/models/base.py	Fri Nov 21 23:45:37 2008 +0000
@@ -29,6 +29,7 @@
 from django.utils.translation import ugettext_lazy
 
 import soc.logic
+import soc.logic.lists
 import soc.logic.out_of_band
 import soc.views.helper.lists
 import soc.views.helper.responses
@@ -125,6 +126,8 @@
 
     new_params['list_redirect_action'] = '/' + params['url_name'] + '/edit'
 
+    description = ugettext_lazy('List of %(name)s in Google Open Source Programs.')
+    new_params['list_description'] = description % params
     new_params['save_message'] = [ugettext_lazy('Profile saved.')]
     new_params['edit_params'] = {
         self.DEF_SUBMIT_MSG_PARAM_NAME: self.DEF_SUBMIT_MSG_PROFILE_SAVED,
@@ -349,9 +352,11 @@
     context['pagination_form'] = helper.lists.makePaginationForm(request, limit)
 
     templates = params['lists_template']
+    description = params['list_description']
 
-    context = helper.lists.setList(request, context, entities, 
-                                 offset, limit, templates)
+    content = helper.lists.getList(request, entities, templates,
+        description, offset, limit)
+    context['list'] = soc.logic.lists.Lists([content])
 
     context['entity_type'] = params['name']
     context['entity_type_plural'] = params['name_plural']