Renderer.py 8.99 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################


30
from Products.CMFCategory.Filter import Filter
Yoshinori Okuji's avatar
Yoshinori Okuji committed
31
from ZODB.POSException import ConflictError
Jérome Perrin's avatar
Jérome Perrin committed
32
from zLOG import LOG, PROBLEM
33 34 35 36

class Renderer(Filter):
  """
    Produces Item list out of category list
37 38

    FIXME: translation
Jérome Perrin's avatar
Jérome Perrin committed
39 40
    ( update: translation is not implemented in Renderer but in calling
      methods, so maybe it should be removed from this API ? -jerome)
41 42
  """

43
  def __init__(self, spec = None, filter = None, portal_type = None,
44
                     display_id = None, sort_id = None,
45
                     display_method = None, sort_method = None, filter_method = None,
46
                     filter_node=0, filter_leave=0,
Jérome Perrin's avatar
Jérome Perrin committed
47
                     is_right_display = 0, translate_display = 0,
48
                     translatation_domain = None, display_base_category = 0,
49
                     base_category = None, base = 1,
50
                     display_none_category = 1, current_category = None,**kw):
51 52 53 54 55 56
    """
    - *display_id*: the id of attribute to "call" to calculate the value to display
                      (getProperty(display_id) -> getDisplayId)

    - *display_method*: a callable method which is used to calculate the value to display

57 58 59 60 61 62
    - *filter_method*: a method to filter items in the list

    - *filter_node*: do not keep node categories

    - *filter_leave*: do not keep leave categories

63 64 65 66 67 68 69 70 71
    - *sort_id*: the id of the attribute to "call" to calculate the value used for sorting.
                Sorting is only applied to default ItemList items.

                          self.getProperty(sort_id)
                    foo       3
                    foo1      1
                    foo2      5
          display order will be (foo1, foo, foo2)

72
    - *sort_method*: a callable method which provides a sort function (?la cmp)
73 74 75 76 77 78 79

    - *is_right_display*: use the right value in the couple as the display value.

    - *translate_display*: set to 1, we call translation on each item

    - *translatation_domain*: domain to use for translation

80 81 82
    - *display_base_category*: set to 1, display base_category before display
      value

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
    - *recursive*: browse recursively to build the ItemList

    - *base_category*: the base category to consider (if None, default is used) API

    - *base*: if set to 0, do not include the base category. If set to 1,
              include the base category. If set to a string, use the string as base.
              This is useful for creationg multiple base categories sharing the same categories.
              (ex. target_region/region/europe)

    - *is_self_excluded*: allows to exclude this category from the displayed list

    - *current_category*: allows to provide a category which is not part of the
                          default ItemList. Very useful for displaying
                          values in a popup menu which can no longer
                          be selected.

    - *display_none_category*: allows to include an empty value. Very useful
                        to define None values or empty lists through
                        popup widgets. If both has_empty_item and
                        current_category are provided, current_category
                        is displayed first.


    """
107
    Filter.__init__(self, spec=spec, filter=filter,
108 109
                    portal_type=portal_type, filter_method=filter_method,
                    filter_node=filter_node, filter_leave=filter_leave)
110 111 112 113 114 115 116
    self.display_id = display_id
    self.sort_id = sort_id
    self.display_method = display_method
    self.sort_method = sort_method
    self.is_right_display = is_right_display
    self.translate_display = translate_display
    self.translatation_domain = translatation_domain
117
    self.display_base_category = display_base_category
118 119 120
    self.base_category = base_category
    self.base = base
    self.display_none_category = display_none_category
121
    self.current_category = current_category
122

123 124 125 126 127 128 129 130
  def getObjectList(self, value_list):
    new_value_list = []
    for value in value_list:
      obj = value.getObject()
      if obj is not None:
        new_value_list.append(obj)
    return new_value_list

131
  def render(self, value_list):
132 133 134
    """
      Returns rendered items
    """
135
    value_list = self.getObjectList(value_list)
136 137 138 139 140 141
    value_list = self.filter(value_list)
    if self.sort_method is not None:
      value_list.sort(self.sort_method)
    elif self.sort_id is not None:
      value_list.sort(lambda x,y: cmp(x.getProperty(self.sort_id), y.getProperty(self.sort_id)))

142 143
    # If base=1 but base_category is None, it is necessary to guess the base category
    # by heuristic.
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
#    if self.base and self.base_category is None:
#      base_category_count_map = {}
#      for value in value_list:
#        if not getattr(value, 'isCategory', 0):
#          continue
#        b = value.getBaseCategoryId()
#        if b in base_category_count_map:
#          base_category_count_map[b] += 1
#        else:
#          base_category_count_map[b] = 1
#      guessed_base_category = None
#      max_count = 0
#      for k,v in base_category_count_map.items():
#        if v > max_count:
#          guessed_base_category = k
#          max_count = v
#      LOG('render', 100, repr(guessed_base_category))
161 162 163 164 165 166 167 168 169 170 171

    # Initialize the list of items.
    item_list = []
    if self.current_category:
      if self.is_right_display:
        item = [None, self.current_category]
      else:
        item = [self.current_category, None]
      item_list.append(item)
    if self.display_none_category:
      if self.is_right_display:
Jérome Perrin's avatar
Jérome Perrin committed
172
        item = ['', '']
173
      else:
Jérome Perrin's avatar
Jérome Perrin committed
174
        item = ['', '']
175 176 177 178 179 180 181 182 183 184
      item_list.append(item)

    for value in value_list:
      #LOG('Renderer', 10, repr(value))
      # Get the label.
      if self.display_method is not None:
        label = self.display_method(value)
      elif self.display_id is not None:
        try:
          label = value.getProperty(self.display_id)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
185 186
        except ConflictError:
          raise
187
        except:
Jérome Perrin's avatar
Jérome Perrin committed
188 189
          LOG('CMFCategory', PROBLEM, 'Renderer was unable to call %s on %s'
               % (self.display_id, value.getRelativeUrl()))
190 191 192 193 194 195 196 197 198 199
          label = None
      else:
        label = None
      # Get the url.
      url = value.getRelativeUrl()
      if self.base:
        if self.base_category:
          # Prepend the specified base category to the url.
          url = self.base_category + '/' + url
        else:
Jérome Perrin's avatar
Jérome Perrin committed
200 201 202 203 204
          # If the base category of this category does not match the guessed
          # base category, merely ignore this category.
          # This is not the job for a Renderer to automatically remove values
          # if we do not specify a filter
          if getattr(value, 'getBaseCategoryId', None) is not None:
205
            continue
206 207 208
          # Remove from now, it might be outdated and useless
          #if value.getBaseCategoryId() != guessed_base_category:
          #  continue
209 210 211 212 213 214 215 216 217 218 219 220
      else:
        if self.base_category:
          # Nothing to do.
          pass
        else:
          # Get rid of the base category of this url, only if this is a category.
          if getattr(value, 'isCategory', 0):
            b = value.getBaseCategoryId()
            url = url[len(b)+1:]
      # Add the pair of a label and an url.
      if label is None:
        label = url
221 222 223 224
      # Add base category in label
      if self.display_base_category:
        if self.base_category:
          bc = value.portal_categories.resolveCategory(self.base_category)
Jérome Perrin's avatar
Jérome Perrin committed
225
          label = '%s/%s' % (bc.getTitleOrId(), label)
226
        else:
Jérome Perrin's avatar
Jérome Perrin committed
227
          if getattr(value, 'getBaseCategoryValue', None) is not None:
228
            bc = value.getBaseCategoryValue()
Jérome Perrin's avatar
Jérome Perrin committed
229
            label = '%s/%s' % (bc.getTitleOrId(), label)
230

231 232 233 234 235 236 237
      if self.is_right_display:
        item = [url, label]
      else:
        item = [label, url]
      item_list.append(item)

    return item_list