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)
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
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)
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')
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)
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'
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')])
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
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
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
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)
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
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
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', ), )
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)
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
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 ''
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))
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
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)
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())
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)
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')
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