Example #1
0
class TransformTool(UniqueObject, ActionProviderBase, Folder):

    id = 'portal_transforms'
    meta_type = id.title().replace('_', ' ')
    isPrincipiaFolderish = 1  # Show up in the ZMI

    implements(IPortalTransformsTool, IEngine)

    meta_types = all_meta_types = (
        {
            'name': 'Transform',
            'action': 'manage_addTransformForm'
        },
        {
            'name': 'TransformsChain',
            'action': 'manage_addTransformsChainForm'
        },
    )

    manage_addTransformForm = PageTemplateFile('addTransform', _www)
    manage_addTransformsChainForm = PageTemplateFile('addTransformsChain',
                                                     _www)
    manage_cacheForm = PageTemplateFile('setCacheTime', _www)
    manage_editTransformationPolicyForm = PageTemplateFile(
        'editTransformationPolicy', _www)
    manage_reloadAllTransforms = PageTemplateFile('reloadAllTransforms', _www)

    manage_options = ((Folder.manage_options[0], ) +
                      Folder.manage_options[2:] + (
                          {
                              'label': 'Caches',
                              'action': 'manage_cacheForm'
                          },
                          {
                              'label': 'Policy',
                              'action': 'manage_editTransformationPolicyForm'
                          },
                          {
                              'label': 'Reload transforms',
                              'action': 'manage_reloadAllTransforms'
                          },
                      ))

    security = ClassSecurityInfo()

    def __init__(self, policies=None, max_sec_in_cache=3600):
        self._mtmap = PersistentMapping()
        self._policies = policies or PersistentMapping()
        self.max_sec_in_cache = max_sec_in_cache
        self._new_style_pt = 1

    # mimetype oriented conversions (iengine interface)

    security.declarePrivate('unregisterTransform')

    def unregisterTransform(self, name):
        """ unregister a transform
        name is the name of a registered transform
        """
        self._unmapTransform(getattr(self, name))
        if name in self.objectIds():
            self._delObject(name)

    security.declarePrivate('convertTo')

    def convertTo(self,
                  target_mimetype,
                  orig,
                  data=None,
                  object=None,
                  usedby=None,
                  context=None,
                  **kwargs):
        """Convert orig to a given mimetype

        * orig is an encoded string

        * data an optional IDataStream object. If None a new datastream will be
        created and returned

        * optional object argument is the object on which is bound the data.
        If present that object will be used by the engine to bound cached data.

        * additional arguments (kwargs) will be passed to the transformations.
        Some usual arguments are : filename, mimetype, encoding

        return an object implementing IDataStream or None if no path has been
        found.
        """
        target_mimetype = str(target_mimetype)

        if object is not None:
            cache = Cache(object, context=context)
            data = cache.getCache(target_mimetype)
            if data is not None:
                time, data = data
                if self.max_sec_in_cache == 0 or time < self.max_sec_in_cache:
                    return data

        if data is None:
            data = self._wrap(target_mimetype)

        registry = getToolByName(self, 'mimetypes_registry')

        if not getattr(aq_base(registry), 'classify', None):
            # avoid problems when importing a site with an old mimetype
            # registry
            return None

        orig_mt = registry.classify(orig,
                                    mimetype=kwargs.get('mimetype'),
                                    filename=kwargs.get('filename'))
        orig_mt = str(orig_mt)
        if not orig_mt:
            log('Unable to guess input mime type (filename=%s, mimetype=%s)' %
                (kwargs.get('mimetype'), kwargs.get('filename')),
                severity=WARNING)
            return None

        target_mt = registry.lookup(target_mimetype)
        if target_mt:
            target_mt = target_mt[0]
        else:
            log('Unable to match target mime type %s' % str(target_mimetype),
                severity=WARNING)
            return None

        ## fastpath
        # If orig_mt and target_mt are the same, we only allow
        # a one-hop transform, a.k.a. filter.
        # XXX disabled filtering for now
        if orig_mt == str(target_mt):
            data.setData(orig)
            md = data.getMetadata()
            md['mimetype'] = str(orig_mt)
            if object is not None:
                cache.setCache(str(target_mimetype), data)
            return data

        ## get a path to output mime type
        requirements = self.getRequirementListByMimetype(
            str(orig_mt), str(target_mt))
        path = self._findPath(orig_mt, target_mt, list(requirements))
        if not path and requirements:
            log('Unable to satisfy requirements %s' % ', '.join(requirements),
                severity=WARNING)
            path = self._findPath(orig_mt, target_mt)

        if not path:
            log('NO PATH FROM %s TO %s : %s' %
                (orig_mt, target_mimetype, path),
                severity=WARNING)
            return None

        if len(path) > 1:
            ## create a chain on the fly (sly)
            transform = chain()
            for t in path:
                transform.registerTransform(t)
        else:
            transform = path[0]

        result = transform.convert(orig,
                                   data,
                                   context=context,
                                   usedby=usedby,
                                   **kwargs)
        self._setMetaData(result, transform)

        # set cache if possible
        if object is not None and result.isCacheable():
            cache.setCache(str(target_mimetype), result)

        # return IDataStream object
        return result

    security.declarePrivate('getRequirementListByMimetype')

    def getRequirementListByMimetype(self, origin_mimetype, target_mimetype):
        """Return requirements only if origin_mimetype
      and target_mimetype are matching transform policy

      As an example pdf => text conversion force a transformation
      to intermediate HTML format, because w3m_dump is a requirement
      to output plain/text.
      But we want using pdf_to_text directly.

      So requirements are returned only if
      origin_mimetype and target_mimetype sastify
      the requirement: ie html_to_text is returned
      only if origin_mimetype  == 'text/html' and
      target_mimetype == 'text/plain'
      """
        result_list = []
        candidate_requirement_list = self._policies.get(target_mimetype, [])
        for candidate_requirement in candidate_requirement_list:
            transform = getattr(self, candidate_requirement)
            if origin_mimetype in transform.inputs:
                result_list.append(candidate_requirement)
        return result_list

    security.declarePublic('convertToData')

    def convertToData(self,
                      target_mimetype,
                      orig,
                      data=None,
                      object=None,
                      usedby=None,
                      context=None,
                      **kwargs):
        """Convert to a given mimetype and return the raw data
        ignoring subobjects. see convertTo for more information
        """
        data = self.convertTo(target_mimetype, orig, data, object, usedby,
                              context, **kwargs)
        if data:
            return data.getData()
        return None

    security.declarePublic('convert')

    def convert(self, name, orig, data=None, context=None, **kwargs):
        """run a tranform of a given name on data

        * name is the name of a registered transform

        see convertTo docstring for more info
        """
        if not data:
            data = self._wrap(name)
        try:
            transform = getattr(self, name)
        except AttributeError:
            raise Exception('No such transform "%s"' % name)
        data = transform.convert(orig, data, context=context, **kwargs)
        self._setMetaData(data, transform)
        return data

    def __call__(self, name, orig, data=None, context=None, **kwargs):
        """run a transform by its name, returning the raw data product

        * name is the name of a registered transform.

        return an encoded string.
        see convert docstring for more info on additional arguments.
        """
        data = self.convert(name, orig, data, context, **kwargs)
        return data.getData()

    # utilities ###############################################################

    def _setMetaData(self, datastream, transform):
        """set metadata on datastream according to the given transform
        (mime type and optionaly encoding)
        """
        md = datastream.getMetadata()
        if hasattr(transform, 'output_encoding'):
            md['encoding'] = transform.output_encoding
        md['mimetype'] = transform.output

    def _wrap(self, name):
        """wrap a data object in an icache"""
        return datastream(name)

    def _unwrap(self, data):
        """unwrap data from an icache"""
        if IDataStream.providedBy(data):
            data = data.getData()
        return data

    def _mapTransform(self, transform):
        """map transform to internal structures"""
        registry = getToolByName(self, 'mimetypes_registry')
        inputs = getattr(transform, 'inputs', None)
        if not inputs:
            raise TransformException('Bad transform %s : no input MIME type' %
                                     (transform))
        for i in inputs:
            mts = registry.lookup(i)
            if not mts:
                msg = 'Input MIME type %r for transform %s is not registered '\
                      'in the MIME types registry' % (i, transform.name())
                raise TransformException(msg)
            for mti in mts:
                for mt in mti.mimetypes:
                    mt_in = self._mtmap.setdefault(mt, PersistentMapping())
                    output = getattr(transform, 'output', None)
                    if not output:
                        msg = 'Bad transform %s : no output MIME type'
                        raise TransformException(msg % transform.name())
                    mto = registry.lookup(output)
                    if not mto:
                        msg = 'Output MIME type %r for transform %s is not '\
                              'registered in the MIME types registry' % \
                              (output, transform.name())
                        raise TransformException(msg)
                    if len(mto) > 1:
                        msg = ("Wildcarding not allowed in transform's output "
                               "MIME type")
                        raise TransformException(msg)

                    for mt2 in mto[0].mimetypes:
                        try:
                            if not transform in mt_in[mt2]:
                                mt_in[mt2].append(transform)
                        except KeyError:
                            mt_in[mt2] = PersistentList([transform])

    def _unmapTransform(self, transform):
        """unmap transform from internal structures"""
        registry = getToolByName(self, 'mimetypes_registry')
        for i in transform.inputs:
            for mti in registry.lookup(i):
                for mt in mti.mimetypes:
                    try:
                        mt_in = self._mtmap[mt]
                    except KeyError:
                        continue
                    output = transform.output
                    mto = registry.lookup(output)
                    for mt2 in mto[0].mimetypes:
                        try:
                            l = mt_in[mt2]
                        except KeyError:
                            continue
                        for i in range(len(l)):
                            if transform.name() == l[i].name():
                                l.pop(i)
                                break
                        else:
                            log('Can\'t find transform %s from %s to %s' %
                                (transform.name(), mti, mt),
                                severity=WARNING)

    def _findPath(self, orig, target, required_transforms=()):
        """return the shortest path for transformation from orig mimetype to
        target mimetype
        """
        if not self._mtmap:
            return None

        orig = str(orig)
        target = str(target)
        # First, let's deal with required transforms.
        if required_transforms:
            # Let's decompose paths, then.
            required_transform = required_transforms.pop(0)
            # The first path must lead to one of the inputs supported
            # by this first required transform.
            # Which input types are supported by this transform ?
            supportedInputs = {}
            for input, outputs in self._mtmap.items():
                for output, transforms in outputs.items():
                    for transform in transforms:
                        if transform.name() == required_transform:
                            supportedInputs[input] = 'ok'
                            # BTW, let's remember the output type
                            transformOutput = output
                            # and remember the transform, it is
                            # useful later
                            requiredTransform = transform
            # Which of these inputs will be reachable with the
            # shortest path ?
            shortest = 9999  # big enough, I guess
            shortestFirstPath = None
            for supportedInput in supportedInputs.keys():
                # We start from orig
                firstOrig = orig
                # And want to reach supportedInput
                firstTarget = supportedInput
                # What's the shortest path ?
                firstPath = self._findPath(firstOrig, firstTarget)
                if firstPath is not None:
                    if len(firstPath) < shortest:
                        # Here is a path which is shorter than others
                        # which also reach the required transform.
                        shortest = len(firstPath)
                        shortestFirstPath = firstPath
            if shortestFirstPath == None:
                return None  # there is no path leading to this transform
            # Then we have to take this transform.
            secondPath = [requiredTransform]
            # From the output of this transform, we then have to
            # reach our target, possible through other required
            # transforms.
            thirdOrig = transformOutput
            thirdTarget = target
            thirdPath = self._findPath(thirdOrig, thirdTarget,
                                       required_transforms)
            if thirdPath is None:
                return None  # no path
            # Final result is the concatenation of these 3 parts
            return shortestFirstPath + secondPath + thirdPath

        if orig == target:
            return []

        # Now let's efficiently find the shortest path from orig
        # to target (without required transforms).
        # The overall idea is that we build all possible paths
        # starting from orig and of given length. And we increment
        # this length until one of these paths reaches our target or
        # until all reachable types have been reached.
        currentPathLength = 0
        pathToType = {orig: []}  # all paths we know, by end of path.

        def typesWithPathOfLength(length):
            '''Returns the lists of known paths of a given length'''
            result = []
            for type_, path in pathToType.items():
                if len(path) == length:
                    result.append(type_)
            return result

        # We will start exploring paths which start from types
        # reachable in zero steps. That is paths which start from
        # orig.
        typesToStartFrom = typesWithPathOfLength(currentPathLength)
        # Explore paths while there are new paths to be explored
        while len(typesToStartFrom) > 0:
            for startingType in typesToStartFrom:
                # Where can we go in one step starting from here ?
                outputs = self._mtmap.get(startingType)
                if outputs:
                    for reachedType, transforms in outputs.items():
                        # Does this lead to a type we never reached before ?
                        if reachedType not in pathToType.keys() and transforms:
                            # Yes, we did not know any path reaching this type
                            # Let's remember the path to here
                            pathToType[reachedType] = (
                                pathToType[startingType] + [transforms[0]])
                            if reachedType == target:
                                # This is the first time we reach our target.
                                # We have our shortest path to target.
                                return pathToType[target]
            # We explored all possible paths of length currentPathLength
            # Let's increment that length.
            currentPathLength += 1
            # What are the next types to start from ?
            typesToStartFrom = typesWithPathOfLength(currentPathLength)
        # We are done exploring paths starting from orig
        # and this exploration did not reach our target.
        # Hence there is no path from orig to target.
        return None

    def _getPaths(self, orig, target, requirements, path=None, result=None):
        """return some of the paths for transformation from orig mimetype to
        target mimetype with the guarantee that the shortest path is included.
        If target is the same as orig, then returns an empty path.
        """

        shortest = 9999
        if result:
            for okPath in result:
                shortest = min(shortest, len(okPath))

        if orig == target:
            return [[]]
        if path is None:
            result = []
            path = []
            requirements = list(requirements)
        outputs = self._mtmap.get(orig)
        if outputs is None:
            return result

        registry = getToolByName(self, 'mimetypes_registry')
        mto = registry.lookup(target)
        # target mimetype aliases
        target_aliases = mto[0].mimetypes

        path.append(None)
        for o_mt, transforms in outputs.items():
            for transform in transforms:
                required = 0
                name = transform.name()
                if name in requirements:
                    requirements.remove(name)
                    required = 1
                if transform in path:
                    # avoid infinite loop...
                    continue
                path[-1] = transform
                if o_mt in target_aliases:
                    if not requirements:
                        result.append(path[:])
                        if len(path[:]) < shortest:
                            # here is a shorter one !
                            shortest = len(path)
                else:
                    if len(path) < shortest:
                        # keep exploring this path, it is still short enough
                        self._getPaths(o_mt, target, requirements, path,
                                       result)
                if required:
                    requirements.append(name)
        path.pop()

        return result

    security.declarePrivate('manage_afterAdd')

    def manage_afterAdd(self, item, container):
        """ overload manage_afterAdd to finish initialization when the
        transform tool is added
        """
        Folder.manage_afterAdd(self, item, container)
        try:
            initialize(self)
        except TransformException:
            # may fail on copy or zexp import
            pass

    security.declareProtected(ManagePortal, 'manage_addTransform')

    def manage_addTransform(self, id, module, REQUEST=None):
        """ add a new transform to the tool """
        transform = Transform(id, module)
        self._setObject(id, transform)
        self._mapTransform(transform)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_main')

    security.declareProtected(ManagePortal, 'manage_addTransform')

    def manage_addTransformsChain(self, id, description, REQUEST=None):
        """ add a new transform to the tool """
        transform = TransformsChain(id, description)
        self._setObject(id, transform)
        self._mapTransform(transform)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_main')

    security.declareProtected(ManagePortal, 'manage_addTransform')

    def manage_setCacheValidityTime(self, seconds, REQUEST=None):
        """set  the lifetime of cached data in seconds"""
        self.max_sec_in_cache = int(seconds)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_main')

    security.declareProtected(ManagePortal, 'reloadTransforms')

    def reloadTransforms(self, ids=()):
        """ reload transforms with the given ids
        if no ids, reload all registered transforms

        return a list of (transform_id, transform_module) describing reloaded
        transforms
        """
        if not ids:
            ids = self.objectIds()
        reloaded = []
        for id in ids:
            o = getattr(self, id)
            o.reload()
            reloaded.append((id, o.module))
            try:
                self._unmapTransform(o)
            except MimeTypeException:
                pass
            try:
                self._mapTransform(o)
            except MimeTypeException:
                pass
        return reloaded

    # Policy handling methods

    def manage_addPolicy(self,
                         output_mimetype,
                         required_transforms,
                         REQUEST=None):
        """ add a policy for a given output mime types"""
        registry = getToolByName(self, 'mimetypes_registry')
        if not registry.lookup(output_mimetype):
            raise TransformException('Unknown MIME type')
        if output_mimetype in self._policies:
            msg = 'A policy for output %s is yet defined' % output_mimetype
            raise TransformException(msg)

        required_transforms = tuple(required_transforms)
        self._policies[output_mimetype] = required_transforms
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(
                self.absolute_url() + '/manage_editTransformationPolicyForm')

    def manage_delPolicies(self, outputs, REQUEST=None):
        """ remove policies for given output mime types"""
        for mimetype in outputs:
            del self._policies[mimetype]
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(
                self.absolute_url() + '/manage_editTransformationPolicyForm')

    security.declareProtected(View, 'listPolicies')

    def listPolicies(self):
        """ return the list of defined policies

        a policy is a 2-uple (output_mime_type, [list of required transforms])
        """
        # XXXFIXME: backward compat, should be removed latter
        if not hasattr(self, '_policies'):
            self._policies = PersistentMapping()
        return self._policies.items()

    # mimetype oriented conversions (iengine interface)

    security.declarePrivate('registerTransform')

    def registerTransform(self, transform):
        """register a new transform

        transform isn't a Zope Transform (the wrapper) but the wrapped
        transform the persistence wrapper will be created here
        """
        # needed when call from transform.transforms.initialize which
        # register non zope transform
        module = str(transform.__module__)
        transform = Transform(transform.name(), module, transform)
        if not ITransform.providedBy(transform):
            raise TransformException('%s does not implement ITransform' %
                                     transform)
        name = transform.name()
        __traceback_info__ = (name, transform)
        if name not in self.objectIds():
            self._setObject(name, transform)
            self._mapTransform(transform)

    security.declareProtected(ManagePortal, 'ZopeFind')

    def ZopeFind(self, *args, **kwargs):
        """Don't break ZopeFind feature when a transform can't be loaded
        """
        try:
            return Folder.ZopeFind(self, *args, **kwargs)
        except MissingBinary:
            log('ZopeFind: catched MissingBinary exception')

    security.declareProtected(View, 'objectItems')

    def objectItems(self, *args, **kwargs):
        """Don't break ZopeFind feature when a transform can't be loaded
        """
        try:
            return Folder.objectItems(self, *args, **kwargs)
        except MissingBinary:
            log('objectItems: catched MissingBinary exception')
            return []

    # available mimetypes ####################################################
    security.declarePrivate('listAvailableTextInputs')

    def listAvailableTextInputs(self):
        """Returns a list of mimetypes that can be used as input for textfields
        by building a list of the inputs beginning with "text/" of all
        transforms.
        """
        available_types = []
        candidate_transforms = [object[1] for object in self.objectItems()]
        for candidate in candidate_transforms:
            for input in candidate.inputs:
                if input.startswith("text/") and input not in available_types:
                    available_types.append(input)
        return available_types

    security.declarePublic('getAvailableTargetMimetypeList')

    def getAvailableTargetMimetypeList(self, source_mimetype):
        """
          Returns a list of mimetypes that can be used as target for
          `source_mimetype` conversion.
        """

        # clean up mimetype from its useless characters
        source_mimetype = parseContentType(source_mimetype)
        source_mimetype = ";".join([source_mimetype.gettype()] +
                                   source_mimetype.getplist())

        # fill dict that will contain all possible conversion for each mimetype
        input_output_dict = {
        }  # {"application/pdf": set(["text/html", "application/msword", ...])}
        for obj in self.objectValues():
            for input_ in obj.inputs:
                if input_ in input_output_dict:
                    input_output_dict[input_].add(obj.output)
                else:
                    input_output_dict[input_] = set([obj.output])

        # browse mimetypes to fill result_set of available target mimetypes
        result_set = set([source_mimetype])
        browsing_list = [source_mimetype]
        input_output_items = input_output_dict.items()
        while len(browsing_list):
            browsing_mimetype = browsing_list.pop()
            for mimetype, output_mimetype_set in input_output_items:
                if (browsing_mimetype == mimetype or
                    (mimetype.endswith("/*") and
                     browsing_mimetype[:len(mimetype[:-1])] == mimetype[:-1])):
                    for output_mimetype in output_mimetype_set:
                        if output_mimetype not in result_set:
                            result_set.add(output_mimetype)
                            browsing_list.append(output_mimetype)

        result_set.remove(source_mimetype)
        return list(result_set)
Example #2
0
File: Field.py Project: ra2003/erp5
class Field:
    """Base class of all fields.
    A field is an object consisting of a widget and a validator.
    """
    security = ClassSecurityInfo()

    # this is a field
    is_field = 1
    # this is not an internal field (can be overridden by subclass)
    internal_field = 0
    # can alternatively render this field with Zope's :record syntax
    # this will be the record's name
    field_record = None

    def __init__(self, id, **kw):
        self.id = id
        # initialize values of fields in form
        self.initialize_values(kw)
        # initialize tales expression for fields in form
        self.initialize_tales()
        # initialize overrides of fields in form
        self.initialize_overrides()

        # initialize message values with defaults
        message_values = {}
        for message_name in self.validator.message_names:
            message_values[message_name] = getattr(self.validator,
                                                   message_name)
        self.message_values = message_values

    security.declareProtected('Change Formulator Fields', 'initialize_values')

    def initialize_values(self, dict):
        """Initialize values for properties, defined by fields in
        associated form.
        """
        values = {}
        for field in self.form.get_fields(include_disabled=1):
            id = field.id
            value = dict.get(id, field.get_value('default'))
            values[id] = value
        self.values = values

    security.declareProtected('Change Formulator Fields', 'initialize_tales')

    def initialize_tales(self):
        """Initialize tales expressions for properties (to nothing).
        """
        tales = {}
        for field in self.form.get_fields():
            id = field.id
            tales[id] = ""
        self.tales = tales

    security.declareProtected('Change Formulator Fields',
                              'initialize_overrides')

    def initialize_overrides(self):
        """Initialize overrides for properties (to nothing).
        """
        overrides = {}
        for field in self.form.get_fields():
            id = field.id
            overrides[id] = ""
        self.overrides = overrides

    security.declareProtected('Access contents information', 'has_value')

    def has_value(self, id):
        """Return true if the field defines such a value.
        """
        if self.values.has_key(id) or self.form.has_field(id):
            return 1
        else:
            return 0

    security.declareProtected('Access contents information', 'get_orig_value')

    def get_orig_value(self, id):
        """Get value for id; don't do any override calculation.
        """
        if self.values.has_key(id):
            return self.values[id]
        else:
            return self.form.get_field(id).get_value('default')

    security.declareProtected('Access contents information', 'get_value')

    def get_value(self, id, **kw):
        """Get value for id.

        Optionally pass keyword arguments that get passed to TALES
        expression.
        """
        tales_expr = self.tales.get(id, "")

        if tales_expr:
            # For some reason, path expressions expect 'here' and 'request'
            # to exist, otherwise they seem to fail. python expressions
            # don't seem to have this problem.

            # add 'here' if not in kw
            if not kw.has_key('here'):
                kw['here'] = self.aq_parent
            if not kw.has_key('request'):
                kw['request'] = self.REQUEST
            value = tales_expr.__of__(self)(field=self,
                                            form=self.aq_parent,
                                            **kw)
        else:
            override = self.overrides.get(id, "")
            if override:
                # call wrapped method to get answer
                value = override.__of__(self)()
            else:
                # get normal value
                value = self.get_orig_value(id)

        # if normal value is a callable itself, wrap it
        if callable(value):
            return value.__of__(self)
        else:
            return value

    security.declareProtected('View management screens', 'get_override')

    def get_override(self, id):
        """Get override method for id (not wrapped)."""
        return self.overrides.get(id, "")

    security.declareProtected('View management screens', 'get_tales')

    def get_tales(self, id):
        """Get tales expression method for id."""
        return self.tales.get(id, "")

    security.declareProtected('Access contents information', 'is_required')

    def is_required(self):
        """Check whether this field is required (utility function)
        """
        return self.has_value('required') and self.get_value('required')

    security.declareProtected('View management screens', 'get_error_names')

    def get_error_names(self):
        """Get error messages.
        """
        return self.validator.message_names

    security.declareProtected('Access contents information',
                              'generate_field_key')

    def generate_field_key(self, validation=0, key=None, key_prefix=None):
        """Generate the key Silva uses to render the field in the form.
      """
        # Patched by JPS for ERP5 in order to
        # dynamically change the name
        if key_prefix is None:
            key_prefix = 'field'
        if key is not None:
            return '%s_%s' % (key_prefix, key)
        if self.field_record is None:
            return '%s_%s' % (key_prefix, self.id)
        elif validation:
            return self.id
        elif isinstance(self.widget, MultiItemsWidget):
            return "%s.%s:record:list" % (self.field_record, self.id)
        else:
            return '%s.%s:record' % (self.field_record, self.id)

    security.declareProtected('Access contents information',
                              'generate_subfield_key')

    def generate_subfield_key(self, id, validation=0, key=None):
        """Generate the key Silva uses to render a sub field.
         Added key parameter for ERP5 in order to be compatible with listbox/matrixbox
      """
        if key is None: key = self.id
        if self.field_record is None or validation:
            return 'subfield_%s_%s' % (key, id)
        return '%s.subfield_%s_%s:record' % (self.field_record, key, id)

    security.declareProtected('View management screens', 'get_error_message')

    def get_error_message(self, name):
        try:
            return self.message_values[name]
        except KeyError:
            if name in self.validator.message_names:
                return getattr(self.validator, name)
            else:
                return "Unknown error: %s" % name

    security.declarePrivate('_render_helper')

    def _render_helper(self,
                       key,
                       value,
                       REQUEST,
                       render_prefix=None,
                       editable=None,
                       **kw):
        value = self._get_default(key, value, REQUEST)
        __traceback_info__ = ('key=%s value=%r' % (key, value))
        if self.get_value('hidden', REQUEST=REQUEST):
            return self.widget.render_hidden(self, key, value, REQUEST)
        else:
            if editable is None:
                editable = self.get_value('editable', REQUEST=REQUEST)
            if not editable:
                return self.widget.render_view(self,
                                               value,
                                               REQUEST=REQUEST,
                                               render_prefix=render_prefix,
                                               **kw)
            else:
                return self.widget.render(self,
                                          key,
                                          value,
                                          REQUEST,
                                          render_prefix=render_prefix,
                                          **kw)

    security.declarePrivate('_render_odt_helper')

    def _render_odt_helper(self, key, value, as_string, ooo_builder, REQUEST,
                           render_prefix, attr_dict, local_name):
        value = self._get_default(key, value, REQUEST)
        __traceback_info__ = ('key=%s value=%r' % (key, value))
        if not self.get_value('editable', REQUEST=REQUEST):
            return self.widget.render_odt_view(self, value, as_string,
                                               ooo_builder, REQUEST,
                                               render_prefix, attr_dict,
                                               local_name)
        else:
            return self.widget.render_odt(self, value, as_string, ooo_builder,
                                          REQUEST, render_prefix, attr_dict,
                                          local_name)

    security.declarePrivate('_render_odt_variable_helper')

    def _render_odt_variable_helper(self, key, value, as_string, ooo_builder,
                                    REQUEST, render_prefix, attr_dict,
                                    local_name):
        value = self._get_default(key, value, REQUEST)
        __traceback_info__ = ('key=%s value=%r' % (key, value))
        return self.widget.render_odt_variable(self, value, as_string,
                                               ooo_builder, REQUEST,
                                               render_prefix, attr_dict,
                                               local_name)

    security.declarePrivate('_get_default')

    def _get_default(self, key, value, REQUEST):
        if value is not None:
            return value
        try:
            value = REQUEST.form[key]
        except (KeyError, AttributeError):
            # fall back on default
            return self.get_value('default')

        # if we enter a string value while the field expects unicode,
        # convert to unicode first
        # this solves a problem when re-rendering a sticky form with
        # values from request
        if (self.has_value('unicode') and self.get_value('unicode')
                and type(value) == type('')):
            return unicode(value, self.get_form_encoding())
        else:
            return value

    security.declarePrivate('_get_user_input_value')

    def _get_user_input_value(self, key, REQUEST):
        """
      Try to get a value of the field from the REQUEST
      """
        return REQUEST.form[key]

    security.declareProtected('View', 'render')

    def render(self,
               value=None,
               REQUEST=None,
               key=None,
               render_prefix=None,
               key_prefix=None,
               editable=None,
               **kw):
        """Render the field widget.
      value -- the value the field should have (for instance
                from validation).
      REQUEST -- REQUEST can contain raw (unvalidated) field
                information. If value is None, REQUEST is searched
                for this value.
      editable -- if not None, this boolean can override the Editable property
                 of the rendered field
      if value and REQUEST are both None, the 'default' property of
      the field will be used for the value.
      """
        return self._render_helper(self.generate_field_key(
            key=key, key_prefix=key_prefix),
                                   value,
                                   REQUEST,
                                   render_prefix=render_prefix,
                                   editable=editable,
                                   **kw)

    security.declareProtected('View', 'render_view')

    def render_view(self, value=None, REQUEST=None, render_prefix=None):
        """Render value to be viewed.
      """
        return self.widget.render_view(self, value, REQUEST=REQUEST)

    security.declareProtected('View', 'render_pdf')

    def render_pdf(self, value=None, REQUEST=None, key=None, **kw):
        """
      render_pdf renders the field for reportlab
      """
        return self.widget.render_pdf(self, value)

    security.declareProtected('View', 'render_html')

    def render_html(self, *args, **kw):
        """
      render_html is used to as definition of render method in Formulator.
      """
        return self.render(*args, **kw)

    security.declareProtected('View', 'render_htmlgrid')

    def render_htmlgrid(self,
                        value=None,
                        REQUEST=None,
                        key=None,
                        render_prefix=None,
                        key_prefix=None):
        """
      render_htmlgrid returns a list of tuple (title, html render)
      """
        # What about CSS ? What about description ? What about error ?
        widget_key = self.generate_field_key(key=key, key_prefix=key_prefix)
        value = self._get_default(widget_key, value, REQUEST)
        __traceback_info__ = ('key=%s value=%r' % (key, value))
        return self.widget.render_htmlgrid(self,
                                           widget_key,
                                           value,
                                           REQUEST,
                                           render_prefix=render_prefix)

    security.declareProtected('View', 'render_odf')

    def render_odf(self,
                   field=None,
                   key=None,
                   value=None,
                   REQUEST=None,
                   render_format='ooo',
                   render_prefix=None):
        return self.widget.render_odf(self, key, value, REQUEST, render_format,
                                      render_prefix)

    security.declareProtected('View', 'render_odt')

    def render_odt(self,
                   key=None,
                   value=None,
                   as_string=True,
                   ooo_builder=None,
                   REQUEST=None,
                   render_prefix=None,
                   attr_dict=None,
                   local_name='p',
                   key_prefix=None):
        field_key = self.generate_field_key(key=key, key_prefix=key_prefix)
        return self._render_odt_helper(field_key, value, as_string,
                                       ooo_builder, REQUEST, render_prefix,
                                       attr_dict, local_name)

    security.declareProtected('View', 'render_odt_variable')

    def render_odt_variable(self,
                            key=None,
                            value=None,
                            as_string=True,
                            ooo_builder=None,
                            REQUEST=None,
                            render_prefix=None,
                            attr_dict=None,
                            local_name='variable-set',
                            key_prefix=None):
        field_key = self.generate_field_key(key=key, key_prefix=key_prefix)
        return self._render_odt_variable_helper(field_key, value, as_string,
                                                ooo_builder, REQUEST,
                                                render_prefix, attr_dict,
                                                local_name)

    security.declareProtected('View', 'render_odt_view')

    def render_odt_view(self,
                        value=None,
                        as_string=True,
                        ooo_builder=None,
                        REQUEST=None,
                        render_prefix=None,
                        attr_dict=None,
                        local_name='p'):
        """Call read-only renderer
      """
        return self.widget.render_odt_view(self, value, as_string, ooo_builder,
                                           REQUEST, render_prefix, attr_dict,
                                           local_name)

    security.declareProtected('View', 'render_odg')

    def render_odg(self,
                   key=None,
                   value=None,
                   as_string=True,
                   ooo_builder=None,
                   REQUEST=None,
                   render_prefix=None,
                   attr_dict=None,
                   local_name='p',
                   key_prefix=None):
        widget_key = self.generate_field_key(key=key, key_prefix=key_prefix)
        value = self._get_default(widget_key, value, REQUEST)
        return self.widget.render_odg(self, value, as_string, ooo_builder,
                                      REQUEST, render_prefix, attr_dict,
                                      local_name)

    security.declareProtected('View', 'render_css')

    def render_css(self, REQUEST=None):
        """
      Generate css content which will be added inline.

      XXX key parameter may be needed.
      """
        return self.widget.render_css(self, REQUEST)

    security.declareProtected('View', 'get_css_list')

    def get_css_list(self, REQUEST=None):
        """
        Returns list of css sheets needed by the field
        to be included in global css imports
      """
        return self.widget.get_css_list(self, REQUEST)

    security.declareProtected('View', 'get_javascript_list')

    def get_javascript_list(self, REQUEST=None):
        """
        Returns list of javascript needed by the field
        to be included in global js imports
      """
        return self.widget.get_javascript_list(self, REQUEST)

    security.declareProtected('View', 'render_dict')

    def render_dict(self, value=None, REQUEST=None, key=None, **kw):
        """
      This is yet another field rendering. It is designed to allow code to
      understand field's value data by providing its type and format when
      applicable.
      """
        return self.widget.render_dict(self, value)

    security.declareProtected('View', 'render_from_request')

    def render_from_request(self, REQUEST, key_prefix=None):
        """Convenience method; render the field widget from REQUEST
        (unvalidated data), or default if no raw data is found.
        """
        return self._render_helper(
            self.generate_field_key(key_prefix=key_prefix), None, REQUEST)

    security.declareProtected('View', 'render_sub_field')

    def render_sub_field(self,
                         id,
                         value=None,
                         REQUEST=None,
                         key=None,
                         render_prefix=None):
        """Render a sub field, as part of complete rendering of widget in
      a form. Works like render() but for sub field.
          Added key parameter for ERP5 in order to be compatible with listbox/matrixbox
      """
        return self._get_sub_form().get_field(id)._render_helper(
            self.generate_subfield_key(id, key=key), value, REQUEST,
            render_prefix)

    security.declareProtected('View', 'render_sub_field_from_request')

    def render_sub_field_from_request(self, id, REQUEST):
        """Convenience method; render the field widget from REQUEST
        (unvalidated data), or default if no raw data is found.
        """
        return self._get_sub_form().get_field(id)._render_helper(
            self.generate_subfield_key(id), None, REQUEST)

    security.declarePrivate('_validate_helper')

    def _validate_helper(self, key, REQUEST):
        value = self.validator.validate(self, key, REQUEST)
        # now call external validator after all other validation
        external_validator = self.get_value('external_validator')
        if external_validator and not external_validator(value, REQUEST):
            self.validator.raise_error('external_validator_failed', self)
        return value

    security.declareProtected('View', 'validate')

    def validate(self, REQUEST, key_prefix=None):
        """Validate/transform the field.
        """
        return self._validate_helper(
            self.generate_field_key(validation=1, key_prefix=key_prefix),
            REQUEST)

    security.declareProtected('View', 'need_validate')

    def need_validate(self, REQUEST, key_prefix=None):
        """Return true if validation is needed for this field.
        """
        return self.validator.need_validate(
            self, self.generate_field_key(validation=1, key_prefix=key_prefix),
            REQUEST)

    security.declareProtected('View', 'validate_sub_field')

    def validate_sub_field(self, id, REQUEST, key=None):
        """Validates a subfield (as part of field validation).
      """
        return self._get_sub_form().get_field(id)._validate_helper(
            self.generate_subfield_key(id, validation=1, key=key), REQUEST)

    def PrincipiaSearchSource(self):
        from Products.Formulator import MethodField
        from Products.Formulator import TALESField

        def getSearchSource(obj):
            obj_type = type(obj)
            if obj_type is MethodField.Method:
                return obj.method_name
            elif obj_type is TALESField.TALESMethod:
                return obj._text
            elif obj_type is unicode:
                return obj.encode('utf-8')
            return str(obj)

        return ' '.join(
            map(getSearchSource, (self.values.values() + self.tales.values() +
                                  self.overrides.values())))
class WebServiceRequest(XMLObject, ZopePageTemplate):
  # CMF Type Definition
  meta_type = 'ERP5 Web Service Request'
  portal_type = 'Web Service Request'

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

  # Default Properties
  property_sheets = ( PropertySheet.Base
                    , PropertySheet.XMLObject
                    , PropertySheet.CategoryCore
                    , PropertySheet.DublinCore
                    , PropertySheet.Reference
                    , PropertySheet.SimpleItem
                    , PropertySheet.Arrow
                    , PropertySheet.Data
                    , PropertySheet.WebServiceRequest
                      )

  isIndexable = 0
  content_type="text/html"

  def getIntegrationSite(self,):
    """
    return integration site if the wsr
    """
    def cached_getIntegrationSite(self):
      parent = self.getParentValue()
      while parent.getPortalType() != "Integration Site":
        parent = parent.getParentValue()
      return parent.getPath()

    cached_getIntegrationSite = CachingMethod(cached_getIntegrationSite,
                                   id="WebServiceRequest_getIntegrationSite",
                                   cache_factory="erp5_content_long")

    integration_site = cached_getIntegrationSite(self)
    return self.getPortalObject().portal_integrations.restrictedTraverse(integration_site)

  def edit(self, **kw):
    """
    Override the edit method to register page template values
    """
    if kw.get('data'):
      self.pt_edit(text=kw['data'], content_type=kw['content_type'])
    return self._edit(**kw)

  def getIDParameterName(self):
    """
    Return the parameter name used for id
    """
    def cached_getIDParameterName(self):
      if self.getDestinationObjectType():
        return "%s_id" %(self.getDestinationObjectType().replace(" ", "_").lower())
      else:
        return "id"
    cached_getIDParameterName = CachingMethod(cached_getIDParameterName,
                                   id="WebServiceRequest_getIDParameterName",
                                   cache_factory="erp5_content_long")
    return cached_getIDParameterName(self)


  def __call__(self, context_document=None, test_mode=False, REQUEST=None, **kw):
    """
    Make this object callable. It will call the method defined in reference using
    the web service connector it is related to
    """
    if REQUEST is not None:
      return self.view()
    #LOG("_call__", 300, kw)
    if kw.has_key("id"):
      kw[self.getIDParameterName()] = str(kw.pop("id"))

    sub_id = None
    if kw.has_key(self.getIDParameterName()) and ID_SEPARATOR in kw[self.getIDParameterName()]:
      kw[self.getIDParameterName()], sub_id = kw[self.getIDParameterName()].split(ID_SEPARATOR)

    object_list = []
    method_name = self.getReference()
    try:
      connection = self.getSourceValue().getConnection()
    except AttributeError:
      LOG("__call__ of %s" %(self.getPath(),), ERROR,
          "Error on getting connection, connector is %s" %(self.getSourceValue(),))
      connection = None
    if connection is None:
      if test_mode:
        self._edit(last_request_parameter=str(kw),
                   last_request_result="",
                   last_request_path="",
                   last_request_error="No connection available, connector is %s" %(self.getSourceValue(),))
        return []
      else:
        raise ValueError, "No connection available"

    # Add specific parameters defined on integration site
    site = self.getIntegrationSite()
    if site.getLanguage():
      kw['language'] = site.getLanguage()

    if site.getStartDate():
      kw['start_date'] = site.getStartDate()

    if site.getStopDate():
      kw['stop_date'] = site.getStopDate()

      
    # Render page template content
    if self.hasData():
      #LOG("passing options %s to self %s" %(kw, self.getPath()), 300, "CALL")
      pt_data = self.pt_render(extra_context={'options': kw, })
      pt_data = pt_data.replace('\n', '')
      kw = {'data': pt_data}

    # transforms parameters
    #LOG("before transformation of params %s" %(kw), 300, self.getPath())
    new_kw = kw.copy()
    args = []
    if self.getDestination():
      for k,v in kw.iteritems():
        new_key = site.getMappingFromProperty(self.getDestinationValue(), k)
        new_kw.pop(k)
        if new_key is None:
          # Some web service does not need explicit parameters
          args.append(v)
        else:
          new_kw[new_key] = v

    kw = new_kw
    #LOG("calling with params args = %s, kw = %s" %(args, kw), 300, self.getPath())
    error = None

    def callRequest(self, method_name, *args, **kw):
      connection = self.getSourceValue().getConnection()
      return getattr(connection, method_name)(*args, **kw)

    # cached_callRequest = CachingMethod(callRequest,
    #                                    id="WebServiceRequest_callRequest",
    #                                    cache_factory="erp5_content_short")
    
    # Call the method
    try:
      url, xml = callRequest(self, method_name, *args, **kw)
    except ConnectionError, msg:
      if test_mode:
        error = msg
        url = connection.url
        xml = ""
      else:
        raise

    # Register information for testing/debug purposes
    if test_mode:
      self._edit(last_request_parameter="args = %s, kw = %s" %(str(args), str(kw)),
                 last_request_result=xml,
                 last_request_path=url,
                 last_request_error=error)

    def buildParserDict(root_mapping):
      parser_dict = {}
      for mapping in root_mapping.contentValues():
        if len(mapping.contentValues()):
          sub_parser_dict = buildParserDict(mapping)
          parser_dict[mapping.getSourceReference()] = (mapping.getDestinationReference(), sub_parser_dict)
        else:
          parser_dict[mapping.getSourceReference()] = (mapping.getDestinationReference(), None)
      return parser_dict

    if self.hasDestination():
      sub_parser_dict = buildParserDict(self.getDestinationValue())
      parser_dict = {self.getDestinationValue().getSourceReference() : (self.getDestinationValue().getDestinationReference(), sub_parser_dict)}
    else:
      return []

    # Parse the result
    if self.getSourceValue().getParserMethodId():
      method = getattr(self, self.getSourceValue().getParserMethodId())
      result_list = method(result=xml, parser_dict=parser_dict)
    else:
      if type(xml) == list:
        result_list = self.parse_dict(parser_dict, xml)
      else:
        parser = etree.XMLParser(target = NewEchoDictTarget(parser_dict))
        # FIXME: About prestashop sync, '&' and '&' char in xml cause problem
        # xml = xml.replace('&', '')
        #LOG("got XML from WSR %s = %s" %(method_name, xml), 300, "will call parser with %s" %(parser_dict))
        result_list = []
        try:
          result_list = etree.XML(xml, parser,)
          #LOG("result_list = %r" %(result_list), 300, "")
        except etree.XMLSyntaxError:
          LOG("WebServiceRequest", ERROR, "Bad XML returned by request %s with kw = %s, xml = %s" %(self.getPath(), kw, xml))
          if test_mode:
            self._edit(last_request_error="Bad XML returned by request, impossible to parse it")
          else:
            raise ValueError, "Bad XML returned by request %s with kw = %s, xml = %s" %(self.getPath(), kw, xml)

    brain = getBrain(self.brain_class_file, self.brain_class_name, reload=1)

    script_id = self.getBrainBuilderScript(None)
    if script_id is not None:
      brain_builder_script = getattr(self, script_id, None)
    else:
      brain_builder_script = None
    if brain_builder_script is not None:
      for result in result_list:
        object_list.extend(brain_builder_script(result, brain))
      if sub_id:
        object_list = [object_list[int(sub_id)-1],]
    else:
      for result in result_list:
        #LOG("initialising brain", INFO, "data is %s" %(result))
        obj = brain(context=self,
                    object_type=self.getDestinationObjectType(),
                    **result)
        object_list.append(obj)

    return object_list
class FGDateField(BaseFormField):
    """ Date/Time Entry Field """

    security = ClassSecurityInfo()

    schema = BaseFieldSchemaStringDefault.copy() + Schema((
        BooleanField(
            'fgShowHM',
            searchable=0,
            required=0,
            default=1,
            widget=BooleanWidget(
                label=_(u'label_fgshowhm_text',
                        default=u'Show Time Selection Options'),
                description=_(u'help_fgshowhm_text', default=u''),
            ),
        ),
        IntegerField(
            'fgStartingYear',
            searchable=0,
            required=0,
            default='1999',
            widget=IntegerWidget(
                label=_(u'label_fgstartingyear_text',
                        default=u'Starting Year'),
                description=_(u"help_fgstartingyear_text"),
            ),
        ),
        IntegerField(
            'fgEndingYear',
            searchable=0,
            required=0,
            default=None,
            widget=IntegerWidget(
                label=_(u'label_fgendingyear_text', default=u'Ending Year'),
                description=_(
                    u'help_fgendingyear_text',
                    default=u"""The last year to offer in the year drop-down.
                 Leave this empty if you wish to instead use a number of future years."""
                ),
            ),
        ),
        IntegerField(
            'fgFutureYears',
            searchable=0,
            required=0,
            default='5',
            widget=IntegerWidget(
                label=_(u'label_fgfutureyears_text',
                        default=u'Future Years To Display'),
                description=_(
                    u'help_fgfutureyears_text',
                    default=
                    u"""The number of future years to offer in the year drop-down.
                 (This is only applicable if you have not specified an ending year.)"""
                ),
            ),
        ),
    ))

    # 'hidden' isn't really useful for this field.
    del schema['hidden']
    # 'serverSide' is not really useful for this field.
    del schema['serverSide']

    # hide references & discussion
    finalizeFieldSchema(schema, folderish=True, moveDiscussion=False)

    # Standard content type setup
    portal_type = meta_type = 'FormDateField'
    archetype_name = 'Date/Time Field'
    content_icon = 'DateTimeField.gif'
    typeDescription = 'A date/time field. Time component is optional.'

    def __init__(self, oid, **kwargs):
        """ initialize class """

        BaseFormField.__init__(self, oid, **kwargs)

        # set a preconfigured field as an instance attribute
        self.fgField = DateTimeField(
            'fg_date_field',
            searchable=0,
            required=0,
            write_permission=View,
            widget=CalendarWidget(),
        )

    security.declareProtected(ModifyPortalContent, 'setFgShowHM')

    def setFgShowHM(self, value, **kw):
        """ set show_hm """

        if type(value) == BooleanType:
            self.fgField.widget.show_hm = value
        else:
            self.fgField.widget.show_hm = value == '1'

        self.fgShowHM = value

    security.declareProtected(ModifyPortalContent, 'setFgStartingYear')

    def setFgStartingYear(self, value, **kw):
        """ set starting_year """

        if value:
            try:
                self.fgField.widget.starting_year = int(value)
                self.fgStartingYear = value
            except ValueError:
                pass
        else:
            self.fgField.widget.starting_year = None
            self.fgStartingYear = value

    security.declareProtected(ModifyPortalContent, 'setFgEndingYear')

    def setFgEndingYear(self, value, **kw):
        """ set ending_year """

        if value:
            try:
                self.fgField.widget.ending_year = int(value)
                self.fgEndingYear = value
            except ValueError:
                pass
        else:
            self.fgField.widget.ending_year = None
            self.fgEndingYear = value

    security.declareProtected(ModifyPortalContent, 'setFgFutureYears')

    def setFgFutureYears(self, value, **kw):
        """ set future_years """

        if value:
            try:
                self.fgField.widget.future_years = int(value)
                self.fgFutureYears = value
            except ValueError:
                pass
        else:
            self.fgField.widget.future_years = None
            self.fgFutureYears = value

    def _toLocalizedTime(self, time, long_format=None):
        tool = getToolByName(self, 'translation_service')
        return tool.ulocalized_time(time, long_format=long_format)

    def htmlValue(self, REQUEST):
        """ return from REQUEST, this field's value, rendered as XHTML.
        """

        value = REQUEST.form.get(self.__name__, 'No Input')

        # The replace('-','/') keeps the DateTime routine from
        # interpreting this as UTC. Odd, but true.

        try:
            dt = DateTime(value.replace('-', '/'))
        except (DateTimeSyntaxError, DateError):
            # probably better to simply return the input
            return cgi.escape(value)

        if self.fgField.widget.show_hm:
            value = self._toLocalizedTime(dt, long_format=True)
        else:
            value = self._toLocalizedTime(dt)

        return cgi.escape(value)

    def specialValidator(self, value, field, REQUEST, errors):
        """ Archetypes isn't validating non-required dates --
            so we need to.
        """

        fname = field.getName()
        month = REQUEST.form.get('%s_month' % fname, '01')
        day = REQUEST.form.get('%s_month' % fname, '01')

        if (month == '00') and (day == '00'):
            value = ''
            REQUEST.form[fname] = ''

        if value and not field.required:
            try:
                dt = DateTime(value)
            except (DateTimeSyntaxError, DateError):
                return "Validation failed(isValidDate): this is not a valid date."
        return 0
class FGMultiSelectField(BaseFormField):
    """ Multiple selection field (select with multiple or check boxes) """

    security = ClassSecurityInfo()

    schema = BaseFieldSchemaLinesDefault.copy() + Schema((
        vocabularyField,
        vocabularyOverrideField,
        StringField(
            'fgFormat',
            searchable=0,
            required=0,
            default='select',
            enforceVocabulary=1,
            vocabulary='formatVocabDL',
            widget=SelectionWidget(
                label=_(u'label_fgmsformat_text',
                        default=u'Presentation Widget'),
                description=_(u'help_fgmsformat_text',
                              default=u"""Useful for stopping spam"""),
            ),
        ),
    ))

    # current Archetypes doesn't really support hidden for
    # multi-select. Use a lines field if you really need this
    del schema['hidden']
    # 'serverSide' is not really useful for this field.
    del schema['serverSide']

    # hide references & discussion
    finalizeFieldSchema(schema, folderish=True, moveDiscussion=False)

    # Standard content type setup
    portal_type = meta_type = 'FormMultiSelectionField'
    archetype_name = 'Multi-Select Field'
    content_icon = 'MultipleListField.gif'
    typeDescription = 'A multiple-selection field'

    def __init__(self, oid, **kwargs):
        """ initialize class """

        BaseFormField.__init__(self, oid, **kwargs)

        # set a preconfigured field as an instance attribute
        self.fgField = LinesVocabularyField(
            'fg_mselection_field',
            searchable=0,
            required=0,
            widget=MultiSelectionWidget(),
            vocabulary='_get_selection_vocab',
            enforceVocabulary=1,
            write_permission=View,
        )

    security.declareProtected(ModifyPortalContent, 'setFgFormat')

    def setFgFormat(self, value, **kw):
        """ set selection format """

        self.fgField.widget.format = value
        self.fgFormat = value

    security.declareProtected(ModifyPortalContent, 'setFgRows')

    def setFgRows(self, value, **kw):
        """ sets widget rows """

        self.fgField.widget.size = value

    security.declareProtected(View, 'getFgRows')

    def getFgRows(self, **kw):
        """ gets widget rows """

        return self.fgField.widget.size

    def formatVocabDL(self):
        """ returns vocabulary for fgFormat """

        return DisplayList(
            (('select', _(u'vocabulary_selection_text', u'Selection list')),
             ('checkbox', _(u'vocabulary_checkbox_text', u'Checkbox list'))))

    def htmlValue(self, REQUEST):
        """ Return value instead of key """

        utils = getToolByName(self, 'plone_utils')
        charset = utils.getSiteEncoding()

        value = REQUEST.form.get(self.__name__, [])

        vocabulary = self.fgField.Vocabulary(self)
        result = []
        for k in value:
            # there'll be an empty string to avoid
            if len(k):
                # vocabulary items are in unicode;
                # so decode the key before lookup
                ku = k.decode(charset)
                v = vocabulary.getValue(ku) or ku
                result.append(v)

        value = u', '.join(result)

        return cgi.escape(value.encode(charset))
class FGIntegerField(BaseFormField):
    """ Integer entry field """

    security = ClassSecurityInfo()

    schema = BaseFieldSchemaStringDefault.copy() + Schema((
        IntegerField(
            'minval',
            searchable=0,
            required=1,
            default='0',
            widget=IntegerWidget(
                label=_(u'label_minval_text',
                        default=u"Minimum Acceptable Value"),
                description=_(u'help_minval_text',
                              default=u"""
            The form will not accept values less than the number you enter here.
                """),
            ),
        ),
        IntegerField(
            'maxval',
            searchable=0,
            required=1,
            default='10000',
            widget=IntegerWidget(
                label=_(u'label_maxval_text',
                        default=u"Maximum Acceptable Value"),
                description=_(u'help_maxval_text',
                              default=u"""
                    The form will not accept values greater than the number you enter here.
                """),
            ),
        ),
        maxlengthField,
        sizeField,
    ))

    # 'hidden' isn't really useful for this field.
    del schema['hidden']
    # 'serverSide' is not really useful for this field.
    del schema['serverSide']

    # hide references & discussion
    finalizeFieldSchema(schema, folderish=True, moveDiscussion=False)

    # Standard content type setup
    portal_type = meta_type = 'FormIntegerField'
    archetype_name = 'Integer Field'
    content_icon = 'IntegerField.gif'
    typeDescription = 'A integer field'

    def __init__(self, oid, **kwargs):
        """ initialize class """

        BaseFormField.__init__(self, oid, **kwargs)

        # set a preconfigured field as an instance attribute
        self.fgField = IntegerField(
            'fg_integer_field',
            searchable=0,
            required=0,
            minval=0,
            maxval=10000,
            validators=(
                'isNotTooLong',
                'inExNumericRange',
            ),
            write_permission=View,
        )
class FGBooleanField(BaseFormField):
    """ Boolean (checkbox) field """

    security = ClassSecurityInfo()

    schema = BaseFieldSchema.copy() + Schema((
        StringField('fgDefault',
            searchable=0,
            required=0,
            widget=BooleanWidget(
                label=_(u'label_fgdefault_text', default=u'Default'),
                description=_(u'help_fgdefault_text', default=u"The value the "
                    "field should contain when the form is first displayed."
                    "Note that this may be overridden dynamically."),
                ),
        ),
        StringField('fgBooleanValidator',
            vocabulary='boolVocabDL',
            enforceVocabulary=1,
            widget=SelectionWidget(label=_(u'label_fgbooleanvalidator_text', default=u'Validator'),
                description=_(u'help_fgbooleanvalidator_text', default=u"""Choose a validator to require a particular response."""),
                ),
        ),
        StringField('fgBoolTrueString',
            required=0,
            searchable=0,
            default='1',
            widget=StringWidget(
                label=_(u'label_fgbooleantruestring_text', default=u"True Display String"),
                description=_(u'help_fgbooleantruestring_text', default=\
                    u"String to use in thanks page and mail when the field's "
                    "checkbox is checked."),
                ),
            ),
        StringField('fgBoolFalseString',
            required=0,
            searchable=0,
            default='0',
            widget=StringWidget(
                label=_(u'label_fgbooleanfalsestring_text', default=u"False Display String"),
                description=_(u'help_fgbooleanfalsestring_text', default=u"""String to use in thanks page and mail when the field's checkbox is unchecked."""),
                ),
            ),
    ))
    schema['required'].widget.description = \
        _(u'help_boolrequired_text', default=u"""NOTE: For a checkbox field, the required flag doesn't do anything beyond
           putting a 'required' marker next to the label. If you wish to require a
           particular input, choose a validator below.
        """)

    # 'hidden' isn't really useful for this field.
    del schema['hidden']
    # 'serverSide' is not really useful for this field.
    del schema['serverSide']

    # hide references & discussion
    finalizeFieldSchema(schema, folderish=True, moveDiscussion=False)

    # Standard content type setup
    portal_type = meta_type = 'FormBooleanField'
    archetype_name = 'Boolean Field'
    content_icon = 'CheckBoxField.gif'
    typeDescription = 'A CheckBox (Boolean) field'

    def __init__(self, oid, **kwargs):
        """ initialize class """

        BaseFormField.__init__(self, oid, **kwargs)

        # set a preconfigured field as an instance attribute
        self.fgField = NRBooleanField(
            'fg_boolean_field',
            searchable=0,
            required=0,
            write_permission=View,
        )

    security.declareProtected(ModifyPortalContent, 'setFgBooleanValidator')

    def setFgBooleanValidator(self, value, **kw):
        """ set boolean validator """

        if value:
            self.fgField.validators = (value, )
        else:
            self.fgField.validators = ()
        self.fgField._validationLayer()

        self.fgBooleanValidator = value

    def boolVocabDL(self):
        """ returns DisplayList of vocabulary for fgBooleanValidator """

        return DisplayList((
            ('', _(u'vocabulary_none_text', u'None')),
            ('isChecked', _(u'vocabulary_ischecked_text', u'Is checked')),
            ('isUnchecked',
             _(u'vocabulary_isnotchecked_text', u'Is not checked')),
        ))

    def htmlValue(self, REQUEST):
        """ Return value instead of key """

        value = REQUEST.form.get(self.__name__, 'No Input')
        if type(value) == BooleanType:
            if value:
                return self.fgBoolTrueString
        elif value == '1':
            return self.fgBoolTrueString

        return self.fgBoolFalseString
Example #8
0
class InstalledProduct(SimpleItem):
    """Class storing information about an installed product

      Let's make sure that this implementation actually fulfills the
      'IInstalledProduct' API.

      >>> from zope.interface.verify import verifyClass
      >>> verifyClass(IInstalledProduct, InstalledProduct)
      True
    """

    meta_type = "Installed Product"

    manage_options = (
        {'label': 'View', 'action': 'manage_installationInfo'},
    ) + SimpleItem.manage_options

    security = ClassSecurityInfo()

    security.declareProtected(ManagePortal, 'manage_installationInfo')
    manage_installationInfo = PageTemplateFile(
        'forms/installed_product_overview', globals(),
        __name__='manage_installationInfo')

    default_cascade = [
        'types', 'skins', 'actions', 'portalobjects', 'workflows', 'slots',
        'registrypredicates', 'adapters', 'utilities']

    def __init__(self, id):
        self.id = id
        self.transcript = []
        self.leftslots = []
        self.rightslots = []
        self.locked = None
        self.hidden = None
        self.installedversion = None
        self.status = 'new'
        self.error = False
        self.afterid = None
        self.beforeid = None
        for key in DEFAULT_CASCADE:
            setattr(self, key, [])

    @security.protected(ManagePortal)
    def update(self, settings, installedversion='', logmsg='',
               status='installed', error=False, locked=False, hidden=False,
               afterid=None, beforeid=None):

        # check for the availability of attributes before assigning
        for att in settings.keys():
            if not hasattr(self.aq_base, att):
                setattr(self, att, [])

        qi = getToolByName(self, 'portal_quickinstaller')
        reg = qi.getAlreadyRegistered()

        for k in settings.keys():
            old = k in reg.keys() and reg[k] or []
            updatelist(getattr(self, k), settings[k], old)

        self.transcript.insert(0, {'timestamp': DateTime(), 'msg': logmsg})
        self.locked = locked
        self.hidden = hidden
        self.installedversion = installedversion
        self.afterid = afterid
        self.beforeid = beforeid

        if status:
            self.status = status

        self.error = error

    @security.protected(ManagePortal)
    def log(self, logmsg):
        """Adds a log to the transcript
        """
        self.transcript.insert(0, {'timestamp': DateTime(), 'msg': logmsg})

    @security.protected(ManagePortal)
    def hasError(self):
        """Returns if the prod is in error state
        """
        return getattr(self, 'error', False)

    @security.protected(ManagePortal)
    def isLocked(self):
        """Is the product locked for uninstall
        """
        return getattr(self, 'locked', False)

    @security.protected(ManagePortal)
    def isHidden(self):
        """Is the product hidden
        """
        return getattr(self, 'hidden', False)

    @security.protected(ManagePortal)
    def isVisible(self):
        return not self.isHidden()

    @security.protected(ManagePortal)
    def isInstalled(self):
        return self.status == 'installed'

    @security.protected(ManagePortal)
    def getStatus(self):
        return self.status

    @security.protected(ManagePortal)
    def getTypes(self):
        return self.types

    @security.protected(ManagePortal)
    def getSkins(self):
        return self.skins

    @security.protected(ManagePortal)
    def getActions(self):
        return self.actions

    @security.protected(ManagePortal)
    def getPortalObjects(self):
        return self.portalobjects

    @security.protected(ManagePortal)
    def getWorkflows(self):
        return self.workflows

    @security.protected(ManagePortal)
    def getLeftSlots(self):
        if getattr(self, 'leftslots', None) is None:
            self.leftslots = []
        return self.leftslots

    @security.protected(ManagePortal)
    def getRightSlots(self):
        if getattr(self, 'rightslots', None) is None:
            self.rightslots = []
        return self.rightslots

    @security.protected(ManagePortal)
    def getSlots(self):
        return self.getLeftSlots() + self.getRightSlots()

    @security.protected(ManagePortal)
    def getValue(self, name):
        return getattr(self, name, [])

    @security.protected(ManagePortal)
    def getRegistryPredicates(self):
        """Return the custom entries in the content_type_registry
        """
        return getattr(self, 'registrypredicates', [])

    @security.protected(ManagePortal)
    def getAfterId(self):
        return self.afterid

    @security.protected(ManagePortal)
    def getBeforeId(self):
        return self.beforeid

    @security.protected(ManagePortal)
    def getTranscriptAsText(self):
        if getattr(self, 'transcript', None):
            msgs = [t['timestamp'].ISO() + '\n' + str(t['msg'])
                    for t in self.transcript]
            return '\n=============\n'.join(msgs)
        else:
            return 'no messages'

    def _getMethod(self, modfunc):
        """Returns a method
        """
        return get_method(self.id, modfunc)

    @security.protected(ManagePortal)
    def getInstallMethod(self):
        """ returns the installer method """
        res = get_install_method(self.id)
        if res is None:
            raise AttributeError('No Install method found for '
                                 'product %s' % self.id)
        else:
            return res

    @security.protected(ManagePortal)
    def getUninstallMethod(self):
        """ returns the uninstaller method """
        return self._getMethod(
            (
                ('Install', 'uninstall'),
                ('Install', 'Uninstall'),
                ('install', 'uninstall'),
                ('install', 'Uninstall'),
            )
        )

    @security.protected(ManagePortal)
    def getAfterInstallMethod(self):
        """ returns the after installer method """
        return self._getMethod(
            (
                ('Install', 'afterInstall'),
                ('install', 'afterInstall'),
            )
        )

    @security.protected(ManagePortal)
    def getBeforeUninstallMethod(self):
        """ returns the before uninstaller method """
        return self._getMethod(
            (
                ('Install', 'beforeUninstall'),
                ('install', 'beforeUninstall'),
            )
        )

    @security.protected(ManagePortal)
    def uninstall(
        self,
        cascade=default_cascade,
        reinstall=False,
        REQUEST=None
    ):
        """Uninstalls the product and removes its dependencies
        """
        portal = getToolByName(self, 'portal_url').getPortalObject()

        # TODO eventually we will land Event system and could remove
        # this 'removal_inprogress' hack
        if self.isLocked() and getattr(portal, 'removal_inprogress', False):
            raise ValueError(
                'The product is locked and cannot be uninstalled!'
            )

        res = ''
        afterRes = ''

        uninstaller = self.getUninstallMethod()
        beforeUninstall = self.getBeforeUninstallMethod()

        if uninstaller:
            uninstaller = uninstaller.__of__(portal)
            try:
                res = uninstaller(portal, reinstall=reinstall)
                # XXX log it
            except TypeError:
                res = uninstaller(portal)
        elif not reinstall:
            # If there is no uninstall-method we run a uninstall-profile
            qi = getToolByName(self, 'portal_quickinstaller')
            uninstall_profile = qi.getUninstallProfile(self.id)
            if uninstall_profile:
                portal_setup = getToolByName(self, 'portal_setup')
                portal_setup.runAllImportStepsFromProfile(
                    'profile-%s' % uninstall_profile['id'])

        if beforeUninstall:
            beforeUninstall = beforeUninstall.__of__(portal)
            beforeRes, cascade = beforeUninstall(
                portal,
                reinstall=reinstall,
                product=self,
                cascade=cascade
            )

        self._cascadeRemove(cascade)

        self.status = 'uninstalled'
        self.log('uninstalled\n' + str(res) + str(afterRes))

        if REQUEST and REQUEST.get('nextUrl', None):
            return REQUEST.RESPONSE.redirect(REQUEST['nextUrl'])

    def _cascadeRemove(self, cascade):
        """Cascaded removal of objects
        """
        portal = getToolByName(self, 'portal_url').getPortalObject()

        if 'types' in cascade:
            portal_types = getToolByName(self, 'portal_types')
            delObjects(portal_types, getattr(aq_base(self), 'types', []))

        if 'skins' in cascade:
            portal_skins = getToolByName(self, 'portal_skins')
            delObjects(portal_skins, getattr(aq_base(self), 'skins', []))

        if (
            'actions' in cascade
            and len(getattr(aq_base(self), 'actions', [])) > 0
        ):
            portal_actions = getToolByName(self, 'portal_actions')
            for info in self.actions:
                if isinstance(info, six.string_types):
                    action = info
                    # Product was installed before CMF 2.1
                    # Try to remove the action from all categories
                    for category in portal_actions.objectIds():
                        cat = portal_actions[category]
                        if action in cat.objectIds():
                            cat._delObject(action)
                else:
                    category, action = info
                    if category in portal_actions.objectIds():
                        cat = portal_actions[category]
                        if action in cat.objectIds():
                            cat._delObject(action)
                        if len(cat.objectIds()) == 0:
                            del cat
                            portal_actions._delObject(category)

        if 'portalobjects' in cascade:
            delObjects(portal, getattr(aq_base(self), 'portalobjects', []))

        if 'workflows' in cascade:
            portal_workflow = getToolByName(self, 'portal_workflow')
            delObjects(
                portal_workflow,
                getattr(aq_base(self), 'workflows', [])
            )

        if 'slots' in cascade:
            if self.getLeftSlots():
                portal.left_slots = [
                    s for s in portal.left_slots
                    if s not in self.getLeftSlots()
                ]
            if self.getRightSlots():
                portal.right_slots = [
                    s for s in portal.right_slots
                    if s not in self.getRightSlots()
                ]

        if 'registrypredicates' in cascade:
            ctr = getToolByName(self, 'content_type_registry')
            ids = [id for id, predicate in ctr.listPredicates()]
            predicates = getattr(aq_base(self), 'registrypredicates', [])
            for pred in predicates:
                if pred in ids:
                    ctr.removePredicate(pred)
                else:
                    logger.warning("Failed to delete '%s' from content type "
                                   "registry" % pred)

        if 'adapters' in cascade:
            adapters = getattr(aq_base(self), 'adapters', [])
            if adapters:
                sm = getSiteManager()
                # TODO: expand this to actually cover adapter registrations

        if 'utilities' in cascade:
            utilities = getattr(aq_base(self), 'utilities', [])
            if utilities:
                sm = getSiteManager()
                mapping = sm.objectItems()

                for registration in utilities:
                    provided = _resolveDottedName(registration[0])
                    name = registration[1]
                    utility = queryUtility(provided, name=name)

                    if utility is not None:
                        sm.unregisterUtility(provided=provided, name=name)

                        # Make sure utilities are removed from the
                        # site manager's mapping as well
                        for name, value in mapping:
                            if value is utility:
                                sm._delObject(name, suppress_events=True)

        rr_js = getToolByName(self, 'portal_javascripts', None)
        rr_css = getToolByName(self, 'portal_css', None)

        if rr_js is not None:
            for js in getattr(aq_base(self), 'resources_js', []):
                rr_js.unregisterResource(js)
        if rr_css is not None:
            for css in getattr(aq_base(self), 'resources_css', []):
                rr_css.unregisterResource(css)

        portal_controlpanel = getToolByName(self, 'portal_controlpanel', None)
        if portal_controlpanel is not None:
            portal_controlpanel.unregisterApplication(self.id)

    @security.protected(ManagePortal)
    def getInstalledVersion(self):
        """Return the version of the product in the moment of installation
        """
        return getattr(self, 'installedversion', None)
Example #9
0
class LanguageManager(Tabs):
    """ """

    security = ClassSecurityInfo()

    # TODO For backwards compatibility with Python 2.1 the variable
    # _languages is a tuple.  Change it to a frozenset.
    _languages = ()
    _default_language = None


    ########################################################################
    # API
    ########################################################################
    def get_languages(self):
        """Returns all the object languages.
        """
        return self._languages


    def set_languages(self, languages):
        """Sets the object languages.
        """
        self._languages = tuple(languages)


    def add_language(self, language):
        """Adds a new language.
        """
        if language not in self._languages:
            self._languages = tuple(self._languages) + (language,)


    def del_language(self, language):
        """Removes a language.
        """
        if language in self._languages:
            languages = [ x for x in self._languages if x != language ]
            self._languages = tuple(languages)


    def get_languages_mapping(self):
        """Returns a list of dictionary, one for each objects language. The
        dictionary contains the language code, its name and a boolean value
        that tells wether the language is the default one or not.
        """
        return [ {'code': x,
                  'name': get_language_name(x),
                  'default': x == self._default_language}
                 for x in self._languages ]


    def get_available_languages(self, **kw):
        """Returns the langauges available. For example, a language could be
        considered as available only if there is some data associated to it.

        This method is used by the language negotiation code (see
        'get_selected_language'), sometimes you will want to redefine it in
        your classes.
        """
        return self._languages


    def get_default_language(self):
        """Returns the default language.

        This method is used by the language negotiation code (see
        'get_selected_language'), sometimes you will want to redefine it in
        your classes.

        For example, maybe you will want to define it to return always a
        default language, even when internally it is None.
        """
        return self._default_language


    ########################################################################
    # Web API
    ########################################################################

    # Security settings
    security.declarePublic('get_languages')
    security.declareProtected('Manage languages', 'set_languages')
    security.declareProtected('Manage languages', 'add_language')
    security.declareProtected('Manage languages', 'del_language')
    security.declarePublic('get_languages_mapping')


    security.declarePublic('get_language_name')
    def get_language_name(self, id=None):
        """
        Returns the name of the given language code.

        XXX Kept here for backwards compatibility only
        """
        if id is None:
            id = self.get_default_language()
        return get_language_name(id)


    security.declarePublic('get_available_languages')
    security.declarePublic('get_default_language')


    # XXX Kept here temporarily, further refactoring needed
    security.declarePublic('get_selected_language')
    def get_selected_language(self, **kw):
        """
        Returns the selected language. Here the language negotiation takes
        place.

        Accepts keyword arguments which will be passed to
        'get_available_languages'.
        """
        available_languages = apply(self.get_available_languages, (), kw)

        return lang_negotiator(available_languages) \
               or self.get_default_language()


    ########################################################################
    # ZMI
    ########################################################################
    manage_options = (
        {'action': 'manage_languages', 'label': u'Languages',
         'help': ('Localizer', 'LM_languages.stx')},)


    def filtered_manage_options(self, REQUEST=None):
        options = Tabs.filtered_manage_options(self, REQUEST=REQUEST)

        # Insert the upgrade form if needed
        if self._needs_upgrade():
            options.insert(0,
                {'action': 'manage_upgradeForm',
                 'label': u'Upgrade',
                 'help': ('Localizer', 'LM_upgrade.stx')})

        # Translate the labels
        r = []
        for option in options:
            option = option.copy()
            option['label'] = _(option['label'])
            r.append(option)

        # Ok
        return r


    security.declareProtected('View management screens', 'manage_languages')
    manage_languages = LocalDTMLFile('ui/LM_languages', globals())


    security.declarePublic('get_all_languages')
    def get_all_languages(self):
        """
        Returns all ISO languages, used by 'manage_languages'.
        """
        return get_languages()


    security.declareProtected('Manage languages', 'manage_addLanguage')
    def manage_addLanguage(self, language, REQUEST=None, RESPONSE=None):
        """ """
        self.add_language(language)

        if RESPONSE is not None:
            RESPONSE.redirect("%s/manage_languages" % REQUEST['URL1'])


    security.declareProtected('Manage languages', 'manage_delLanguages')
    def manage_delLanguages(self, languages, REQUEST, RESPONSE):
        """ """
        for language in languages:
            self.del_language(language)

        RESPONSE.redirect("%s/manage_languages" % REQUEST['URL1'])


    security.declareProtected('Manage languages', 'manage_changeDefaultLang')
    def manage_changeDefaultLang(self, language, REQUEST=None, RESPONSE=None):
        """ """
        self._default_language = language

        if REQUEST is not None:
            RESPONSE.redirect("%s/manage_languages" % REQUEST['URL1'])


    # Unicode support, custom ZMI
    manage_page_header = LocalDTMLFile('ui/manage_page_header', globals())


    ########################################################################
    # Upgrade
    def _needs_upgrade(self):
        return False


    def _upgrade(self):
        pass


    security.declarePublic('need_upgrade')
    def need_upgrade(self):
        """ """
        return self._needs_upgrade()


    security.declareProtected(
        'Manage Access Rules', 'manage_upgradeForm', 'manage_upgrade')
    manage_upgradeForm = LocalDTMLFile('ui/LM_upgrade', globals())
    def manage_upgrade(self, REQUEST, RESPONSE):
        """ """
        self._upgrade()
        RESPONSE.redirect('manage_main')
Example #10
0
class BikaSetup(folder.ATFolder):
    """LIMS Setup
    """
    implements(IBikaSetup, IHaveNoBreadCrumbs)

    schema = schema
    security = ClassSecurityInfo()

    def getAttachmentsPermitted(self):
        """Attachments permitted
        """
        if self.getARAttachmentOption() in ['r', 'p'] \
           or self.getAnalysisAttachmentOption() in ['r', 'p']:
            return True
        else:
            return False

    def getStickerTemplates(self):
        """Get the sticker templates
        """
        out = [[t['id'], t['title']] for t in _getStickerTemplates()]
        return DisplayList(out)

    def getARAttachmentsPermitted(self):
        """AR attachments permitted
        """
        if self.getARAttachmentOption() == 'n':
            return False
        else:
            return True

    def getAnalysisAttachmentsPermitted(self):
        """Analysis attachments permitted
        """
        if self.getAnalysisAttachmentOption() == 'n':
            return False
        else:
            return True

    def getAnalysisServicesVocabulary(self):
        """
        Get all active Analysis Services from Bika Setup and return them as Display List.
        """
        bsc = getToolByName(self, 'bika_setup_catalog')
        brains = bsc(portal_type='AnalysisService', is_active=True)
        items = [(b.UID, b.Title) for b in brains]
        items.insert(0, ("", ""))
        items.sort(lambda x, y: cmp(x[1], y[1]))
        return DisplayList(list(items))

    def getPrefixFor(self, portal_type):
        """Return the prefix for a portal_type.
           If not found, simply uses the portal_type itself
        """
        prefix = [
            p for p in self.getIDFormatting()
            if p['portal_type'] == portal_type
        ]
        if prefix:
            return prefix[0]['prefix']
        else:
            return portal_type

    def getCountries(self):
        items = [(x['ISO'], x['Country']) for x in COUNTRIES]
        items.sort(lambda x, y: cmp(x[1], y[1]))
        return items

    def isRejectionWorkflowEnabled(self):
        """Return true if the rejection workflow is enabled (its checkbox is set)
        """
        widget = self.getRejectionReasons()
        # widget will be something like:
        # [{'checkbox': u'on', 'textfield-2': u'b', 'textfield-1': u'c', 'textfield-0': u'a'}]
        if len(widget) > 0:
            checkbox = widget[0].get('checkbox', False)
            return True if checkbox == 'on' and len(widget[0]) > 1 else False
        else:
            return False

    def getRejectionReasonsItems(self):
        """Return the list of predefined rejection reasons
        """
        reasons = self.getRejectionReasons()
        if not reasons:
            return []
        reasons = reasons[0]
        keys = filter(lambda key: key != "checkbox", reasons.keys())
        return map(lambda key: reasons[key], sorted(keys)) or []

    def _getNumberOfRequiredVerificationsVocabulary(self):
        """
        Returns a DisplayList with the available options for the
        multi-verification list: '1', '2', '3', '4'
        :returns: DisplayList with the available options for the
            multi-verification list
        """
        items = [(1, '1'), (2, '2'), (3, '3'), (4, '4')]
        return IntDisplayList(list(items))

    def getIDServerValuesHTML(self):
        number_generator = getUtility(INumberGenerator)
        keys = number_generator.keys()
        values = number_generator.values()
        results = []
        for i in range(len(keys)):
            results.append('%s: %s' % (keys[i], values[i]))
        return "\n".join(results)
Example #11
0
class SyndicationTool(UniqueObject, SimpleItem, ActionProviderBase):
    """
        The syndication tool manages the site-wide policy for
        syndication of folder content as RSS.
    """

    __implements__ = ActionProviderBase.__implements__

    id = 'portal_syndication'
    meta_type = 'Default Syndication Tool'

    security = ClassSecurityInfo()

    #Default Sitewide Values
    isAllowed = 0
    syUpdatePeriod = 'daily'
    syUpdateFrequency = 1
    syUpdateBase = DateTime()
    max_items = 15

    #ZMI Methods
    manage_options = (
        ActionProviderBase.manage_options +
        ({
            'label': 'Overview',
            'action': 'overview',
            'help': ('CMFDefault', 'Syndication-Tool_Overview.stx')
        }, {
            'label': 'Properties',
            'action': 'propertiesForm',
            'help': ('CMFDefault', 'Syndication-Tool_Properties.stx')
        }, {
            'label': 'Policies',
            'action': 'policiesForm',
            'help': ('CMFDefault', 'Syndication-Tool_Policies.stx')
        }, {
            'label': 'Reports',
            'action': 'reportForm',
            'help': ('CMFDefault', 'Syndication-Tool_Reporting.stx')
        }))

    security.declareProtected(ManagePortal, 'overview')
    overview = HTMLFile('synOverview', _dtmldir)

    security.declareProtected(ManagePortal, 'propertiesForm')
    propertiesForm = HTMLFile('synProps', _dtmldir)

    security.declareProtected(ManagePortal, 'policiesForm')
    policiesForm = HTMLFile('synPolicies', _dtmldir)

    security.declareProtected(ManagePortal, 'reportForm')
    reportForm = HTMLFile('synReports', _dtmldir)

    security.declareProtected(ManagePortal, 'editProperties')

    def editProperties(self,
                       updatePeriod=None,
                       updateFrequency=None,
                       updateBase=None,
                       isAllowed=None,
                       max_items=None,
                       REQUEST=None):
        """
        Edit the properties for the SystemWide defaults on the
        SyndicationTool.
        """
        if isAllowed is not None:
            self.isAllowed = isAllowed

        if updatePeriod is not None:
            self.syUpdatePeriod = updatePeriod
        else:
            try:
                del self.syUpdatePeriod
            except (AttributeError, KeyError):
                pass

        if updateFrequency is not None:
            self.syUpdateFrequency = int(updateFrequency)
        else:
            try:
                del self.syUpdateFrequency
            except (AttributeError, KeyError):
                pass

        if updateBase is not None:
            if type(updateBase) is type(''):
                updateBase = DateTime(updateBase)
            self.syUpdateBase = updateBase
        else:
            try:
                del self.syUpdateBase
            except (AttributeError, KeyError):
                pass

        if max_items is not None:
            self.max_items = int(max_items)
        else:
            try:
                del self.max_items
            except (AttributeError, KeyError):
                pass

        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() +
                                         '/propertiesForm' +
                                         '?manage_tabs_message=Tool+Updated.')

    security.declarePublic('editSyInformationProperties')

    def editSyInformationProperties(self,
                                    obj,
                                    updatePeriod=None,
                                    updateFrequency=None,
                                    updateBase=None,
                                    max_items=None,
                                    REQUEST=None):
        """
        Edit syndication properties for the obj being passed in.
        These are held on the syndication_information object.
        Not Sitewide Properties.
        """
        if not _checkPermission(ManageProperties, obj):
            raise AccessControl_Unauthorized

        syInfo = getattr(obj, 'syndication_information', None)

        if syInfo is None:
            raise 'Syndication is Disabled'

        if updatePeriod is not None:
            syInfo.syUpdatePeriod = updatePeriod
        else:
            syInfo.syUpdatePeriod = self.syUpdatePeriod

        if updateFrequency is not None:
            syInfo.syUpdateFrequency = int(updateFrequency)
        else:
            syInfo.syUpdateFrequency = self.syUpdateFrequency

        if updateBase is not None:
            if type(updateBase) is type(''):
                updateBase = DateTime(updateBase)
            syInfo.syUpdateBase = updateBase
        else:
            syInfo.syUpdateBase = self.syUpdateBase

        if max_items is not None:
            syInfo.max_items = int(max_items)
        else:
            syInfo.max_items = self.max_items

    security.declarePublic('enableSyndication')

    def enableSyndication(self, obj):
        """
        Enable syndication for the obj
        """
        if not self.isSiteSyndicationAllowed():
            raise 'Syndication is Disabled'

        if hasattr(aq_base(obj), 'syndication_information'):
            raise 'Syndication Information Exists'

        syInfo = SyndicationInformation()
        obj._setObject('syndication_information', syInfo)
        syInfo = obj._getOb('syndication_information')
        syInfo.syUpdatePeriod = self.syUpdatePeriod
        syInfo.syUpdateFrequency = self.syUpdateFrequency
        syInfo.syUpdateBase = self.syUpdateBase
        syInfo.max_items = self.max_items
        syInfo.description = "Channel Description"

    security.declarePublic('disableSyndication')

    def disableSyndication(self, obj):
        """
        Disable syndication for the obj; and remove it.
        """
        syInfo = getattr(obj, 'syndication_information', None)

        if syInfo is None:
            raise 'This object does not have Syndication Information'

        obj._delObject('syndication_information')

    security.declarePublic('getSyndicatableContent')

    def getSyndicatableContent(self, obj):
        """
        An interface for allowing folderish items to implement an
        equivalent of PortalFolderBase.contentValues()
        """
        if hasattr(obj, 'synContentValues'):
            values = obj.synContentValues()
        else:
            values = PortalFolderBase.contentValues(obj)
        return values

    security.declarePublic('buildUpdatePeriods')

    def buildUpdatePeriods(self):
        """
        Return a list of possible update periods for the xmlns: sy
        """
        updatePeriods = (('hourly', 'Hourly'), ('daily', 'Daily'), ('weekly',
                                                                    'Weekly'),
                         ('monthly', 'Monthly'), ('yearly', 'Yearly'))
        return updatePeriods

    security.declarePublic('isSiteSyndicationAllowed')

    def isSiteSyndicationAllowed(self):
        """
        Return sitewide syndication policy
        """
        return self.isAllowed

    security.declarePublic('isSyndicationAllowed')

    def isSyndicationAllowed(self, obj=None):
        """
        Check whether syndication is enabled for the site.  This
        provides for extending the method to check for whether a
        particular obj is enabled, allowing for turning on only
        specific folders for syndication.
        """
        syInfo = getattr(aq_base(obj), 'syndication_information', None)
        if syInfo is None:
            return 0
        else:
            return self.isSiteSyndicationAllowed()

    security.declarePublic('getUpdatePeriod')

    def getUpdatePeriod(self, obj=None):
        """
        Return the update period for the RSS syn namespace.
        This is either on the object being passed or the
        portal_syndication tool (if a sitewide value or default
        is set)

        NOTE:  Need to add checks for sitewide policies!!!
        """
        if not self.isSiteSyndicationAllowed():
            raise 'Syndication is Not Allowed'

        if obj is None:
            return self.syUpdatePeriod

        syInfo = getattr(obj, 'syndication_information', None)

        if syInfo is not None:
            return syInfo.syUpdatePeriod
        else:
            return 'Syndication is Not Allowed'

    security.declarePublic('getUpdateFrequency')

    def getUpdateFrequency(self, obj=None):
        """
        Return the update frequency (as a positive integer) for
        the syn namespace.  This is either on the object being
        pass or the portal_syndication tool (if a sitewide value
        or default is set).

        Note:  Need to add checks for sitewide policies!!!
        """
        if not self.isSiteSyndicationAllowed():
            raise 'Syndication is not Allowed'

        if obj is None:
            return self.syUpdateFrequency

        syInfo = getattr(obj, 'syndication_information', None)
        if syInfo is not None:
            return syInfo.syUpdateFrequency
        else:
            return 'Syndication is not Allowed'

    security.declarePublic('getUpdateBase')

    def getUpdateBase(self, obj=None):
        """
        Return the base date to be used with the update frequency
        and the update period to calculate a publishing schedule.

        Note:  I'm not sure what's best here, creation date, last
        modified date (of the folder being syndicated) or some
        arbitrary date.  For now, I'm going to build a updateBase
        time from zopetime and reformat it to meet the W3CDTF.
        Additionally, sitewide policy checks might have a place
        here...
        """
        if not self.isSiteSyndicationAllowed():
            raise 'Syndication is not Allowed'

        if obj is None:
            when = self.syUpdateBase
            return when.ISO()

        syInfo = getattr(obj, 'syndication_information', None)
        if syInfo is not None:
            when = syInfo.syUpdateBase
            return when.ISO()
        else:
            return 'Syndication is not Allowed'

    security.declarePublic('getHTML4UpdateBase')

    def getHTML4UpdateBase(self, obj):
        """
        Return HTML4 formated UpdateBase DateTime
        """
        if not self.isSiteSyndicationAllowed():
            raise 'Syndication is not Allowed'

        if obj is None:
            when = syUpdateBase
            return when.HTML4()

        syInfo = getattr(obj, 'syndication_information', None)
        if syInfo is not None:
            when = syInfo.syUpdateBase
            return when.HTML4()
        else:
            return 'Syndication is not Allowed'

    def getMaxItems(self, obj=None):
        """
        Return the max_items to be displayed in the syndication
        """
        if not self.isSiteSyndicationAllowed():
            raise 'Syndication is not Allowed'

        if obj is None:
            return self.max_items

        syInfo = getattr(obj, 'syndication_information', None)
        if syInfo is not None:
            return syInfo.max_items
        else:
            return 'Syndication is not Allowed'
Example #12
0
class IDFormattingField(RecordsField):
    """A list of prefixes per portal_type
    """
    _properties = RecordsField._properties.copy()
    _properties.update({
        'type':
        'prefixes',
        'subfields':
        ('portal_type', 'form', 'sequence_type', 'context', 'counter_type',
         'counter_reference', 'prefix', 'split_length'),
        'subfield_labels': {
            'portal_type': 'Portal Type',
            'form': 'Format',
            'sequence_type': 'Seq Type',
            'context': 'Context',
            'counter_type': 'Counter Type',
            'counter_reference': 'Counter Ref',
            'prefix': 'Prefix',
            'split_length': 'Split Length',
        },
        'subfield_readonly': {
            'portal_type': False,
            'form': False,
            'sequence_type': False,
            'context': False,
            'counter_type': False,
            'counter_reference': False,
            'prefix': False,
            'split_length': False,
        },
        'subfield_sizes': {
            'portal_type': 20,
            'form': 30,
            'sequence_type': 1,
            'context': 12,
            'counter_type': 1,
            'counter_reference': 12,
            'prefix': 12,
            'split_length': 5,
        },
        'subfield_types': {
            'sequence_type': 'selection',
            'counter_type': 'selection',
            'split_length': 'int',
        },
        'subfield_vocabularies': {
            'sequence_type': 'getSequenceTypes',
            'counter_type': 'getCounterTypes',
        },
        'subfield_maxlength': {
            'form': 256,
        },
    })

    security = ClassSecurityInfo()

    def getSequenceTypes(self, instance=None):
        return DisplayList([('', ''), ('counter', 'Counter'),
                            ('generated', 'Generated')])

    def getCounterTypes(self, instance=None):
        return DisplayList([('', ''), ('backreference', 'Backreference'),
                            ('contained', 'Contained')])
Example #13
0
class ComponentTool(BaseTool):
    """
  This tool provides methods to load the the different types of components of
  the ERP5 framework: Document classes, interfaces, mixin classes, fields,
  accessors, etc.
  """
    id = "portal_components"
    meta_type = "ERP5 Component Tool"
    portal_type = "Component Tool"

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

    @classmethod
    def _applyAllStaticSecurity(cls):
        """
    Apply static security on portal_components to ensure that nobody can
    change Permissions, only 'ghost' Developer Role has Permissions to
    add/modify/delete Components. Also, make these permissions read-only
    thanks to 'property'.

    cls is erp5.portal_type.Component Tool and not this class as this function
    is called on Portal Type class when loading Componet Tool Portal Type
    class
    """
        from AccessControl.Permission import getPermissions, pname
        for permission_name, _, _ in getPermissions():
            if permission_name == 'Reset dynamic classes':
                permission_function = lambda self: ('Manager', )
            elif permission_name in ('Change permissions',
                                     'Define permissions'):
                permission_function = lambda self: ()
            elif (permission_name.startswith('Access ')
                  or permission_name.startswith('View')
                  or permission_name == 'WebDAV access'):
                permission_function = lambda self: ('Developer', 'Manager')
            else:
                permission_function = lambda self: ('Developer', )

            setattr(cls, pname(permission_name), property(permission_function))

    def _isBootstrapRequired(self):
        """
    Required by synchronizeDynamicModules() to bootstrap an empty site and
    thus create portal_components

    XXX-arnau: Only bt5 items for now
    """
        return False

    def _bootstrap(self):
        """
    Required by synchronizeDynamicModules() to bootstrap an empty site and
    thus create portal_components

    XXX-arnau: Only bt5 items for now
    """
        pass

    security.declareProtected(Permissions.ResetDynamicClasses, 'reset')

    def reset(self,
              force=False,
              reset_portal_type_at_transaction_boundary=False):
        """
    Reset all ZODB Component packages. A cache cookie is used to check whether
    the reset is necessary when force is not specified. This allows to make
    sure that all ZEO clients get reset (checked in __of__ on ERP5Site) when
    one given ZEO client gets reset when Component(s) are modified or
    invalidated.

    Also, as resetting ZODB Components Package usually implies to reset Portal
    Type as Classes (because the former are used as bases), perform the reset
    by default.

    XXX-arnau: for now, this is a global reset but it might be improved in the
    future if required...
    """
        portal = self.getPortalObject()

        global last_sync
        if force:
            # Hard invalidation to force sync between nodes
            portal.newCacheCookie('component_packages')
            last_sync = portal.getCacheCookie('component_packages')
        else:
            cookie = portal.getCacheCookie('component_packages')
            if cookie == last_sync:
                return False

            last_sync = cookie

        LOG("ERP5Type.Tool.ComponentTool", INFO, "Resetting Components")

        # Make sure that it is not possible to load Components or load Portal Type
        # class when Components are reset through aq_method_lock
        import erp5.component
        from Products.ERP5Type.dynamic.component_package import ComponentDynamicPackage
        with aq_method_lock:
            for package in erp5.component.__dict__.itervalues():
                if isinstance(package, ComponentDynamicPackage):
                    package.reset()

            erp5.component.ref_manager.gc()
        if reset_portal_type_at_transaction_boundary:
            portal.portal_types.resetDynamicDocumentsOnceAtTransactionBoundary(
            )
        else:
            from Products.ERP5Type.dynamic.portal_type_class import synchronizeDynamicModules
            synchronizeDynamicModules(self, force)

        return True

    security.declareProtected(Permissions.ResetDynamicClasses,
                              'resetOnceAtTransactionBoundary')

    def resetOnceAtTransactionBoundary(self):
        """
    Schedule a single reset at the end of the transaction. The idea behind
    this is that a reset is (very) costly and that we want to do it as little
    often as possible.  Moreover, doing it twice in a transaction is useless
    (but still twice as costly).
    """
        tv = getTransactionalVariable()
        key = 'ComponentTool.resetOnceAtTransactionBoundary'
        if key not in tv:
            tv[key] = None
            transaction.get().addBeforeCommitHook(self.reset,
                                                  args=(True, True))

    __test_text_content_template = '''\
##############################################################################
#
# %s
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################

from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase

class Test(ERP5TypeTestCase):
  """
  A Sample Test Class
  """

  def getBusinessTemplateList(self):
    """
    Tuple of Business Templates we need to install
    """
    return ('erp5_base',)

  def afterSetUp(self):
    """
    This is ran before anything, used to set the environment
    """
    # here, you can create the categories and objects your test will depend on
    pass

  def test_sampleTest(self):
    """
    A Sample Test

    For the method to be called during the test,
    its name must start with 'test'.

    See https://docs.python.org/2/library/unittest.html for help with available
    assertion methods.
    """
    self.assertEqual(0, 1)
''' % DEFAULT_TEST_TEMPLATE_COPYRIGHT

    def newContent(self, *args, **kwargs):
        """
    Create new content. If this is a Test Component and no text_content has
    been given, then define a default template to help user, likewise
    ClassTool with filesystem live tests

    XXX-arnau: should include more templates like ClassTool
    """
        if kwargs.get('portal_type') == 'Test Component':
            kwargs.setdefault('text_content',
                              self.__test_text_content_template)

        return super(ComponentTool, self).newContent(*args, **kwargs)

    security.declarePrivate('_getCommaSeparatedParameterList')

    def _getCommaSeparatedParameterList(self, parameter_list):
        # clean parameter_list and split it by commas if necessary
        if not parameter_list:
            parameter_list = ()
        elif isinstance(parameter_list, basestring):
            parameter_list = tuple(
                parameter_name.strip()
                for parameter_name in parameter_list.split(',')
                if parameter_name.strip())
        return parameter_list

    security.declareProtected(Permissions.ManagePortal, 'runLiveTest')

    def runLiveTest(self,
                    test_list=None,
                    run_only=None,
                    debug=False,
                    verbose=False):
        """
    Launch live tests

    run_only=STRING      Run only specified test methods delimited with
                         commas (e.g. testFoo,testBar). This can be regular
                         expressions.
    debug=boolean        Invoke debugger on errors / failures.
    verbose=boolean      Display more information when running tests
    """
        from StringIO import StringIO
        global global_stream
        global live_test_running
        self.serialize()
        if live_test_running:
            LOG('ComponentTool', INFO, 'Live test already running')
            return ''

        global_stream = StringIO()
        test_list = self._getCommaSeparatedParameterList(test_list)
        if not test_list:
            # no test to run
            return ''

        # Allow having strings for verbose and debug
        verbose = int(verbose) and True or False
        debug = int(debug) and True or False
        run_only = self._getCommaSeparatedParameterList(run_only)
        verbosity = verbose and 2 or 1
        request_server_url = self.REQUEST.get('SERVER_URL')

        try:
            live_test_running = True
            from Products.ERP5Type.tests.ERP5TypeLiveTestCase import runLiveTest
            try:
                result = runLiveTest(test_list,
                                     run_only=run_only,
                                     debug=debug,
                                     stream=global_stream,
                                     request_server_url=request_server_url,
                                     verbosity=verbosity)
            except ImportError:
                import traceback
                traceback.print_exc(file=global_stream)
            global_stream.seek(0)
            return global_stream.read()
        finally:
            live_test_running = False

    security.declareProtected(Permissions.ManagePortal, 'readTestOutput')

    def readTestOutput(self, position=0):
        """
    Return unread part of the test result
    """
        result = ''
        position = int(position)
        global global_stream
        if global_stream is not None:
            global_stream.seek(position)
            result = global_stream.read()
        return result
Example #14
0
class TRAColeccionSolicitudesCadenas(OrderedBaseFolder, TRAColeccionArquetipos, TRAConRegistroActividad, TRAColeccionSolicitudesCadenas_Operaciones):
    """
    """
    security = ClassSecurityInfo()
    __implements__ = (getattr(OrderedBaseFolder,'__implements__',()),) + (getattr(TRAColeccionArquetipos,'__implements__',()),) + (getattr(TRAConRegistroActividad,'__implements__',()),) + (getattr(TRAColeccionSolicitudesCadenas_Operaciones,'__implements__',()),)

    # This name appears in the 'add' box
    archetype_name = 'Coleccion de Solicitudes de creacion de Cadenas'

    meta_type = 'TRAColeccionSolicitudesCadenas'
    portal_type = 'TRAColeccionSolicitudesCadenas'


    # Change Audit fields

    creation_date_field = 'fechaCreacion'
    creation_user_field = 'usuarioCreador'
    modification_date_field = 'fechaModificacion'
    modification_user_field = 'usuarioModificador'
    deletion_date_field = 'fechaEliminacion'
    deletion_user_field = 'usuarioEliminador'
    is_inactive_field = 'estaInactivo'
    change_counter_field = 'contadorCambios'
    change_log_field = 'registroDeCambios'



    allowed_content_types = ['TRASolicitudCadena'] + list(getattr(TRAColeccionArquetipos, 'allowed_content_types', [])) + list(getattr(TRAConRegistroActividad, 'allowed_content_types', [])) + list(getattr(TRAColeccionSolicitudesCadenas_Operaciones, 'allowed_content_types', []))
    filter_content_types             = 1
    global_allow                     = 0
    #content_icon = 'TRAColeccionSolicitudesCadenas.gif'
    immediate_view                   = 'Tabular'
    default_view                     = 'Tabular'
    suppl_views                      = ['Tabular',]
    typeDescription                  = "Coleccion de solicitudes realizadas por los desarrolladores, para crear nuevas cadenas."
    typeDescMsgId                    =  'gvSIGi18n_TRAColeccionSolicitudesCadenas_help'
    archetype_name2                  = 'Strings creation request collection'
    typeDescription2                 = '''Collection of requests by developers to create new strings.'''
    archetype_name_msgid             = 'gvSIGi18n_TRAColeccionSolicitudesCadenas_label'
    factory_methods                  = { 'TRASolicitudCadena' : 'fCrearSolicitudCadena',}
    factory_enablers                 = { 'TRASolicitudCadena' : [ 'fUseCaseCheckDoableFactory', 'Create_TRASolicitudCadena',]}
    propagate_delete_impact_to       = None
    allow_discussion = False


    actions =  (


       {'action': "string:${object_url}/TRAConfirmarCrearCadenas",
        'category': "object_buttons",
        'id': 'TRACreateStrings',
        'name': 'Create Strings',
        'permissions': ("Modify portal content",),
        'condition': """python:object.TRAgvSIGi18n_tool.fUseCaseCheckDoable( object, 'Create_TRASolicitudCadena')"""
       },


       {'action': "string:${object_url}/TRAConfirmarLimpiarCadenas",
        'category': "object_buttons",
        'id': 'TRACleanupStrings',
        'name': 'Clean-up Strings',
        'permissions': ("Modify portal content",),
        'condition': """python:object.TRAgvSIGi18n_tool.fUseCaseCheckDoable( object, 'Cleanup_TRAColeccionSolicitudesCadenas')"""
       },


       {'action': "string:${object_url}/TRACrear_SolicitudCadena/?theNewTypeName=TRASolicitudCadena&theAggregationName=solicitudesCadenas",
        'category': "object_buttons",
        'id': 'TRACreateSolicitudCadena',
        'name': 'Create New String Request',
        'permissions': ("View",),
        'condition': """python:object.TRAgvSIGi18n_tool.fUseCaseCheckDoable(object, 'Create_TRASolicitudCadena')"""
       },


       {'action': "string:${object_url}/folder_listing",
        'category': "folder",
        'id': 'folderlisting',
        'name': 'Folder Listing',
        'permissions': ("View",),
        'condition': """python:0"""
       },


       {'action': "string:${object_url}/reference_graph",
        'category': "object",
        'id': 'references',
        'name': 'References',
        'permissions': ("Modify portal content",),
        'condition': """python:0"""
       },


       {'action': "string:${object_url}/Tabular",
        'category': "object",
        'id': 'view',
        'name': 'View',
        'permissions': ("View",),
        'condition': """python:object.TRAgvSIGi18n_tool.fUseCaseCheckDoable( object, 'View_any_TRA_element')"""
       },


       {'action': "string:${object_url}/MDDChanges",
        'category': "object_buttons",
        'id': 'mddchanges',
        'name': 'Changes',
        'permissions': ("View",),
        'condition': """python:object.TRAgvSIGi18n_tool.fUseCaseCheckDoable( object, 'Changes_on_any_TRA_element')"""
       },


       {'action': "string:$object_url/Editar",
        'category': "object",
        'id': 'edit',
        'name': 'Edit',
        'permissions': ("Modify portal content",),
        'condition': """python:object.fAllowWrite() and object.TRAgvSIGi18n_tool.fRoleQuery_IsAnyRol( object, [ 'Manager', 'Owner', 'TRACreator', 'TRAManager', 'TRACoordinator',])"""
       },


       {'action': "string:${object_url}/TRAFlushCache_action",
        'category': "object_buttons",
        'id': 'tra_flushcache',
        'name': 'Flush',
        'permissions': ("View",),
        'condition': """python:object.TRAgvSIGi18n_tool.fRoleQuery_IsAnyRol( object, [ 'Manager', 'Owner', 'TRACreator', 'TRAManager', 'TRACoordinator',])"""
       },


       {'action': "string:${object_url}/TRAInventory_action",
        'category': "object_buttons",
        'id': 'TRA_inventario',
        'name': 'Inventory',
        'permissions': ("View",),
        'condition': """python:object.TRAgvSIGi18n_tool.fUseCaseCheckDoable( object, 'Inventory_TRAElemento')"""
       },


       {'action': "string:${object_url}/sharing",
        'category': "object",
        'id': 'local_roles',
        'name': 'Sharing',
        'permissions': ("Manage properties",),
        'condition': """python:0"""
       },


       {'action': "string:${object_url}/TRASeguridadUsuarioConectado",
        'category': "object_buttons",
        'id': 'TRA_SeguridadUsuarioConectado',
        'name': 'Permissions',
        'permissions': ("View",),
        'condition': """python:object.TRAgvSIGi18n_tool.fUseCaseCheckDoable( object, 'Permissions_on_any_TRA_element')"""
       },


       {'action': "string:$object_url/content_status_history",
        'category': "object",
        'id': 'content_status_history',
        'name': 'State',
        'permissions': ("View",),
        'condition': """python:0"""
       },


       {'action': "string:${object_url}/TRARecatalog_action",
        'category': "object_buttons",
        'id': 'TRA_recatalogar',
        'name': 'ReCatalog',
        'permissions': ("View",),
        'condition': """python:object.TRAgvSIGi18n_tool.fUseCaseCheckDoable( object, 'ReCatalog_TRAElemento')"""
       },


       {'action': "string:${object_url}/MDDCacheStatus/",
        'category': "object_buttons",
        'id': 'mddcachestatus',
        'name': 'Cache',
        'permissions': ("View",),
        'condition': """python:object.TRAgvSIGi18n_tool.fUseCaseCheckDoable( object, 'CacheStatus_on_any_TRA_element')"""
       },


       {'action': "string:${object_url}/TRAResetPermissions_action",
        'category': "object_buttons",
        'id': 'TRA_reestablecerpermisos',
        'name': 'Reset Permissions',
        'permissions': ("View",),
        'condition': """python:object.TRAgvSIGi18n_tool.fUseCaseCheckDoable( object, 'ResetPermissions_TRAElemento')"""
       },


       {'action': "string:${object_url}/TRAVerifyPermissions_action",
        'category': "object_buttons",
        'id': 'TRA_verificarpermisos',
        'name': 'Verify Permissions',
        'permissions': ("View",),
        'condition': """python:object.fHasTRAtool() and object.TRAgvSIGi18n_tool.fUseCaseCheckDoable( object, 'VerifyPermissions_TRAElemento')"""
       },


    )

    _at_rename_after_creation = True

    schema = TRAColeccionSolicitudesCadenas_schema

    ##code-section class-header #fill in your manual code here
    ##/code-section class-header

    # Methods

    security.declarePublic('manage_beforeDelete')
    def manage_beforeDelete(self,item,container):
        """
        """
        
        return TRAColeccionArquetipos.manage_beforeDelete( self, item, container)

    security.declarePublic('manage_afterAdd')
    def manage_afterAdd(self,item,container):
        """
        """
        
        return TRAColeccionArquetipos.manage_afterAdd( self, item, container)

    security.declarePublic('cb_isCopyable')
    def cb_isCopyable(self):
        """
        """
        
        return False

    security.declarePublic('displayContentsTab')
    def displayContentsTab(self):
        """
        """
        
        return False

    security.declarePublic('fIsCacheable')
    def fIsCacheable(self):
        """
        """
        
        return True

    security.declarePublic('fExtraLinks')
    def fExtraLinks(self):
        """
        """
        
        return TRAElemento_Operaciones.fExtraLinks( self)

    security.declarePublic('manage_pasteObjects')
    def manage_pasteObjects(self,cb_copy_data,REQUEST):
        """
        """
        
        return self
Example #15
0
class FGRichLabelField(BaseFormField):
    """ Rich-text label field """

    security = ClassSecurityInfo()

    schema = BareFieldSchema.copy() + Schema((
        TextField(
            'fgDefault',
            searchable=0,
            required=0,
            validators=('isTidyHtmlWithCleanup', ),
            default_content_type='text/html',
            default_output_type='text/x-html-safe',
            allowable_content_types=zconf.ATDocument.allowed_content_types,
            widget=RichWidget(
                label=_(u'label_fglabelbody_text', default=u'Label body'),
                description=_(u'help_fglabelbody_text',
                              default=u"""
                    The text to display in the form.
                """),
                allow_file_upload=False,
            ),
        ),
        TALESString(
            'fgTDefault',
            schemata='overrides',
            searchable=0,
            required=0,
            validators=('talesvalidator', ),
            default='',
            write_permission=EDIT_TALES_PERMISSION,
            widget=StringWidget(
                label=_(u'label_fgtdefault_text',
                        default=u"Default Expression"),
                description=_(u'help_fgtdefault_text',
                              default=u"""
                    A TALES expression that will be evaluated when the form is displayed
                    to get the field default value.
                    Leave empty if unneeded. Your expression should evaluate as a string.
                    PLEASE NOTE: errors in the evaluation of this expression will cause
                    an error on form display.
                """),
                size=70,
            ),
        ),
    ))

    schema['title'].widget.label = _(u'label_title', default=u"Title")
    schema['title'].widget.description = _(u'help_notdisplayed_text',
                                           default=u"Not displayed on form.")
    schema['description'].widget.visible = {
        'view': 'invisible',
        'edit': 'invisible'
    }

    # Standard content type setup
    portal_type = meta_type = 'FormRichLabelField'
    archetype_name = 'Rich Label Field'
    content_icon = 'RichLabelField.gif'
    typeDescription = 'A rich-text label'

    def __init__(self, oid, **kwargs):
        """ initialize class """

        BaseFormField.__init__(self, oid, **kwargs)

        # set a preconfigured field as an instance attribute
        self.fgField = HtmlTextField(
            'fg_text_field',
            searchable=0,
            required=0,
            write_permission=View,
            default_content_type='text/html',
            default_output_type='text/x-html-safe',
            allowable_content_types=zconf.ATDocument.allowed_content_types,
            widget=RichLabelWidget(),
        )

    security.declareProtected(ModifyPortalContent, 'setFgDefault')

    def setFgDefault(self, value, **kw):
        """ set default for field """

        self.fgField.default = value

    def getRawFgDefault(self, **kw):
        """ get default for field """

        return self.fgField.default

    security.declareProtected(View, 'isBinary')

    def isBinary(self, key):
        return False

    security.declareProtected(View, 'getContentType')

    def getContentType(self, key=None):
        return 'text/html'

    def isLabel(self):
        return True
Example #16
0
class MemberData (SimpleItem):
    security = ClassSecurityInfo()

    def __init__(self, tool, id):
        self.id = id
        # Make a temporary reference to the tool.
        # The reference will be removed by notifyModified().
        self._tool = tool

    security.declarePrivate('notifyModified')
    def notifyModified(self):
        # Links self to parent for full persistence.
        tool = getattr(self, '_tool', None)
        if tool is not None:
            del self._tool
            tool.registerMemberData(self, self.getId())

    security.declarePublic('getUser')
    def getUser(self):
        # The user object is our context, but it's possible for
        # restricted code to strip context while retaining
        # containment.  Therefore we need a simple security check.
        parent = aq_parent(self)
        bcontext = aq_base(parent)
        bcontainer = aq_base(aq_parent(aq_inner(self)))
        if bcontext is bcontainer or not hasattr(bcontext, 'getUserName'):
            raise 'MemberDataError', "Can't find user data"
        # Return the user object, which is our context.
        return parent

    def getTool(self):
        return aq_parent(aq_inner(self))

    security.declarePublic('getMemberId')
    def getMemberId(self):
        return self.getUser().getUserName()

    security.declareProtected(CMFCorePermissions.SetOwnProperties, 'setProperties')
    def setProperties(self, properties=None, **kw):
        '''Allows the authenticated member to set his/her own properties.
        Accepts either keyword arguments or a mapping for the "properties"
        argument.
        '''
        if properties is None:
            properties = kw
        membership = getToolByName(self, 'portal_membership')
        registration = getToolByName(self, 'portal_registration', None)
        if not membership.isAnonymousUser():
            member = membership.getAuthenticatedMember()
            if registration:
                failMessage = registration.testPropertiesValidity(properties, member)
                if failMessage is not None:
                    raise 'Bad Request', failMessage
            member.setMemberProperties(properties)
        else:
            raise 'Bad Request', 'Not logged in.'

    security.declarePrivate('setMemberProperties')
    def setMemberProperties(self, mapping):
        '''Sets the properties of the member.
        '''
        # Sets the properties given in the MemberDataTool.
        tool = self.getTool()
        for id in tool.propertyIds():
            if mapping.has_key(id):
                if not self.__class__.__dict__.has_key(id):
                    value = mapping[id]
                    if type(value)==type(''):
                        proptype = tool.getPropertyType(id) or 'string'
                        if type_converters.has_key(proptype):
                            value = type_converters[proptype](value)
                    setattr(self, id, value)
        # Hopefully we can later make notifyModified() implicit.
        self.notifyModified()

    # XXX: s.b., getPropertyForMember(member, id, default)?

    security.declarePublic('getProperty')
    def getProperty(self, id, default=_marker):

        tool = self.getTool()
        base = aq_base( self )

        # First, check the wrapper (w/o acquisition).
        value = getattr( base, id, _marker )
        if value is not _marker:
            return value

        # Then, check the tool and the user object for a value.
        tool_value = tool.getProperty( id, _marker )
        user_value = getattr( self.getUser(), id, _marker )

        # If the tool doesn't have the property, use user_value or default
        if tool_value is _marker:
            if user_value is not _marker:
                return user_value
            elif default is not _marker:
                return default
            else:
                raise ValueError, 'The property %s does not exist' % id

        # If the tool has an empty property and we have a user_value, use it
        if not tool_value and user_value is not _marker:
            return user_value

        # Otherwise return the tool value
        return tool_value

    security.declarePrivate('getPassword')
    def getPassword(self):
        """Return the password of the user."""
        return self.getUser()._getPassword()

    security.declarePrivate('setSecurityProfile')
    def setSecurityProfile(self, password=None, roles=None, domains=None):
        """Set the user's basic security profile"""
        u = self.getUser()
        # This is really hackish.  The Zope User API needs methods
        # for performing these functions.
        if password is not None:
            u.__ = password
        if roles is not None:
            u.roles = roles
        if domains is not None:
            u.domains = domains

    def __str__(self):
        return self.getMemberId()

    ### User object interface ###

    security.declarePublic('getUserName')
    def getUserName(self):
        """Return the username of a user"""
        return self.getUser().getUserName()

    security.declarePublic('getId')
    def getId(self):
        """Get the ID of the user. The ID can be used, at least from
        Python, to get the user from the user's
        UserDatabase"""
        return self.getUser().getId()

    security.declarePublic('getRoles')
    def getRoles(self):
        """Return the list of roles assigned to a user."""
        return self.getUser().getRoles()

    security.declarePublic('getRolesInContext')
    def getRolesInContext(self, object):
        """Return the list of roles assigned to the user,
           including local roles assigned in context of
           the passed in object."""
        return self.getUser().getRolesInContext(object)

    security.declarePublic('getDomains')
    def getDomains(self):
        """Return the list of domain restrictions for a user"""
        return self.getUser().getDomains()

    security.declarePublic('has_role')
    def has_role(self, roles, object=None):
        """Check to see if a user has a given role or roles."""
        return self.getUser().has_role(roles, object)
Example #17
0
class FGFileField(BaseFormField):
    """ File (upload) field """

    security = ClassSecurityInfo()

    schema = BaseFieldSchema.copy() + Schema((StringField(
        'fgMaxMB',
        searchable=0,
        required=0,
        default=0,
        widget=IntegerWidget(
            label=_(u'label_filemaxmb_text',
                    default=u'Maximum Upload Size (Megabytes)'),
            description=_(u'help_filemaxmb_text',
                          default=u"""Set to 0 for no limit."""),
        ),
    ), ))

    # 'hidden' isn't really useful for a file field.
    del schema['hidden']
    # 'serverSide' is not really useful for this field.
    del schema['serverSide']

    finalizeFieldSchema(schema, folderish=True, moveDiscussion=False)

    # Standard content type setup
    portal_type = meta_type = 'FormFileField'
    archetype_name = 'File Field'
    content_icon = 'FileField.gif'
    typeDescription = 'A file field'

    def __init__(self, oid, **kwargs):
        """ initialize class """

        BaseFormField.__init__(self, oid, **kwargs)

        # set a preconfigured field as an instance attribute
        self.fgField = FileField(
            'fg_file_field',
            searchable=0,
            required=0,
            write_permission=View,
            accessor='nullAccessor',
            maxsize=0,
            validators=('isMaxSize', ),
        )

    def isFileField(self):
        return True

    security.declareProtected(ModifyPortalContent, 'setFgMaxMB')

    def setFgMaxMB(self, value, **kw):
        """ set the maxmb """

        self.fgField.maxsize = int(value)

    security.declareProtected(View, 'getFgMaxMB')

    def getFgMaxMB(self, **kw):
        """ get the maxmb """

        return self.fgField.maxsize

    def htmlValue(self, REQUEST):

        file = REQUEST.form.get('%s_file' % self.fgField.getName())
        if isinstance(file, FileUpload) and file.filename != '':
            file.seek(0)
            fdata = file.read()
            filename = file.filename
            mimetype, enc = guess_content_type(filename, fdata, None)
            return "%s: %s bytes" % (mimetype, len(fdata))
        else:
            return 'No Input'

    def getFieldFormName(self):
        """field widget appends '_file' file to field id"""

        return self.fgField.getName() + '_file'

    # some File methods to make the FGFileField behave like a file object
    # this solves an issue with Archetype trying to read the file and
    # determine the mime type

    def seek(self, offset, whence=None):
        return

    def read(self, size=None):
        return ''

    def tell(self):
        return 0
Example #18
0
class MemberDataTool (UniqueObject, SimpleItem, PropertyManager, ActionProviderBase):
    '''This tool wraps user objects, making them act as Member objects.
    '''
    id = 'portal_memberdata'
    meta_type = 'CMF Member Data Tool'
    _actions = []
    _v_temps = None
    _properties = ()

    security = ClassSecurityInfo()

    manage_options=( ActionProviderBase.manage_options +
                     ({ 'label' : 'Overview'
                       , 'action' : 'manage_overview'
                       }
                     , { 'label' : 'Contents'
                       , 'action' : 'manage_showContents'
                       }
                     )
                   + PropertyManager.manage_options
                   + SimpleItem.manage_options
                   )

    #
    #   ZMI methods
    #
    security.declareProtected( CMFCorePermissions.ManagePortal
                             , 'manage_overview' )
    manage_overview = DTMLFile( 'explainMemberDataTool', _dtmldir )

    security.declareProtected( CMFCorePermissions.ViewManagementScreens
                             , 'manage_showContents')
    manage_showContents = DTMLFile('memberdataContents', _dtmldir )

    security.declareProtected( CMFCorePermissions.ViewManagementScreens
                             , 'getContentsInformation',)


    def __init__(self):
        self._members = OOBTree()
        # Create the default properties.
        self._setProperty('email', '', 'string')
        self._setProperty('portal_skin', '', 'string')
        self._setProperty('listed', '', 'boolean')
        self._setProperty('login_time', '2000/01/01', 'date')
        self._setProperty('last_login_time', '2000/01/01', 'date')

    #
    #   'portal_memberdata' interface methods
    #
    security.declarePrivate('listActions')
    def listActions(self, info=None):
        """
        Return actions provided via tool.
        """
        return self._actions

    security.declarePrivate('getMemberDataContents')
    def getMemberDataContents(self):
        '''
        Return the number of members stored in the _members
        BTree and some other useful info
        '''
        membertool   = getToolByName(self, 'portal_membership')
        members      = self._members
        user_list    = membertool.listMemberIds()
        member_list  = members.keys()
        member_count = len(members)
        orphan_count = 0

        for member in member_list:
            if member not in user_list:
                orphan_count = orphan_count + 1

        return [{ 'member_count' : member_count,
                  'orphan_count' : orphan_count }]

    security.declarePrivate( 'searchMemberDataContents' )
    def searchMemberDataContents( self, search_param, search_term ):
        """ Search members """
        res = []

        if search_param == 'username':
            search_param = 'id'

        for user_wrapper in self._members.values():
            searched = getattr( user_wrapper, search_param, None )
            if searched is not None and string.find( searched, search_term ) != -1:
                res.append( { 'username' : getattr( user_wrapper, 'id' )
                            , 'email' : getattr( user_wrapper, 'email', '' )
                            }
                          )

        return res

    security.declarePrivate('pruneMemberDataContents')
    def pruneMemberDataContents(self):
        '''
        Compare the user IDs stored in the member data
        tool with the list in the actual underlying acl_users
        and delete anything not in acl_users
        '''
        membertool= getToolByName(self, 'portal_membership')
        members   = self._members
        user_list = membertool.listMemberIds()

        for tuple in members.items():
            member_name = tuple[0]
            member_obj  = tuple[1]
            if member_name not in user_list:
                del members[member_name]

    security.declarePrivate('wrapUser')
    def wrapUser(self, u):
        '''
        If possible, returns the Member object that corresponds
        to the given User object.
        '''
        id = u.getUserName()
        members = self._members
        if not members.has_key(id):
            # Get a temporary member that might be
            # registered later via registerMemberData().
            temps = self._v_temps
            if temps is not None and temps.has_key(id):
                m = temps[id]
            else:
                base = aq_base(self)
                m = MemberData(base, id)
                if temps is None:
                    self._v_temps = {id:m}
                else:
                    temps[id] = m
        else:
            m = members[id]
        # Return a wrapper with self as containment and
        # the user as context.
        return m.__of__(self).__of__(u)

    security.declarePrivate('registerMemberData')
    def registerMemberData(self, m, id):
        '''
        Adds the given member data to the _members dict.
        This is done as late as possible to avoid side effect
        transactions and to reduce the necessary number of
        entries.
        '''
        self._members[id] = m
Example #19
0
class FGFixedPointField(BaseFormField):
    """ Fixed-Point (float) entry field """

    security = ClassSecurityInfo()

    schema = BaseFieldSchemaStringDefault.copy() + Schema((
        FixedPointField(
            'minval',
            searchable=0,
            required=0,
            default='0.0',
            widget=DecimalWidget(
                label=_(u'label_minval_text',
                        default=u"Minimum Acceptable Value"),
                description=_(u'help_minval_text',
                              default=u"""
            The form will not accept values less than the number you enter here.
                """),
                size=8,
            ),
        ),
        FixedPointField(
            'maxval',
            searchable=0,
            required=1,
            default='10000.0',
            widget=DecimalWidget(
                label=_(u'label_maxval_text',
                        default=u"Maximum Acceptable Value"),
                description=_(u'help_maxval_text',
                              default=u"""
                    The form will not accept values greater than the number you enter here.
                """),
                size=8,
            ),
        ),
        maxlengthField,
        sizeField,
    ))

    # 'hidden' isn't really useful for this field.
    del schema['hidden']
    # 'serverSide' is not really useful for this field.
    del schema['serverSide']

    # and, required has only limited use ...
    schema['required'].widget.description = _(u"help_fprequired_text", default = \
        u"""NOTE: For a fixed-point field, the required flag will not allow
           entry of a '0' value.
        """)

    # hide references & discussion
    finalizeFieldSchema(schema, folderish=True, moveDiscussion=False)

    # Standard content type setup
    portal_type = meta_type = 'FormFixedPointField'
    archetype_name = 'Fixed-Point Field'
    content_icon = 'FloatField.gif'
    typeDescription = 'A fixed-point (float) field'

    def __init__(self, oid, **kwargs):
        """ initialize class """

        BaseFormField.__init__(self, oid, **kwargs)

        # set a preconfigured field as an instance attribute
        self.fgField = FixedPointField(
            'fg_date_field',
            searchable=0,
            required=0,
            write_permission=View,
            validators=(
                'isNotTooLong',
                'inExNumericRange',
            ),
        )
Example #20
0
class ARImport(BaseFolder):
    security = ClassSecurityInfo()
    schema = schema
    displayContentsTab = False
    implements(IARImport)

    _at_rename_after_creation = True

    def _renameAfterCreation(self, check_auto_id=False):
        renameAfterCreation(self)

    def guard_validate_transition(self):
        """We may only attempt validation if file data has been uploaded.
        """
        data = self.getOriginalFile()
        if data and len(data):
            return True

    def workflow_before_validate(self):
        """This function transposes values from the provided file into the
        ARImport object's fields, and checks for invalid values.

        If errors are found:
            - Validation transition is aborted.
            - Errors are stored on object and displayed to user.

        """
        # Re-set the errors on this ARImport each time validation is attempted.
        # When errors are detected they are immediately appended to this field.
        self.setErrors([])

        self.validate_headers()
        self.validate_samples()

        if self.getErrors():
            addStatusMessage(self.REQUEST, _p('Validation errors.'), 'error')
            transaction.commit()
            self.REQUEST.response.write(
                '<script>document.location.href="%s/edit"</script>' %
                (self.absolute_url()))
        self.REQUEST.response.write(
            '<script>document.location.href="%s/view"</script>' %
            (self.absolute_url()))

    def at_post_edit_script(self):
        workflow = getToolByName(self, 'portal_workflow')
        trans_ids = [t['id'] for t in workflow.getTransitionsFor(self)]
        if 'validate' in trans_ids:
            workflow.doActionFor(self, 'validate')

    def workflow_script_import(self):
        """Create objects from valid ARImport
        """
        def convert_date_string(datestr):
            return datestr.replace('-', '/')

        def lookup_sampler_uid(import_user):
            #Lookup sampler's uid
            found = False
            userid = None
            user_ids = []
            users = getUsers(self, ['LabManager', 'Sampler']).items()
            for (samplerid, samplername) in users:
                if import_user == samplerid:
                    found = True
                    userid = samplerid
                    break
                if import_user == samplername:
                    user_ids.append(samplerid)
            if found:
                return userid
            if len(user_ids) == 1:
                return user_ids[0]
            if len(user_ids) > 1:
                #raise ValueError('Sampler %s is ambiguous' % import_user)
                return None
            #Otherwise
            #raise ValueError('Sampler %s not found' % import_user)
            return None

        bsc = getToolByName(self, 'bika_setup_catalog')
        workflow = getToolByName(self, 'portal_workflow')
        client = self.aq_parent

        title = _('Submitting AR Import')
        description = _('Creating and initialising objects')
        bar = ProgressBar(self, self.REQUEST, title, description)
        notify(InitialiseProgressBar(bar))

        profiles = [x.getObject() for x in bsc(portal_type='AnalysisProfile')]

        gridrows = self.schema['SampleData'].get(self)
        row_cnt = 0
        for therow in gridrows:
            row = therow.copy()
            row_cnt += 1
            # Create Sample
            sample = _createObjectByType('Sample', client, tmpID())
            sample.unmarkCreationFlag()
            # First convert all row values into something the field can take
            sample.edit(**row)
            sample._renameAfterCreation()
            event.notify(ObjectInitializedEvent(sample))
            sample.at_post_create_script()
            swe = self.bika_setup.getSamplingWorkflowEnabled()
            if swe:
                workflow.doActionFor(sample, 'sampling_workflow')
            else:
                workflow.doActionFor(sample, 'no_sampling_workflow')
            part = _createObjectByType('SamplePartition', sample, 'part-1')
            part.unmarkCreationFlag()
            if swe:
                workflow.doActionFor(part, 'sampling_workflow')
            else:
                workflow.doActionFor(part, 'no_sampling_workflow')
            container = self.get_row_container(row)
            if container:
                part.edit(Container=container)

            # Profiles are titles, profile keys, or UIDS: convert them to UIDs.
            newprofiles = []
            for title in row['Profiles']:
                objects = [
                    x for x in profiles
                    if title in (x.getProfileKey(), x.UID(), x.Title())
                ]
                for obj in objects:
                    newprofiles.append(obj.UID())
            row['Profiles'] = newprofiles

            # BBB in bika.lims < 3.1.9, only one profile is permitted
            # on an AR.  The services are all added, but only first selected
            # profile name is stored.
            row['Profile'] = newprofiles[0] if newprofiles else None

            # Same for analyses
            newanalyses = set(
                self.get_row_services(row) +
                self.get_row_profile_services(row))
            row['Analyses'] = []
            # get batch
            batch = self.schema['Batch'].get(self)
            if batch:
                row['Batch'] = batch
            # Add AR fields from schema into this row's data
            row['ClientReference'] = self.getClientReference()
            row['ClientOrderNumber'] = self.getClientOrderNumber()
            row['Contact'] = self.getContact()
            row['DateSampled'] = convert_date_string(row['DateSampled'])
            if row['Sampler']:
                row['Sampler'] = lookup_sampler_uid(row['Sampler'])

            # Create AR
            ar = _createObjectByType("AnalysisRequest", client, tmpID())
            ar.setSample(sample)
            ar.unmarkCreationFlag()
            ar.edit(**row)
            ar._renameAfterCreation()
            ar.setAnalyses(list(newanalyses))
            for analysis in ar.getAnalyses(full_objects=True):
                analysis.setSamplePartition(part)
            ar.at_post_create_script()
            if swe:
                workflow.doActionFor(ar, 'sampling_workflow')
            else:
                workflow.doActionFor(ar, 'no_sampling_workflow')

            # If the Sampling Workflow field values are valid,
            # and the SamplingWorkflow is enabled, we will
            # automatically kick off the "sample" transition now
            tids = [t['id'] for t in get_transitions_for(ar)]
            if 'sample' in tids and ar.getSampler() and ar.getDateSampled():
                do_transition_for(ar, 'sample')

            progress_index = float(row_cnt) / len(gridrows) * 100
            progress = ProgressState(self.REQUEST, progress_index)
            notify(UpdateProgressEvent(progress))
        # document has been written to, and redirect() fails here
        self.REQUEST.response.write(
            '<script>document.location.href="%s"</script>' %
            (self.aq_parent.absolute_url()))

    def get_header_values(self):
        """Scrape the "Header" values from the original input file
        """
        lines = self.getOriginalFile().data.splitlines()
        reader = csv.reader(lines)
        header_fields = header_data = []
        for row in reader:
            if not any(row):
                continue
            if row[0].strip().lower() == 'header':
                header_fields = [x.strip() for x in row][1:]
                continue
            if row[0].strip().lower() == 'header data':
                header_data = [x.strip() for x in row][1:]
                break
        if not (header_data or header_fields):
            return None
        if not (header_data and header_fields):
            self.error("File is missing header row or header data")
            return None
        # inject us out of here
        values = dict(zip(header_fields, header_data))
        # blank cell from sheet will probably make it in here:
        if '' in values:
            del (values[''])
        return values

    def save_header_data(self):
        """Save values from the file's header row into their schema fields.
        """
        client = self.aq_parent

        headers = self.get_header_values()
        if not headers:
            return False

        if client:
            self.setClient(client)

        for h, f in [('File name', 'Filename'), ('No of Samples', 'NrSamples'),
                     ('Client name', 'ClientName'), ('Client ID', 'ClientID'),
                     ('Client Order Number', 'ClientOrderNumber'),
                     ('Client Reference', 'ClientReference')]:
            v = headers.get(h, None)
            if v:
                field = self.schema[f]
                field.set(self, v)
            del (headers[h])
        # Primary Contact
        v = headers.get('Contact', None)
        contacts = [x for x in client.objectValues('Contact')]
        contact = [c for c in contacts if c.Title() == v]
        if contact:
            self.schema['Contact'].set(self, contact)
        else:
            self.error("Specified contact '%s' does not exist; using '%s'" %
                       (v, contacts[0].Title()))
            self.schema['Contact'].set(self, contacts[0])
        del (headers['Contact'])

        # CCContacts
        field_value = {
            'CCNamesReport': '',
            'CCEmailsReport': '',
            'CCNamesInvoice': '',
            'CCEmailsInvoice': ''
        }
        for h, f in [
                # csv header name      DataGrid Column ID
            ('CC Names - Report', 'CCNamesReport'),
            ('CC Emails - Report', 'CCEmailsReport'),
            ('CC Names - Invoice', 'CCNamesInvoice'),
            ('CC Emails - Invoice', 'CCEmailsInvoice'),
        ]:
            if h in headers:
                values = [x.strip() for x in headers.get(h, '').split(",")]
                field_value[f] = values if values else ''
                del (headers[h])
        self.schema['CCContacts'].set(self, [field_value])

        if headers:
            unexpected = ','.join(headers.keys())
            self.error("Unexpected header fields: %s" % unexpected)

    def get_sample_values(self):
        """Read the rows specifying Samples and return a dictionary with
        related data.
        """
        res = {'samples': []}
        lines = self.getOriginalFile().data.splitlines()
        reader = csv.reader(lines)
        found_samples = False
        for row in reader:
            # Continue until the last column0="Samples" row is found
            if row[0].strip().lower() == 'samples':
                found_samples = True
                res['headers'] = [x.strip() for x in row]
                continue
            if not found_samples:
                continue
            # Get values for this sample row
            vals = [x.strip() for x in row]
            # ignore these (they were always ignored, this is just explicit!)
            if vals[0] in [
                    'Analysis price', 'Total Analyses or Profiles',
                    'Total price excl Tax'
            ]:
                continue
            if not any(vals):
                continue
            res['samples'].append(zip(res['headers'], vals))
        return res

    def save_sample_data(self):
        """Save values from the file's header row into the DataGrid columns
        after doing some very basic validation
        """
        bsc = getToolByName(self, 'bika_setup_catalog')
        keywords = self.bika_setup_catalog.uniqueValuesFor('getKeyword')
        profiles = []
        for p in bsc(portal_type='AnalysisProfile'):
            p = p.getObject()
            profiles.append(p.Title())
            profiles.append(p.getProfileKey())

        sample_data = self.get_sample_values()
        if not sample_data:
            return False

        # columns that we expect, but do not find, are listed here.
        # we report on them only once, after looping through sample rows.
        missing = set()

        # This contains all sample header rows that were not handled
        # by this code
        unexpected = set()

        # Save other errors here instead of sticking them directly into
        # the field, so that they show up after MISSING and before EXPECTED
        errors = []

        # This will be the new sample-data field value, when we are done.
        grid_rows = []

        row_nr = 0
        for row in sample_data['samples']:
            row = dict(row)
            # Skip blanks
            if not any(row.values()):
                continue
            row_nr += 1

            # sid is just for referring the user back to row X in their
            # in put spreadsheet
            gridrow = {'sid': row['Samples']}
            del (row['Samples'])

            gridrow = {'ClientSampleID': row['ClientSampleID']}
            del (row['ClientSampleID'])

            gridrow['Sampler'] = row['Sampler']
            del (row['Sampler'])

            # We'll use this later to verify the number against selections
            if 'Total number of Analyses or Profiles' in row:
                nr_an = row['Total number of Analyses or Profiles']
                del (row['Total number of Analyses or Profiles'])
            else:
                nr_an = 0
            try:
                nr_an = int(nr_an)
            except ValueError:
                nr_an = 0

            # TODO this is ignored and is probably meant to serve some purpose.
            del (row['Price excl Tax'])

            # match against sample schema
            for k, v in row.items():
                if k in ['Analyses', 'Profiles']:
                    continue
                if k in sample_schema:
                    del (row[k])
                    if v:
                        try:
                            value = self.munge_field_value(
                                sample_schema, row_nr, k, v)
                            gridrow[k] = value
                        except ValueError as e:
                            errors.append(e.message)

            # match against ar schema
            for k, v in row.items():
                if k in ['Analyses', 'Profiles']:
                    continue
                if k in ar_schema:
                    del (row[k])
                    if v:
                        try:
                            value = self.munge_field_value(
                                ar_schema, row_nr, k, v)
                            gridrow[k] = value
                        except ValueError as e:
                            errors.append(e.message)

            # Count and remove Keywords and Profiles from the list
            gridrow['Analyses'] = []
            for k, v in row.items():
                if k in keywords:
                    del (row[k])
                    if str(v).strip().lower() not in ('', '0', 'false'):
                        gridrow['Analyses'].append(k)
            gridrow['Profiles'] = []
            for k, v in row.items():
                if k in profiles:
                    del (row[k])
                    if str(v).strip().lower() not in ('', '0', 'false'):
                        gridrow['Profiles'].append(k)
            if len(gridrow['Analyses']) + len(gridrow['Profiles']) != nr_an:
                errors.append(
                    "Row %s: Number of analyses does not match provided value"
                    % row_nr)

            grid_rows.append(gridrow)

        self.setSampleData(grid_rows)

        if missing:
            self.error("SAMPLES: Missing expected fields: %s" %
                       ','.join(missing))

        for thing in errors:
            self.error(thing)

        if unexpected:
            self.error("Unexpected header fields: %s" % ','.join(unexpected))

    def get_batch_header_values(self):
        """Scrape the "Batch Header" values from the original input file
        """
        lines = self.getOriginalFile().data.splitlines()
        reader = csv.reader(lines)
        batch_headers = batch_data = []
        found_headers = False
        for row in reader:
            # Find headers
            if row[0].strip().lower() == 'batch header':
                found_headers = True
                batch_headers = [x.strip() for x in row][1:]
                continue
            if not found_headers:
                continue
            # Next row is batch data.
            batch_data = [x.strip() for x in row][1:]
            break
        values = dict(zip(batch_headers, batch_data))
        return values

    def create_or_reference_batch(self):
        """Save reference to batch, if existing batch specified
        Create new batch, if possible with specified values
        """
        client = self.aq_parent
        batch_headers = self.get_batch_header_values()
        if not batch_headers:
            return False
        # if the Batch's Title is specified and exists, no further
        # action is required. We will just set the Batch field to
        # use the existing object.
        batch_title = batch_headers.get('title', False)
        if batch_title:
            existing_batch = [
                x for x in client.objectValues('Batch')
                if x.title == batch_title
            ]
            if existing_batch:
                self.setBatch(existing_batch[0])
                return existing_batch[0]
        # If the batch title is specified but does not exist,
        # we will attempt to create the bach now.
        if 'title' in batch_headers:
            if 'id' in batch_headers:
                del (batch_headers['id'])
            if '' in batch_headers:
                del (batch_headers[''])
            batch = _createObjectByType('Batch', client, tmpID())
            batch.processForm()
            batch.edit(**batch_headers)
            self.setBatch(batch)

    def munge_field_value(self, schema, row_nr, fieldname, value):
        """Convert a spreadsheet value into a field value that fits in
        the corresponding schema field.
        - boolean: All values are true except '', 'false', or '0'.
        - reference: The title of an object in field.allowed_types;
            returns a UID or list of UIDs
        - datetime: returns a string value from ulocalized_time

        Tho this is only used during "Saving" of csv data into schema fields,
        it will flag 'validation' errors, as this is the only chance we will
        get to complain about these field values.

        """
        field = schema[fieldname]
        if field.type == 'boolean':
            value = str(value).strip().lower()
            value = '' if value in ['0', 'no', 'false', 'none'] else '1'
            return value
        if field.type == 'reference':
            value = str(value).strip()
            brains = self.lookup(field.allowed_types, Title=value)
            if not brains:
                brains = self.lookup(field.allowed_types, UID=value)
            if not brains:
                raise ValueError('Row %s: value is invalid (%s=%s)' %
                                 (row_nr, fieldname, value))
            if field.multiValued:
                return [b.UID for b in brains] if brains else []
            else:
                return brains[0].UID if brains else None
        if field.type == 'datetime':
            try:
                value = DateTime(value)
                return ulocalized_time(value,
                                       long_format=True,
                                       time_only=False,
                                       context=self)
            except:
                raise ValueError('Row %s: value is invalid (%s=%s)' %
                                 (row_nr, fieldname, value))
        return str(value)

    def validate_headers(self):
        """Validate headers fields from schema
        """

        pc = getToolByName(self, 'portal_catalog')
        pu = getToolByName(self, "plone_utils")

        client = self.aq_parent

        # Verify Client Name
        if self.getClient() != client:
            self.error("%s: value is invalid (%s)." %
                       ('Client name', self.getClient()))

        # Verify Client ID
        if self.getClientID() != client.getClientID():
            self.error("%s: value is invalid (%s)." %
                       ('Client ID', self.getClientID()))

        existing_arimports = pc(portal_type='ARImport',
                                review_state=['valid', 'imported'])
        # Verify Client Order Number
        for arimport in existing_arimports:
            if arimport.UID == self.UID() \
                    or not arimport.getClientOrderNumber():
                continue
            arimport = arimport.getObject()

            if arimport.getClientOrderNumber() == self.getClientOrderNumber():
                self.error('%s: already used by existing ARImport.' %
                           'ClientOrderNumber')
                break

        # Verify Client Reference
        for arimport in existing_arimports:
            if arimport.UID == self.UID() \
                    or not arimport.getClientReference():
                continue
            arimport = arimport.getObject()
            if arimport.getClientReference() == self.getClientReference():
                self.error('%s: already used by existing ARImport.' %
                           'ClientReference')
                break

        # getCCContacts has no value if object is not complete (eg during test)
        if self.getCCContacts():
            cc_contacts = self.getCCContacts()[0]
            contacts = [x for x in client.objectValues('Contact')]
            contact_names = [c.Title() for c in contacts]
            # validate Contact existence in this Client
            for k in ['CCNamesReport', 'CCNamesInvoice']:
                for val in cc_contacts[k]:
                    if val and val not in contact_names:
                        self.error('%s: value is invalid (%s)' % (k, val))
        else:
            cc_contacts = {
                'CCNamesReport': [],
                'CCEmailsReport': [],
                'CCNamesInvoice': [],
                'CCEmailsInvoice': []
            }
            # validate Contact existence in this Client
            for k in ['CCEmailsReport', 'CCEmailsInvoice']:
                for val in cc_contacts.get(k, []):
                    if val and not pu.validateSingleNormalizedEmailAddress(
                            val):
                        self.error('%s: value is invalid (%s)' % (k, val))

    def validate_samples(self):
        """Scan through the SampleData values and make sure
        that each one is correct
        """

        bsc = getToolByName(self, 'bika_setup_catalog')
        keywords = bsc.uniqueValuesFor('getKeyword')
        profiles = []
        for p in bsc(portal_type='AnalysisProfile'):
            p = p.getObject()
            profiles.append(p.Title())
            profiles.append(p.getProfileKey())

        row_nr = 0
        for gridrow in self.getSampleData():
            row_nr += 1

            # validate against sample and ar schemas
            for k, v in gridrow.items():
                if k in ['Analysis', 'Profiles']:
                    break
                if k in sample_schema:
                    try:
                        self.validate_against_schema(sample_schema, row_nr, k,
                                                     v)
                        continue
                    except ValueError as e:
                        self.error(e.message)
                        break
                if k in ar_schema:
                    try:
                        self.validate_against_schema(ar_schema, row_nr, k, v)
                    except ValueError as e:
                        self.error(e.message)

            an_cnt = 0
            for v in gridrow['Analyses']:
                if v and v not in keywords:
                    self.error("Row %s: value is invalid (%s=%s)" %
                               ('Analysis keyword', row_nr, v))
                else:
                    an_cnt += 1
            for v in gridrow['Profiles']:
                if v and v not in profiles:
                    self.error("Row %s: value is invalid (%s=%s)" %
                               ('Profile Title', row_nr, v))
                else:
                    an_cnt += 1
            if not an_cnt:
                self.error("Row %s: No valid analyses or profiles" % row_nr)

    def validate_against_schema(self, schema, row_nr, fieldname, value):
        """
        """
        field = schema[fieldname]
        if field.type == 'boolean':
            value = str(value).strip().lower()
            return value
        if field.type == 'reference':
            value = str(value).strip()
            if field.required and not value:
                raise ValueError("Row %s: %s field requires a value" %
                                 (row_nr, fieldname))
            if not value:
                return value
            brains = self.lookup(field.allowed_types, UID=value)
            if not brains:
                raise ValueError("Row %s: value is invalid (%s=%s)" %
                                 (row_nr, fieldname, value))
            if field.multiValued:
                return [b.UID for b in brains] if brains else []
            else:
                return brains[0].UID if brains else None
        if field.type == 'datetime':
            try:
                ulocalized_time(DateTime(value),
                                long_format=True,
                                time_only=False,
                                context=self)
            except:
                raise ValueError('Row %s: value is invalid (%s=%s)' %
                                 (row_nr, fieldname, value))
        return value

    def lookup(self, allowed_types, **kwargs):
        """Lookup an object of type (allowed_types).  kwargs is sent
        directly to the catalog.
        """
        at = getToolByName(self, 'archetype_tool')
        for portal_type in allowed_types:
            catalog = at.catalog_map.get(portal_type, [None])[0]
            catalog = getToolByName(self, catalog)
            kwargs['portal_type'] = portal_type
            brains = catalog(**kwargs)
            if brains:
                return brains

    def get_row_services(self, row):
        """Return a list of services which are referenced in Analyses.
        values may be UID, Title or Keyword.
        """
        bsc = getToolByName(self, 'bika_setup_catalog')
        services = set()
        for val in row.get('Analyses', []):
            brains = bsc(portal_type='AnalysisService', getKeyword=val)
            if not brains:
                brains = bsc(portal_type='AnalysisService', title=val)
            if not brains:
                brains = bsc(portal_type='AnalysisService', UID=val)
            if brains:
                services.add(brains[0].UID)
            else:
                self.error("Invalid analysis specified: %s" % val)
        return list(services)

    def get_row_profile_services(self, row):
        """Return a list of services which are referenced in profiles
        values may be UID, Title or ProfileKey.
        """
        bsc = getToolByName(self, 'bika_setup_catalog')
        services = set()
        profiles = [x.getObject() for x in bsc(portal_type='AnalysisProfile')]
        for val in row.get('Profiles', []):
            objects = [
                x for x in profiles
                if val in (x.getProfileKey(), x.UID(), x.Title())
            ]
            if objects:
                for service in objects[0].getService():
                    services.add(service.UID())
            else:
                self.error("Invalid profile specified: %s" % val)
        return list(services)

    def get_row_container(self, row):
        """Return a sample container
        """
        bsc = getToolByName(self, 'bika_setup_catalog')
        val = row.get('Container', False)
        if val:
            brains = bsc(portal_type='Container', UID=row['Container'])
            if brains:
                brains[0].getObject()
        return None

    def get_row_profiles(self, row):
        bsc = getToolByName(self, 'bika_setup_catalog')
        profiles = []
        for profile_title in row.get('Profiles', []):
            profile = bsc(portal_type='AnalysisProfile', title=profile_title)
            profiles.append(profile)
        return profiles

    def Vocabulary_SamplePoint(self):
        vocabulary = CatalogVocabulary(self)
        vocabulary.catalog = 'bika_setup_catalog'
        folders = [self.bika_setup.bika_samplepoints]
        if IClient.providedBy(self.aq_parent):
            folders.append(self.aq_parent)
        return vocabulary(allow_blank=True, portal_type='SamplePoint')

    def Vocabulary_SampleType(self):
        vocabulary = CatalogVocabulary(self)
        vocabulary.catalog = 'bika_setup_catalog'
        folders = [self.bika_setup.bika_sampletypes]
        if IClient.providedBy(self.aq_parent):
            folders.append(self.aq_parent)
        return vocabulary(allow_blank=True, portal_type='SampleType')

    def Vocabulary_Container(self):
        vocabulary = CatalogVocabulary(self)
        vocabulary.catalog = 'bika_setup_catalog'
        return vocabulary(allow_blank=True, portal_type='Container')

    def Vocabulary_Priority(self):
        vocabulary = CatalogVocabulary(self)
        vocabulary.catalog = 'bika_setup_catalog'
        return vocabulary(allow_blank=True, portal_type='ARPriority')

    def error(self, msg):
        errors = list(self.getErrors())
        errors.append(msg)
        self.setErrors(errors)
Example #21
0
class FGStringField(BaseFormField):
    """ A string entry field """

    security = ClassSecurityInfo()

    schema = BaseFieldSchemaStringDefault.copy() + Schema((
        maxlengthField,
        sizeField,
        StringField(
            'fgStringValidator',
            vocabulary='stringValidatorsDL',
            enforceVocabulary=1,
            widget=SelectionWidget(
                label=_(u'label_fgstringvalidator_text', default=u'Validator'),
                description=_(
                    u'help_fgstringvalidator_text',
                    default=u"""Tests input against simple string patterns."""
                ),
            ),
        ),
    ))

    # hide references & discussion
    finalizeFieldSchema(schema, folderish=True, moveDiscussion=False)

    # Standard content type setup
    portal_type = meta_type = 'FormStringField'
    archetype_name = 'String Field'
    content_icon = 'StringField.gif'
    typeDescription = 'A string field'

    def __init__(self, oid, **kwargs):
        """ initialize class """

        BaseFormField.__init__(self, oid, **kwargs)

        # set a preconfigured field as an instance attribute
        self.fgField = StringField(
            'fg_string_field',
            searchable=0,
            required=0,
            write_permission=View,
            validators=('isNotTooLong', ),
        )

    def stringValidatorsDL(self):
        """ return a display list of string validators.
        """

        fgt = getToolByName(self, 'formgen_tool')
        return fgt.getStringValidatorsDL()

    def setFgStringValidator(self, value, **kw):
        """ set simple validator """

        fgt = getToolByName(self, 'formgen_tool')

        if value and (value != 'vocabulary_none_text'):
            fgtid = fgt.stringValidators[value].get('id')
            if fgtid:
                self.fgField.validators = ('isNotTooLong', fgtid)
        else:
            self.fgField.validators = ('isNotTooLong', )
        self.fgField._validationLayer()

        self.fgStringValidator = value
Example #22
0
class ARTemplate(BaseContent):
    security = ClassSecurityInfo()
    schema = schema
    displayContentsTab = False

    _at_rename_after_creation = True
    def _renameAfterCreation(self, check_auto_id=False):
        from bika.lims.idserver import renameAfterCreation
        renameAfterCreation(self)

    security.declarePublic('AnalysisProfiles')
    def AnalysisProfiles(self, instance=None):
        instance = instance or self
        bsc = getToolByName(instance, 'bika_setup_catalog')
        items = []
        for p in bsc(portal_type='AnalysisProfile',
                      inactive_state='active',
                      sort_on = 'sortable_title'):
            p = p.getObject()
            title = p.Title()
            items.append((p.UID(), title))
        items = [['','']] + list(items)
        return DisplayList(items)

    def setSampleType(self, value, **kw):
        """ convert object title to UID
        """
        uid = None
        if value:
            bsc = getToolByName(self, 'bika_setup_catalog')
            items = bsc(portal_type = 'SampleType', title = value)
            if not items:
                msg = _("${sampletype} is not a valid sample type",
                        mapping={'sampletype':value})
                self.plone_utils.addPortalMessage(msg, 'error')
                self.REQUEST.response.redirect(self.absolute_url())
                return False
            uid = items[0].UID
        return self.Schema()['SampleType'].set(self, uid)

    def getSampleType(self, **kw):
        """ retrieve referenced object and return it's title
        """
        item = self.Schema()['SampleType'].get(self)
        return item and item.Title() or ''

    def setSamplePoint(self, value, **kw):
        """ convert object title to UID
        """
        uid = None
        if value:
            # Strip "Lab: " from sample point title
            value = value.replace("%s: " % _("Lab"), '')
            bsc = getToolByName(self, 'bika_setup_catalog')
            items = bsc(portal_type = 'SamplePoint', title = value)
            if not items:
                msg = _("${samplepoint} is not a valid sample point",
                        mapping={'samplepoint':value})
                self.plone_utils.addPortalMessage(msg, 'error')
                self.REQUEST.response.redirect(self.absolute_url())
                return False
            uid = items[0].UID
        return self.Schema()['SamplePoint'].set(self, uid)

    def getSamplePoint(self, **kw):
        """ retrieve referenced object and return it's title
        """
        item = self.Schema()['SamplePoint'].get(self)
        return item and item.Title() or ''
Example #23
0
class FGSelectionField(BaseFormField):
    """ Selection Field (radio buttons or select) """

    security = ClassSecurityInfo()

    schema = BaseFieldSchemaStringDefault.copy() + Schema((
        vocabularyField,
        vocabularyOverrideField,
        StringField(
            'fgFormat',
            searchable=0,
            required=0,
            default='flex',
            enforceVocabulary=1,
            vocabulary='formatVocabDL',
            widget=SelectionWidget(
                label=_(u'label_fgformat_text',
                        default=u'Presentation Widget'),
                description=_(u'help_fgformat_text', default=u''),
            ),
        ),
    ))

    # 'hidden' isn't really useful for a selection field.
    # Just use a hidden string field if you really need this.
    del schema['hidden']
    # 'serverSide' is not really useful for this field.
    del schema['serverSide']

    # hide references & discussion
    finalizeFieldSchema(schema, folderish=True, moveDiscussion=False)

    # Standard content type setup
    portal_type = meta_type = 'FormSelectionField'
    archetype_name = 'Selection Field'
    content_icon = 'ListField.gif'
    typeDescription = 'A selection field'

    def __init__(self, oid, **kwargs):
        """ initialize class """

        BaseFormField.__init__(self, oid, **kwargs)

        # set a preconfigured field as an instance attribute
        self.fgField = StringVocabularyField(
            'fg_selection_field',
            searchable=0,
            required=0,
            widget=SelectionWidget(),
            vocabulary='_get_selection_vocab',
            enforceVocabulary=1,
            write_permission=View,
        )

    security.declareProtected(ModifyPortalContent, 'setFgFormat')

    def setFgFormat(self, value, **kw):
        """ set selection format """

        self.fgField.widget.format = value
        self.fgFormat = value

    def formatVocabDL(self):
        """ returns vocabulary for fgFormat """

        return DisplayList((
            ('flex',
             _(u'vocabulary_flex_text',
               u'Flexible (radio for short, select for longer)')),
            ('select', _(u'vocabulary_selection_text', u'Selection list')),
            ('radio', _(u'vocabulary_radio_text', u'Radio buttons')),
        ))

    def htmlValue(self, REQUEST):
        """ Return value instead of key """

        utils = getToolByName(self, 'plone_utils')
        charset = utils.getSiteEncoding()

        value = REQUEST.form.get(self.__name__, '')

        # note that vocabulary items are in unicode;
        # so, we must decode before lookup
        vu = value.decode(charset)

        vocabulary = self.fgField.Vocabulary(self)
        v = vocabulary.getValue(vu) or vu

        return cgi.escape(v.encode(charset))
Example #24
0
class InvoiceBatch(BaseFolder):
    """ Container for Invoice instances """
    implements(IInvoiceBatch)
    security = ClassSecurityInfo()
    displayContentsTab = False
    schema = schema

    security.declareProtected(ManageInvoices, 'invoices')

    def invoices(self):
        return self.objectValues('Invoice')

    # security.declareProtected(PostInvoiceBatch, 'post')
    # def post(self, REQUEST = None):
    #     """ Post invoices
    #     """
    #     map (lambda e: e._post(), self.invoices())
    #     if REQUEST:
    #         REQUEST.RESPONSE.redirect('invoicebatch_invoices')

    security.declareProtected(ManageInvoices, 'createInvoice')

    def createInvoice(self, client_uid, items):
        """ Creates and invoice for a client and a set of items
        """
        invoice_id = self.generateUniqueId('Invoice')
        invoice = _createObjectByType("Invoice", self, invoice_id)
        invoice.edit(
            Client=client_uid,
            InvoiceDate=DateTime(),
        )
        invoice.processForm()
        invoice.invoice_lineitems = []
        for item in items:
            lineitem = InvoiceLineItem()
            if item.portal_type == 'AnalysisRequest':
                lineitem['ItemDate'] = item.getDatePublished()
                lineitem['OrderNumber'] = item.getRequestID()
                lineitem['AnalysisRequest'] = item
                description = get_invoice_item_description(item)
                lineitem['ItemDescription'] = description
            elif item.portal_type == 'SupplyOrder':
                lineitem['ItemDate'] = item.getDateDispatched()
                lineitem['OrderNumber'] = item.getOrderNumber()
                description = get_invoice_item_description(item)
                lineitem['ItemDescription'] = description
            lineitem['Subtotal'] = item.getSubtotal()
            lineitem['VATAmount'] = item.getVATAmount()
            lineitem['Total'] = item.getTotal()
            invoice.invoice_lineitems.append(lineitem)
        invoice.reindexObject()
        return invoice

    security.declarePublic('current_date')

    def current_date(self):
        """ return current date """
        return DateTime()

    def guard_cancel_transition(self):
        if not isBasicTransitionAllowed(self):
            return False
        return True

    def guard_reinstate_transition(self):
        if not isBasicTransitionAllowed(self):
            return False
        return True
Example #25
0
class FGTextField(BaseFormField):
    """ Text (textarea) field """

    security = ClassSecurityInfo()

    schema = BaseFieldSchemaTextDefault.copy()
    schema += Schema((BooleanField(
        'validateNoLinkSpam',
        searchable=0,
        required=0,
        default=False,
        widget=BooleanWidget(
            label=_(u'label_validate_link_spam_text',
                    default=u"Reject Text with Links?"),
            description=_(u'help_validate_link_spam_text',
                          default=u"""Useful for stopping spam"""),
        ),
    ), ))

    # Standard content type setup
    portal_type = meta_type = 'FormTextField'
    archetype_name = 'Text Field'
    content_icon = 'TextAreaField.gif'
    typeDescription = 'A text area field'

    def __init__(self, oid, **kwargs):
        """ initialize class """

        BaseFormField.__init__(self, oid, **kwargs)

        # set a preconfigured field as an instance attribute
        self.fgField = PlainTextField(
            'fg_text_field',
            searchable=0,
            required=0,
            write_permission=View,
            validators=(
                'isNotTooLong',
                'isNotLinkSpam',
            ),
            default_content_type='text/plain',
            allowable_content_types=('text/plain', ),
            widget=TextAreaWidget(maxlength=0, ),
            validate_no_link_spam=0,
        )

    security.declareProtected(View, 'isBinary')

    def isBinary(self, key):
        return False

    security.declareProtected(View, 'getContentType')

    def getContentType(self, key=None):
        return 'text/plain'

    def setValidateNoLinkSpam(self, value):
        """
        for BBB, to make sure the validator gets enabled on legacy text fields
        """
        self.fgField.validators = ('isNotTooLong', 'isNotLinkSpam')
        self.fgField._validationLayer()

        self.fgField.validate_no_link_spam = value

    def getValidateNoLinkSpam(self):
        return getattr(self.fgField, 'validate_no_link_spam', 0)
Example #26
0
class Source(SimpleItem):
    """ Source """

    meta_type = SOURCE_METATYPE
    product_name = NAAYATHESAURUS_PRODUCT_NAME
    icon = 'misc_/NaayaThesaurus/source.gif'

    manage_options = (
        {'label':'Properties',  'action':'properties_html'},
        {'label':'Management',      'action':'sources_html'},
        {'label':'Statistics',      'action':'statistics_html'},
        {'label':'Undo',        'action':'manage_UndoForm'},)

    security = ClassSecurityInfo()

    def __init__(self, id, title):
        """ constructor """
        self.id =       id
        self.title =    title
        self.sources =  PersistentMapping()


    #basic properties
    security.declareProtected(view_management_screens, 'manageBasicProperties')
    def manageBasicProperties(self, title='', REQUEST=None):
        """ manage basic properties for Source """
        self.title = title
        self._p_changed = 1
        if REQUEST:
            self.setSessionInfoTrans('Saved changes.')
            return REQUEST.RESPONSE.redirect('properties_html')


    #sources management
    def __add_source(self, source_id, source_name):
        #create a new item
        item = SourceItem(source_id, source_name)
        self.sources[source_id] = item
        self.catalog.CatalogObject(item)

    def __update_source(self, source_id, old_source_id, source_name):
        #modify an item
        item = self.sources.get(old_source_id)
        if item is not None:
            self.__delete_source(old_source_id)
        self.__add_source(source_id, source_name)

    def __delete_source(self, ids):
        #delete 1 or more items
        ids = th_utils().utConvertToList(ids)
        collection = self.get_sources()

        for id in ids:
            self.catalog.UncatalogObject(collection[id])
            del collection[id]


    #source constrints
    security.declareProtected(view_management_screens, 'getIdsList')
    def getIdsList(self, ids, all=0):
        """ """
        if all: return self.sources.keys()
        return th_utils().getIdsList(ids)

    #sources getters
    def get_sources(self):
        #get all sources
        return self.sources

    def get_sources_sorted(self):
        #get all sources sorted
        return th_utils().utSortObjsListByAttr(self.sources.values(), 'source_id', 0)

    def get_source_by_id(self, id):
        #get an item
        try:    return self.sources[id]
        except: return None

    def get_source_item_data(self, id):
        #get an item data
        item = self.get_source_by_id(id)
        if item is not None: 
            return ['update', item.source_id, item.source_name]
        else:
            return ['add', '', '']


    #sources api
    security.declareProtected(view_management_screens, 'manage_add_source')
    def manage_add_source(self, source_id='', source_name='', REQUEST=None):
        """ manage sources """
        if not source_id: source_id = th_utils().utGenRandomId()
        self.__add_source(source_id, source_name)
        if REQUEST:
            self.setSessionInfoTrans('Record added.')
            REQUEST.RESPONSE.redirect('sources_html')

    security.declareProtected(view_management_screens, 'manage_update_source')
    def manage_update_source(self, source_id='', old_source_id='', source_name='', REQUEST=None):
        """ update source """
        self.__update_source(source_id, old_source_id, source_name)
        if REQUEST:
            self.setSessionInfoTrans('Record updated.')
            REQUEST.RESPONSE.redirect('sources_html')

    security.declareProtected(view_management_screens, 'manage_delete_sources')
    def manage_delete_sources(self, ids=[], delete_all='', REQUEST=None):
        """ delete sources """
        if delete_all:  ids = self.getIdsList(ids, 1)
        else:           ids = self.getIdsList(ids)
        self.__delete_source(ids)
        if REQUEST:
            self.setSessionInfoTrans('Selected records deleted.')
            REQUEST.RESPONSE.redirect('sources_html')

    security.declareProtected(view_management_screens, 'getSourceItemData')
    def getSourceItemData(self):
        """ return a source based on its ID """
        return self.get_source_item_data(self.REQUEST.get('source_id', None))


    #statistics
    def getAllSrc(self):
        query = [('meta_type',SOURCE_ITEM_METATYPE)]
        return self.catalog.searchCatalog(query)

    def getSrcNumber(self):
        return len(self.getAllSrc())


    #management tabs
    security.declareProtected(view_management_screens, 'properties_html')
    properties_html =       PageTemplateFile("%s/zpt/Source/properties" % NAAYATHESAURUS_PATH, globals())

    security.declareProtected(view_management_screens, 'sources_html')
    sources_html =        PageTemplateFile("%s/zpt/Source/sources" % NAAYATHESAURUS_PATH, globals())

    security.declareProtected(view_management_screens, 'statistics_html')
    statistics_html =   PageTemplateFile("%s/zpt/Source/statistics" % NAAYATHESAURUS_PATH, globals())
Example #27
0
File: Field.py Project: ra2003/erp5
class ZMIField(
        OFS.SimpleItem.Item,
        Acquisition.Implicit,
        Persistent,
        Field,
):
    """Base class for a field implemented as a Python (file) product.
    """
    security = ClassSecurityInfo()

    security.declareObjectProtected('View')

    # the various tabs of a field
    manage_options = (
        {
            'label': 'Edit',
            'action': 'manage_main',
            'help': ('Formulator', 'fieldEdit.txt')
        },
        {
            'label': 'TALES',
            'action': 'manage_talesForm',
            'help': ('Formulator', 'fieldTales.txt')
        },
        {
            'label': 'Override',
            'action': 'manage_overrideForm',
            'help': ('Formulator', 'fieldOverride.txt')
        },
        {
            'label': 'Messages',
            'action': 'manage_messagesForm',
            'help': ('Formulator', 'fieldMessages.txt')
        },
        {
            'label': 'Test',
            'action': 'fieldTest',
            'help': ('Formulator', 'fieldTest.txt')
        },
    ) + OFS.SimpleItem.SimpleItem.manage_options

    security.declareProtected('View', 'title')

    def title(self):
        """The title of this field."""
        return self.get_value('title')

    # display edit screen as main management screen
    security.declareProtected('View management screens', 'manage_main')
    manage_main = DTMLFile('dtml/fieldEdit', globals())

    security.declareProtected('Change Formulator Fields', 'manage_edit')

    def manage_edit(self, REQUEST):
        """Submit Field edit form.
        """
        try:
            # validate the form and get results
            result = self.form.validate(REQUEST)
        except ValidationError, err:
            if REQUEST:
                message = "Error: %s - %s" % (err.field.get_value('title'),
                                              err.error_text)
                return self.manage_main(self,
                                        REQUEST,
                                        manage_tabs_message=message)
            else:
                raise

        self._edit(result)

        if REQUEST:
            message = "Content changed."
            return self.manage_main(self, REQUEST, manage_tabs_message=message)
Example #28
0
class FGRichTextField(BaseFormField):
    """ Rich-text (visual editor) field """

    from Products.ATContentTypes.config import HAS_MX_TIDY

    security = ClassSecurityInfo()

    schema = BaseFieldSchemaRichTextDefault.copy()

    # 'hidden' isn't really useful for an RT field.
    # Just use a hidden string field if you really need this.
    del schema['hidden']
    # 'serverSide' is not really useful for this field.
    del schema['serverSide']

    if HAS_MX_TIDY:
        schema = schema + Schema((StringField(
            'fgStringValidator',
            vocabulary='htmlValidatorsDL',
            enforceVocabulary=1,
            default='isTidyHtmlWithCleanup',
            widget=SelectionWidget(
                label=_(u'label_fgstringvalidator_text', default=u'Validator'),
                description=_(
                    u'help_fgrtvalidator_text',
                    default=u"""Input tests using HTMLTidy (if installed)."""),
            ),
        ), ))

    # Standard content type setup
    portal_type = meta_type = 'FormRichTextField'
    archetype_name = 'RichText Field'
    content_icon = 'RichTextField.gif'
    typeDescription = 'A rich text area field'

    def __init__(self, oid, **kwargs):
        """ initialize class """

        BaseFormField.__init__(self, oid, **kwargs)

        # set a preconfigured field as an instance attribute
        self.fgField = HtmlTextField(
            'fg_text_field',
            searchable=0,
            required=0,
            write_permission=View,
            validators=(
                'isNotTooLong',
                'isTidyHtmlWithCleanup',
            ),
            default_content_type='text/html',
            default_output_type='text/x-html-safe',
            allowable_content_types=zconf.ATDocument.allowed_content_types,
            widget=RichWidget(allow_file_upload=False, ),
        )

    security.declareProtected(View, 'isBinary')

    def isBinary(self, key):
        return False

    security.declareProtected(View, 'getContentType')

    def getContentType(self, key=None):
        return 'text/html'

    def htmlValidatorsDL(self):
        """ return a display list of string validators.
            this is a hack for 118n
        """

        return DisplayList((
            ('', _(u'vocabulary_none_text', u'None')),
            ('isTidyHtml',
             _(u'vocabulary_istidyhtml_text',
               u'Is Tidy HTML (fails on errors and warnings)')),
            ('isTidyHtmlWithCleanup',
             _(u'vocabulary_istidyhtmlwithcleanup_text',
               u'Tidy HTML With Cleanup (fails on errors, cleans up rest)')),
        ))

    def htmlValue(self, REQUEST):
        """ override, as this field is already html.
        """

        return REQUEST.form.get(self.__name__, 'No Input')
Example #29
0
class UndoTool(UniqueObject, SimpleItem, ActionProviderBase):
    """ This tool is used to undo changes.
    """

    implements(IUndoTool)
    __implements__ = (z2IUndoTool, ActionProviderBase.__implements__)

    id = 'portal_undo'
    meta_type = 'CMF Undo Tool'

    security = ClassSecurityInfo()

    manage_options = (ActionProviderBase.manage_options +
                      SimpleItem.manage_options + ({
                          'label': 'Overview',
                          'action': 'manage_overview'
                      }, ))
    #
    #   ZMI methods
    #
    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = DTMLFile('explainUndoTool', _dtmldir)

    #
    #   'portal_undo' interface methods
    #
    security.declareProtected(ListUndoableChanges,
                              'listUndoableTransactionsFor')

    def listUndoableTransactionsFor(self,
                                    object,
                                    first_transaction=None,
                                    last_transaction=None,
                                    PrincipiaUndoBatchSize=None):
        '''Lists all transaction IDs the user is allowed to undo.
        '''
        # arg list for undoable_transactions() changed in Zope 2.2.
        portal = queryUtility(ISiteRoot)
        if site is None:
            # fallback
            portal = self.aq_inner.aq_parent

        transactions = portal.undoable_transactions(
            first_transaction=first_transaction,
            last_transaction=last_transaction,
            PrincipiaUndoBatchSize=PrincipiaUndoBatchSize)
        for t in transactions:
            # Ensure transaction ids don't have embedded LF.
            t['id'] = t['id'].replace('\n', '')
        if not _checkPermission(ManagePortal, portal):
            # Filter out transactions done by other members of the portal.
            user_id = _getAuthenticatedUser(self).getId()
            transactions = filter(lambda record, user_id=user_id: record[
                'user_name'].split()[-1] == user_id,
                                  transactions)
        return transactions

    security.declarePublic('undo')

    def undo(self, object, transaction_info):
        """
            Undo the list of transactions passed in 'transaction_info',
            first verifying that the current user is allowed to undo them.
        """
        # Belt and suspenders:  make sure that the user is actually
        # allowed to undo the transation(s) in transaction_info.

        xids = {}  # set of allowed transaction IDs

        allowed = self.listUndoableTransactionsFor(object)

        for xid in map(lambda x: x['id'], allowed):
            xids[xid] = 1

        if type(transaction_info) == type(''):
            transaction_info = [transaction_info]

        for tinfo in transaction_info:
            if not xids.get(tinfo, None):
                raise AccessControl_Unauthorized

        object.manage_undo_transactions(transaction_info)
class Transformation(MappedValue, VariatedMixin, Amount, AmountGeneratorMixin):
    """
  Build of material - contains a list of transformed resources

  Use of default_resource... (to define the variation range,
  to ...)

  XXX Transformation works only for a maximum of 3 variation base category...
  Matrixbox must be rewritten for a clean implementation of n base category
  """
    meta_type = 'ERP5 Transformation'
    portal_type = 'Transformation'

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

    # Declarative properties
    property_sheets = (
        PropertySheet.Comment,
        PropertySheet.Version
        #, PropertySheet.Resource
        ,
        PropertySheet.TransformedResource,
        PropertySheet.Transformation,
        PropertySheet.Order,
        PropertySheet.Task)

    def getAggregatedAmountList(self, *args, **kw):
        """
    """
        getAggregatedAmountList = \
          super(Transformation, self).getAggregatedAmountList
        # Detect old use of getAggregatedAmountList
        if 'context' in kw:
            context = kw.pop('context')
        else:
            if not args or isinstance(args[0], (list, tuple)):
                return getAggregatedAmountList(*args, **kw)
            context, args = args[0], args[1:]
        warn(
            "The API of getAggregatedAmountList has changed:"
            " it must be called on the context instead of passing"
            " the context as first parameter", DeprecationWarning)
        # XXX add a 'transformation_amount_generator' group type
        kw['amount_generator_type_list'] = ('Transformation',
                                            'Transformed Resource',
                                            'Transformation Operation',
                                            'Assorted Resource')
        if context is not None:
            context = (context, )
        return getAggregatedAmountList(context, *args, **kw)

    def getQuantity(self, default=None):
        # Used for amount generation
        # (Transformation is defined for 1 unit of target resource)
        return 1.

    # Predicate Value implementation
    #   asPredicate takes into account the resource
    # XXX-JPS not Impl.

    # Mapped Value implementation
    #  Transformation itself provides no properties or categories
    def getMappedValuePropertyList(self):
        return ()

    def getMappedValueBaseCategoryList(self, *args, **kw):
        return ()

    # IVariationRange and IVariated Implementation
    security.declareProtected(Permissions.AccessContentsInformation,
                              'updateVariationCategoryList')

    def updateVariationCategoryList(self):
        """
    Check if variation category list of the resource has changed and update
    transformation and transformation line
    """
        self.setVariationBaseCategoryList(self.getVariationBaseCategoryList())
        transformation_line_list = self.contentValues()
        for transformation_line in transformation_line_list:
            transformation_line.updateVariationCategoryList()

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getVariationRangeBaseCategoryList')

    def getVariationRangeBaseCategoryList(self):
        """
    Returns possible variation base_category ids of the
    default resource which can be used as variation axis
    in the transformation.
    """
        resource = self.getResourceValue()
        if resource is not None:
            result = resource.getVariationBaseCategoryList()
        else:
            # XXX result = self.getBaseCategoryIds()
            # Why calling this method ?
            # Get a global variable which define a list of variation base category
            result = self.getPortalVariationBaseCategoryList()
        return result

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getVariationRangeBaseCategoryItemList')

    def getVariationRangeBaseCategoryItemList(self,
                                              display_id='getTitleOrId',
                                              **kw):
        """
    Returns possible variations of the transformation
    as a list of tuples (id, title). This is mostly
    useful in ERP5Form instances to generate selection
    menus.
    """
        return self.portal_categories.getItemList(
            self.getVariationRangeBaseCategoryList(),
            display_id=display_id,
            **kw)

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getVariationRangeCategoryItemList')

    def getVariationRangeCategoryItemList(self,
                                          base_category_list=(),
                                          omit_individual_variation=0,
                                          display_base_category=1,
                                          **kw):
        """
    Returns possible variation category values for the
    transformation according to the default resource.
    Possible category values are provided as a list of
    tuples (id, title). This is mostly
    useful in ERP5Form instances to generate selection
    menus.
    User may want to define generic transformation without
    any defined resource.
    """
        if base_category_list is ():
            base_category_list = self.getVariationBaseCategoryList()

        resource = self.getResourceValue()
        if resource is not None:
            result = resource.getVariationCategoryItemList(
                base_category_list=base_category_list,
                omit_individual_variation=omit_individual_variation,
                display_base_category=display_base_category,
                **kw)
        else:
            # No resource is define on transformation.
            # We want to display content of base categories
            result = self.portal_categories.getCategoryChildTitleItemList(
                base_category_list, base=1, display_none_category=0)
        return result

    security.declareProtected(Permissions.AccessContentsInformation,
                              'setVariationBaseCategoryList')

    def setVariationBaseCategoryList(self, value):
        """
    Define the possible base categories and reindex object
    """
        self._setVariationBaseCategoryList(value)
        self.reindexObject()

    security.declareProtected(Permissions.AccessContentsInformation,
                              'getVariationCategoryItemList')

    def getVariationCategoryItemList(self,
                                     base_category_list=(),
                                     base=1,
                                     display_id='title',
                                     current_category=None,
                                     **kw):
        """
    Returns the list of possible variations
    XXX Copied and modified from VariatedMixin
    Result is left display.
    """
        variation_category_item_list = []
        if base_category_list == ():
            base_category_list = self.getVariationBaseCategoryList()

        category_renderer = Renderer(is_right_display=0,
                                     display_none_category=0,
                                     base=base,
                                     current_category=current_category,
                                     display_id='logical_path',
                                     **kw)

        for base_category in base_category_list:
            variation_category_list = self.getVariationCategoryList(
                base_category_list=[base_category])

            category_list = []
            object_list = []
            for variation_category in variation_category_list:
                resource = self.portal_categories.resolveCategory(
                    variation_category)
                if resource.getPortalType() == 'Category':
                    category_list.append(resource)
                else:
                    object_list.append(resource)

            variation_category_item_list.extend(category_renderer.\
                                                    render(category_list))

            variation_category_item_list.extend(Renderer(
                                     is_right_display=0,
                                     base_category=base_category,
                                     display_none_category=0, base=base,
                                     current_category=current_category,
                                     display_id=display_id,**kw).\
                                                       render(object_list))
        return variation_category_item_list