Beispiel #1
0
    def getCriterionList(self, **kw):
        """
      Returns the list of criteria which are defined by the Predicate.

      Each criterion is returned in a TempBase instance intended to be
      displayed in a ListBox.

      XXX - It would be better to return criteria in a Criterion class
            instance
    """
        # We do not create PersistentMappings first time we *see* Predicate_view.
        # Instead, we create them first time we modify Predicate document.
        if not self.getCriterionPropertyList():
            return []
        if getattr(aq_base(self), '_identity_criterion', None) is None:
            self._identity_criterion = PersistentMapping()
            self._range_criterion = PersistentMapping()
        criterion_dict = {}
        for p in self.getCriterionPropertyList():
            criterion_dict[p] = newTempBase(self, 'new_%s' % p)
            criterion_dict[p].identity = self._identity_criterion.get(p, None)
            criterion_dict[p].uid = 'new_%s' % p
            criterion_dict[p].property = p
            criterion_dict[p].min = self._range_criterion.get(p,
                                                              (None, None))[0]
            criterion_dict[p].max = self._range_criterion.get(p,
                                                              (None, None))[1]
        criterion_list = criterion_dict.values()
        criterion_list.sort()
        return criterion_list
Beispiel #2
0
    def getCriterionList(self, **kw):
        """
      Returns the list of criteria which are defined by the Predicate.

      Each criterion is returned in a TempBase instance intended to be
      displayed in a ListBox.

      XXX - It would be better to return criteria in a Criterion class
            instance
    """
        # We do not create PersistentMappings first time we *see* Predicate_view.
        # Instead, we create them first time we modify Predicate document.
        if not self.getCriterionPropertyList():
            return []
        if getattr(aq_base(self), "_identity_criterion", None) is None:
            self._identity_criterion = PersistentMapping()
            self._range_criterion = PersistentMapping()
        criterion_dict = {}
        for p in self.getCriterionPropertyList():
            criterion_dict[p] = newTempBase(self, "new_%s" % p)
            criterion_dict[p].identity = self._identity_criterion.get(p, None)
            criterion_dict[p].uid = "new_%s" % p
            criterion_dict[p].property = p
            criterion_dict[p].min = self._range_criterion.get(p, (None, None))[0]
            criterion_dict[p].max = self._range_criterion.get(p, (None, None))[1]
        criterion_list = criterion_dict.values()
        criterion_list.sort()
        return criterion_list
Beispiel #3
0
  def setCriterion(self, property, identity=None, min=None, max=None, **kw):
    """
      This methods sets parameters of a criterion. There is at most one
      criterion per property. Defined parameters are

      identity -- if not None, allows for testing identity of the property
                  with the provided value

      min      -- if not None, allows for testing that the property
                  is greater than min

      max      -- if not None, allows for testing that the property
                  is greater than max

    """
    # XXX 'min' and 'max' are built-in functions.
    if getattr(aq_base(self), '_identity_criterion', None) is None:
      self._identity_criterion = PersistentMapping()
      self._range_criterion = PersistentMapping()
    if identity is not None :
      self._identity_criterion[property] = identity
    if min == '':
      min = None
    if max == '':
      max = None
    if min is None and max is None:
      try:
        del self._range_criterion[property]
      except KeyError:
        pass
    else:
      self._range_criterion[property] = (min, max)
    self.reindexObject()
Beispiel #4
0
  def __init__ (self, id, title='', pdf_file=''):
    # holds all the cell informations, even those not related to this form
    self.all_cells = PersistentMapping()
    # holds the cells related to this pdf form
    self.cells = PersistentMapping()

    # File constructor will set the file content
    File.__init__(self, id, title, pdf_file)
Beispiel #5
0
  def __init__ (self, id, title='', pdf_file=''):
    # holds information about all cells, even those not related to this form
    self.all_cells = PersistentMapping()
    # holds the cells related to this pdf form
    self.cells = PersistentMapping()

    # File constructor will set the file content
    File.__init__(self, id, title, pdf_file)
Beispiel #6
0
    def _setCellRange(self, *args, **kw):
        """Set a new range for a matrix

      Each value for each axis is assigned an integer id.
      If the number of axis changes, everything is reset.
      Otherwise, ids are never changed, so that cells never need to be renamed:
      this means no sort is garanteed, and there can be holes.
      """
        base_id = kw.get('base_id', 'cell')
        # Get (initialize if necessary) index for considered matrix (base_id).
        try:
            index = aq_base(self).index
        except AttributeError:
            index = self.index = PersistentMapping()
        to_delete = []
        try:
            index = index[base_id]
            if len(args) != len(index):
                # The number of axis changes so we'll delete all existing cells and
                # renumber everything from 1.
                to_delete = INFINITE_SET,
                index.clear()
        except KeyError:
            index[base_id] = index = PersistentMapping()
        # For each axis ...
        for i, axis in enumerate(args):
            # ... collect old axis keys and allocate ids for new ones.
            axis = set(axis)
            last_id = -1
            try:
                id_dict = index[i]
            except KeyError:
                index[i] = id_dict = PersistentMapping()
            else:
                delete = set()
                to_delete.append(delete)
                for k, v in id_dict.items():
                    try:
                        axis.remove(k)
                        if last_id < v:
                            last_id = v
                    except KeyError:
                        delete.add(v)
                        del id_dict[k]
                # At this point, last_id contains the greatest id.
            for k in sorted(axis):
                last_id += 1
                id_dict[k] = last_id
        # Remove old cells if any.
        if any(to_delete):
            prefix = base_id + '_'
            prefix_len = len(prefix)
            for cell_id in list(self.objectIds()):
                if cell_id.startswith(prefix):
                    for i, j in enumerate(cell_id[prefix_len:].split('_')):
                        if int(j) in to_delete[i]:
                            self._delObject(cell_id)
                            break
Beispiel #7
0
    def setPredicateCategoryList(self, category_list):
        """
      This method updates a Predicate by implementing an
      AND operation on all predicates (or categories)
      provided in category_list. Categories behave as a
      special kind of predicate which only acts on category
      membership.

      WARNING: this method does not take into account scripts at
      this point.
    """
        category_tool = aq_inner(self.portal_categories)
        base_category_id_list = category_tool.objectIds()
        membership_criterion_category_list = []
        membership_criterion_base_category_list = []
        multimembership_criterion_base_category_list = []
        test_method_id_list = []
        criterion_property_list = []
        # reset criterions
        self._identity_criterion = PersistentMapping()
        self._range_criterion = PersistentMapping()

        for c in category_list:
            bc = c.split('/')[0]
            if bc in base_category_id_list:
                # This is a category
                membership_criterion_category_list.append(c)
                membership_criterion_base_category_list.append(bc)
            else:
                predicate_value = category_tool.resolveCategory(c)
                if predicate_value is not None:
                    criterion_property_list.extend(
                        predicate_value.getCriterionPropertyList())
                    membership_criterion_category_list.extend(
                        predicate_value.getMembershipCriterionCategoryList())
                    membership_criterion_base_category_list.extend(
                        predicate_value.getMembershipCriterionBaseCategoryList(
                        ))
                    multimembership_criterion_base_category_list.extend(
                        predicate_value.
                        getMultimembershipCriterionBaseCategoryList())
                    test_method_id_list += list(
                        predicate_value.getTestMethodIdList() or [])
                    for p in predicate_value.getCriterionList():
                        self.setCriterion(p.property,
                                          identity=p.identity,
                                          min=p.min,
                                          max=p.max)
        self.setCriterionPropertyList(criterion_property_list)
        self._setMembershipCriterionCategoryList(
            membership_criterion_category_list)
        self._setMembershipCriterionBaseCategoryList(
            membership_criterion_base_category_list)
        self._setMultimembershipCriterionBaseCategoryList(
            multimembership_criterion_base_category_list)
        self._setTestMethodIdList(test_method_id_list)
        self.reindexObject()
Beispiel #8
0
 def __init__(self, *args, **kw):
   self.signature_methods = PersistentMapping()
   self.add_signature_method(OAuthSignatureMethod_PLAINTEXT())
   self.add_signature_method(OAuthSignatureMethod_HMAC_SHA1())
   self.consumer = OAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET)
   self.my_request_token = OAuthToken('requestkey', 'requestsecret')
   self.my_access_token = OAuthToken('accesskey', 'accesssecret')
   self.nonce = 'nonce'
   self.verifier = VERIFIER
Beispiel #9
0
 def setLastGeneratedId(self, new_id, id_group=None):
   """
   Set a new last id. This is usefull in order to reset
   a sequence of ids.
   """
   if getattr(aq_base(self), 'dict_ids', None) is None:
     self.dict_ids = PersistentMapping()
   if id_group is not None and id_group != 'None':
     self.dict_ids[id_group] = new_id
Beispiel #10
0
 def getDictLengthIdsItems(self):
   """
     Return a copy of dict_length_ids.
     This is a workaround to access the persistent mapping content from ZSQL
     method to be able to insert initial tuples in the database at creation.
   """
   if getattr(self, 'dict_length_ids', None) is None:
     self.dict_length_ids = PersistentMapping()
   return self.dict_length_ids.items()
def AddNewLocalVariableDict(self):
    """
  Function to add a new Local Variable for a Data Notebook
  """
    new_dict = PersistentMapping()
    variable_dict = PersistentMapping()
    module_dict = PersistentMapping()
    new_dict['variables'] = variable_dict
    new_dict['imports'] = module_dict
    return new_dict
Beispiel #12
0
 def getLastGeneratedId(self, id_group=None, default=None):
   """
   Get the last id generated
   """
   warnings.warn('getLastGeneratedId is deprecated', DeprecationWarning)
   if getattr(aq_base(self), 'dict_ids', None) is None:
     self.dict_ids = PersistentMapping()
   last_id = None
   if id_group is not None and id_group != 'None':
     last_id = self.dict_ids.get(id_group, default)
   return last_id
Beispiel #13
0
    def setPredicateCategoryList(self, category_list):
        """
      This method updates a Predicate by implementing an
      AND operation on all predicates (or categories)
      provided in category_list. Categories behave as a
      special kind of predicate which only acts on category
      membership.

      WARNING: this method does not take into account scripts at
      this point.
    """
        category_tool = aq_inner(self.portal_categories)
        base_category_id_list = category_tool.objectIds()
        membership_criterion_category_list = []
        membership_criterion_base_category_list = []
        multimembership_criterion_base_category_list = []
        test_method_id_list = []
        criterion_property_list = []
        # reset criterions
        self._identity_criterion = PersistentMapping()
        self._range_criterion = PersistentMapping()

        for c in category_list:
            bc = c.split("/")[0]
            if bc in base_category_id_list:
                # This is a category
                membership_criterion_category_list.append(c)
                membership_criterion_base_category_list.append(bc)
            else:
                predicate_value = category_tool.resolveCategory(c)
                if predicate_value is not None:
                    criterion_property_list.extend(predicate_value.getCriterionPropertyList())
                    membership_criterion_category_list.extend(predicate_value.getMembershipCriterionCategoryList())
                    membership_criterion_base_category_list.extend(
                        predicate_value.getMembershipCriterionBaseCategoryList()
                    )
                    multimembership_criterion_base_category_list.extend(
                        predicate_value.getMultimembershipCriterionBaseCategoryList()
                    )
                    test_method_id_list += list(predicate_value.getTestMethodIdList() or [])
                    for p in predicate_value.getCriterionList():
                        self.setCriterion(p.property, identity=p.identity, min=p.min, max=p.max)
        self.setCriterionPropertyList(criterion_property_list)
        self._setMembershipCriterionCategoryList(membership_criterion_category_list)
        self._setMembershipCriterionBaseCategoryList(membership_criterion_base_category_list)
        self._setMultimembershipCriterionBaseCategoryList(multimembership_criterion_base_category_list)
        self._setTestMethodIdList(test_method_id_list)
        self.reindexObject()
Beispiel #14
0
 def newObject(self, object=None, xml=None, simulate=False, # pylint: disable=redefined-builtin
               reset_local_roles=True, reset_workflow=True):
   """
     modify the object with datas from
     the xml (action section)
   """
   args = {}
   if simulate:
     return
   # Retrieve the list of users with a role and delete default roles
   if reset_local_roles:
     user_role_list = [x[0] for x in object.get_local_roles()]
     object.manage_delLocalRoles(user_role_list)
   if getattr(object, 'workflow_history', None) is not None and reset_workflow:
     object.workflow_history = PersistentMapping()
   if xml.prefix == 'xupdate':
     xml = xml[0]
   for subnode in xml.xpath('*'):
     #get only Element nodes (not Comments or Processing instructions)
     if subnode.xpath('name()') not in NOT_EDITABLE_PROPERTY_LIST:
       keyword_type = self.getPropertyType(subnode)
       # This is the case where the property is a list
       keyword = subnode.xpath('name()')
       args[keyword] = self.convertXmlValue(subnode, keyword_type)
     elif subnode.xpath('local-name()') in ADDABLE_PROPERTY_LIST\
                                                          + (XML_OBJECT_TAG,):
       self.addNode(object=object, xml=subnode, force=True)
   # We should first edit the object
   args = self.getFormatedArgs(args=args)
   # edit the object with a dictionnary of arguments,
   # like {"telephone_number":"02-5648"}
   self.editDocument(object=object, **args)
   if getattr(object, 'manage_afterEdit', None) is not None:
     object.manage_afterEdit()
   self.afterNewObject(object)
Beispiel #15
0
 def generateNewId(self,
                   id_group=None,
                   default=None,
                   method=_marker,
                   id_generator=None):
     """
   Generate the next id in the sequence of ids of a particular group
 """
     if id_group in (None, 'None'):
         raise ValueError, '%s is not a valid id_group' % (repr(id_group), )
     # for compatibilty with sql data, must not use id_group as a list
     if not isinstance(id_group, str):
         id_group = repr(id_group)
         warnings.warn(
             'id_group must be a string, other types '
             'are deprecated.', DeprecationWarning)
     if id_generator is None:
         id_generator = 'document'
     if method is not _marker:
         warnings.warn("Use of 'method' argument is deprecated",
                       DeprecationWarning)
     try:
         #use _getLatestGeneratorValue here for that the technical level
         #must not call the method
         last_generator = self._getLatestGeneratorValue(id_generator)
         new_id = last_generator.generateNewId(id_group=id_group, \
                                               default=default)
     except KeyError:
         # XXX backward compatiblity
         if self.getTypeInfo():
             LOG('generateNewId', ERROR, 'while generating id')
             raise
         else:
             # Compatibility code below, in case the last version of erp5_core
             # is not installed yet
             warnings.warn(
                 "You are using an old version of erp5_core to generate"
                 "ids.\nPlease update erp5_core business template to "
                 "use new id generators", DeprecationWarning)
             dict_ids = getattr(aq_base(self), 'dict_ids', None)
             if dict_ids is None:
                 dict_ids = self.dict_ids = PersistentMapping()
             new_id = None
             # Getting the last id
             if default is None:
                 default = 0
             marker = []
             new_id = dict_ids.get(id_group, marker)
             if method is _marker:
                 if new_id is marker:
                     new_id = default
                 else:
                     new_id = new_id + 1
             else:
                 if new_id is marker:
                     new_id = default
                 new_id = method(new_id)
             # Store the new value
             dict_ids[id_group] = new_id
     return new_id
 def _setEncodedPassword(
         self,
         value,
         format='default',  # pylint: disable=redefined-builtin
 ):
     password = getattr(aq_base(self), 'password', None)
     if password is None or isinstance(password, basestring):
         password = self.password = PersistentMapping()
     self.password[format] = value
Beispiel #17
0
 def setLastGeneratedId(self, new_id, id_group=None):
   """
   Set a new last id. This is usefull in order to reset
   a sequence of ids.
   """
   if getattr(aq_base(self), 'dict_ids', None) is None:
     self.dict_ids = PersistentMapping()
   if id_group is not None and id_group != 'None':
     self.dict_ids[id_group] = new_id
Beispiel #18
0
 def getDictLengthIdsItems(self):
   """
     Return a copy of dict_length_ids.
     This is a workaround to access the persistent mapping content from ZSQL
     method to be able to insert initial tuples in the database at creation.
   """
   if getattr(self, 'dict_length_ids', None) is None:
     self.dict_length_ids = PersistentMapping()
   return self.dict_length_ids.items()
Beispiel #19
0
 def getLastGeneratedId(self, id_group=None, default=None):
   """
   Get the last id generated
   """
   warnings.warn('getLastGeneratedId is deprecated', DeprecationWarning)
   if getattr(aq_base(self), 'dict_ids', None) is None:
     self.dict_ids = PersistentMapping()
   last_id = None
   if id_group is not None and id_group != 'None':
     last_id = self.dict_ids.get(id_group, default)
   return last_id
Beispiel #20
0
 def _getResponseHeaderRuleDictForModification(self):
     """
   Retrieve persistent rule dict storage.
   Use only when a modification is requested, to avoid creating useless
   subobjects.
   """
     try:
         return self._response_header_rule_dict
     except AttributeError:
         self._response_header_rule_dict = rule_dict = PersistentMapping()
         return rule_dict
Beispiel #21
0
 def edit(self, **kwd):
     """
   The edit method is overriden so that any time a
   criterion_property_list property is defined, a list of criteria
   is created to match the provided criterion_property_list.
 """
     if getattr(aq_base(self), '_identity_criterion', None) is None:
         self._identity_criterion = PersistentMapping()
         self._range_criterion = PersistentMapping()
     if 'criterion_property_list' in kwd:
         criterion_property_list = kwd['criterion_property_list']
         identity_criterion = PersistentMapping()
         range_criterion = PersistentMapping()
         for criterion in self._identity_criterion.iterkeys():
             if criterion in criterion_property_list:
                 identity_criterion[criterion] = self._identity_criterion[
                     criterion]
         for criterion in self._range_criterion.iterkeys():
             if criterion in criterion_property_list:
                 range_criterion[criterion] = self._range_criterion[
                     criterion]
         self._identity_criterion = identity_criterion
         self._range_criterion = range_criterion
     kwd['reindex_object'] = 1
     return self._edit(**kwd)
Beispiel #22
0
 def edit(self, **kwd):
   """
     The edit method is overriden so that any time a
     criterion_property_list property is defined, a list of criteria
     is created to match the provided criterion_property_list.
   """
   if getattr(aq_base(self), '_identity_criterion', None) is None:
     self._identity_criterion = PersistentMapping()
     self._range_criterion = PersistentMapping()
   if 'criterion_property_list' in kwd:
     criterion_property_list = kwd['criterion_property_list']
     identity_criterion = PersistentMapping()
     range_criterion = PersistentMapping()
     for criterion in self._identity_criterion.iterkeys() :
       if criterion in criterion_property_list :
         identity_criterion[criterion] = self._identity_criterion[criterion]
     for criterion in self._range_criterion.iterkeys() :
       if criterion in criterion_property_list :
         range_criterion[criterion] = self._range_criterion[criterion]
     self._identity_criterion = identity_criterion
     self._range_criterion = range_criterion
   kwd['reindex_object'] = 1
   return self._edit(**kwd)
Beispiel #23
0
    def addVariable(self, id, text, REQUEST=None):
        '''
        Add a variable expression.
        '''
        if self.var_exprs is None:
            self.var_exprs = PersistentMapping()

        expr = None
        if text:
            expr = Expression(str(text))
        self.var_exprs[id] = expr

        if REQUEST is not None:
            return self.manage_variables(REQUEST, 'Variable added.')
Beispiel #24
0
  def getCriterionList(self, **kw):
    """
      Returns the list of criteria which are defined by the Predicate.

      Each criterion is returned in a TempBase instance intended to be
      displayed in a ListBox.

      XXX - It would be better to return criteria in a Criterion class
            instance
    """
    if getattr(aq_base(self), '_identity_criterion', None) is None:
      self._identity_criterion = PersistentMapping()
      self._range_criterion = PersistentMapping()
    criterion_dict = {}
    for p in self.getCriterionPropertyList():
      criterion_dict[p] = newTempBase(self, 'new_%s' % p)
      criterion_dict[p].identity = self._identity_criterion.get(p, None)
      criterion_dict[p].uid = 'new_%s' % p
      criterion_dict[p].property = p
      criterion_dict[p].min = self._range_criterion.get(p, (None, None))[0]
      criterion_dict[p].max = self._range_criterion.get(p, (None, None))[1]
    criterion_list = criterion_dict.values()
    criterion_list.sort()
    return criterion_list
  def _checkDataStructureMigration(self, id_generator):
    """ First, simulate previous data structure which is using
    PersisntentMapping as the storage, then migrate to OOBTree.
    Then, migrate the id generator again from OOBTree to OOBtree
    just to be sure."""
    id_generator_reference  = id_generator.getReference()

    reference_portal_type_dict = {
      'test_sql_non_continuous_increasing':'SQL Non Continuous ' \
                                           'Increasing Id Generator',
      'test_zodb_continuous_increasing':'ZODB Continuous ' \
                                        'Increasing Id Generator'
    }
    try:
      portal_type = reference_portal_type_dict[id_generator_reference]
      self.assertEqual(id_generator.getPortalType(), portal_type)
    except:
      raise ValueError("reference is not valid: %s" % id_generator_reference)

    self._setLastIdDict(id_generator, PersistentMapping()) # simulate previous
    last_id_dict = self._getLastIdDict(id_generator)

    # setUp the data for migration test
    self._setUpLastMaxIdDict(id_generator_reference)

    # test migration: PersistentMapping to OOBTree
    self.assertTrue(isinstance(last_id_dict, PersistentMapping))
    self._assertIdGeneratorLastMaxIdDict(id_generator)
    id_generator.rebuildGeneratorIdDict() # migrate the dict
    self._assertIdGeneratorLastMaxIdDict(id_generator)

    # test migration: OOBTree to OOBTree. this changes nothing, just to be sure
    last_id_dict = self._getLastIdDict(id_generator)
    self.assertTrue(isinstance(last_id_dict, OOBTree))
    self._assertIdGeneratorLastMaxIdDict(id_generator)
    id_generator.rebuildGeneratorIdDict() # migrate the dict
    self._assertIdGeneratorLastMaxIdDict(id_generator)

    # test migration: SQL to OOBTree
    if id_generator.getPortalType() == \
      'SQL Non Continuous Increasing Id Generator':
      self._setLastIdDict(id_generator, OOBTree()) # set empty one
      last_id_dict = self._getLastIdDict(id_generator)
      assert(len(last_id_dict), 0) # 0 because it is empty
      self.assertTrue(isinstance(last_id_dict, OOBTree))
      # migrate the dict totally from sql table in this case
      id_generator.rebuildGeneratorIdDict()
      self._assertIdGeneratorLastMaxIdDict(id_generator)
Beispiel #26
0
  def getResetPasswordKey(self, user_login):
    # generate expiration date
    expiration_date = DateTime() + self._expiration_day

    # generate a random string
    key = self._generateUUID()
    # XXX before r26093, _password_request_dict was initialized by an OOBTree and
    # replaced by a dict on each request, so if it's data structure is not up
    # to date, we update it if needed
    if not isinstance(self._password_request_dict, PersistentMapping):
      LOG('ERP5.PasswordTool', INFO, 'Updating password_request_dict to'
                                     ' PersistentMapping')
      self._password_request_dict = PersistentMapping()

    # register request
    self._password_request_dict[key] = (user_login, expiration_date)
    return key
Beispiel #27
0
    def setVariables(self, ids=[], REQUEST=None):
        ''' set values for Variables set by this state
        '''
        if self.var_exprs is None:
            self.var_exprs = PersistentMapping()

        ve = self.var_exprs

        if REQUEST is not None:
            for id in ve.keys():
                fname = 'varexpr_%s' % id

                val = REQUEST[fname]
                expr = None
                if val:
                    expr = Expression(str(REQUEST[fname]))
                ve[id] = expr

            return self.manage_variables(REQUEST, 'Variables changed.')
Beispiel #28
0
def WorkflowTool_setStatusOf(self, wf_id, ob, status):
    """ Append an entry to the workflow history.

    o Invoked by workflow definitions.
    """
    wfh = None
    has_history = 0
    if getattr(aq_base(ob), 'workflow_history', None) is not None:
        history = ob.workflow_history
        if history is not None:
            has_history = 1
            wfh = history.get(wf_id, None)
            if wfh is not None and not isinstance(wfh, WorkflowHistoryList):
                wfh = WorkflowHistoryList(list(wfh))
                ob.workflow_history[wf_id] = wfh
    if wfh is None:
        wfh = WorkflowHistoryList()
        if not has_history:
            ob.workflow_history = PersistentMapping()
        ob.workflow_history[wf_id] = wfh
    wfh.append(status)
Beispiel #29
0
    def recordProperty(self, id):
        """
    Records the current value of a property.

    id -- ID of the property
    """
        for property_info in self.getPropertyMap():
            if property_info['id'] == id:
                if property_info['type'] in list_types:
                    value = self.getPropertyList(id)
                else:
                    value = self.getProperty(id)
                break
        else:
            if id in self.getBaseCategoryList():
                value = self.getPropertyList(id)
            else:  # should be local property
                value = self.getProperty(id)
        try:
            self._getRecordedPropertyDict()[id] = value
        except AttributeError:
            self._recorded_property_dict = PersistentMapping({id: value})
Beispiel #30
0
 def setTranslationDomain(self, prop_name, domain):
     """
 Set a translation domain for given property.
 """
     try:
         property_domain_dict = aq_base(self)._property_domain_dict
     except AttributeError:
         self._property_domain_dict = property_domain_dict = PersistentMapping(
         )
     else:
         # BBB: If domain dict is not a stand-alone peristent object, changes made
         # to it won't be persistently stored. It used to work because the whole
         # dict was replaced, hence triggering a change on self. But this creates
         # an inconvenient API. For the sake of keeping BT diffs quiet, don't cast
         # that dict into a PersistentMapping.
         if not isinstance(property_domain_dict, Persistent):
             self._p_changed = 1
     property_domain_dict[prop_name] = TranslationInformation(
         prop_name, domain)
     # Reset accessor cache
     self.getPortalObject().portal_types.\
       resetDynamicDocumentsOnceAtTransactionBoundary()
Beispiel #31
0
    def _updateWorkflowHistory(self, document, status_dict):
        """
    Change the state of the object.
    """
        # Create history attributes if needed
        if getattr(aq_base(document), 'workflow_history', None) is None:
            document.workflow_history = PersistentMapping()
            # XXX this _p_changed is apparently not necessary
            document._p_changed = 1

        # Add an entry for the workflow in the history
        workflow_key = self._generateHistoryKey()
        if not document.workflow_history.has_key(workflow_key):
            document.workflow_history[workflow_key] = ()

        # Update history
        document.workflow_history[workflow_key] += (status_dict, )
        # XXX this _p_changed marks the document modified, but the
        # only the PersistentMapping is modified
        document._p_changed = 1
        # XXX this _p_changed is apparently not necessary
        document.workflow_history._p_changed = 1
Beispiel #32
0
    def setStatusOf(self, wf_id, ob, status):
        """ Append an entry to the workflow history.

    o Invoked by workflow definitions.
    """
        from Products.ERP5Type.Workflow import WorkflowHistoryList as NewWorkflowHistoryList
        wfh = None
        has_history = 0
        if getattr(aq_base(ob), 'workflow_history', None) is not None:
            history = ob.workflow_history
            if history is not None:
                has_history = 1
                wfh = history.get(wf_id, None)
                if wfh is not None and not isinstance(wfh,
                                                      NewWorkflowHistoryList):
                    wfh = NewWorkflowHistoryList(wfh)
                    ob.workflow_history[wf_id] = wfh
        if wfh is None:
            wfh = NewWorkflowHistoryList()
            if not has_history:
                ob.workflow_history = PersistentMapping()
            ob.workflow_history[wf_id] = wfh
        wfh.append(status)
Beispiel #33
0
 def dumpDictLengthIdsItems(self):
   """
     Store persistently data from SQL table portal_ids.
   """
   portal_catalog = getattr(self, 'portal_catalog').getSQLCatalog()
   query = getattr(portal_catalog, 'z_portal_ids_dump')
   dict_length_ids = getattr(aq_base(self), 'dict_length_ids', None)
   if dict_length_ids is None:
     dict_length_ids = self.dict_length_ids = PersistentMapping()
   for line in query().dictionaries():
     id_group = line['id_group']
     last_id = line['last_id']
     stored_last_id = self.dict_length_ids.get(id_group)
     if stored_last_id is None:
       self.dict_length_ids[id_group] = Length(last_id)
     else:
       stored_last_id_value = stored_last_id()
       if stored_last_id_value < last_id:
         stored_last_id.set(last_id)
       else:
         if stored_last_id_value > last_id:
           LOG('IdTool', WARNING, 'ZODB value (%r) for group %r is higher ' \
               'than SQL value (%r). Keeping ZODB value untouched.' % \
               (stored_last_id, id_group, last_id))
Beispiel #34
0
    def _checkConsistency(self, fixit=0):
      """
        Constraint API.
      """
      # Check useless cells
      to_delete_set = set()
      error_list = []
      def addError(error_message):
        if fixit:
          error_message += ' (fixed)'
        error = (self.getRelativeUrl(),
                 'XMLMatrix inconsistency',
                 102,
                 error_message)

        error_list.append(error)

      # We make sure first that there is an index
      if getattr(aq_base(self), 'index', None) is None:
        self.index = PersistentMapping()
      # We will check each cell of the matrix the matrix

      # XXX This code assumes the following predicate:
      #   each subobject of an XMLMatrix is either a Cell that needs
      #   consistency checking OR ( is not a Cell, and has an id that is
      #   not like "(\w+_)+(\d+_)*\d+" )
      # But Documents inheriting XMLMatrix can have unrelated, non-cell
      # subobjects, possibly with id looking like some_id_2. If it ever happens,
      # an error will be wrongly raised.
      for obj in self.objectValues():
        object_id = obj.getId()
        # obect_id is equal to something like 'something_quantity_3_2'
        # So we need to check for every object.id if the value
        # looks like good or not. We split the name
        # check each key in index
        # First we make sure this is a cell
        object_id_split = object_id.split('_')

        base_id = None
        cell_coordinate_list = []
        while object_id_split:
          coordinate = None
          try:
            coordinate = int(object_id_split[-1])
          except ValueError:
            # The last item is not a coordinate, object_id_split hence
            # only contains the base_id elements
            base_id = '_'.join(object_id_split)
            break
          else:
            cell_coordinate_list.insert(0, coordinate)
            # the last item is a coordinate not part of base_id
            object_id_split.pop()

        current_dimension = len(cell_coordinate_list)
        if current_dimension > 0 and base_id is not None:
            if not self.index.has_key(base_id):
              # The matrix does not have this base_id
              addError("There is no index for base_id %s" % base_id)
              to_delete_set.add(object_id)
              continue

            # Check empty indices.
            empty_list = []
            base_item = self.index[base_id]
            for key, value in base_item.iteritems():
              if value is None or len(value) == 0:
                addError("There is no id for the %dth axis of base_id %s" % (key, base_id))
                empty_list.append(key)
            if fixit:
              for i in empty_list:
                del base_item[key]

            len_id = len(base_item)
            if current_dimension != len_id:
              addError("Dimension of cell is %s but should be %s" % (current_dimension,
                                                                     len_id))
              to_delete_set.add(object_id)
            else :
              for i, coordinate in enumerate(cell_coordinate_list):
                if coordinate >= len(base_item[i]):
                  addError("Cell %s is out of bound" % object_id)
                  to_delete_set.add(object_id)
                  break

      if fixit and len(to_delete_set) > 0:
        self.manage_delObjects(list(to_delete_set))

      return error_list
 def _solveBySplitting(self, activate_kw=None):
   """
   contains all the logic to split. This method is convenient in case
   another solver needs it.
   """
   solver_dict = {}
   new_movement_list = []
   configuration_dict = self.getConfigurationPropertyDict()
   delivery_dict = {}
   for simulation_movement in self.getDeliveryValueList():
     delivery_dict.setdefault(simulation_movement.getDeliveryValue(),
                              []).append(simulation_movement)
   for movement, simulation_movement_list in delivery_dict.iteritems():
     decision_quantity = movement.getQuantity()
     delivery_solver = self.getParentValue().newContent(
       portal_type=configuration_dict['delivery_solver'],
       temp_object=True)
     delivery_solver.setDeliveryValueList(simulation_movement_list)
     # Update the quantity using delivery solver algorithm
     split_list = delivery_solver.setTotalQuantity(decision_quantity,
                                                   activate_kw=activate_kw)
     # Create split movements
     for (simulation_movement, split_quantity) in split_list:
       split_index = 0
       simulation_id = simulation_movement.getId().split("_split_")[0]
       new_id = "%s_split_%s" % (simulation_id, split_index)
       applied_rule = simulation_movement.getParentValue()
       while getattr(aq_base(applied_rule), new_id, None) is not None:
         split_index += 1
         new_id = "%s_split_%s" % (simulation_id, split_index)
       # Copy at same level
       kw = _getPropertyAndCategoryList(simulation_movement)
       kw.update(delivery=None, quantity=split_quantity)
       new_movement = applied_rule.newContent(
         new_id, simulation_movement.getPortalType(),
         activate_kw=activate_kw, **kw)
       new_movement_list.append(new_movement)
       # Dirty code until IPropertyRecordable is revised.
       # Merge original simulation movement recorded property to new one.
       recorded_property_dict = simulation_movement._getRecordedPropertyDict(None)
       if recorded_property_dict:
         new_movement_recorded_property_dict = new_movement._getRecordedPropertyDict(None)
         if new_movement_recorded_property_dict is None:
           new_movement_recorded_property_dict = new_movement._recorded_property_dict = PersistentMapping()
         new_movement_recorded_property_dict.update(recorded_property_dict)
       # record zero quantity property, because this was originally zero.
       # without this, splitanddefer after accept decision does not work
       # properly.
       current_quantity = new_movement.getQuantity()
       new_movement.setQuantity(0)
       new_movement.recordProperty('quantity')
       new_movement.setQuantity(current_quantity)
       start_date = configuration_dict.get('start_date', None)
       if start_date is not None:
         new_movement.recordProperty('start_date')
         new_movement.setStartDate(start_date)
       stop_date = configuration_dict.get('stop_date', None)
       if stop_date is not None:
         new_movement.recordProperty('stop_date')
         new_movement.setStopDate(stop_date)
       if activate_kw:
         new_movement.setDefaultActivateParameterDict({})
       simulation_movement.expand(activate_kw=activate_kw)
       new_movement.expand(activate_kw=activate_kw)
   # Finish solving
   if self.getPortalObject().portal_workflow.isTransitionPossible(
     self, 'succeed'):
     self.succeed()
   solver_dict["new_movement_list"] = new_movement_list
   return solver_dict
Beispiel #36
0
class IdTool(BaseTool):
  """
    This tools handles the generation of IDs.
  """
  id = 'portal_ids'
  meta_type = 'ERP5 Id Tool'
  portal_type = 'Id Tool'
  title = 'Id Generators'

  # Declarative Security
  security = ClassSecurityInfo()

  security.declareProtected( Permissions.ManagePortal, 'manage_overview' )
  manage_overview = DTMLFile( 'explainIdTool', _dtmldir )

  def newContent(self, *args, **kw):
    """
      the newContent is overriden to not use generateNewId
    """
    if id not in kw:
      new_id = self._generateNextId()
      if new_id is not None:
        kw['id'] = new_id
      else:
        raise ValueError('Failed to gererate id')
    return BaseTool.newContent(self, *args, **kw)

  def _get_id(self, id):
    """
      _get_id is overrided to not use generateNewId
      It is used for example when an object is cloned
    """
    if self._getOb(id, None) is None :
      return id
    return self._generateNextId()

  @caching_instance_method(id='IdTool._getLatestIdGenerator',
    cache_factory='erp5_content_long')
  def _getLatestIdGenerator(self, reference):
    """
      Tries to find the id_generator with the latest version
      from the current object.
      Use the low-level to create a site without catalog
    """
    assert reference
    id_last_generator = None
    version_last_generator = 0
    for generator in self.objectValues():
      if generator.getReference() == reference:
        # Version Property Sheet defines 'version' property as a 'string'
        version = int(generator.getVersion())
        if version > version_last_generator:
          id_last_generator = generator.getId()
          version_last_generator = version
    if id_last_generator is None:
      raise KeyError(repr(reference))
    return id_last_generator

  def _getLatestGeneratorValue(self, id_generator):
    """
      Return the last generator with the reference
    """
    return self._getOb(self._getLatestIdGenerator(id_generator))

  security.declareProtected(Permissions.AccessContentsInformation,
                            'generateNewId')
  def generateNewId(self, id_group=None, default=None, method=_marker,
                    id_generator=None, poison=False):
    """
      Generate the next id in the sequence of ids of a particular group
    """
    if id_group in (None, 'None'):
      raise ValueError('%r is not a valid id_group' % id_group)
    # for compatibilty with sql data, must not use id_group as a list
    if not isinstance(id_group, str):
      id_group = repr(id_group)
      warnings.warn('id_group must be a string, other types '
                    'are deprecated.', DeprecationWarning)
    if id_generator is None:
      id_generator = 'document'
    if method is not _marker:
      warnings.warn("Use of 'method' argument is deprecated", DeprecationWarning)
    try:
      #use _getLatestGeneratorValue here for that the technical level
      #must not call the method
      last_generator = self._getLatestGeneratorValue(id_generator)
      new_id = last_generator.generateNewId(
        id_group=id_group,
        default=default,
        poison=poison,
      )
    except KeyError:
      # XXX backward compatiblity
      if self.getTypeInfo():
        LOG('generateNewId', ERROR, 'while generating id')
        raise
      else:
        # Compatibility code below, in case the last version of erp5_core
        # is not installed yet
        warnings.warn("You are using an old version of erp5_core to generate"
                      "ids.\nPlease update erp5_core business template to "
                      "use new id generators", DeprecationWarning)
        dict_ids = getattr(aq_base(self), 'dict_ids', None)
        if dict_ids is None:
          dict_ids = self.dict_ids = PersistentMapping()
        new_id = None
        # Getting the last id
        if default is None:
          default = 0
        marker = []
        new_id = dict_ids.get(id_group, marker)
        if method is _marker:
          if new_id is marker:
            new_id = default
          else:
            new_id = new_id + 1
        else:
          if new_id is marker:
            new_id = default
          new_id = method(new_id)
        # Store the new value
        dict_ids[id_group] = new_id
    return new_id

  security.declareProtected(Permissions.AccessContentsInformation,
                            'generateNewIdList')
  def generateNewIdList(self, id_group=None, id_count=1, default=None,
                        store=_marker, id_generator=None, poison=False):
    """
      Generate a list of next ids in the sequence of ids of a particular group
    """
    if id_group in (None, 'None'):
      raise ValueError('%r is not a valid id_group' % id_group)
    # for compatibilty with sql data, must not use id_group as a list
    if not isinstance(id_group, str):
      id_group = repr(id_group)
      warnings.warn('id_group must be a string, other types '
                    'are deprecated.', DeprecationWarning)
    if id_generator is None:
      id_generator = 'uid'
    if store is not _marker:
      warnings.warn("Use of 'store' argument is deprecated.",
                    DeprecationWarning)
    try:
      #use _getLatestGeneratorValue here for that the technical level
      #must not call the method
      last_generator = self._getLatestGeneratorValue(id_generator)
      new_id_list = last_generator.generateNewIdList(id_group=id_group,
                         id_count=id_count, default=default, poison=poison)
    except (KeyError, ValueError):
      # XXX backward compatiblity
      if self.getTypeInfo():
        LOG('generateNewIdList', ERROR, 'while generating id')
        raise
      else:
        # Compatibility code below, in case the last version of erp5_core
        # is not installed yet
        warnings.warn("You are using an old version of erp5_core to generate"
                      "ids.\nPlease update erp5_core business template to "
                      "use new id generators", DeprecationWarning)
        new_id = None
        if default is None:
          default = 1
        # XXX It's temporary, a New API will be implemented soon
        #     the code will be change
        portal = self.getPortalObject()
        try:
          query = portal.IdTool_zGenerateId
          commit = portal.IdTool_zCommit
        except AttributeError:
          portal_catalog = portal.portal_catalog.getSQLCatalog()
          query = portal_catalog.z_portal_ids_generate_id
          commit = portal_catalog.z_portal_ids_commit
        try:
          result = query(id_group=id_group, id_count=id_count, default=default)
        finally:
          commit()
        new_id = result[0]['LAST_INSERT_ID()']
        if store:
          if getattr(aq_base(self), 'dict_length_ids', None) is None:
            # Length objects are stored in a persistent mapping: there is one
            # Length object per id_group.
            self.dict_length_ids = PersistentMapping()
          if self.dict_length_ids.get(id_group) is None:
            self.dict_length_ids[id_group] = Length(new_id)
          self.dict_length_ids[id_group].set(new_id)
        if six.PY2:
          new_id_list = range(new_id - id_count, new_id)
        else:
          new_id_list = list(range(new_id - id_count, new_id))
    return new_id_list

  security.declareProtected(Permissions.ModifyPortalContent,
                            'initializeGenerator')
  def initializeGenerator(self, id_generator=None, all=False):
    """
    Initialize generators. This is mostly used when a new ERP5 site
    is created. Some generators will need to do some initialization like
    creating SQL Database, prepare some data in ZODB, etc
    """
    if not all:
      #Use _getLatestGeneratorValue here for that the technical level
      #must not call the method
      last_generator = self._getLatestGeneratorValue(id_generator)
      last_generator.initializeGenerator()
    else:
      # recovery all the generators and initialize them
      for generator in self.objectValues(\
                       portal_type='Application Id Generator'):
        generator.initializeGenerator()

  security.declareProtected(Permissions.ModifyPortalContent,
                            'clearGenerator')
  def clearGenerator(self, id_generator=None, all=False):
    """
    Clear generators data. This can be usefull when working on a
    development instance or in some other rare cases. This will
    loose data and must be use with caution

    This can be incompatible with some particular generator implementation,
    in this case a particular error will be raised (to be determined and
    added here)
    """
    if not all:
      #Use _getLatestGeneratorValue here for that the technical level
      #must not call the method
      last_generator = self._getLatestGeneratorValue(id_generator)
      last_generator.clearGenerator()

    else:
      if len(self.objectValues()) == 0:
        # compatibility with old API
        self.getPortalObject().IdTool_zDropTable()
        self.getPortalObject().IdTool_zCreateTable()
      for generator in self.objectValues(\
                       portal_type='Application Id Generator'):
        generator.clearGenerator()

  ## XXX Old API deprecated
  #backward compatibility
  security.declareProtected(Permissions.AccessContentsInformation,
                           'generateNewLengthIdList')
  generateNewLengthIdList = generateNewIdList

  security.declareProtected(Permissions.AccessContentsInformation,
                            'getLastLengthGeneratedId')
  def getLastLengthGeneratedId(self, id_group, default=None):
    """
    Get the last length id generated
    """
    warnings.warn('getLastLengthGeneratedId is deprecated',
                   DeprecationWarning)
    # check in persistent mapping if exists
    if getattr(aq_base(self), 'dict_length_ids', None) is not None:
      last_id = self.dict_length_ids.get(id_group)
      if last_id is not None:
        return last_id.value - 1
    # otherwise check in mysql
    # XXX It's temporary, a New API will be implemented soon
    #     the code will be change
    portal = self.getPortalObject()
    try:
      query = portal.IdTool_zGetLastId
    except AttributeError:
      query = portal.portal_catalog.getSQLCatalog().z_portal_ids_get_last_id
    result = query(id_group=id_group)
    if len(result):
      try:
        return result[0]['last_id']
      except KeyError:
        return result[0]['LAST_INSERT_ID()']
    return default

  security.declareProtected(Permissions.AccessContentsInformation,
                            'getLastGeneratedId')
  def getLastGeneratedId(self, id_group=None, default=None):
    """
    Get the last id generated
    """
    warnings.warn('getLastGeneratedId is deprecated', DeprecationWarning)
    if getattr(aq_base(self), 'dict_ids', None) is None:
      self.dict_ids = PersistentMapping()
    last_id = None
    if id_group is not None and id_group != 'None':
      last_id = self.dict_ids.get(id_group, default)
    return last_id

  security.declareProtected(Permissions.ModifyPortalContent,
                            'setLastGeneratedId')
  def setLastGeneratedId(self, new_id, id_group=None):
    """
    Set a new last id. This is usefull in order to reset
    a sequence of ids.
    """
    if getattr(aq_base(self), 'dict_ids', None) is None:
      self.dict_ids = PersistentMapping()
    if id_group is not None and id_group != 'None':
      self.dict_ids[id_group] = new_id

  security.declareProtected(Permissions.AccessContentsInformation,
                           'generateNewLengthId')
  def generateNewLengthId(self, id_group=None, default=None, store=_marker):
     """Generates an Id using a conflict free id generator. Deprecated.
     """
     warnings.warn('generateNewLengthId is deprecated.\n'
                   'Use generateNewIdList with a sql id_generator',
                   DeprecationWarning)
     if store is not _marker:
       return self.generateNewIdList(id_group=id_group,
                        id_count=1, default=default, store=store)[0]
     return self.generateNewIdList(id_group=id_group,
                        id_count=1, default=default)[0]

  security.declareProtected(Permissions.AccessContentsInformation,
                            'getDictLengthIdsItems')
  def getDictLengthIdsItems(self):
    """
      Return a copy of dict_length_ids.
      This is a workaround to access the persistent mapping content from ZSQL
      method to be able to insert initial tuples in the database at creation.
    """
    if getattr(self, 'dict_length_ids', None) is None:
      self.dict_length_ids = PersistentMapping()
    return self.dict_length_ids.items()

  security.declarePrivate('dumpDictLengthIdsItems')
  def dumpDictLengthIdsItems(self):
    """
      Store persistently data from SQL table portal_ids.
    """
    portal_catalog = getattr(self, 'portal_catalog').getSQLCatalog()
    query = getattr(portal_catalog, 'z_portal_ids_dump')
    dict_length_ids = getattr(aq_base(self), 'dict_length_ids', None)
    if dict_length_ids is None:
      dict_length_ids = self.dict_length_ids = PersistentMapping()
    for line in query().dictionaries():
      id_group = line['id_group']
      last_id = line['last_id']
      stored_last_id = self.dict_length_ids.get(id_group)
      if stored_last_id is None:
        self.dict_length_ids[id_group] = Length(last_id)
      else:
        stored_last_id_value = stored_last_id()
        if stored_last_id_value < last_id:
          stored_last_id.set(last_id)
        else:
          if stored_last_id_value > last_id:
            LOG('IdTool', WARNING, 'ZODB value (%r) for group %r is higher ' \
                'than SQL value (%r). Keeping ZODB value untouched.' % \
                (stored_last_id, id_group, last_id))
Beispiel #37
0
class PasswordTool(BaseTool):
  """
    PasswordTool is used to allow a user to change its password
  """
  title = 'Password Tool'
  id = 'portal_password'
  meta_type = 'ERP5 Password Tool'
  portal_type = 'Password Tool'
  allowed_types = ()

  # Declarative Security
  security = ClassSecurityInfo()

  security.declareProtected(Permissions.ManagePortal, 'manage_overview' )
  manage_overview = DTMLFile( 'explainPasswordTool', _dtmldir )


  _expiration_day = 1
  _password_request_dict = {}

  def __init__(self, id=None):
    if id is None:
      id = self.__class__.id
    self._password_request_dict = PersistentMapping()
    # XXX no call to BaseTool.__init__ ?
    # BaseTool.__init__(self, id)

  security.declareProtected('Manage users', 'getResetPasswordKey')
  def getResetPasswordKey(self, user_login):
    # generate expiration date
    expiration_date = DateTime() + self._expiration_day

    # generate a random string
    key = self._generateUUID()
    # XXX before r26093, _password_request_dict was initialized by an OOBTree and
    # replaced by a dict on each request, so if it's data structure is not up
    # to date, we update it if needed
    if not isinstance(self._password_request_dict, PersistentMapping):
      LOG('ERP5.PasswordTool', INFO, 'Updating password_request_dict to'
                                     ' PersistentMapping')
      self._password_request_dict = PersistentMapping()

    # register request
    self._password_request_dict[key] = (user_login, expiration_date)
    return key

  security.declareProtected('Manage users', 'getResetPasswordUrl')
  def getResetPasswordUrl(self, user_login=None, key=None, site_url=None):
    if user_login is not None:
      # XXX Backward compatibility
      key = self.getResetPasswordKey(user_login)

    parameter = urlencode(dict(reset_key=key))
    method = self._getTypeBasedMethod("getSiteUrl")
    if method is not None:
      base_url = method()
    else:
      base_url = "%s/portal_password/PasswordTool_viewResetPassword" % (
        site_url,)
    url = "%s?%s" %(base_url, parameter)
    return url

  security.declareProtected('Manage users', 'getResetPasswordUrl')
  def getExpirationDateForKey(self, key=None):
    return self._password_request_dict[key][1]


  def mailPasswordResetRequest(self, user_login=None, REQUEST=None,
                              notification_message=None, sender=None,
                              store_as_event=False):
    """
    Create a random string and expiration date for request
    Parameters:
    user_login -- Reference of the user to send password reset link
    REQUEST -- Request object
    notification_message -- Notification Message Document used to build the email.
                            As default, a standart text will be used.
    sender -- Sender (Person or Organisation) of the email.
            As default, the default email address will be used
    store_as_event -- whenever CRM is available, store
                        notifications as events
    """
    if REQUEST is None:
      REQUEST = get_request()

    if user_login is None:
      user_login = REQUEST["user_login"]

    site_url = self.getPortalObject().absolute_url()
    if REQUEST and 'came_from' in REQUEST:
      site_url = REQUEST.came_from

    msg = None
    # check user exists, and have an email
    user_list = self.getPortalObject().acl_users.\
                      erp5_users.getUserByLogin(user_login)
    if len(user_list) == 0:
      msg = translateString("User ${user} does not exist.",
                            mapping={'user':user_login})
    else:
      # We use checked_permission to prevent errors when trying to acquire
      # email from organisation
      user = user_list[0]
      email_value = user.getDefaultEmailValue(
              checked_permission='Access content information')
      if email_value is None or not email_value.asText():
        msg = translateString(
            "User ${user} does not have an email address, please contact site "
            "administrator directly", mapping={'user':user_login})
    if msg:
      if REQUEST is not None:
        parameter = urlencode(dict(portal_status_message=msg))
        ret_url = '%s/login_form?%s' % \
                  (site_url, parameter)
        return REQUEST.RESPONSE.redirect( ret_url )
      return msg

    key = self.getResetPasswordKey(user_login=user_login)
    url = self.getResetPasswordUrl(key=key, site_url=site_url)

    # send mail
    message_dict = {'instance_name':self.getPortalObject().getTitle(),
                    'reset_password_link':url,
                    'expiration_date':self.getExpirationDateForKey(key)}

    if notification_message is None:
      subject = translateString("[${instance_name}] Reset of your password",
          mapping={'instance_name': self.getPortalObject().getTitle()})
      subject = subject.translate()
      message = translateString("\nYou requested to reset your ${instance_name}"\
                " account password.\n\n" \
                "Please copy and paste the following link into your browser: \n"\
                "${reset_password_link}\n\n" \
                "Please note that this link will be valid only one time, until "\
                "${expiration_date}.\n" \
                "After this date, or after having used this link, you will have to make " \
                "a new request\n\n" \
                "Thank you",
                mapping=message_dict)
      message = message.translate()
    else:
      subject = notification_message.getTitle()
      if notification_message.getContentType() == "text/html":
        message = notification_message.asEntireHTML(substitution_method_parameter_dict=message_dict)
      else:
        message = notification_message.asText(substitution_method_parameter_dict=message_dict)

    self.getPortalObject().portal_notifications.sendMessage(sender=sender, recipient=[user,],
                                                            subject=subject, message=message,
                                                            store_as_event=store_as_event)
    if REQUEST is not None:
      msg = translateString("An email has been sent to you.")
      parameter = urlencode(dict(portal_status_message=msg))
      ret_url = '%s/login_form?%s' % (site_url, parameter)
      return REQUEST.RESPONSE.redirect( ret_url )

  def _generateUUID(self, args=""):
    """
    Generate a unique id that will be used as url for password
    """
    # this code is based on
    # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/213761
    # by Carl Free Jr
    # as uuid module is only available in pyhton 2.5
    t = long( time.time() * 1000 )
    r = long( random.random()*100000000000000000L )
    try:
      a = socket.gethostbyname( socket.gethostname() )
    except:
      # if we can't get a network address, just imagine one
      a = random.random()*100000000000000000L
    data = ' '.join((str(t), str(r), str(a), str(args)))
    data = md5_new(data).hexdigest()
    return data


  def resetPassword(self, reset_key=None, REQUEST=None):
    """
    """
    # XXX-Aurel : is it used ?
    if REQUEST is None:
      REQUEST = get_request()
    user_login, expiration_date = self._password_request_dict.get(reset_key, (None, None))
    site_url = self.getPortalObject().absolute_url()
    if REQUEST and 'came_from' in REQUEST:
      site_url = REQUEST.came_from
    if reset_key is None or user_login is None:
      ret_url = '%s/login_form' % site_url
      return REQUEST.RESPONSE.redirect( ret_url )

    # check date
    current_date = DateTime()
    if current_date > expiration_date:
      msg = translateString("Date has expire.")
      parameter = urlencode(dict(portal_status_message=msg))
      ret_url = '%s/login_form?%s' % (site_url, parameter)
      return REQUEST.RESPONSE.redirect( ret_url )

    # redirect to form as all is ok
    REQUEST.set("password_key", reset_key)
    return self.reset_password_form(REQUEST=REQUEST)


  def removeExpiredRequests(self, **kw):
    """
    Browse dict and remove expired request
    """
    current_date = DateTime()
    for key, (login, date) in self._password_request_dict.items():
      if date < current_date:
        self._password_request_dict.pop(key)


  def changeUserPassword(self, password, password_key, password_confirm=None,
                         user_login=None, REQUEST=None, **kw):
    """
    Reset the password for a given login
    """
    # check the key
    register_user_login, expiration_date = self._password_request_dict.get(
                                                    password_key, (None, None))

    current_date = DateTime()
    msg = None
    if REQUEST is None:
      REQUEST = get_request()
    site_url = self.getPortalObject().absolute_url()
    if REQUEST and 'came_from' in REQUEST:
      site_url = REQUEST.came_from
    if self.getWebSiteValue():
      site_url = self.getWebSiteValue().absolute_url()
    if register_user_login is None:
      msg = "Key not known. Please ask reset password."
    elif user_login is not None and register_user_login != user_login:
      msg = translateString("Bad login provided.")
    elif current_date > expiration_date:
      msg = translateString("Date has expire.")
    if msg is not None:
      if REQUEST is not None:
        parameter = urlencode(dict(portal_status_message=msg))
        ret_url = '%s/login_form?%s' % (site_url, parameter)
        return REQUEST.RESPONSE.redirect( ret_url )
      else:
        return msg

    # all is OK, change password and remove it from request dict
    self._password_request_dict.pop(password_key)
    persons = self.getPortalObject().acl_users.erp5_users.getUserByLogin(register_user_login)
    person = persons[0]
    person._forceSetPassword(password)
    person.reindexObject()
    if REQUEST is not None:
      msg = translateString("Password changed.")
      parameter = urlencode(dict(portal_status_message=msg))
      ret_url = '%s/login_form?%s' % (site_url, parameter)
      return REQUEST.RESPONSE.redirect( ret_url )
Beispiel #38
0
class OAuthTool(BaseTool):
  """
    OAuthTool is used to allow API authentification
  """
  title = 'OAuth Tool'
  id = 'portal_oauth'
  meta_type = 'ERP5 OAuth Tool'
  portal_type = 'OAuth Tool'
  allowed_types = ()

  # Declarative Security
  security = ClassSecurityInfo()

  security.declareProtected(Permissions.ManagePortal, 'manage_overview' )
  manage_overview = DTMLFile( 'explainOAuthTool', _dtmldir )
  signature_methods = {}
  
  def __init__(self, *args, **kw):
    self.signature_methods = PersistentMapping()
    self.add_signature_method(OAuthSignatureMethod_PLAINTEXT())
    self.add_signature_method(OAuthSignatureMethod_HMAC_SHA1())
    self.consumer = OAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET)
    self.my_request_token = OAuthToken('requestkey', 'requestsecret')
    self.my_access_token = OAuthToken('accesskey', 'accesssecret')
    self.nonce = 'nonce'
    self.verifier = VERIFIER


  def add_signature_method(self, signature_method):
      self.signature_methods[signature_method.get_name()] = signature_method
      return self.signature_methods

  def fetch_request_token(self, oauth_request):
      """Processes a request_token request and returns the
      request token on success.
      """
      try:
          # Get the request token for authorization.
          token = self._get_token(oauth_request, 'request')
      except OAuthError:
          LOG("initial token request called", 300, "")
          # No token required for the initial token request.
          version = self._get_version(oauth_request)
          consumer = self._get_consumer(oauth_request)
          try:
              callback = self.get_callback(oauth_request)
          except OAuthError:
              callback = None # 1.0, no callback specified.
          self._check_signature(oauth_request, consumer, None)
          # Fetch a new token.
          if consumer.key == self.consumer.key:
            if callback:
                # want to check here if callback is sensible
                # for mock store, we assume it is
                LOG("setting callback method %s" %(callback), 300, "")
                self.my_request_token.set_callback(callback)
            token = self.my_request_token
          else:
            token = None

      return token

  def fetch_access_token(self, oauth_request):
      """Processes an access_token request and returns the
      access token on success.
      """
      version = self._get_version(oauth_request)
      consumer = self._get_consumer(oauth_request)
      try:
          verifier = self._get_verifier(oauth_request)
      except OAuthError:
          verifier = None
      # Get the request token.
      token = self._get_token(oauth_request, 'request')
      self._check_signature(oauth_request, consumer, token)

      if consumer.key == self.consumer.key and \
             token.key == self.my_request_token.key and \
             verifier == self.verifier:
        # want to check here if token is authorized
        # for mock store, we assume it is
        return self.my_access_token
      return None

  def verify_request(self, oauth_request):
      """Verifies an api call and checks all the parameters."""
      # -> consumer and token
      version = self._get_version(oauth_request)
      consumer = self._get_consumer(oauth_request)
      # Get the access token.
      token = self._get_token(oauth_request, 'access')
      self._check_signature(oauth_request, consumer, token)
      parameters = oauth_request.get_nonoauth_parameters()
      return consumer, token, parameters

  def authorize_token(self, token, user):
      """Authorize a request token."""
      if token.key == self.my_request_token.key:
        return self.my_request_token
      return None

  def get_callback(self, oauth_request):
      """Get the callback URL."""
      return oauth_request.get_parameter('oauth_callback')

  def build_authenticate_header(self, realm=''):
      """Optional support for the authenticate header."""
      return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}

  def _get_version(self, oauth_request):
      """Verify the correct version request for this server."""
      try:
          version = oauth_request.get_parameter('oauth_version')
      except:
          version = VERSION
      if version and version != VERSION:
          raise OAuthError('OAuth version %s not supported.' % str(version))
      return version

  def _get_signature_method(self, oauth_request):
      """Figure out the signature with some defaults."""
      try:
          signature_method = oauth_request.get_parameter(
              'oauth_signature_method')
      except:
          signature_method = SIGNATURE_METHOD
      try:
          # Get the signature method object.
          signature_method = self.signature_methods[signature_method]
      except:
          signature_method_names = ', '.join(self.signature_methods.keys())
          raise OAuthError('Signature method %s not supported try one of the '
              'following: %s' % (signature_method, signature_method_names))

      return signature_method

  def _get_consumer(self, oauth_request):
      consumer_key = oauth_request.get_parameter('oauth_consumer_key')
      if consumer_key != self.consumer.key:
          raise OAuthError('Invalid consumer.')
      return self.consumer

  def _get_token(self, oauth_request, token_type='access'):
      """Try to find the token for the provided request token key."""
      token_field = oauth_request.get_parameter('oauth_token')
      token_attrib = getattr(self, 'my_%s_token' % token_type)
      if token_field == token_attrib.key:
          try:
              callback = self.get_callback(oauth_request)
          except OAuthError:
              callback = None # 1.0, no callback specified.
              LOG("setting callback method %s" %(callback), 300, "in _get_token")
          token_attrib.set_callback(callback)
          return token_attrib
      else:
          raise OAuthError('Invalid %s token: %s' % (token_type, token_field))

  def _get_verifier(self, oauth_request):
      return oauth_request.get_parameter('oauth_verifier')

  def _check_signature(self, oauth_request, consumer, token):
      timestamp, nonce = oauth_request._get_timestamp_nonce()
      self._check_timestamp(timestamp)
      self._check_nonce(consumer, token, nonce)
      signature_method = self._get_signature_method(oauth_request)
      try:
          signature = oauth_request.get_parameter('oauth_signature')
      except:
          raise OAuthError('Missing signature.')
      # Validate the signature.
      valid_sig = signature_method.check_signature(oauth_request, consumer,
          token, signature)
      if not valid_sig:
          key, base = signature_method.build_signature_base_string(
              oauth_request, consumer, token)
          raise OAuthError('Invalid signature. Expected signature base '
              'string: %s' % base)
      built = signature_method.build_signature(oauth_request, consumer, token)

  def _check_timestamp(self, timestamp):
      """Verify that timestamp is recentish.""" 
      timestamp = int(timestamp)
      now = int(time.time())
      lapsed = abs(now - timestamp)
      if lapsed > TIMESTAMP_THRESHOLD:
          raise OAuthError('Expired timestamp: given %d and now %s has a '
              'greater difference than threshold %d' %
              (timestamp, now, TIMESTAMP_THRESHOLD))

  def _check_nonce(self, consumer, token, nonce):
      """Verify that the nonce is uniqueish."""
      if token and consumer.key == self.consumer.key and \
             (token.key == self.my_request_token.key or token.key == self.my_access_token.key) \
             and nonce == self.nonce:
        raise OAuthError('Nonce already used: %s' % str(nonce))
  
  def send_oauth_error(self, err, REQUEST):
    """ return error """
    print err
    REQUEST.response.setStatus(status=401, reason=err)
    return REQUEST.response

  def call(self, REQUEST=None, **kw):
    """ this method handle all the call on the portal """
    
    path = REQUEST.getURL()
    headers = REQUEST._auth
    command = REQUEST['REQUEST_METHOD']
    parameters = REQUEST.form
    postdata = None
    LOG("-------call--------", 300, "\npath %s\nheader %s\ncommand %s\nparameters %s\n\nXXXXXXXXXXXXXXX" %(path, headers, command, parameters))
    # if command == "POST": 
    #   import pdb
    #   pdb.set_trace()

    # construct the oauth request from the request parameters
    oauth_request = OAuthRequest.from_request(command, path, headers=headers, parameters=parameters, query_string=postdata)
    # request token
    if path.startswith(REQUEST_TOKEN_URL):
        try:
            # create a request token
            token = self.fetch_request_token(oauth_request)
            LOG("Return %s" %(token.to_string()), 300, "")
            return token.to_string()
            # # send okay response
            # self.send_response(200, 'OK')
            # self.end_headers()
            # # return the token
            # self.wfile.write(token.to_string())
        except OAuthError, err:
            raise
            LOG("Error returned %s" %(err,), 300, "")
            self.send_oauth_error(err, REQUEST)
        return

    # user authorization
    if path.startswith(AUTHORIZATION_URL):
        try:
            return self.manage_oauth_authorize(oauth_token=self._get_token(oauth_request, "request"),
                                               oauth_callback=self.get_callback(oauth_request))
            # get the request token
            # token = self.fetch_request_token(oauth_request)
            # # authorize the token (kind of does nothing for now)
            # token = self.authorize_token(token, None)
            # token.set_verifier(VERIFIER)
            # return token.get_callback_url()
            # send okay response
            # self.send_response(200, 'OK')
            # self.end_headers()
            # # return the callback url (to show server has it)
            # self.wfile.write(token.get_callback_url())
        except OAuthError, err:
            self.send_oauth_error(err, REQUEST)
        return
Beispiel #39
0
class Predicate(XMLObject):
  """
    A Predicate object defines a list of criterions
    which can be applied to test a document or to search for documents.

    Predicates are defined by a combination of PropertySheet values
    (ex. membership_criterion_list) and criterion list (ex. quantity
    is between 0 and 10). An additional script can be associated to
    extend the standard Predicate semantic with any additional
    script based test.

    The idea between Predicate in ERP5 is to have a simple
    way of defining simple predicates which can be later
    searched through a simplistic rule based engine and which can
    still provide complete expressivity through additional scripting.

    The approach is intended to provide the expressivity of a rule
    based system without the burden of building a fully expressive
    rule engine.
  """
  meta_type = 'ERP5 Predicate'
  portal_type = 'Predicate'
  add_permission = Permissions.AddPortalContent
  isPredicate = ConstantGetter('isPredicate', value=True)

  # Declarative security
  security = ClassSecurityInfo()
  security.declareObjectProtected(Permissions.AccessContentsInformation)

  # Declarative properties
  property_sheets = ( PropertySheet.Base
                    , PropertySheet.Predicate
                    , PropertySheet.CategoryCore
                    , PropertySheet.SortIndex
                    )

  # Declarative interfaces
  zope.interface.implements( interfaces.IPredicate, )

  security.declareProtected( Permissions.AccessContentsInformation, 'test' )
  def test(self, context, tested_base_category_list=None, 
           strict_membership=0, isMemberOf=None, **kw):
    """
      A Predicate can be tested on a given context.
      Parameters can passed in order to ignore some conditions.

      - tested_base_category_list:  this is the list of category that we do
        want to test. For example, we might want to test only the
        destination or the source of a predicate.
      - if strict_membership is specified, we should make sure that we
        are strictly a member of tested categories
      - isMemberOf can be a function caching results for
        CategoryTool.isMemberOf: it is always called with given 'context' and
        'strict_membership' values, and different categories.
    """
    self = self.asPredicate()
    if self is None:
      # asPredicate returned None, so this predicate never applies.
      # But if we reach this it is because catalog is not up to date.
      return False

    result = 1
    if getattr(aq_base(self), '_identity_criterion', None) is None:
      self._identity_criterion = PersistentMapping()
      self._range_criterion = PersistentMapping()
#    LOG('PREDICATE TEST', 0,
#        'testing %s on context of %s' % \
#        (self.getRelativeUrl(), context.getRelativeUrl()))
    for property, value in self._identity_criterion.iteritems():
      if isinstance(value, (list, tuple)):
        result = context.getProperty(property) in value
      else:
        result = context.getProperty(property) == value
#      LOG('predicate test', 0,
#          '%s after prop %s : %s == %s' % \
#          (result, property, context.getProperty(property), value))
      if not result:
        return result
    for property, (min, max) in self._range_criterion.iteritems():
      value = context.getProperty(property)
      if min is not None:
        result = value >= min
#        LOG('predicate test', 0,
#            '%s after prop %s : %s >= %s' % \
#            (result, property, value, min))
        if not result:
          return result
      if max is not None:
        result = value < max
#        LOG('predicate test', 0,
#            '%s after prop %s : %s < %s' % \
#            (result, property, value, max))
        if not result:
          return result
    multimembership_criterion_base_category_list = \
        self.getMultimembershipCriterionBaseCategoryList()
    membership_criterion_base_category_list = \
        self.getMembershipCriterionBaseCategoryList()
    tested_base_category = {}
#    LOG('predicate test', 0,
#        'categories will be tested in multi %s single %s as %s' % \
#        (multimembership_criterion_base_category_list,
#        membership_criterion_base_category_list,
#        self.getMembershipCriterionCategoryList()))
    # Test category memberships. Enable the read-only transaction cache
    # because this part is strictly read-only, and context.isMemberOf
    # is very expensive when the category list has many items.
    if isMemberOf is None:
      isMemberOf = context._getCategoryTool().isMemberOf
    with readOnlyTransactionCache():
      for c in self.getMembershipCriterionCategoryList():
        bc = c.split('/', 1)[0]
        if tested_base_category_list is None or bc in tested_base_category_list:
          if bc in multimembership_criterion_base_category_list:
            if not isMemberOf(context, c, strict_membership=strict_membership):
              return 0
          elif bc in membership_criterion_base_category_list and \
               not tested_base_category.get(bc):
            tested_base_category[bc] = \
              isMemberOf(context, c, strict_membership=strict_membership)
    if 0 in tested_base_category.itervalues():
      return 0

    # Test method calls
    test_method_id_list = self.getTestMethodIdList()
    if test_method_id_list is not None :
      for test_method_id in test_method_id_list :
        if test_method_id is not None:
          method = getattr(context,test_method_id)
          try:
            result = method(self)
          except TypeError:
            if method.func_code.co_argcount != isinstance(method, MethodType):
              raise
            # backward compatibilty with script that takes no argument
            warn('Predicate %s uses an old-style method (%s) that does not'
                 ' take the predicate as argument' % (
               self.getRelativeUrl(), method.__name__), DeprecationWarning)
            result = method()
#          LOG('predicate test', 0,
#              '%s after method %s ' % (result, test_method_id))
          if not result:
            return result
    test_tales_expression = self.getTestTalesExpression()
    if test_tales_expression != 'python: True':
      expression = Expression(test_tales_expression)
      from Products.ERP5Type.Utils import createExpressionContext
      # evaluate a tales expression with the tested value as context
      result = expression(createExpressionContext(context))
    return result

  @UnrestrictedMethod
  def _unrestrictedResolveCategory(self, *args):
    # Categories used on predicate can be not available to user query, which
    # shall be applied with predicate.
    portal_categories = getToolByName(self, 'portal_categories')
    return portal_categories.resolveCategory(*args)

  security.declareProtected( Permissions.AccessContentsInformation,
                             'buildSQLQuery' )
  def buildSQLQuery(self, strict_membership=0, table='category',
                          join_table='catalog', join_column='uid',
                          **kw):
    """
      A Predicate can be rendered as an SQL expression. This
      can be used to generate SQL requests in reports or in
      catalog search queries.

      XXX - This method is not implemented yet
    """
    # Build the identity criterion
    catalog_kw = {}
    catalog_kw.update(kw) # query_table, REQUEST, ignore_empty_string, **kw
    criterion_list = self.getCriterionList()
    # BBB: accessor is not present on old Predicate property sheet.
    if criterion_list or getattr(self, 'isEmptyPredicateValid', lambda: True)():
      for criterion in criterion_list:
        if criterion.min and criterion.max:
          catalog_kw[criterion.property] = { 'query' : (criterion.min, criterion.max),
                                             'range' : 'minmax'
                                           }
        elif criterion.min:
          catalog_kw[criterion.property] = { 'query' : criterion.min,
                                             'range' : 'min'
                                           }
        elif criterion.max:
          catalog_kw[criterion.property] = { 'query' : criterion.max,
                                             'range' : 'max'
                                           }
        else:
          # if a filter was passed as argument
          if catalog_kw.has_key(criterion.property):
            if isinstance(catalog_kw[criterion.property], (tuple, list)):
              catalog_filter_set = set(catalog_kw[criterion.property])
            else:
              catalog_filter_set = set([catalog_kw[criterion.property]])
            if isinstance(criterion.identity, (tuple, list)):
              parameter_filter_set = set(criterion.identity)
            else:
              parameter_filter_set = set([criterion.identity])
            catalog_kw[criterion.property] = \
                list(catalog_filter_set.intersection(parameter_filter_set))
          else:
            catalog_kw[criterion.property] = criterion.identity
    else:
      # By catalog definition, no object has uid 0, so this condition forces an
      # empty result.
      catalog_kw['uid'] = 0

    portal_catalog = getToolByName(self, 'portal_catalog')

    from_table_dict = {}

    # First build SQL for membership criteria
    # It would be much nicer if all this was handled by the catalog in a central place
    membership_dict = {}
    for base_category in self.getMembershipCriterionBaseCategoryList():
      membership_dict[base_category] = [] # Init dict with valid base categories
    for category in self.getMembershipCriterionCategoryList():
      base_category = category.split('/')[0] # Retrieve base category
      if membership_dict.has_key(base_category):
        category_value = self._unrestrictedResolveCategory(category, None)
        if category_value is not None:
          table_alias = "single_%s_%s" % (table, base_category)
          from_table_dict[table_alias] = 'category'
          membership_dict[base_category].append(category_value.asSQLExpression(
                                          strict_membership=strict_membership,
                                          table=table_alias,
                                          base_category=base_category))
    membership_select_list = []
    for expression_list in membership_dict.values():
      or_expression = ' OR '.join(expression_list)
      if or_expression:
        membership_select_list.append('( %s )' % or_expression)

    # Then build SQL for multimembership_dict criteria
    multimembership_dict = {}
    for base_category in self.getMultimembershipCriterionBaseCategoryList():
      multimembership_dict[base_category] = [] # Init dict with valid base categories
    join_count = 0
    for category in self.getMembershipCriterionCategoryList():
      base_category = category.split('/')[0] # Retrieve base category
      if multimembership_dict.has_key(base_category):
        category_value = self._unrestrictedResolveCategory(category)
        if category_value is not None:
          join_count += 1
          table_alias = "multi_%s_%s" % (table, join_count)
          from_table_dict[table_alias] = 'category'
          multimembership_dict[base_category].append(category_value.asSQLExpression(
                                          strict_membership=strict_membership,
                                          table=table_alias,
                                          base_category=base_category))
    multimembership_select_list = []
    for expression_list in multimembership_dict.values():
      and_expression = ' AND '.join(expression_list)
      if and_expression:
        multimembership_select_list.append(and_expression)

    # Build the join where expression
    join_select_list = []
    for k in from_table_dict.iterkeys():
      join_select_list.append('%s.%s = %s.uid' % (join_table, join_column, k))

    sql_text = ' AND '.join(join_select_list + membership_select_list +
                            multimembership_select_list)

    # Now merge identity and membership criteria
    if len(sql_text):
      catalog_kw['where_expression'] = SQLQuery(sql_text)
    else:
      catalog_kw['where_expression'] = ''
    # force implicit join
    catalog_kw['implicit_join'] = True
    sql_query = portal_catalog.buildSQLQuery(**catalog_kw)
    # XXX from_table_list is None most of the time after the explicit_join work
    for alias, table in sql_query['from_table_list']:
      if from_table_dict.has_key(alias):
        raise KeyError, "The same table is used twice for an identity criterion and for a membership criterion"
      from_table_dict[alias] = table
    sql_query['from_table_list'] = from_table_dict.items()
    return sql_query

  # Compatibililty SQL Sql
  security.declareProtected( Permissions.AccessContentsInformation, 'buildSqlQuery' )
  buildSqlQuery = buildSQLQuery

  security.declareProtected( Permissions.AccessContentsInformation, 'asSQLExpression' )
  def asSQLExpression(self, strict_membership=0, table='category'):
    """
      A Predicate can be rendered as an SQL expression. This
      can be used to generate SQL requests in reports or in
      catalog search queries.
    """
    return self.buildSQLQuery(strict_membership=strict_membership, table=table)['where_expression']

  # Compatibililty SQL Sql
  security.declareProtected( Permissions.AccessContentsInformation, 'asSqlExpression' )
  asSqlExpression = asSQLExpression

  security.declareProtected( Permissions.AccessContentsInformation, 'asSQLJoinExpression' )
  def asSQLJoinExpression(self, strict_membership=0, table='category', join_table='catalog', join_column='uid'):
    """
    """
    table_list = self.buildSQLQuery(strict_membership=strict_membership, table=table)['from_table_list']
    sql_text_list = map(lambda (a,b): '%s AS %s' % (b,a), filter(lambda (a,b): a != join_table, table_list))
    return ' , '.join(sql_text_list)

  # Compatibililty SQL Sql
  security.declareProtected( Permissions.AccessContentsInformation, 'asSqlJoinExpression' )
  asSqlJoinExpression = asSQLJoinExpression

  def searchResults(self, **kw):
    """
    """
    portal_catalog = getToolByName(self, 'portal_catalog')
    return portal_catalog.searchResults(build_sql_query_method=self.buildSQLQuery,**kw)

  def countResults(self, REQUEST=None, used=None, **kw):
    """
    """
    portal_catalog = getToolByName(self, 'portal_catalog')
    return portal_catalog.countResults(build_sql_query_method=self.buildSQLQuery,**kw)

  security.declareProtected( Permissions.AccessContentsInformation, 'getCriterionList' )
  def getCriterionList(self, **kw):
    """
      Returns the list of criteria which are defined by the Predicate.

      Each criterion is returned in a TempBase instance intended to be
      displayed in a ListBox.

      XXX - It would be better to return criteria in a Criterion class
            instance
    """
    if getattr(aq_base(self), '_identity_criterion', None) is None:
      self._identity_criterion = PersistentMapping()
      self._range_criterion = PersistentMapping()
    criterion_dict = {}
    for p in self.getCriterionPropertyList():
      criterion_dict[p] = newTempBase(self, 'new_%s' % p)
      criterion_dict[p].identity = self._identity_criterion.get(p, None)
      criterion_dict[p].uid = 'new_%s' % p
      criterion_dict[p].property = p
      criterion_dict[p].min = self._range_criterion.get(p, (None, None))[0]
      criterion_dict[p].max = self._range_criterion.get(p, (None, None))[1]
    criterion_list = criterion_dict.values()
    criterion_list.sort()
    return criterion_list

  security.declareProtected( Permissions.ModifyPortalContent, 'setCriterion' )
  def setCriterion(self, property, identity=None, min=None, max=None, **kw):
    """
      This methods sets parameters of a criterion. There is at most one
      criterion per property. Defined parameters are

      identity -- if not None, allows for testing identity of the property
                  with the provided value

      min      -- if not None, allows for testing that the property
                  is greater than min

      max      -- if not None, allows for testing that the property
                  is greater than max

    """
    # XXX 'min' and 'max' are built-in functions.
    if getattr(aq_base(self), '_identity_criterion', None) is None:
      self._identity_criterion = PersistentMapping()
      self._range_criterion = PersistentMapping()
    if identity is not None :
      self._identity_criterion[property] = identity
    if min == '':
      min = None
    if max == '':
      max = None
    if min is None and max is None:
      try:
        del self._range_criterion[property]
      except KeyError:
        pass
    else:
      self._range_criterion[property] = (min, max)
    self.reindexObject()

  security.declareProtected( Permissions.ModifyPortalContent, 'edit' )
  def edit(self, **kwd):
    """
      The edit method is overriden so that any time a
      criterion_property_list property is defined, a list of criteria
      is created to match the provided criterion_property_list.
    """
    if getattr(aq_base(self), '_identity_criterion', None) is None:
      self._identity_criterion = PersistentMapping()
      self._range_criterion = PersistentMapping()
    if 'criterion_property_list' in kwd:
      criterion_property_list = kwd['criterion_property_list']
      identity_criterion = PersistentMapping()
      range_criterion = PersistentMapping()
      for criterion in self._identity_criterion.iterkeys() :
        if criterion in criterion_property_list :
          identity_criterion[criterion] = self._identity_criterion[criterion]
      for criterion in self._range_criterion.iterkeys() :
        if criterion in criterion_property_list :
          range_criterion[criterion] = self._range_criterion[criterion]
      self._identity_criterion = identity_criterion
      self._range_criterion = range_criterion
    kwd['reindex_object'] = 1
    return self._edit(**kwd)

  # Predicate fusion method
  security.declareProtected( Permissions.ModifyPortalContent, 'setPredicateCategoryList' )
  def setPredicateCategoryList(self, category_list):
    """
      This method updates a Predicate by implementing an
      AND operation on all predicates (or categories)
      provided in category_list. Categories behave as a
      special kind of predicate which only acts on category
      membership.

      WARNING: this method does not take into account scripts at
      this point.
    """
    category_tool = aq_inner(self.portal_categories)
    base_category_id_list = category_tool.objectIds()
    membership_criterion_category_list = []
    membership_criterion_base_category_list = []
    multimembership_criterion_base_category_list = []
    test_method_id_list = []
    criterion_property_list = []
    # reset criterions
    self._identity_criterion = PersistentMapping()
    self._range_criterion = PersistentMapping()

    for c in category_list:
      bc = c.split('/')[0]
      if bc in base_category_id_list:
        # This is a category
        membership_criterion_category_list.append(c)
        membership_criterion_base_category_list.append(bc)
      else:
        predicate_value = category_tool.resolveCategory(c)
        if predicate_value is not None:
          criterion_property_list.extend(predicate_value.getCriterionPropertyList())
          membership_criterion_category_list.extend(
                      predicate_value.getMembershipCriterionCategoryList())
          membership_criterion_base_category_list.extend(
                      predicate_value.getMembershipCriterionBaseCategoryList())
          multimembership_criterion_base_category_list.extend(
                      predicate_value.getMultimembershipCriterionBaseCategoryList())
          test_method_id_list += list(predicate_value.getTestMethodIdList() or [])
          for p in predicate_value.getCriterionList():
            self.setCriterion(p.property, identity=p.identity, min=p.min, max=p.max)
    self.setCriterionPropertyList(criterion_property_list)
    self._setMembershipCriterionCategoryList(membership_criterion_category_list)
    self._setMembershipCriterionBaseCategoryList(membership_criterion_base_category_list)
    self._setMultimembershipCriterionBaseCategoryList(multimembership_criterion_base_category_list)
    self._setTestMethodIdList(test_method_id_list)
    self.reindexObject()

  security.declareProtected(Permissions.AccessContentsInformation, 'generatePredicate')
  def generatePredicate(self, multimembership_criterion_base_category_list=(),
                        membership_criterion_base_category_list=(),
                        criterion_property_list=(),
                        identity_criterion=None,
                        range_criterion=None,):
    """
    This method generates a new temporary predicate based on an ad-hoc
    interpretation of local properties of an object. For example,
    a start_range_min property will be interpreted as a way to define
    a min criterion on start_date.

    The purpose of this method is to be called from
    a script called PortalType_asPredicate to ease the generation of
    Predicates based on range properties. It should be considered mostly
    as a trick to simplify the development of Predicates and forms.
    """
    new_membership_criterion_category_list = list(self.getMembershipCriterionCategoryList())
    new_membership_criterion_base_category_list = list(self.getMembershipCriterionBaseCategoryList())
    new_multimembership_criterion_base_category_list = list(self.getMultimembershipCriterionBaseCategoryList())

    for base_category in multimembership_criterion_base_category_list:
      category_list = self.getProperty(base_category + '_list')
      if category_list is not None and len(category_list)>0:
        for category in category_list:
          new_membership_criterion_category_list.append(base_category + '/' + category)
        if base_category not in new_multimembership_criterion_base_category_list:
          new_multimembership_criterion_base_category_list.append(base_category)

    for base_category in membership_criterion_base_category_list:
      category_list = self.getProperty(base_category + '_list')
      if category_list is not None and len(category_list)>0:
        for category in category_list:
          new_membership_criterion_category_list.append(base_category + '/' + category)
        if base_category not in new_membership_criterion_base_category_list:
          new_membership_criterion_base_category_list.append(base_category)

    new_criterion_property_list =  list(self.getCriterionPropertyList())

    # We need to build new criteria for asContext, and we should not
    # modify the original, so we always make copies. Since the usage is
    # temporary, use dicts instead of persistent mappings.
    new_identity_criterion = dict(getattr(self, '_identity_criterion', None) or
                                  {})
    new_identity_criterion.update(identity_criterion or {})
    new_range_criterion = dict(getattr(self, '_range_criterion', None) or {})
    new_range_criterion.update(range_criterion or {})

    # Look at local properties and make it criterion properties
    for property in criterion_property_list:
      if property not in self.getCriterionPropertyList() \
        and property in self.propertyIds():
          new_criterion_property_list.append(property)
          property_min = property + '_range_min'
          property_max = property + '_range_max'
          if getattr(self, 'get%s' % convertToUpperCase(property), None) is not None\
            and self.getProperty(property) is not None:
            new_identity_criterion[property] = self.getProperty(property)
          elif getattr(self, 'get%s' % convertToUpperCase(property_min), None) is not None:
            min = self.getProperty(property_min)
            max = self.getProperty(property_max)
            new_range_criterion[property] = (min,max)
    # Return a new context with new properties, like if
    # we have a predicate with local properties
    new_self = self.asContext(
        membership_criterion_category=new_membership_criterion_category_list,
        membership_criterion_base_category=new_membership_criterion_base_category_list,
        multimembership_criterion_base_category=new_multimembership_criterion_base_category_list,
        criterion_property_list=new_criterion_property_list,
        _identity_criterion=new_identity_criterion,
        _range_criterion=new_range_criterion)

    return new_self

  security.declareProtected(Permissions.AccessContentsInformation,
                            'asPredicate')
  def asPredicate(self, script_id=None):
    """
      This method tries to convert the current Document into a predicate
      looking up methods named Class_asPredictae, MetaType_asPredicate, PortalType_asPredicate
    """
    cache = getTransactionalVariable()
    key = id(self), script_id
    if 'asPredicate' in cache:
      cache = cache['asPredicate']
      if key in cache:
        return cache[key]
    else:
      cache = cache['asPredicate'] = {}
    script = self._getTypeBasedMethod('asPredicate', script_id)
    if script is not None:
      self = script()
    cache[key] = self
    return self

  def searchPredicate(self, **kw):
    """
      Returns a list of documents matching the predicate

      TO BE IMPLEMENTED using portal_catalog(**kw)
    """
    pass

  security.declareProtected(Permissions.AccessContentsInformation,
                            'getMembershipCriterionCategoryList')
  def getMembershipCriterionCategoryList(self, filter=None, **kw):
    """
    If filter is specified, return category only or document only
    in membership_criterion_category values.
    """
    all_list = self._baseGetMembershipCriterionCategoryList()
    if filter in ('category', 'document'):
      portal_categories = self.getPortalObject().portal_categories
      result_dict = {'category':[], 'document':[]}
      for x in all_list:
        try:
          if portal_categories.restrictedTraverse(x).getPortalType() == \
             'Category':
            result_dict['category'].append(x)
          else:
            result_dict['document'].append(x)
        except KeyError:
          result_dict['document'].append(x)
      return result_dict[filter]
    else:
      return all_list

  security.declareProtected(Permissions.ModifyPortalContent,
                            'setMembershipCriterionDocumentList' )
  def setMembershipCriterionDocumentList(self, document_list):
    """
    Appends to membership_criterion_category values.
    """
    return self.setMembershipCriterionCategoryList(
      (self.getMembershipCriterionCategoryList() + document_list))
Beispiel #40
0
 def generateNewIdList(self, id_group=None, id_count=1, default=None,
                       store=_marker, id_generator=None, poison=False):
   """
     Generate a list of next ids in the sequence of ids of a particular group
   """
   if id_group in (None, 'None'):
     raise ValueError('%r is not a valid id_group' % id_group)
   # for compatibilty with sql data, must not use id_group as a list
   if not isinstance(id_group, str):
     id_group = repr(id_group)
     warnings.warn('id_group must be a string, other types '
                   'are deprecated.', DeprecationWarning)
   if id_generator is None:
     id_generator = 'uid'
   if store is not _marker:
     warnings.warn("Use of 'store' argument is deprecated.",
                   DeprecationWarning)
   try:
     #use _getLatestGeneratorValue here for that the technical level
     #must not call the method
     last_generator = self._getLatestGeneratorValue(id_generator)
     new_id_list = last_generator.generateNewIdList(id_group=id_group,
                        id_count=id_count, default=default, poison=poison)
   except (KeyError, ValueError):
     # XXX backward compatiblity
     if self.getTypeInfo():
       LOG('generateNewIdList', ERROR, 'while generating id')
       raise
     else:
       # Compatibility code below, in case the last version of erp5_core
       # is not installed yet
       warnings.warn("You are using an old version of erp5_core to generate"
                     "ids.\nPlease update erp5_core business template to "
                     "use new id generators", DeprecationWarning)
       new_id = None
       if default is None:
         default = 1
       # XXX It's temporary, a New API will be implemented soon
       #     the code will be change
       portal = self.getPortalObject()
       try:
         query = portal.IdTool_zGenerateId
         commit = portal.IdTool_zCommit
       except AttributeError:
         portal_catalog = portal.portal_catalog.getSQLCatalog()
         query = portal_catalog.z_portal_ids_generate_id
         commit = portal_catalog.z_portal_ids_commit
       try:
         result = query(id_group=id_group, id_count=id_count, default=default)
       finally:
         commit()
       new_id = result[0]['LAST_INSERT_ID()']
       if store:
         if getattr(aq_base(self), 'dict_length_ids', None) is None:
           # Length objects are stored in a persistent mapping: there is one
           # Length object per id_group.
           self.dict_length_ids = PersistentMapping()
         if self.dict_length_ids.get(id_group) is None:
           self.dict_length_ids[id_group] = Length(new_id)
         self.dict_length_ids[id_group].set(new_id)
       if six.PY2:
         new_id_list = range(new_id - id_count, new_id)
       else:
         new_id_list = list(range(new_id - id_count, new_id))
   return new_id_list
Beispiel #41
0
 def updateMovementCollection(rule, context, *args, **kw):
   orig_updateMovementCollection(rule, context, *args, **kw)
   new_parent = context.getParentValue()
   for sm in context.getMovementList():
     delivery = sm.getDelivery()
     if delivery:
       sm_dict = old_dict.pop(delivery)
     else:
       sm_dict = order_dict[new_parent]
     order_dict[sm] = sm_dict
     k = get_matching_key(sm)
     sm_list = sm_dict.pop(k, ())
     if len(sm_list) > 1:
       # Heuristic to find matching old simulation movements for the
       # currently expanded applied rule. We first try to preserve same
       # tree structure (new & old parent SM match), then we look for an
       # old possible parent that is in the same branch.
       try:
         old_parent = old_dict[new_parent]
       except KeyError:
         old_parent = simulation_tool
       best_dict = {}
       for old_sm in sm_list:
         parent = old_sm.getParentValue().getParentValue()
         if parent is old_parent:
           parent = None
         elif not (parent.aq_inContextOf(old_parent) or
                   old_parent.aq_inContextOf(parent)):
           continue
         best_dict.setdefault(parent, []).append(old_sm)
       try:
         best_sm_list = best_dict[None]
       except KeyError:
         best_sm_list, = best_dict.values()
       if len(best_sm_list) < len(sm_list):
         sm_dict[k] = list(set(sm_list).difference(best_sm_list))
       sm_list = best_sm_list
       if len(sm_list) > 1:
         kw = sm.__dict__.copy()
     # We may have several old matching SM, e.g. in case of split.
     for old_sm in sm_list:
       movement = old_sm.getDeliveryValue()
       if sm is None:
         sm = context.newContent(portal_type=rule.movement_type)
         sm.__dict__ = dict(kw, **sm.__dict__)
         order_dict[sm] = sm_dict
       if delivery:
         assert movement.getRelativeUrl() == delivery
       elif movement is not None:
         sm._setDeliveryValue(movement)
         delivery_set.add(sm.getExplanationValue())
       try:
         sm.delivery_ratio = old_sm.aq_base.delivery_ratio
       except AttributeError:
         pass
       recorded_property_dict = {}
       edit_kw = {}
       kw['quantity'] = 0
       for tester in rule._getUpdatingTesterList():
         old = get_original_property_dict(tester, old_sm, sm, movement)
         if old is not None:
           new = tester.getUpdatablePropertyDict(sm, movement)
           if old != new:
             edit_kw.update(old)
             if 'quantity' in new and old_sm is not sm_list[-1]:
               quantity = new.pop('quantity')
               kw['quantity'] = quantity - old.pop('quantity')
               if new != old or sm.quantity != quantity:
                 raise NotImplementedError # quantity_unit/efficiency ?
             else:
               recorded_property_dict.update(new)
       if recorded_property_dict:
         sm._recorded_property_dict = PersistentMapping(
           recorded_property_dict)
       sm._edit(**edit_kw)
       old_dict[sm] = old_sm
       sm = None
Beispiel #42
0
class XMLMatrix(Folder):
    """
        A mix-in class which provides a matrix like
        access to objects. Matrices are of any dimension.
        A single XMLMatrix may contain multiple matrices,
        of different dimension. Each matrix is associated to
        a so-called 'base_id'.

        We still must make XMLMatrix a subclass of Base so
        that we can inherit from ExtensionClass.Base
        which is required for multiple inheritance to work
        as expected. Read this for more information:

        http://www.daa.com.au/pipermail/pygtk/2001-May/001180.html

        In our case, we will use Folder because we want to inherit
        from Folder consistency checking

    """

    # Declarative security
    security = ClassSecurityInfo()

    # Matrix Methods
    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCell' )
    def getCell(self, *kw , **kwd):
      """
          Access a cell at row and column
      """
      if getattr(aq_base(self), 'index', None) is None:
        return None

      base_id = kwd.get('base_id', "cell")
      if not self.index.has_key(base_id):
        return None

      cell_id = self.keyToId(kw, base_id = base_id)
      if cell_id is None:
        return None
      return self.get(cell_id)

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellProperty' )
    def getCellProperty(self, *kw , **kwd):
      """
          Get a property of a cell at row and column
      """
      base_id= kwd.get('base_id', "cell")
      cell = self.getCell(*kw, **kwd)
      if cell is None:
        return None

      return cell.getProperty(base_id)

    security.declareProtected( Permissions.AccessContentsInformation,
                               'hasCell' )
    def hasCell(self, *kw , **kwd):
      """
          Checks if matrix corresponding to base_id contains cell specified
          by *kw coordinates.
      """
      return self.getCell(*kw, **kwd) is not None

    security.declareProtected( Permissions.AccessContentsInformation,
                               'hasCellContent' )
    def hasCellContent(self, base_id='cell'):
      """
          Checks if matrix corresponding to base_id contains cells.
      """
      aq_self = aq_base(self)

      if getattr(aq_self, 'index', None) is None:
        return 0

      if not self.index.has_key(base_id):
        return 0

      for i in self.getCellIds(base_id=base_id):
        if hasattr(self, i): # We should try to use aq_self if possible but XXX
          return 1

      return 0

    security.declareProtected( Permissions.AccessContentsInformation,
                               'hasInRange' )
    def hasInRange(self, *kw , **kwd):
      """
          Checks if *kw coordinates are in the range of the
        matrix in kwd['base_id'].
      """
      if getattr(aq_base(self), 'index', None) is None:
        return 0

      base_id = kwd.get('base_id', "cell")
      if not self.index.has_key(base_id):
        return 0
      base_item = self.index[base_id]
      for i, my_id in enumerate(kw):
        if not base_item.has_key(i) or not base_item[i].has_key(my_id):
          return 0

      return 1

    security.declareProtected( Permissions.ModifyPortalContent,
                               '_setCellRange' )
    def _setCellRange(self, *kw, **kwd):
      """
          Set a new range for a matrix.
          If needed, it will resize and/or reorder matrix content.
      """
      movement = {} # Maps original cell id to its new id for each moved cell.
      new_index = PersistentMapping()

      base_id = kwd.get('base_id', 'cell')
      if getattr(aq_base(self), 'index', None) is None:
        self.index = PersistentMapping()

      # Return if previous range is the same
      current_range = self.getCellRange(base_id=base_id)
      if current_range == list(kw): # kw is a tuple
        return

      # Create the new index for the range given in *kw
      # *kw practical example:
      # kw = [ ['color/blue', 'color/red'], ['size/S', 'size/L']]
      for i, index_ids in enumerate(kw):
        temp = PersistentMapping()
        for j, my_id in enumerate(index_ids):
          temp[my_id] = j
        new_index[i] = temp

      if self.index.has_key(base_id):
        # Compute cell movement from their position in previous range to their
        # position in the new range.
        for i, i_value in self.index[base_id].iteritems():
          if new_index.has_key(i):
            temp = {}
            for my_id, my_value in i_value.iteritems():
              temp[my_value] = new_index[i].get(my_id)
            movement[i] = temp

      # List all valid cell ids for current base_id.
      object_id_list = []
      for obj in self.objectValues():
        object_id = obj.getId()
        if object_id.find(base_id) == 0:
          # Check that all '_'-separated fields are of int type.
          if (object_id) > len(base_id):
            try:
              int(object_id[len(base_id)+1:].split('_')[0])
              test = self._getOb(object_id) # If the object was created
                                            # during this transaction,
                                            # then we do not need to
                                            # work on it
              object_id_list.append(object_id)
            except (ValueError, KeyError):
              pass

      # Prepend 'temp_' to all cells, to avoid id conflicts while renaming.
      for object_id in object_id_list:
        new_name = 'temp_' + object_id
        obj = self._getOb(object_id)
        self._delObject(object_id)
        obj.id = new_name
        self._setObject(new_name, aq_base(obj))

      # Rename all cells to their final name.
      for object_id in object_id_list:
        # Retrieve the place of the object, for movement_0_0 it is ['0','0']
        object_place = object_id[len(base_id)+1:].split('_')
        to_delete = 1
        # We must have the same number of dimensions
        if len(object_place) == len(new_index):
          # Let us browse each dimension of the previous index
          for i in range(len(object_place)):
            # Build each part of the nex id by looking up int. values
            old_place = int(object_place[i])
            # We are looking inside the movement dictionnary where
            # we should move the object, so for example
            # 'qantity_2_5' is renamed as 'quantity_4_3'
            if movement.has_key(i):
              if movement[i].has_key(old_place):
                # Replace the place of the cell only if there where a change
                if (movement[i][old_place]) != None:
                  object_place[i] = str(movement[i][old_place])
                  to_delete = 0
                else:
                  object_place[i] = None

            # XXX In this case, we delete every cell wich are not in the
            # movement dictionnary, may be this is very bad, so may be
            # we need to add an extra check here, ie if
            # if movement[i].has_key(old_place) returns None,
            # We may want to keep the cell, but only if we are sure
            # the movement dictionnary will not define a new cell with this id

        new_object_id_list = []

        temp_object_id = 'temp_' + object_id
        o = self._getOb(temp_object_id)
        if not to_delete and not (None in object_place):
          self._delObject(temp_object_id) # In all cases, we have
                                          # to remove the temp object
          object_place.insert(0, base_id)
          new_name = '_'.join(object_place)
          o.id = new_name
          new_object_id_list.extend(new_name)
          self._setObject(new_name, aq_base(o))

          if new_name != object_id:
            # Theses two lines are very important, if the object is renamed
            # then we must uncatalog the previous one
            o.unindexObject(path='%s/%s' % (self.getUrl() , object_id))
            # Force a new uid to be allocated, because unindexObject creates
            # an activity which will later delete lines from catalog based
            # on their uid, and it is not garanted that indexation will happen
            # after this deletion.
            # It is bad to waste uids, but this data structure offers no
            # alternative because cell id gives its index in the matrix,
            # so reordering axes requires the cell id to change.
            # XXX: It can be improved, but requires most of this file to be
            # rewritten, and compatibility code must be written as data
            # structure would most probably change.
            o.uid = None
          o.reindexObject() # we reindex in case position has changed
                            # uid should be consistent
        else:
          # In all cases, we have to remove the temp object
          #WARNING -> if path is not good, it will not be able to uncatalog !!!
          #o.immediateReindexObject() # STILL A PROBLEM -> getUidForPath XXX

          if object_id not in new_object_id_list: # do not unindex a new object
            o.isIndexable = ConstantGetter('isIndexable', value=True)
            o.unindexObject(path='%s/%s' % (self.getUrl() , object_id))
            # unindexed already forced
            o.isIndexable = ConstantGetter('isIndexable', value=False)
          self._delObject(temp_object_id) # object will be removed
                                               # from catalog automaticaly
      # We don't need the old index any more, we
      # can set the new index
      self.index[base_id] = new_index

    security.declareProtected( Permissions.ModifyPortalContent, 'setCellRange' )
    def setCellRange(self, *kw, **kwd):
      """
          Update the matrix ranges using provided lists of indexes (kw).

          Any number of list can be provided
      """
      self._setCellRange(*kw, **kwd)
      self.reindexObject()

    security.declareProtected(Permissions.ModifyPortalContent,
                              '_updateCellRange')
    def _updateCellRange(self, base_id, **kw):
      """
        The asCellRange script is Portal Type dependent
        which is not the case with this kind of code
        a better implementation consists in defining asCellRange as a
        generic method at matrix level (OverridableMethod(portal_type))
        which lookups for script in class, meta_type and PT form
        interaction could be implemented with interaction workflow
        this method should be renamed updateCellRange or updateMatrixCellRange
        base_id is parameter of updateCellRange

        asCellRange scripts should be unified if possible
      """
      script = self._getTypeBasedMethod('asCellRange', **kw)
      if script is None:
        raise UnboundLocalError,\
               "Did not find cell range script for portal type: %r" %\
               self.getPortalType()
      cell_range = script(base_id=base_id, matrixbox=0, **kw)
      self._setCellRange(base_id=base_id, *cell_range)

    security.declareProtected(Permissions.ModifyPortalContent,
                              'updateCellRange')
    def updateCellRange(self, base_id='cell', **kw):
      """ same as _updateCellRange, but reindex the object. """
      self._updateCellRange(base_id=base_id, **kw)
      self.reindexObject()


    security.declareProtected( Permissions.ModifyPortalContent,
                               '_renameCellRange' )
    def _renameCellRange(self, *kw, **kwd):
      """
          Rename a range for a matrix, this method can
          also handle a changement of the size of a matrix
      """
      base_id = kwd.get('base_id', 'cell')

      if getattr(aq_base(self), 'index', None) is None:
        self.index = PersistentMapping()

      # Return if previous range is the same
      current_range = self.getCellRange(base_id=base_id) or []
      if current_range == list(kw): # kw is a tuple
        LOG('XMLMatrix',0,'return form _setCellRange - no need to change range')
        return

      current_len = len(current_range)
      new_len = len(kw)
      len_delta = new_len - current_len

      # We must make sure the base_id exists
      # in the event of a matrix creation for example
      if not self.index.has_key(base_id):
        # Create an index for this base_id
        self.index[base_id] = PersistentMapping()

      cell_id_list = []
      for cell_id in self.getCellIdList(base_id = base_id):
        if self.get(cell_id) is not None:
          cell_id_list.append(cell_id)

      # First, delete all cells which are out of range.
      size_list = map(len, kw)
      if len_delta < 0:
        size_list.extend([1] * (-len_delta))
      def is_in_range(cell_id):
        for i, index in enumerate(cell_id[len(base_id)+1:].split('_')):
          if int(index) >= size_list[i]:
            self._delObject(cell_id)
            return False
        return True
      cell_id_list = filter(is_in_range, cell_id_list)

      # Secondly, rename coordinates. This does not change cell ids.
      for i in range(max(new_len, current_len)):
        if i >= new_len:
          del self.index[base_id][i]
        else:
          if i >= current_len:
            self.index[base_id][i] = PersistentMapping()
          for place in self.index[base_id][i].keys():
            if place not in kw[i]:
              del self.index[base_id][i][place]

          for j, place in enumerate(kw[i]):
            self.index[base_id][i][place] = j

      # Lastly, rename ids and catalog/uncatalog everything.
      if len_delta > 0:
        # Need to move, say, base_1_2 -> base_1_2_0
        appended_id = '_0' * len_delta
        for old_id in cell_id_list:
          cell = self.get(old_id)
          if cell is not None:
            new_id = old_id + appended_id
            self._delObject(old_id)
            cell.isIndexable = ConstantGetter('isIndexable', value=False)
            cell.id = new_id
            self._setObject(new_id, aq_base(cell))
            cell.isIndexable = ConstantGetter('isIndexable', value=True)
            cell.reindexObject()
            #cell.unindexObject(path='%s/%s' % (self.getUrl(), old_id))
      elif len_delta < 0:
        # Need to move, say, base_1_2_0 -> base_1_2
        removed_id_len = 2 * (-len_delta)
        for old_id in cell_id_list:
          cell = self.get(old_id)
          if cell is not None:
            new_id = old_id[:-removed_id_len]
            self._delObject(old_id)
            cell.isIndexable = ConstantGetter('isIndexable', value=False)
            cell.id = new_id
            self._setObject(new_id, aq_base(cell))
            cell.isIndexable = ConstantGetter('isIndexable', value=True)
            cell.reindexObject()
            #cell.unindexObject(path='%s/%s' % (self.getUrl(), old_id))

    security.declareProtected( Permissions.ModifyPortalContent,
                               'renameCellRange' )
    def renameCellRange(self, *kw, **kwd):
      """
          Update the matrix ranges using provided lists of indexes (kw).
          This keep cell values if we add/remove dimensions
          Any number of list can be provided
      """
      self._renameCellRange(*kw, **kwd)
      self.reindexObject()

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getCellRange')
    def getCellRange(self, base_id='cell'):
      """
          Returns the cell range as a list of index ids
      """
      if getattr(aq_base(self), 'index', None) is None:
        return []
      cell_range = self.index.get(base_id, None)
      if cell_range is None:
        return None

      result = []
      for value in cell_range.itervalues():
        result_items = sorted(value.iteritems(), key=lambda x:x[1])
        result.append([x[0] for x in result_items])
      return result

    security.declareProtected( Permissions.ModifyPortalContent, 'newCell' )
    def newCell(self, *kw, **kwd):
      """
          This method creates a new cell
      """
      if getattr(aq_base(self), 'index', None) is None:
        return None
      base_id = kwd.get('base_id', "cell")
      if not self.index.has_key(base_id):
        return None

      cell_id = self.keyToId(kw, base_id = base_id)
      if cell_id is None:
        raise KeyError, 'Invalid key: %s' % str(kw)

      cell = self.get(cell_id)
      if cell is not None:
        return cell
      else:
        return self.newCellContent(cell_id,**kwd)

    security.declareProtected( Permissions.ModifyPortalContent, 'newCellContent' )
    def newCellContent(self, cell_id, portal_type=None, **kw):
      """
        Creates a new content as a cell. This method is
        meant to be overriden by subclasses.
      """
      if portal_type is None:
        for x in self.allowedContentTypes():
          portal_type_id = x.getId()
          if portal_type_id.endswith(' Cell'):
            portal_type = portal_type_id
            break

      return self.newContent(id=cell_id, portal_type=portal_type, **kw)

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellKeyList' )
    def getCellKeyList(self, base_id = 'cell'):
      """
        Returns a list of possible keys as tuples
      """
      if getattr(aq_base(self), 'index', None) is None:
        return ()
      if not self.index.has_key(base_id):
        return ()
      index = self.index[base_id]
      id_tuple = [v.keys() for v in index.itervalues()]
      if len(id_tuple) == 0:
        return ()
      return cartesianProduct(id_tuple)

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellKeys' )
    getCellKeys = getCellKeyList

    # We should differenciate in future existing tuples from possible tuples
    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellRangeKeyList' )
    getCellRangeKeyList = getCellKeyList

    security.declareProtected( Permissions.AccessContentsInformation, 'keyToId' )
    def keyToId(self, kw, base_id = 'cell'):
      """
        Converts a key into a cell id
      """
      index = self.index[base_id]
      cell_id_list = [base_id]
      append = cell_id_list.append
      for i, item in enumerate(kw):
        try:
          append(str(index[i][item]))
        except KeyError:
          return None
      return '_'.join(cell_id_list)

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellIdList' )
    def getCellIdList(self, base_id = 'cell'):
      """
        Returns a list of possible ids as tuples
      """
      if getattr(aq_base(self), 'index', None) is None:
        return ()
      if not self.index.has_key(base_id):
        return ()
      result = []
      append = result.append
      for kw in self.getCellKeys(base_id = base_id):
        cell_id = self.keyToId(kw, base_id = base_id )
        if cell_id is not None:
          append(cell_id)

      return result

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellIds' )
    getCellIds = getCellIdList

    security.declareProtected( Permissions.AccessContentsInformation, 'cellIds' )
    cellIds = getCellIdList

    # We should differenciate in future all possible ids for existing ids
    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellRangeIdList' )
    getCellRangeIdList = getCellIdList

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellValueList' )
    def getCellValueList(self, base_id = 'cell'):
      """
        Returns a list of cell values as tuples
      """
      result = []
      append = result.append
      for id in self.getCellIdList(base_id=base_id):
        o = self.get(id)
        if o is not None:
          append(o)
      return result

    security.declareProtected( Permissions.AccessContentsInformation,
                               'cellValues' )
    cellValues = getCellValueList

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getMatrixList' )
    def getMatrixList(self):
      """
        Return possible base_id values
      """
      if getattr(aq_base(self), 'index', None) is None:
        return ()
      return self.index.keys()

    security.declareProtected( Permissions.ModifyPortalContent, 'delMatrix' )
    def delMatrix(self, base_id = 'cell'):
      """
        Delete all cells for a given base_id

        XXX BAD NAME: make a difference between deleting matrix and matrix cells
      """
      ids = self.getCellIds(base_id = base_id)
      my_ids = []
      append = my_ids.append
      for i in self.objectIds():
        if i in ids:
          append(i)

      if len(my_ids) > 0:
        self.manage_delObjects(ids=my_ids)

    security.declareProtected( Permissions.AccessContentsInformation,
                               'delCells' )
    delCells = delMatrix

    security.declareProtected( Permissions.AccessContentsInformation,
                               '_checkConsistency' )
    def _checkConsistency(self, fixit=0):
      """
        Constraint API.
      """
      # Check useless cells
      to_delete_set = set()
      error_list = []
      def addError(error_message):
        if fixit:
          error_message += ' (fixed)'
        error = (self.getRelativeUrl(),
                 'XMLMatrix inconsistency',
                 102,
                 error_message)

        error_list.append(error)

      # We make sure first that there is an index
      if getattr(aq_base(self), 'index', None) is None:
        self.index = PersistentMapping()
      # We will check each cell of the matrix the matrix

      # XXX This code assumes the following predicate:
      #   each subobject of an XMLMatrix is either a Cell that needs
      #   consistency checking OR ( is not a Cell, and has an id that is
      #   not like "(\w+_)+(\d+_)*\d+" )
      # But Documents inheriting XMLMatrix can have unrelated, non-cell
      # subobjects, possibly with id looking like some_id_2. If it ever happens,
      # an error will be wrongly raised.
      for obj in self.objectValues():
        object_id = obj.getId()
        # obect_id is equal to something like 'something_quantity_3_2'
        # So we need to check for every object.id if the value
        # looks like good or not. We split the name
        # check each key in index
        # First we make sure this is a cell
        object_id_split = object_id.split('_')

        base_id = None
        cell_coordinate_list = []
        while object_id_split:
          coordinate = None
          try:
            coordinate = int(object_id_split[-1])
          except ValueError:
            # The last item is not a coordinate, object_id_split hence
            # only contains the base_id elements
            base_id = '_'.join(object_id_split)
            break
          else:
            cell_coordinate_list.insert(0, coordinate)
            # the last item is a coordinate not part of base_id
            object_id_split.pop()

        current_dimension = len(cell_coordinate_list)
        if current_dimension > 0 and base_id is not None:
            if not self.index.has_key(base_id):
              # The matrix does not have this base_id
              addError("There is no index for base_id %s" % base_id)
              to_delete_set.add(object_id)
              continue

            # Check empty indices.
            empty_list = []
            base_item = self.index[base_id]
            for key, value in base_item.iteritems():
              if value is None or len(value) == 0:
                addError("There is no id for the %dth axis of base_id %s" % (key, base_id))
                empty_list.append(key)
            if fixit:
              for i in empty_list:
                del base_item[key]

            len_id = len(base_item)
            if current_dimension != len_id:
              addError("Dimension of cell is %s but should be %s" % (current_dimension,
                                                                     len_id))
              to_delete_set.add(object_id)
            else :
              for i, coordinate in enumerate(cell_coordinate_list):
                if coordinate >= len(base_item[i]):
                  addError("Cell %s is out of bound" % object_id)
                  to_delete_set.add(object_id)
                  break

      if fixit and len(to_delete_set) > 0:
        self.manage_delObjects(list(to_delete_set))

      return error_list

    security.declareProtected( Permissions.ModifyPortalContent, 'notifyAfterUpdateRelatedContent' )
    def notifyAfterUpdateRelatedContent(self, previous_category_url, new_category_url):
      """
        We must do some matrix range update in the event matrix range
        is defined by a category
      """
      LOG('XMLMatrix notifyAfterUpdateRelatedContent', 0, str(new_category_url))
      update_method = self.portal_categories.updateRelatedCategory
      for base_id in self.getMatrixList():
        cell_range = self.getCellRange(base_id=base_id)
        new_cell_range = []
        for range_item_list in cell_range:
          new_range_item_list = map(lambda c: update_method(c, previous_category_url, new_category_url), range_item_list)
          new_cell_range.append(new_range_item_list)
        kwd = {'base_id': base_id}
        LOG('XMLMatrix notifyAfterUpdateRelatedContent matrix', 0, str(base_id))
        LOG('XMLMatrix notifyAfterUpdateRelatedContent _renameCellRange', 0, str(new_cell_range))
        self._renameCellRange(*new_cell_range,**kwd)
Beispiel #43
0
class IdTool(BaseTool):
  """
    This tools handles the generation of IDs.
  """
  zope.interface.implements(interfaces.IIdTool)
  id = 'portal_ids'
  meta_type = 'ERP5 Id Tool'
  portal_type = 'Id Tool'

  # Declarative Security
  security = ClassSecurityInfo()

  security.declareProtected( Permissions.ManagePortal, 'manage_overview' )
  manage_overview = DTMLFile( 'explainIdTool', _dtmldir )

  def newContent(self, *args, **kw):
    """
      the newContent is overriden to not use generateNewId
    """
    if not kw.has_key(id):
      new_id = self._generateNextId()
      if new_id is not None:
        kw['id'] = new_id
      else:
        raise ValueError('Failed to gererate id')
    return BaseTool.newContent(self, *args, **kw)

  def _get_id(self, id):
    """
      _get_id is overrided to not use generateNewId
      It is used for example when an object is cloned
    """
    if self._getOb(id, None) is None :
      return id
    return self._generateNextId()

  @caching_instance_method(id='IdTool._getLatestIdGenerator',
    cache_factory='erp5_content_long')
  def _getLatestIdGenerator(self, reference):
    """
      Tries to find the id_generator with the latest version
      from the current object.
      Use the low-level to create a site without catalog
    """
    assert reference
    id_tool = self.getPortalObject().portal_ids
    id_last_generator = None
    version_last_generator = 0
    for generator in id_tool.objectValues():
      if generator.getReference() == reference:
        version = generator.getVersion()
        if version > version_last_generator:
          id_last_generator = generator.getId()
          version_last_generator = generator.getVersion()
    if id_last_generator is None:
      raise KeyError, 'The generator %s is not present' % (reference,)
    return id_last_generator

  def _getLatestGeneratorValue(self, id_generator):
    """
      Return the last generator with the reference
    """
    id_tool = self.getPortalObject().portal_ids
    last_id_generator = self._getLatestIdGenerator(id_generator)
    last_generator = id_tool._getOb(last_id_generator)
    return last_generator

  security.declareProtected(Permissions.AccessContentsInformation,
                            'generateNewId')
  def generateNewId(self, id_group=None, default=None, method=_marker,
                    id_generator=None):
    """
      Generate the next id in the sequence of ids of a particular group
    """
    if id_group in (None, 'None'):
      raise ValueError, '%s is not a valid id_group' % (repr(id_group), )
    # for compatibilty with sql data, must not use id_group as a list
    if not isinstance(id_group, str):
      id_group = repr(id_group)
      warnings.warn('id_group must be a string, other types '
                    'are deprecated.', DeprecationWarning)
    if id_generator is None:
      id_generator = 'document'
    if method is not _marker:
      warnings.warn("Use of 'method' argument is deprecated", DeprecationWarning)
    try:
      #use _getLatestGeneratorValue here for that the technical level
      #must not call the method
      last_generator = self._getLatestGeneratorValue(id_generator)
      new_id = last_generator.generateNewId(id_group=id_group, \
                                            default=default)
    except KeyError:
      template_tool = getattr(self, 'portal_templates', None)
      revision = template_tool.getInstalledBusinessTemplateRevision('erp5_core')
      # XXX backward compatiblity
      if int(revision) > 1561:
        LOG('generateNewId', ERROR, 'while generating id')
        raise
      else:
        # Compatibility code below, in case the last version of erp5_core
        # is not installed yet
        warnings.warn("You are using an old version of erp5_core to generate"
                      "ids.\nPlease update erp5_core business template to "
                      "use new id generators", DeprecationWarning)
        dict_ids = getattr(aq_base(self), 'dict_ids', None)
        if dict_ids is None:
          dict_ids = self.dict_ids = PersistentMapping()
        new_id = None
        # Getting the last id
        if default is None:
          default = 0
        marker = []
        new_id = dict_ids.get(id_group, marker)
        if method is _marker:
          if new_id is marker:
            new_id = default
          else:
            new_id = new_id + 1
        else:
          if new_id is marker:
            new_id = default
          new_id = method(new_id)
        # Store the new value
        dict_ids[id_group] = new_id
    return new_id

  security.declareProtected(Permissions.AccessContentsInformation,
                            'generateNewIdList')
  def generateNewIdList(self, id_group=None, id_count=1, default=None,
                        store=_marker, id_generator=None):
    """
      Generate a list of next ids in the sequence of ids of a particular group
    """
    if id_group in (None, 'None'):
      raise ValueError, '%s is not a valid id_group' % (repr(id_group), )
    # for compatibilty with sql data, must not use id_group as a list
    if not isinstance(id_group, str):
      id_group = repr(id_group)
      warnings.warn('id_group must be a string, other types '
                    'are deprecated.', DeprecationWarning)
    if id_generator is None:
      id_generator = 'uid'
    if store is not _marker:
      warnings.warn("Use of 'store' argument is deprecated.",
                    DeprecationWarning)
    try:
      #use _getLatestGeneratorValue here for that the technical level
      #must not call the method
      last_generator = self._getLatestGeneratorValue(id_generator)
      new_id_list = last_generator.generateNewIdList(id_group=id_group,
                         id_count=id_count, default=default)
    except (KeyError, ValueError):
      template_tool = getattr(self, 'portal_templates', None)
      revision = template_tool.getInstalledBusinessTemplateRevision('erp5_core')
      # XXX backward compatiblity
      if int(revision) > 1561:
        LOG('generateNewIdList', ERROR, 'while generating id')
        raise
      else:
        # Compatibility code below, in case the last version of erp5_core
        # is not installed yet
        warnings.warn("You are using an old version of erp5_core to generate"
                      "ids.\nPlease update erp5_core business template to "
                      "use new id generators", DeprecationWarning)
        new_id = None
        if default is None:
          default = 1
        # XXX It's temporary, a New API will be implemented soon
        #     the code will be change
        portal = self.getPortalObject()
        query = getattr(portal, 'IdTool_zGenerateId', None)
        commit = getattr(portal, 'IdTool_zCommit', None)

        if query is None or commit is None:
          portal_catalog = getattr(self, 'portal_catalog').getSQLCatalog()
          query = getattr(portal_catalog, 'z_portal_ids_generate_id')
          commit = getattr(portal_catalog, 'z_portal_ids_commit')
        if None in (query, commit):
          raise AttributeError, 'Error while generating Id: ' \
            'idTool_zGenerateId and/or idTool_zCommit could not ' \
            'be found.'
        try:
          result = query(id_group=id_group, id_count=id_count, default=default)
        finally:
          commit()
        new_id = result[0]['LAST_INSERT_ID()']
        if store:
          if getattr(aq_base(self), 'dict_length_ids', None) is None:
            # Length objects are stored in a persistent mapping: there is one
            # Length object per id_group.
            self.dict_length_ids = PersistentMapping()
          if self.dict_length_ids.get(id_group) is None:
            self.dict_length_ids[id_group] = Length(new_id)
          self.dict_length_ids[id_group].set(new_id)
        new_id_list = range(new_id - id_count, new_id)
    return new_id_list

  security.declareProtected(Permissions.ModifyPortalContent,
                            'initializeGenerator')
  def initializeGenerator(self, id_generator=None, all=False):
    """
    Initialize generators. This is mostly used when a new ERP5 site
    is created. Some generators will need to do some initialization like
    creating SQL Database, prepare some data in ZODB, etc
    """
    if not all:
      #Use _getLatestGeneratorValue here for that the technical level
      #must not call the method
      last_generator = self._getLatestGeneratorValue(id_generator)
      last_generator.initializeGenerator()
    else:
      # recovery all the generators and initialize them
      for generator in self.objectValues(\
                       portal_type='Application Id Generator'):
        generator.initializeGenerator()

  security.declareProtected(Permissions.ModifyPortalContent,
                            'clearGenerator')
  def clearGenerator(self, id_generator=None, all=False):
    """
    Clear generators data. This can be usefull when working on a
    development instance or in some other rare cases. This will
    loose data and must be use with caution

    This can be incompatible with some particular generator implementation,
    in this case a particular error will be raised (to be determined and
    added here)
    """
    if not all:
      #Use _getLatestGeneratorValue here for that the technical level
      #must not call the method
      last_generator = self._getLatestGeneratorValue(id_generator)
      last_generator.clearGenerator()

    else:
      if len(self.objectValues()) == 0:
        # compatibility with old API
        self.getPortalObject().IdTool_zDropTable()
        self.getPortalObject().IdTool_zCreateTable()
      for generator in self.objectValues(\
                       portal_type='Application Id Generator'):
        generator.clearGenerator()

  ## XXX Old API deprecated
  #backward compatibility
  generateNewLengthIdList = generateNewIdList

  security.declareProtected(Permissions.AccessContentsInformation,
                            'getLastLengthGeneratedId')
  def getLastLengthGeneratedId(self, id_group, default=None):
    """
    Get the last length id generated
    """
    warnings.warn('getLastLengthGeneratedId is deprecated',
                   DeprecationWarning)
    # check in persistent mapping if exists
    if getattr(aq_base(self), 'dict_length_ids', None) is not None:
      last_id = self.dict_length_ids.get(id_group)
      if last_id is not None:
        return last_id.value - 1
    # otherwise check in mysql
    # XXX It's temporary, a New API will be implemented soon
    #     the code will be change
    portal = self.getPortalObject()
    query = getattr(portal, 'IdTool_zGetLastId', None)
    if query is None:
      portal_catalog = getattr(self, 'portal_catalog').getSQLCatalog()
      query = getattr(portal_catalog, 'z_portal_ids_get_last_id')
    if query is None:
      raise AttributeError, 'Error while getting last Id: ' \
            'IdTool_zGetLastId could not ' \
            'be found.'
    result = query(id_group=id_group)
    if len(result):
      try:
        return result[0]['last_id']
      except KeyError:
        return result[0]['LAST_INSERT_ID()']
    return default

  security.declareProtected(Permissions.AccessContentsInformation,
                            'getLastGeneratedId')
  def getLastGeneratedId(self, id_group=None, default=None):
    """
    Get the last id generated
    """
    warnings.warn('getLastGeneratedId is deprecated', DeprecationWarning)
    if getattr(aq_base(self), 'dict_ids', None) is None:
      self.dict_ids = PersistentMapping()
    last_id = None
    if id_group is not None and id_group != 'None':
      last_id = self.dict_ids.get(id_group, default)
    return last_id

  security.declareProtected(Permissions.ModifyPortalContent,
                            'setLastGeneratedId')
  def setLastGeneratedId(self, new_id, id_group=None):
    """
    Set a new last id. This is usefull in order to reset
    a sequence of ids.
    """
    if getattr(aq_base(self), 'dict_ids', None) is None:
      self.dict_ids = PersistentMapping()
    if id_group is not None and id_group != 'None':
      self.dict_ids[id_group] = new_id

  security.declareProtected(Permissions.AccessContentsInformation,
                           'generateNewLengthId')
  def generateNewLengthId(self, id_group=None, default=None, store=_marker):
     """Generates an Id using a conflict free id generator. Deprecated.
     """
     warnings.warn('generateNewLengthId is deprecated.\n'
                   'Use generateNewIdList with a sql id_generator',
                   DeprecationWarning)
     if store is not _marker:
       return self.generateNewIdList(id_group=id_group,
                        id_count=1, default=default, store=store)[0]
     return self.generateNewIdList(id_group=id_group,
                        id_count=1, default=default)[0]

  security.declareProtected(Permissions.AccessContentsInformation,
                            'getDictLengthIdsItems')
  def getDictLengthIdsItems(self):
    """
      Return a copy of dict_length_ids.
      This is a workaround to access the persistent mapping content from ZSQL
      method to be able to insert initial tuples in the database at creation.
    """
    if getattr(self, 'dict_length_ids', None) is None:
      self.dict_length_ids = PersistentMapping()
    return self.dict_length_ids.items()

  security.declarePrivate('dumpDictLengthIdsItems')
  def dumpDictLengthIdsItems(self):
    """
      Store persistently data from SQL table portal_ids.
    """
    portal_catalog = getattr(self, 'portal_catalog').getSQLCatalog()
    query = getattr(portal_catalog, 'z_portal_ids_dump')
    dict_length_ids = getattr(aq_base(self), 'dict_length_ids', None)
    if dict_length_ids is None:
      dict_length_ids = self.dict_length_ids = PersistentMapping()
    for line in query().dictionaries():
      id_group = line['id_group']
      last_id = line['last_id']
      stored_last_id = self.dict_length_ids.get(id_group)
      if stored_last_id is None:
        self.dict_length_ids[id_group] = Length(last_id)
      else:
        stored_last_id_value = stored_last_id()
        if stored_last_id_value < last_id:
          stored_last_id.set(last_id)
        else:
          if stored_last_id_value > last_id:
            LOG('IdTool', WARNING, 'ZODB value (%r) for group %r is higher ' \
                'than SQL value (%r). Keeping ZODB value untouched.' % \
                (stored_last_id, id_group, last_id))
Beispiel #44
0
class PDFForm(File):
  """This class allows to fill PDF Form with TALES expressions,
    using a TALES expression for each cell.

  TODO:
    * cache compiled TALES
    * set _v_errors when setting invalid TALES (setCellTALES can raise, but
      not doEditCells)
  """

  meta_type = "ERP5 PDF Form"
  icon = "www/PDFForm.png"

  # Those 2 are ugly names, but we keep compatibility
  # the page range we want to print (a TALES expr)
  __page_range__ = ''
  # the method to format values (a TALES expr)
  __format_method__ = ''

  # Declarative Security
  security = ClassSecurityInfo()

  # Declarative properties
  _properties = File._properties + (
      {'id' : 'download_url', 'type' : 'lines', 'mode' : 'w' },
      {'id' : 'business_template_include_content',
              'type' : 'boolean', 'mode' : 'w' },
  )
  download_url = ()
  business_template_include_content = 1
  
  # Constructors
  constructors =   (manage_addPDFForm, addPDFForm)

  manage_options =  ( (
        {'label':'Edit Cell TALES', 'action':'manage_cells'},
        {'label':'Display Cell Names', 'action':'showCellNames'},
        {'label':'Test PDF generation', 'action':'generatePDF'},
        {'label':'View original', 'action':'viewOriginal'},
        {'label':'Download PDF content from URL', 'action':'downloadPdfContent'},
      ) +
      filter(lambda option:option['label'] != "View", File.manage_options)
  )

  def __init__ (self, id, title='', pdf_file=''):
    # holds all the cell informations, even those not related to this form
    self.all_cells = PersistentMapping()
    # holds the cells related to this pdf form
    self.cells = PersistentMapping()

    # File constructor will set the file content
    File.__init__(self, id, title, pdf_file)

  security.declareProtected(Permissions.ManagePortal, 'manage_upload')
  def manage_upload(self, file=None, REQUEST=None) :
    """ Zope calls this when the content of the enclosed file changes.
    The 'cells' attribute is updated, but already defined cells are not
    erased, they are saved in the 'all_cells' attribute so if the pdf
    file is reverted, you do not loose the cells definitions.
    """
    if not file or not hasattr(file, "read") :
      raise ValueError ("The pdf form file should not be empty")

    file.seek(0) # file is always valid here
    values = PDFTk().dumpDataFields(file)
    self.cells = {}
    for v in values :
      if v["FieldType"] not in ("Button", "Choice")\
                    or not int(v["FieldFlags"]) & 65536:
        k = v["FieldName"]
        if not self.all_cells.has_key(k) :
          self.cells[k] = ""
        else :
          self.cells[k] = self.all_cells[k]
    self.all_cells.update(self.cells)
    file.seek(0)
    File.manage_upload(self, file, REQUEST)
    if REQUEST:
      message = "Saved changes."
      return self.manage_main(self, REQUEST, manage_tabs_message=message)

  security.declareProtected(Permissions.ViewManagementScreens, 'manage_cells')
  manage_cells = PageTemplateFile('www/PDFForm_manageCells',
                                   globals(), __name__='manage_cells')

  security.declareProtected(Permissions.View, 'manage_FTPget')
  def manage_FTPget(self, REQUEST=None, RESPONSE=None) :
    """ get this pdf form via webDAV/FTP, it returns an XML
    representation of all the fields, then the pdf itself."""
    from xml.dom.minidom import getDOMImplementation
    impl = getDOMImplementation()
    newdoc = impl.createDocument(None, "pdfform", None)
    top_element = newdoc.documentElement
    cells = newdoc.createElement('cells')
    pdfform_cell_list = self.cells.keys()
    pdfform_cell_list.sort()
    for cell in pdfform_cell_list :
      cell_node = newdoc.createElement('cell')
      cell_node.setAttribute('name', cell)
      tales = newdoc.createTextNode(self.cells[cell])
      cell_node.appendChild(tales)
      cells.appendChild(cell_node)

    top_element.appendChild(cells)
    pdf_data = newdoc.createElement('pdf_data')
    pdf_content = newdoc.createTextNode(str(self.data))
    pdf_data.appendChild(pdf_content)
    top_element.appendChild(pdf_data)
    content = newdoc.toprettyxml('  ')
    if RESPONSE :
      RESPONSE.setHeader('Content-Type', 'application/x-erp5-pdfform')
      RESPONSE.setHeader('Content-Length', len(content))
      RESPONSE.write(content)
    return content
  manage_DAVget = manage_FTPget

  security.declareProtected(Permissions.ManagePortal, 'PUT')
  def PUT(self, REQUEST, RESPONSE):
    """(does not) Handle HTTP PUT requests."""
    RESPONSE.setStatus(501)
    return RESPONSE
  manage_FTPput = PUT

  security.declareProtected(Permissions.View, 'hasPdfContent')
  def hasPdfContent(self) :
    """Return true if there is an enclosed PDF in this PDF Form."""
    return self.data is not None and len(self.data) > 0

  security.declareProtected(Permissions.ManagePortal, 'downloadPdfContent')
  def downloadPdfContent(self, REQUEST=None) :
    """Download the pdf content from one of `download_url` URL """
    for url in self.getProperty('download_url') :
      try :
        response = urllib.urlopen(url)
      except IOError, e :
        LOG("PDFForm", WARNING, "Unable to download from %s" % url, e)
        continue
      if response.headers.getheader('Content-Type') != 'application/pdf':
        LOG("PDFForm", WARNING, "%s is not application/pdf" % url)
        continue
      self.manage_upload(cStringIO.StringIO(response.read()))
      self.content_type = 'application/pdf'
      if REQUEST is not None :
        return REQUEST.RESPONSE.redirect(
              "%s/manage_main?manage_tabs_message=Content+Downloaded"
              % self.absolute_url())
      return
    raise ValueError, "Unable to download from any url from the "\
                      "`download_url` property."
Beispiel #45
0
class XMLMatrix(Folder):
    """
        A mix-in class which provides a matrix like
        access to objects. Matrices are of any dimension.
        A single XMLMatrix may contain multiple matrices,
        of different dimension. Each matrix is associated to
        a so-called 'base_id'.

        We still must make XMLMatrix a subclass of Base so
        that we can inherit from ExtensionClass.Base
        which is required for multiple inheritance to work
        as expected. Read this for more information:

        http://www.daa.com.au/pipermail/pygtk/2001-May/001180.html

        In our case, we will use Folder because we want to inherit
        from Folder consistency checking

    """

    # Declarative security
    security = ClassSecurityInfo()

    # Matrix Methods
    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCell' )
    def getCell(self, *kw , **kwd):
      """
          Access a cell at row and column
      """
      if getattr(aq_base(self), 'index', None) is None:
        return None

      base_id = kwd.get('base_id', "cell")
      if not self.index.has_key(base_id):
        return None

      cell_id = self.keyToId(kw, base_id = base_id)
      if cell_id is None:
        return None
      return self.get(cell_id)

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellProperty' )
    def getCellProperty(self, *kw , **kwd):
      """
          Get a property of a cell at row and column
      """
      base_id= kwd.get('base_id', "cell")
      cell = self.getCell(*kw, **kwd)
      if cell is None:
        return None

      return cell.getProperty(base_id)

    security.declareProtected( Permissions.AccessContentsInformation,
                               'hasCell' )
    def hasCell(self, *kw , **kwd):
      """
          Checks if matrix corresponding to base_id contains cell specified
          by *kw coordinates.
      """
      return self.getCell(*kw, **kwd) is not None

    security.declareProtected( Permissions.AccessContentsInformation,
                               'hasCellContent' )
    def hasCellContent(self, base_id='cell'):
      """
          Checks if matrix corresponding to base_id contains cells.
      """
      aq_self = aq_base(self)

      if getattr(aq_self, 'index', None) is None:
        return 0

      if not self.index.has_key(base_id):
        return 0

      for i in self.getCellIds(base_id=base_id):
        if hasattr(self, i): # We should try to use aq_self if possible but XXX
          return 1

      return 0

    security.declareProtected( Permissions.AccessContentsInformation,
                               'hasInRange' )
    def hasInRange(self, *kw , **kwd):
      """
          Checks if *kw coordinates are in the range of the
        matrix in kwd['base_id'].
      """
      if getattr(aq_base(self), 'index', None) is None:
        return 0

      base_id = kwd.get('base_id', "cell")
      if not self.index.has_key(base_id):
        return 0
      base_item = self.index[base_id]
      for i, my_id in enumerate(kw):
        if not base_item.has_key(i) or not base_item[i].has_key(my_id):
          return 0

      return 1

    security.declareProtected( Permissions.ModifyPortalContent,
                               '_setCellRange' )
    def _setCellRange(self, *args, **kw):
      """Set a new range for a matrix

      Each value for each axis is assigned an integer id.
      If the number of axis changes, everything is reset.
      Otherwise, ids are never changed, so that cells never need to be renamed:
      this means no sort is garanteed, and there can be holes.
      """
      base_id = kw.get('base_id', 'cell')
      # Get (initialize if necessary) index for considered matrix (base_id).
      try:
        index = aq_base(self).index
      except AttributeError:
        index = self.index = PersistentMapping()
      to_delete = []
      try:
        index = index[base_id]
        if len(args) != len(index):
          # The number of axis changes so we'll delete all existing cells and
          # renumber everything from 1.
          to_delete = INFINITE_SET,
          index.clear()
      except KeyError:
        index[base_id] = index = PersistentMapping()
      # For each axis ...
      for i, axis in enumerate(args):
        # ... collect old axis keys and allocate ids for new ones.
        axis = set(axis)
        last_id = -1
        try:
          id_dict = index[i]
        except KeyError:
          index[i] = id_dict = PersistentMapping()
        else:
          delete = set()
          to_delete.append(delete)
          for k, v in id_dict.items():
            try:
              axis.remove(k)
              if last_id < v:
                last_id = v
            except KeyError:
              delete.add(v)
              del id_dict[k]
          # At this point, last_id contains the greatest id.
        for k in sorted(axis):
          last_id += 1
          id_dict[k] = last_id
      # Remove old cells if any.
      if any(to_delete):
        prefix = base_id + '_'
        prefix_len = len(prefix)
        for cell_id in list(self.objectIds()):
          if cell_id.startswith(prefix):
            for i, j in enumerate(cell_id[prefix_len:].split('_')):
              if int(j) in to_delete[i]:
                self._delObject(cell_id)
                break

    security.declareProtected( Permissions.ModifyPortalContent, 'setCellRange' )
    def setCellRange(self, *kw, **kwd):
      """
          Update the matrix ranges using provided lists of indexes (kw).

          Any number of list can be provided
      """
      self._setCellRange(*kw, **kwd)
      self.reindexObject()

    security.declareProtected(Permissions.ModifyPortalContent,
                              '_updateCellRange')
    def _updateCellRange(self, base_id, **kw):
      """
        The asCellRange script is Portal Type dependent
        which is not the case with this kind of code
        a better implementation consists in defining asCellRange as a
        generic method at matrix level (OverridableMethod(portal_type))
        which lookups for script in class, meta_type and PT form
        interaction could be implemented with interaction workflow
        this method should be renamed updateCellRange or updateMatrixCellRange
        base_id is parameter of updateCellRange

        asCellRange scripts should be unified if possible
      """
      script = self._getTypeBasedMethod('asCellRange', **kw)
      if script is None:
        raise UnboundLocalError,\
               "Did not find cell range script for portal type: %r" %\
               self.getPortalType()
      cell_range = script(base_id=base_id, matrixbox=0, **kw)
      self._setCellRange(base_id=base_id, *cell_range)

    security.declareProtected(Permissions.ModifyPortalContent,
                              'updateCellRange')
    def updateCellRange(self, base_id='cell', **kw):
      """ same as _updateCellRange, but reindex the object. """
      self._updateCellRange(base_id=base_id, **kw)
      self.reindexObject()


    security.declareProtected( Permissions.ModifyPortalContent,
                               '_renameCellRange' )
    def _renameCellRange(self, *kw, **kwd):
      """
          Rename a range for a matrix, this method can
          also handle a changement of the size of a matrix
      """
      base_id = kwd.get('base_id', 'cell')

      if getattr(aq_base(self), 'index', None) is None:
        self.index = PersistentMapping()

      # Return if previous range is the same
      current_range = self.getCellRange(base_id=base_id) or []
      if current_range == list(kw): # kw is a tuple
        LOG('XMLMatrix',0,'return form _setCellRange - no need to change range')
        return

      current_len = len(current_range)
      new_len = len(kw)
      len_delta = new_len - current_len

      # We must make sure the base_id exists
      # in the event of a matrix creation for example
      if not self.index.has_key(base_id):
        # Create an index for this base_id
        self.index[base_id] = PersistentMapping()

      cell_id_list = []
      for cell_id in self.getCellIdList(base_id = base_id):
        if self.get(cell_id) is not None:
          cell_id_list.append(cell_id)

      # First, delete all cells which are out of range.
      size_list = map(len, kw)
      if len_delta < 0:
        size_list.extend([1] * (-len_delta))
      def is_in_range(cell_id):
        for i, index in enumerate(cell_id[len(base_id)+1:].split('_')):
          if int(index) >= size_list[i]:
            self._delObject(cell_id)
            return False
        return True
      cell_id_list = filter(is_in_range, cell_id_list)

      # Secondly, rename coordinates. This does not change cell ids.
      for i in range(max(new_len, current_len)):
        if i >= new_len:
          del self.index[base_id][i]
        else:
          if i >= current_len:
            self.index[base_id][i] = PersistentMapping()
          for place in self.index[base_id][i].keys():
            if place not in kw[i]:
              del self.index[base_id][i][place]

          for j, place in enumerate(kw[i]):
            self.index[base_id][i][place] = j

      # Lastly, rename ids and catalog/uncatalog everything.
      if len_delta > 0:
        # Need to move, say, base_1_2 -> base_1_2_0
        appended_id = '_0' * len_delta
        for old_id in cell_id_list:
          cell = self.get(old_id)
          if cell is not None:
            new_id = old_id + appended_id
            self._delObject(old_id)
            cell.isIndexable = ConstantGetter('isIndexable', value=False)
            cell.id = new_id
            self._setObject(new_id, aq_base(cell))
            cell.isIndexable = ConstantGetter('isIndexable', value=True)
            cell.reindexObject()
            #cell.unindexObject(path='%s/%s' % (self.getUrl(), old_id))
      elif len_delta < 0:
        # Need to move, say, base_1_2_0 -> base_1_2
        removed_id_len = 2 * (-len_delta)
        for old_id in cell_id_list:
          cell = self.get(old_id)
          if cell is not None:
            new_id = old_id[:-removed_id_len]
            self._delObject(old_id)
            cell.isIndexable = ConstantGetter('isIndexable', value=False)
            cell.id = new_id
            self._setObject(new_id, aq_base(cell))
            cell.isIndexable = ConstantGetter('isIndexable', value=True)
            cell.reindexObject()
            #cell.unindexObject(path='%s/%s' % (self.getUrl(), old_id))

    security.declareProtected( Permissions.ModifyPortalContent,
                               'renameCellRange' )
    def renameCellRange(self, *kw, **kwd):
      """
          Update the matrix ranges using provided lists of indexes (kw).
          This keep cell values if we add/remove dimensions
          Any number of list can be provided
      """
      self._renameCellRange(*kw, **kwd)
      self.reindexObject()

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getCellRange')
    def getCellRange(self, base_id='cell'):
      """
          Returns the cell range as a list of index ids
      """
      try:
        cell_range = aq_base(self).index[base_id]
      except (AttributeError, KeyError):
        return []
      return [x.keys() for _, x in sorted(cell_range.iteritems())]

    security.declareProtected( Permissions.ModifyPortalContent, 'newCell' )
    def newCell(self, *kw, **kwd):
      """
          This method creates a new cell
      """
      if getattr(aq_base(self), 'index', None) is None:
        return None
      base_id = kwd.get('base_id', "cell")
      if not self.index.has_key(base_id):
        return None

      cell_id = self.keyToId(kw, base_id = base_id)
      if cell_id is None:
        raise KeyError, 'Invalid key: %s' % str(kw)

      cell = self.get(cell_id)
      if cell is not None:
        return cell
      else:
        return self.newCellContent(cell_id,**kwd)

    security.declareProtected( Permissions.ModifyPortalContent, 'newCellContent' )
    def newCellContent(self, cell_id, portal_type=None, **kw):
      """
        Creates a new content as a cell. This method is
        meant to be overriden by subclasses.
      """
      if portal_type is None:
        for x in self.allowedContentTypes():
          portal_type_id = x.getId()
          if portal_type_id.endswith(' Cell'):
            portal_type = portal_type_id
            break

      return self.newContent(id=cell_id, portal_type=portal_type, **kw)

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellKeyList' )
    def getCellKeyList(self, base_id = 'cell'):
      """
        Returns a list of possible keys as tuples
      """
      if getattr(aq_base(self), 'index', None) is None:
        return ()
      if not self.index.has_key(base_id):
        return ()
      index = self.index[base_id]
      id_tuple = [v.keys() for v in index.itervalues()]
      if len(id_tuple) == 0:
        return ()
      return cartesianProduct(id_tuple)

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellKeys' )
    getCellKeys = getCellKeyList

    # We should differenciate in future existing tuples from possible tuples
    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellRangeKeyList' )
    getCellRangeKeyList = getCellKeyList

    security.declareProtected( Permissions.AccessContentsInformation, 'keyToId' )
    def keyToId(self, kw, base_id = 'cell'):
      """
        Converts a key into a cell id
      """
      index = self.index[base_id]
      cell_id_list = [base_id]
      append = cell_id_list.append
      for i, item in enumerate(kw):
        try:
          append(str(index[i][item]))
        except KeyError:
          return None
      return '_'.join(cell_id_list)

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellIdList' )
    def getCellIdList(self, base_id = 'cell'):
      """
        Returns a list of possible ids as tuples
      """
      if getattr(aq_base(self), 'index', None) is None:
        return ()
      if not self.index.has_key(base_id):
        return ()
      result = []
      append = result.append
      for kw in self.getCellKeys(base_id = base_id):
        cell_id = self.keyToId(kw, base_id = base_id )
        if cell_id is not None:
          append(cell_id)

      return result

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellIds' )
    getCellIds = getCellIdList

    security.declareProtected( Permissions.AccessContentsInformation, 'cellIds' )
    cellIds = getCellIdList

    # We should differenciate in future all possible ids for existing ids
    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellRangeIdList' )
    getCellRangeIdList = getCellIdList

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getCellValueList' )
    def getCellValueList(self, base_id = 'cell'):
      """
        Returns a list of cell values as tuples
      """
      result = []
      append = result.append
      for id in self.getCellIdList(base_id=base_id):
        o = self.get(id)
        if o is not None:
          append(o)
      return result

    security.declareProtected( Permissions.AccessContentsInformation,
                               'cellValues' )
    cellValues = getCellValueList

    security.declareProtected( Permissions.AccessContentsInformation,
                               'getMatrixList' )
    def getMatrixList(self):
      """
        Return possible base_id values
      """
      if getattr(aq_base(self), 'index', None) is None:
        return ()
      return self.index.keys()

    security.declareProtected( Permissions.ModifyPortalContent, 'delMatrix' )
    def delMatrix(self, base_id = 'cell'):
      """
        Delete all cells for a given base_id

        XXX BAD NAME: make a difference between deleting matrix and matrix cells
      """
      ids = self.getCellIds(base_id = base_id)
      my_ids = []
      append = my_ids.append
      for i in self.objectIds():
        if i in ids:
          append(i)

      if len(my_ids) > 0:
        self.manage_delObjects(ids=my_ids)

    security.declareProtected( Permissions.AccessContentsInformation,
                               'delCells' )
    delCells = delMatrix

    security.declareProtected( Permissions.AccessContentsInformation,
                               '_checkConsistency' )
    def _checkConsistency(self, fixit=0):
      """
        Constraint API.
      """
      # Check useless cells
      to_delete_set = set()
      error_list = []
      def addError(error_message):
        if fixit:
          error_message += ' (fixed)'
        error = (self.getRelativeUrl(),
                 'XMLMatrix inconsistency',
                 102,
                 error_message)

        error_list.append(error)

      # We make sure first that there is an index
      if getattr(aq_base(self), 'index', None) is None:
        self.index = PersistentMapping()
      # We will check each cell of the matrix the matrix

      # XXX This code assumes the following predicate:
      #   each subobject of an XMLMatrix is either a Cell that needs
      #   consistency checking OR ( is not a Cell, and has an id that is
      #   not like "(\w+_)+(\d+_)*\d+" )
      # But Documents inheriting XMLMatrix can have unrelated, non-cell
      # subobjects, possibly with id looking like some_id_2. If it ever happens,
      # an error will be wrongly raised.
      for obj in self.objectValues():
        object_id = obj.getId()
        # obect_id is equal to something like 'something_quantity_3_2'
        # So we need to check for every object.id if the value
        # looks like good or not. We split the name
        # check each key in index
        # First we make sure this is a cell
        object_id_split = object_id.split('_')

        base_id = None
        cell_coordinate_list = []
        while object_id_split:
          coordinate = None
          try:
            coordinate = int(object_id_split[-1])
          except ValueError:
            # The last item is not a coordinate, object_id_split hence
            # only contains the base_id elements
            base_id = '_'.join(object_id_split)
            break
          else:
            cell_coordinate_list.insert(0, coordinate)
            # the last item is a coordinate not part of base_id
            object_id_split.pop()

        current_dimension = len(cell_coordinate_list)
        if current_dimension > 0 and base_id is not None:
            if not self.index.has_key(base_id):
              # The matrix does not have this base_id
              addError("There is no index for base_id %s" % base_id)
              to_delete_set.add(object_id)
              continue

            # Check empty indices.
            empty_list = []
            base_item = self.index[base_id]
            for key, value in base_item.iteritems():
              if value is None or len(value) == 0:
                addError("There is no id for the %dth axis of base_id %s" % (key, base_id))
                empty_list.append(key)
            if fixit:
              for i in empty_list:
                del base_item[key]

            len_id = len(base_item)
            if current_dimension != len_id:
              addError("Dimension of cell is %s but should be %s" % (current_dimension,
                                                                     len_id))
              to_delete_set.add(object_id)
            else :
              for i, coordinate in enumerate(cell_coordinate_list):
                if coordinate >= len(base_item[i]):
                  addError("Cell %s is out of bound" % object_id)
                  to_delete_set.add(object_id)
                  break

      if fixit and len(to_delete_set) > 0:
        self.manage_delObjects(list(to_delete_set))

      return error_list

    security.declareProtected( Permissions.ModifyPortalContent, 'notifyAfterUpdateRelatedContent' )
    def notifyAfterUpdateRelatedContent(self, previous_category_url, new_category_url):
      """
        We must do some matrix range update in the event matrix range
        is defined by a category
      """
      LOG('XMLMatrix notifyAfterUpdateRelatedContent', 0, str(new_category_url))
      update_method = self.portal_categories.updateRelatedCategory
      for base_id in self.getMatrixList():
        cell_range = self.getCellRange(base_id=base_id)
        new_cell_range = []
        for range_item_list in cell_range:
          new_range_item_list = map(lambda c: update_method(c, previous_category_url, new_category_url), range_item_list)
          new_cell_range.append(new_range_item_list)
        kwd = {'base_id': base_id}
        LOG('XMLMatrix notifyAfterUpdateRelatedContent matrix', 0, str(base_id))
        LOG('XMLMatrix notifyAfterUpdateRelatedContent _renameCellRange', 0, str(new_cell_range))
        self._renameCellRange(*new_cell_range,**kwd)
Beispiel #46
0
    def _setCellRange(self, *kw, **kwd):
      """
          Set a new range for a matrix.
          If needed, it will resize and/or reorder matrix content.
      """
      movement = {} # Maps original cell id to its new id for each moved cell.
      new_index = PersistentMapping()

      base_id = kwd.get('base_id', 'cell')
      if getattr(aq_base(self), 'index', None) is None:
        self.index = PersistentMapping()

      # Return if previous range is the same
      current_range = self.getCellRange(base_id=base_id)
      if current_range == list(kw): # kw is a tuple
        return

      # Create the new index for the range given in *kw
      # *kw practical example:
      # kw = [ ['color/blue', 'color/red'], ['size/S', 'size/L']]
      for i, index_ids in enumerate(kw):
        temp = PersistentMapping()
        for j, my_id in enumerate(index_ids):
          temp[my_id] = j
        new_index[i] = temp

      if self.index.has_key(base_id):
        # Compute cell movement from their position in previous range to their
        # position in the new range.
        for i, i_value in self.index[base_id].iteritems():
          if new_index.has_key(i):
            temp = {}
            for my_id, my_value in i_value.iteritems():
              temp[my_value] = new_index[i].get(my_id)
            movement[i] = temp

      # List all valid cell ids for current base_id.
      object_id_list = []
      for obj in self.objectValues():
        object_id = obj.getId()
        if object_id.find(base_id) == 0:
          # Check that all '_'-separated fields are of int type.
          if (object_id) > len(base_id):
            try:
              int(object_id[len(base_id)+1:].split('_')[0])
              test = self._getOb(object_id) # If the object was created
                                            # during this transaction,
                                            # then we do not need to
                                            # work on it
              object_id_list.append(object_id)
            except (ValueError, KeyError):
              pass

      # Prepend 'temp_' to all cells, to avoid id conflicts while renaming.
      for object_id in object_id_list:
        new_name = 'temp_' + object_id
        obj = self._getOb(object_id)
        self._delObject(object_id)
        obj.id = new_name
        self._setObject(new_name, aq_base(obj))

      # Rename all cells to their final name.
      for object_id in object_id_list:
        # Retrieve the place of the object, for movement_0_0 it is ['0','0']
        object_place = object_id[len(base_id)+1:].split('_')
        to_delete = 1
        # We must have the same number of dimensions
        if len(object_place) == len(new_index):
          # Let us browse each dimension of the previous index
          for i in range(len(object_place)):
            # Build each part of the nex id by looking up int. values
            old_place = int(object_place[i])
            # We are looking inside the movement dictionnary where
            # we should move the object, so for example
            # 'qantity_2_5' is renamed as 'quantity_4_3'
            if movement.has_key(i):
              if movement[i].has_key(old_place):
                # Replace the place of the cell only if there where a change
                if (movement[i][old_place]) != None:
                  object_place[i] = str(movement[i][old_place])
                  to_delete = 0
                else:
                  object_place[i] = None

            # XXX In this case, we delete every cell wich are not in the
            # movement dictionnary, may be this is very bad, so may be
            # we need to add an extra check here, ie if
            # if movement[i].has_key(old_place) returns None,
            # We may want to keep the cell, but only if we are sure
            # the movement dictionnary will not define a new cell with this id

        new_object_id_list = []

        temp_object_id = 'temp_' + object_id
        o = self._getOb(temp_object_id)
        if not to_delete and not (None in object_place):
          self._delObject(temp_object_id) # In all cases, we have
                                          # to remove the temp object
          object_place.insert(0, base_id)
          new_name = '_'.join(object_place)
          o.id = new_name
          new_object_id_list.extend(new_name)
          self._setObject(new_name, aq_base(o))

          if new_name != object_id:
            # Theses two lines are very important, if the object is renamed
            # then we must uncatalog the previous one
            o.unindexObject(path='%s/%s' % (self.getUrl() , object_id))
            # Force a new uid to be allocated, because unindexObject creates
            # an activity which will later delete lines from catalog based
            # on their uid, and it is not garanted that indexation will happen
            # after this deletion.
            # It is bad to waste uids, but this data structure offers no
            # alternative because cell id gives its index in the matrix,
            # so reordering axes requires the cell id to change.
            # XXX: It can be improved, but requires most of this file to be
            # rewritten, and compatibility code must be written as data
            # structure would most probably change.
            o.uid = None
          o.reindexObject() # we reindex in case position has changed
                            # uid should be consistent
        else:
          # In all cases, we have to remove the temp object
          #WARNING -> if path is not good, it will not be able to uncatalog !!!
          #o.immediateReindexObject() # STILL A PROBLEM -> getUidForPath XXX

          if object_id not in new_object_id_list: # do not unindex a new object
            o.isIndexable = ConstantGetter('isIndexable', value=True)
            o.unindexObject(path='%s/%s' % (self.getUrl() , object_id))
            # unindexed already forced
            o.isIndexable = ConstantGetter('isIndexable', value=False)
          self._delObject(temp_object_id) # object will be removed
                                               # from catalog automaticaly
      # We don't need the old index any more, we
      # can set the new index
      self.index[base_id] = new_index
Beispiel #47
0
  def generateNewIdList(self, id_group=None, id_count=1, default=None,
                        store=_marker, id_generator=None):
    """
      Generate a list of next ids in the sequence of ids of a particular group
    """
    if id_group in (None, 'None'):
      raise ValueError, '%s is not a valid id_group' % (repr(id_group), )
    # for compatibilty with sql data, must not use id_group as a list
    if not isinstance(id_group, str):
      id_group = repr(id_group)
      warnings.warn('id_group must be a string, other types '
                    'are deprecated.', DeprecationWarning)
    if id_generator is None:
      id_generator = 'uid'
    if store is not _marker:
      warnings.warn("Use of 'store' argument is deprecated.",
                    DeprecationWarning)
    try:
      #use _getLatestGeneratorValue here for that the technical level
      #must not call the method
      last_generator = self._getLatestGeneratorValue(id_generator)
      new_id_list = last_generator.generateNewIdList(id_group=id_group,
                         id_count=id_count, default=default)
    except (KeyError, ValueError):
      template_tool = getattr(self, 'portal_templates', None)
      revision = template_tool.getInstalledBusinessTemplateRevision('erp5_core')
      # XXX backward compatiblity
      if int(revision) > 1561:
        LOG('generateNewIdList', ERROR, 'while generating id')
        raise
      else:
        # Compatibility code below, in case the last version of erp5_core
        # is not installed yet
        warnings.warn("You are using an old version of erp5_core to generate"
                      "ids.\nPlease update erp5_core business template to "
                      "use new id generators", DeprecationWarning)
        new_id = None
        if default is None:
          default = 1
        # XXX It's temporary, a New API will be implemented soon
        #     the code will be change
        portal = self.getPortalObject()
        query = getattr(portal, 'IdTool_zGenerateId', None)
        commit = getattr(portal, 'IdTool_zCommit', None)

        if query is None or commit is None:
          portal_catalog = getattr(self, 'portal_catalog').getSQLCatalog()
          query = getattr(portal_catalog, 'z_portal_ids_generate_id')
          commit = getattr(portal_catalog, 'z_portal_ids_commit')
        if None in (query, commit):
          raise AttributeError, 'Error while generating Id: ' \
            'idTool_zGenerateId and/or idTool_zCommit could not ' \
            'be found.'
        try:
          result = query(id_group=id_group, id_count=id_count, default=default)
        finally:
          commit()
        new_id = result[0]['LAST_INSERT_ID()']
        if store:
          if getattr(aq_base(self), 'dict_length_ids', None) is None:
            # Length objects are stored in a persistent mapping: there is one
            # Length object per id_group.
            self.dict_length_ids = PersistentMapping()
          if self.dict_length_ids.get(id_group) is None:
            self.dict_length_ids[id_group] = Length(new_id)
          self.dict_length_ids[id_group].set(new_id)
        new_id_list = range(new_id - id_count, new_id)
    return new_id_list
Beispiel #48
0
 def __init__(self, id=None):
   if id is None:
     id = self.__class__.id
   self._password_request_dict = PersistentMapping()
Beispiel #49
0
  def test(self, context, tested_base_category_list=None, 
           strict_membership=0, **kw):
    """
      A Predicate can be tested on a given context.
      Parameters can passed in order to ignore some conditions.

      - tested_base_category_list:  this is the list of category that we do
        want to test. For example, we might want to test only the
        destination or the source of a predicate.
      - if strict_membership is specified, we should make sure that we
        are strictly a member of tested categories
    """
    self = self.asPredicate()
    result = 1
    if getattr(aq_base(self), '_identity_criterion', None) is None:
      self._identity_criterion = PersistentMapping()
      self._range_criterion = PersistentMapping()
#    LOG('PREDICATE TEST', 0,
#        'testing %s on context of %s' % \
#        (self.getRelativeUrl(), context.getRelativeUrl()))
    for property, value in self._identity_criterion.iteritems():
      if isinstance(value, (list, tuple)):
        result = context.getProperty(property) in value
      else:
        result = context.getProperty(property) == value
#      LOG('predicate test', 0,
#          '%s after prop %s : %s == %s' % \
#          (result, property, context.getProperty(property), value))
      if not result:
        return result
    for property, (min, max) in self._range_criterion.iteritems():
      value = context.getProperty(property)
      if min is not None:
        result = value >= min
#        LOG('predicate test', 0,
#            '%s after prop %s : %s >= %s' % \
#            (result, property, value, min))
        if not result:
          return result
      if max is not None:
        result = value < max
#        LOG('predicate test', 0,
#            '%s after prop %s : %s < %s' % \
#            (result, property, value, max))
        if not result:
          return result
    multimembership_criterion_base_category_list = \
        self.getMultimembershipCriterionBaseCategoryList()
    membership_criterion_base_category_list = \
        self.getMembershipCriterionBaseCategoryList()
    tested_base_category = {}
#    LOG('predicate test', 0,
#        'categories will be tested in multi %s single %s as %s' % \
#        (multimembership_criterion_base_category_list,
#        membership_criterion_base_category_list,
#        self.getMembershipCriterionCategoryList()))
    membership_criterion_category_list = \
                            self.getMembershipCriterionCategoryList()
    if tested_base_category_list is not None:
      membership_criterion_category_list = [x for x in \
          membership_criterion_category_list if x.split('/', 1)[0] in \
          tested_base_category_list]

    # Test category memberships. Enable the read-only transaction cache
    # temporarily, if not enabled, because this part is strictly read-only,
    # and context.isMemberOf is very expensive, when the category list has
    # many items.
    enabled = getReadOnlyTransactionCache() is not None
    try:
      if not enabled:
        enableReadOnlyTransactionCache()
      for c in membership_criterion_category_list:
        bc = c.split('/', 1)[0]
        if (bc not in tested_base_category) and \
           (bc in multimembership_criterion_base_category_list):
          tested_base_category[bc] = 1
        elif (bc not in tested_base_category) and \
             (bc in membership_criterion_base_category_list):
          tested_base_category[bc] = 0
        if (bc in multimembership_criterion_base_category_list):
          tested_base_category[bc] = tested_base_category[bc] and \
                                     context.isMemberOf(c, 
                                         strict_membership=strict_membership)
#        LOG('predicate test', 0,
#            '%s after multi membership to %s' % \
#            (tested_base_category[bc], c))
        elif (bc in membership_criterion_base_category_list):
          tested_base_category[bc] = tested_base_category[bc] or \
                                     context.isMemberOf(c,
                                         strict_membership=strict_membership)
    finally:
      if not enabled:
        disableReadOnlyTransactionCache()

#        LOG('predicate test', 0,
#            '%s after single membership to %s' % \
#            (tested_base_category[bc], c))
    result = 0 not in tested_base_category.values()
#    LOG('predicate test', 0,
#        '%s after category %s ' % (result, tested_base_category.items()))
    if not result:
      return result
    # Test method calls
    test_method_id_list = self.getTestMethodIdList()
    if test_method_id_list is not None :
      for test_method_id in test_method_id_list :
        if test_method_id is not None:
          method = getattr(context,test_method_id)
          try:
            result = method(self)
          except TypeError:
            if method.func_code.co_argcount != isinstance(method, MethodType):
              raise
            # backward compatibilty with script that takes no argument
            warn('Predicate %s uses an old-style method (%s) that does not'
                 ' take the predicate as argument' % (
               self.getRelativeUrl(), method.__name__), DeprecationWarning)
            result = method()
#          LOG('predicate test', 0,
#              '%s after method %s ' % (result, test_method_id))
          if not result:
            return result
    test_tales_expression = self.getTestTalesExpression()
    if test_tales_expression != 'python: True':
      expression = Expression(test_tales_expression)
      from Products.ERP5Type.Utils import createExpressionContext
      # evaluate a tales expression with the tested value as context
      result = expression(createExpressionContext(context))
    return result
Beispiel #50
0
  def test(self, context, tested_base_category_list=None, 
           strict_membership=0, isMemberOf=None, **kw):
    """
      A Predicate can be tested on a given context.
      Parameters can passed in order to ignore some conditions.

      - tested_base_category_list:  this is the list of category that we do
        want to test. For example, we might want to test only the
        destination or the source of a predicate.
      - if strict_membership is specified, we should make sure that we
        are strictly a member of tested categories
      - isMemberOf can be a function caching results for
        CategoryTool.isMemberOf: it is always called with given 'context' and
        'strict_membership' values, and different categories.
    """
    self = self.asPredicate()
    if self is None:
      # asPredicate returned None, so this predicate never applies.
      # But if we reach this it is because catalog is not up to date.
      return False

    result = 1
    if getattr(aq_base(self), '_identity_criterion', None) is None:
      self._identity_criterion = PersistentMapping()
      self._range_criterion = PersistentMapping()
#    LOG('PREDICATE TEST', 0,
#        'testing %s on context of %s' % \
#        (self.getRelativeUrl(), context.getRelativeUrl()))
    for property, value in self._identity_criterion.iteritems():
      if isinstance(value, (list, tuple)):
        result = context.getProperty(property) in value
      else:
        result = context.getProperty(property) == value
#      LOG('predicate test', 0,
#          '%s after prop %s : %s == %s' % \
#          (result, property, context.getProperty(property), value))
      if not result:
        return result
    for property, (min, max) in self._range_criterion.iteritems():
      value = context.getProperty(property)
      if min is not None:
        result = value >= min
#        LOG('predicate test', 0,
#            '%s after prop %s : %s >= %s' % \
#            (result, property, value, min))
        if not result:
          return result
      if max is not None:
        result = value < max
#        LOG('predicate test', 0,
#            '%s after prop %s : %s < %s' % \
#            (result, property, value, max))
        if not result:
          return result
    multimembership_criterion_base_category_list = \
        self.getMultimembershipCriterionBaseCategoryList()
    membership_criterion_base_category_list = \
        self.getMembershipCriterionBaseCategoryList()
    tested_base_category = {}
#    LOG('predicate test', 0,
#        'categories will be tested in multi %s single %s as %s' % \
#        (multimembership_criterion_base_category_list,
#        membership_criterion_base_category_list,
#        self.getMembershipCriterionCategoryList()))
    # Test category memberships. Enable the read-only transaction cache
    # because this part is strictly read-only, and context.isMemberOf
    # is very expensive when the category list has many items.
    if isMemberOf is None:
      isMemberOf = context._getCategoryTool().isMemberOf
    with readOnlyTransactionCache():
      for c in self.getMembershipCriterionCategoryList():
        bc = c.split('/', 1)[0]
        if tested_base_category_list is None or bc in tested_base_category_list:
          if bc in multimembership_criterion_base_category_list:
            if not isMemberOf(context, c, strict_membership=strict_membership):
              return 0
          elif bc in membership_criterion_base_category_list and \
               not tested_base_category.get(bc):
            tested_base_category[bc] = \
              isMemberOf(context, c, strict_membership=strict_membership)
    if 0 in tested_base_category.itervalues():
      return 0

    # Test method calls
    test_method_id_list = self.getTestMethodIdList()
    if test_method_id_list is not None :
      for test_method_id in test_method_id_list :
        if test_method_id is not None:
          method = getattr(context,test_method_id)
          try:
            result = method(self)
          except TypeError:
            if method.func_code.co_argcount != isinstance(method, MethodType):
              raise
            # backward compatibilty with script that takes no argument
            warn('Predicate %s uses an old-style method (%s) that does not'
                 ' take the predicate as argument' % (
               self.getRelativeUrl(), method.__name__), DeprecationWarning)
            result = method()
#          LOG('predicate test', 0,
#              '%s after method %s ' % (result, test_method_id))
          if not result:
            return result
    test_tales_expression = self.getTestTalesExpression()
    if test_tales_expression != 'python: True':
      expression = Expression(test_tales_expression)
      from Products.ERP5Type.Utils import createExpressionContext
      # evaluate a tales expression with the tested value as context
      result = expression(createExpressionContext(context))
    return result
Beispiel #51
0
    def _renameCellRange(self, *kw, **kwd):
      """
          Rename a range for a matrix, this method can
          also handle a changement of the size of a matrix
      """
      base_id = kwd.get('base_id', 'cell')

      if getattr(aq_base(self), 'index', None) is None:
        self.index = PersistentMapping()

      # Return if previous range is the same
      current_range = self.getCellRange(base_id=base_id) or []
      if current_range == list(kw): # kw is a tuple
        LOG('XMLMatrix',0,'return form _setCellRange - no need to change range')
        return

      current_len = len(current_range)
      new_len = len(kw)
      len_delta = new_len - current_len

      # We must make sure the base_id exists
      # in the event of a matrix creation for example
      if not self.index.has_key(base_id):
        # Create an index for this base_id
        self.index[base_id] = PersistentMapping()

      cell_id_list = []
      for cell_id in self.getCellIdList(base_id = base_id):
        if self.get(cell_id) is not None:
          cell_id_list.append(cell_id)

      # First, delete all cells which are out of range.
      size_list = map(len, kw)
      if len_delta < 0:
        size_list.extend([1] * (-len_delta))
      def is_in_range(cell_id):
        for i, index in enumerate(cell_id[len(base_id)+1:].split('_')):
          if int(index) >= size_list[i]:
            self._delObject(cell_id)
            return False
        return True
      cell_id_list = filter(is_in_range, cell_id_list)

      # Secondly, rename coordinates. This does not change cell ids.
      for i in range(max(new_len, current_len)):
        if i >= new_len:
          del self.index[base_id][i]
        else:
          if i >= current_len:
            self.index[base_id][i] = PersistentMapping()
          for place in self.index[base_id][i].keys():
            if place not in kw[i]:
              del self.index[base_id][i][place]

          for j, place in enumerate(kw[i]):
            self.index[base_id][i][place] = j

      # Lastly, rename ids and catalog/uncatalog everything.
      if len_delta > 0:
        # Need to move, say, base_1_2 -> base_1_2_0
        appended_id = '_0' * len_delta
        for old_id in cell_id_list:
          cell = self.get(old_id)
          if cell is not None:
            new_id = old_id + appended_id
            self._delObject(old_id)
            cell.isIndexable = ConstantGetter('isIndexable', value=False)
            cell.id = new_id
            self._setObject(new_id, aq_base(cell))
            cell.isIndexable = ConstantGetter('isIndexable', value=True)
            cell.reindexObject()
            #cell.unindexObject(path='%s/%s' % (self.getUrl(), old_id))
      elif len_delta < 0:
        # Need to move, say, base_1_2_0 -> base_1_2
        removed_id_len = 2 * (-len_delta)
        for old_id in cell_id_list:
          cell = self.get(old_id)
          if cell is not None:
            new_id = old_id[:-removed_id_len]
            self._delObject(old_id)
            cell.isIndexable = ConstantGetter('isIndexable', value=False)
            cell.id = new_id
            self._setObject(new_id, aq_base(cell))
            cell.isIndexable = ConstantGetter('isIndexable', value=True)
            cell.reindexObject()