Variated.py 16.1 KB
Newer Older
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1 2
##############################################################################
#
3
# Copyright (c) 2002, 2006 Nexedi SARL and Contributors. All Rights Reserved.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4
#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
Jean-Paul Smets's avatar
Jean-Paul Smets committed
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 30
#
# 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.
#
##############################################################################

from AccessControl import ClassSecurityInfo
from Globals import InitializeClass
31 32
from Products.CMFCore.utils import getToolByName

33
from Products.ERP5Type import Context, interfaces, Permissions
Jean-Paul Smets's avatar
Jean-Paul Smets committed
34
from Products.ERP5Type.Base import Base
35
from Products.CMFCategory.Renderer import Renderer
Jean-Paul Smets's avatar
Jean-Paul Smets committed
36

37 38
from warnings import warn

Jean-Paul Smets's avatar
Jean-Paul Smets committed
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
class Variated(Base):
  """
    Variated is a mix-in class for all classes which implement
    the Variated Interface.

    A Variable object is an object which can variate
    according to multiple dimensions. Variable objects include:

    - a Resource instance

    - an Amount instance (a Movement, a DeliveryLine, etc.)

    - an Item

    - a TransformedResource instance
  """

  # Declarative security
  security = ClassSecurityInfo()

  # Declarative interfaces
60
  __implements__ = (interfaces.IVariated, )
Jean-Paul Smets's avatar
Jean-Paul Smets committed
61

62 63
  security.declareProtected(Permissions.AccessContentsInformation, 
                            'getVariationBaseCategoryList')
64
  def getVariationBaseCategoryList(self, omit_optional_variation=0,
65
      omit_option_base_category=None, omit_individual_variation=0):
66 67
    """
      Return the list of variation base category.
68
      If omit_optional_variation==1, do not include base category
69 70
      considered as option (ex: industrial_phase).
    """
71 72 73 74 75 76
    #XXX backwards compatibility
    if omit_option_base_category is not None:
      warn("Please use omit_optional_variation instead of"\
          " omit_option_base_category.", DeprecationWarning)
      omit_optional_variation = omit_option_base_category

77
    vbcl = self._baseGetVariationBaseCategoryList()
78
    if omit_optional_variation == 1:
79 80 81 82 83
      # XXX First implementation
      # option base category list is a portal method, until the creation
      # of a good API.
      option_base_category_list = self.getPortalOptionBaseCategoryList()
      vbcl = [x for x in vbcl if x not in option_base_category_list]
84 85 86 87 88 89
    else:
      vbcl.extend(self.getOptionalVariationBaseCategoryList())
      
    if omit_individual_variation == 0:
      vbcl.extend(self.getIndividualVariationBaseCategoryList())
      
90 91
    return vbcl

92 93
  security.declareProtected(Permissions.AccessContentsInformation, 
                            '_getVariationCategoryList')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
94 95
  def _getVariationCategoryList(self, base_category_list = ()):
    if base_category_list is ():
96 97
      base_category_list = self.getVariationBaseCategoryList()
#       base_category_list = self.getVariationRangeBaseCategoryList()
98
    return self.getAcquiredCategoryMembershipList(base_category_list, base=1)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
99

100 101
  security.declareProtected(Permissions.AccessContentsInformation, 
                            'getVariationCategoryList')
102
  def getVariationCategoryList(self, base_category_list=(),
103
      omit_optional_variation=0, omit_option_base_category=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
104 105 106
    """
      Returns the list of possible variations
    """
107 108 109 110 111 112
    #XXX backwards compatibility
    if omit_option_base_category is not None:
      warn("Please use omit_optional_variation instead of"\
          " omit_option_base_category.", DeprecationWarning)
      omit_optional_variation = omit_option_base_category

113 114
    return self._getVariationCategoryList(
                                  base_category_list=base_category_list)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
115

116 117 118
  security.declareProtected(Permissions.AccessContentsInformation, 
                            'getVariationCategoryItemList')
  def getVariationCategoryItemList(self, base_category_list=(), base=1,
119 120 121
      display_id='logical_path', display_base_category=1,
      current_category=None, omit_optional_variation=0,
      omit_option_base_category=None, **kw):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
122
    """
123
      Returns the list of possible variations
Jean-Paul Smets's avatar
Jean-Paul Smets committed
124
    """
125 126 127 128 129 130
    #XXX backwards compatibility
    if omit_option_base_category is not None:
      warn("Please use omit_optional_variation instead of"\
          " omit_option_base_category.", DeprecationWarning)
      omit_optional_variation = omit_option_base_category

131
    variation_category_item_list = []
132 133
    if current_category is not None:
      variation_category_item_list.append((current_category,current_category))
134 135

    if base_category_list is ():
136
      base_category_list = self.getVariationBaseCategoryList()
137
      if omit_optional_variation == 1:
138 139
        base_category_list = [x for x in base_category_list if x not in
                              self.getPortalOptionBaseCategoryList()]
140 141
    # Prepare 2 rendering
    portal_categories = self.portal_categories
142 143
    for base_category in base_category_list:
      variation_category_list = self._getVariationCategoryList(
144
                                       base_category_list=[base_category])
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
      
      category_list = []
      object_list = []
      for variation_category_path in variation_category_list:
        try:
          variation_category = portal_categories.resolveCategory(
                                    variation_category_path)
          var_cat_portal_type = variation_category.getPortalType()
        except AttributeError:
          variation_category_item_list.append((variation_category_path,
                                               variation_category_path))
        else:
          if var_cat_portal_type != 'Category':
            object_list.append(variation_category)
          else:
            category_list.append(variation_category)
      # Render categories
162
      variation_category_item_list.extend(Renderer(
163 164 165
                             display_base_category=display_base_category,
                             display_none_category=0, base=base,
                             current_category=current_category,
166 167
                             display_id=display_id, **kw).\
                                               render(category_list))
168
      # Render the others
169 170 171 172 173 174 175
      variation_category_item_list.extend(Renderer(
                             base_category=base_category,
                             display_base_category=display_base_category,
                             display_none_category=0, base=base,
                             current_category=current_category,
                             display_id='title', **kw).\
                                               render(object_list))
176
    return variation_category_item_list
177
  
178 179 180 181 182 183 184 185 186 187 188 189 190 191
  # XXX Is it used ?
#   def getVariationCategoryTitleOrIdItemList(self, base_category_list=(), 
#                                             base=1, **kw):
#     """
#     Returns a list of tuples by parsing recursively all categories in a
#     given list of base categories. Uses getTitleOrId as method
#     """
#     return self.getVariationCategoryItemList(
#                    display_id='title_or_id', 
#                    base_category_list=base_category_list, base=base, **kw)

  security.declareProtected(Permissions.ModifyPortalContent, 
                            '_setVariationCategoryList')
  def _setVariationCategoryList(self, node_list, base_category_list=()):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
192
    if base_category_list is ():
193
      base_category_list = self.getVariationBaseCategoryList()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
194 195
    self._setCategoryMembership(base_category_list,node_list,base=1)

196 197 198 199 200
  security.declareProtected(Permissions.ModifyPortalContent, 
                            'setVariationCategoryList')
  def setVariationCategoryList(self, node_list, base_category_list=()):
    self._setVariationCategoryList(node_list, 
                                   base_category_list=base_category_list)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
201 202 203 204
    self.reindexObject()

  # Range
  security.declareProtected(Permissions.AccessContentsInformation,
205
                            'getVariationRangeBaseCategoryList')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
206 207
  def getVariationRangeBaseCategoryList(self):
      """
208
      Returns possible variation base_category ids.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
209
      """
210 211 212
      # Get a portal method which defines a list of 
      # variation base category
      return self.getPortalVariationBaseCategoryList()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
213 214

  security.declareProtected(Permissions.AccessContentsInformation,
215 216 217 218
                            'getVariationRangeBaseCategoryItemList')
  def getVariationRangeBaseCategoryItemList(self, base=1, 
                                            display_id='getTitle', 
                                            current_category=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
219 220 221 222 223 224
      """
        Returns possible variations of the resource
        as a list of tuples (id, title). This is mostly
        useful in ERP5Form instances to generate selection
        menus.
      """
225 226
      return self.portal_categories.getItemList(
                            self.getVariationBaseCategoryList())
Jean-Paul Smets's avatar
Jean-Paul Smets committed
227

228 229
  security.declareProtected(Permissions.AccessContentsInformation,
                                    'getVariationBaseCategoryItemList')
230
  def getVariationBaseCategoryItemList(self, display_id='title_or_id',
231 232
        omit_optional_variation=0, omit_option_base_category=None,
        omit_individual_variation=0):
233 234 235 236 237 238
      """
        Returns base category of the resource
        as a list of tuples (title, id). This is mostly
        useful in ERP5Form instances to generate selection
        menus.
      """
239 240 241 242 243 244
      #XXX backwards compatibility
      if omit_option_base_category is not None:
        warn("Please use omit_optional_variation instead of"\
            " omit_option_base_category.", DeprecationWarning)
        omit_optional_variation = omit_option_base_category

245
      variation_base_category_list = self.getVariationBaseCategoryList(
246 247
          omit_optional_variation=omit_optional_variation,
          omit_individual_variation=omit_individual_variation)
248 249 250 251 252 253 254 255
      result = []
      for base_category in variation_base_category_list:
        bc = self.portal_categories.resolveCategory(base_category)
        result.extend(Renderer(display_base_category=0, 
                               display_none_category=0, base=1,
                               display_id=display_id).render([bc]))
      return result

Jean-Paul Smets's avatar
Jean-Paul Smets committed
256
  # Methods for matrix UI widgets
257 258
  # XXX FIXME Those method are depreciated.
  # We now use _asCellRange scripts.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
259
  security.declareProtected(Permissions.AccessContentsInformation,
260
                            'getLineVariationRangeCategoryItemList')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
261 262 263 264 265 266
  def getLineVariationRangeCategoryItemList(self):
    """
      Returns possible variations in line
    """
    try:
      resource = self.getDefaultResourceValue()
Yoshinori Okuji's avatar
Yoshinori Okuji committed
267
    except AttributeError:
Jean-Paul Smets's avatar
Jean-Paul Smets committed
268 269
      resource = None
    if resource is not None:
270 271 272
      clist = resource.getVariationRangeCategoryItemList(
                       base_category_list=self.getVariationBaseCategoryLine(),
                       root=0)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
273 274 275 276 277 278 279 280 281 282 283 284
    else:
      clist = [(None,None)]
    return clist

  security.declareProtected(Permissions.AccessContentsInformation,
                                       'getColumnVariationRangeCategoryItemList')
  def getColumnVariationRangeCategoryItemList(self):
    """
      Returns possible variations in column
    """
    try:
      resource = self.getDefaultResourceValue()
Yoshinori Okuji's avatar
Yoshinori Okuji committed
285
    except AttributeError:
Jean-Paul Smets's avatar
Jean-Paul Smets committed
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
      resource = None
    if resource is not None:
      clist = resource.getVariationRangeCategoryItemList(base_category_list =
                                       self.getVariationBaseCategoryColumn(), root=0)
    else:
      clist = [(None,None)]
    return clist

  security.declareProtected(Permissions.AccessContentsInformation,
                               'getTabVariationRangeCategoryItemList')
  def getTabVariationRangeCategoryItemList(self):
    """
      Returns possible variations in tab
    """
    try:
      resource = self.getDefaultResourceValue()
Yoshinori Okuji's avatar
Yoshinori Okuji committed
302
    except AttributeError:
Jean-Paul Smets's avatar
Jean-Paul Smets committed
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
      resource = None
    if resource is not None:
      clist = resource.getVariationRangeCategoryItemList(base_category_list =
                                       self.getVariationBaseCategoryTabList(), root=0)
    else:
      clist = [(None,None)]
    return clist

  # Help
  security.declareProtected(Permissions.AccessContentsInformation,
                                        'getMatrixVariationRangeBaseCategoryList')
  def getMatrixVariationRangeBaseCategoryList(self):
    """
      Return base categories used in the matrix
    """
    line_bc= self.getVariationBaseCategoryLine()
    column_bc = self.getVariationBaseCategoryColumn()
    # We need to copy values first
    tab_bc = list(self.getVariationBaseCategoryTabList())
    result = tab_bc
    if line_bc is not None and line_bc is not '':
      result += [line_bc]
    if column_bc is not None and column_bc is not '':
      result += [column_bc]
    return result

329 330 331
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getVariationRangeCategoryItemList')
  def getVariationRangeCategoryItemList(self, base_category_list=(), base=1, 
332 333
                                        root=1,
                                        display_method_id='getCategoryChildLogicalPathItemList',
334
                                        display_base_category=1,
335
                                        current_category=None, **kw):
336 337 338 339 340 341 342 343 344
    """
    Returns possible variations
      => [(display, value)]
    """
    result = []
    if base_category_list is ():
      base_category_list = self.getVariationBaseCategoryList()
    elif type(base_category_list) is type('a'):
      base_category_list = (base_category_list, )
345 346

    traverse = getToolByName(self, 'portal_categories').unrestrictedTraverse
347 348
    # Render categories
    for base_category in base_category_list:
349
      result += getattr(traverse(base_category), display_method_id)(
350 351
                             base=base,
                             display_base_category=display_base_category,
352
                             display_none_category=0, **kw)
353 354 355 356 357 358
    # Return result
    return result

  security.declareProtected(Permissions.AccessContentsInformation,
                            'getVariationRangeCategoryList')
  def getVariationRangeCategoryList(self, base_category_list=(), base=1,
359 360
                                    root=1, current_category=None,
                                    omit_individual_variation=0):
361 362 363 364
    """
      Returns the range of acceptable categories
    """
    vrcil = self.getVariationRangeCategoryItemList(
365 366 367 368
                          base_category_list=base_category_list,
                          base=base, root=root, 
                          current_category=current_category,
                          omit_individual_variation=omit_individual_variation)
369
    # display is on left
370
    return [x[1] for x in vrcil]
371

Jean-Paul Smets's avatar
Jean-Paul Smets committed
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
  # Context related methods
  security.declarePublic('newVariationValue')
  def newVariationValue(self, context=None, REQUEST=None, **kw):
    # PERFORMANCE ISSUE
    from Products.ERP5.VariationValue import newVariationValue
    if context is None:
      return newVariationValue(REQUEST=REQUEST, **kw)
    else:
      return newVariationValue(context=context, REQUEST=REQUEST, **kw)

  # Provide a string representation of variations
  security.declarePublic('getVariationText')
  def getVariationText(self):
    """
      Provide a string representation of variation
    """
    category_list = list(self.getVariationCategoryList())
    category_list.sort()
    return '\n'.join(category_list)

InitializeClass(Variated)