def new_custom_domain_model(type_key, domain_interface, archetype_key): domain_model_name = naming.model_name(type_key) assert archetype_key, \ "Custom descriptor %r does not specify an archetype" % (type_key) archetype = getattr(MODEL_MODULE, naming.model_name(archetype_key)) # AttributeError # !+ assert archetype constraints domain_model = type(domain_model_name, (archetype, ), { "__module__": MODEL_MODULE.__name__, "extended_properties": [], }) # apply domain_interface classImplements(domain_model, domain_interface) # set on MODEL_MODULE (register on type_info downstream) setattr(MODEL_MODULE, domain_model_name, domain_model) # db map custom domain class from sqlalchemy.orm import mapper mapper( domain_model, inherits=archetype, polymorphic_on=utils.get_local_table(archetype).c.type, polymorphic_identity= type_key, #naming.polymorphic_identity(domain_model), ) log.info("new_custom_domain_model [%s] %s.%s" % (type_key, MODEL_MODULE.__name__, domain_model_name)) return domain_model
def new_custom_domain_model(type_key, domain_interface, archetype_key): domain_model_name = naming.model_name(type_key) assert archetype_key, \ "Custom descriptor %r does not specify an archetype" % (type_key) archetype = getattr(MODEL_MODULE, naming.model_name(archetype_key)) # AttributeError # !+ assert archetype constraints domain_model = type(domain_model_name, (archetype,), { "__module__": MODEL_MODULE.__name__, "extended_properties": [], } ) # apply domain_interface classImplements(domain_model, domain_interface) # set on MODEL_MODULE (register on type_info downstream) setattr(MODEL_MODULE, domain_model_name, domain_model) # db map custom domain class from sqlalchemy.orm import mapper mapper(domain_model, inherits=archetype, polymorphic_on=utils.get_local_table(archetype).c.type, polymorphic_identity=type_key, #naming.polymorphic_identity(domain_model), ) log.info("new_custom_domain_model [%s] %s.%s" % ( type_key, MODEL_MODULE.__name__, domain_model_name)) return domain_model
def register_new_custom_type(type_key, workflow_key, archetype_key): """Retrieve (create if needed) a domain interface and model for type_key, and register as new entry on TYPE_REGISTER. """ # generate custom domain interface domain_iface_name = naming.model_interface_name(type_key) try: domain_iface = resolve("%s.%s" % (INTERFACE_MODULE.__name__, domain_iface_name)) log.warn("Custom interface ALREADY EXISTS: %s" % (domain_iface)) except ImportError: domain_iface = new_custom_domain_interface(type_key, domain_iface_name) # generate custom domain_model domain_model_name = naming.model_name(type_key) try: domain_model = resolve("%s.%s" % (MODEL_MODULE.__name__, domain_model_name)) log.warn("Custom domain model ALREADY EXISTS: %s" % (domain_model)) except ImportError: domain_model = new_custom_domain_model(type_key, domain_iface, archetype_key) # type_info entry ti = TI(workflow_key, domain_iface, domain_model) ti.custom = True TYPE_REGISTRY.append((type_key, ti)) log.info("Registered custom type [%s]: %s" % (archetype_key, type_key)) return type_key, ti
def output_features_by_type(): """ provides a list of features per type """ from bungeni.capi import capi from zope.dottedname.resolve import resolve from bungeni.alchemist.catalyst import MODEL_MODULE from bungeni.utils import naming # get a list of available types in a list li_available_types = [] for type_key, ti in capi.iter_type_info(): li_available_types.append(type_key) li_features = [] li_features.append("<featuresByType>") for type_key, ti in capi.iter_type_info(): obj = resolve("%s.%s" % (MODEL_MODULE.__name__, naming.model_name(type_key))) if len(obj.available_dynamic_features) > 0: li_features.append(' <features for="%s">' % type_key) for dyn_feature in obj.available_dynamic_features: workflow = False # check if feature is a type if dyn_feature in li_available_types: # feature is a type, so it has a workflow workflow = True li_features.append( ' <feature name="%(name)s" workflow="%(wf)s" />' % {"name": dyn_feature, "wf": workflow} ) li_features.append(" </features>") li_features.append("</featuresByType>") return "\n".join(li_features).encode("utf-8")
def retrieve_domain_model(type_key): """Infer and retrieve the target domain model class from the type key. Raise AttributeError if not defined on model module. !+ mv to catalyst.utils? """ return resolve("%s.%s" % (MODEL_MODULE.__name__, naming.model_name(type_key)))
def register_new_custom_type(type_key, workflow_key, custom_archetype_key, sys_archetype_key): """Retrieve (create if needed) a domain interface and model for type_key, and register as new entry on TYPE_REGISTER. """ archetype_model = resolve( "%s.%s" % (MODEL_MODULE.__name__, naming.model_name(custom_archetype_key))) # validate that custom archetype uses correct system archetype if custom_archetype_key != sys_archetype_key: sys_archetype_model = resolve( "%s.%s" % (MODEL_MODULE.__name__, naming.model_name(sys_archetype_key))) assert issubclass(archetype_model, sys_archetype_model), \ "Custom archetype %r for type %r is not a sub-type of %r." % ( custom_archetype_key, type_key, sys_archetype_key) # generate custom domain interface domain_iface_name = naming.model_interface_name(type_key) try: domain_iface = resolve("%s.%s" % (INTERFACE_MODULE.__name__, domain_iface_name)) log.warn("Custom interface ALREADY EXISTS: %s" % (domain_iface)) except ImportError: domain_iface = new_custom_domain_interface(type_key, domain_iface_name) # generate custom domain_model domain_model_name = naming.model_name(type_key) try: domain_model = resolve("%s.%s" % (MODEL_MODULE.__name__, domain_model_name)) log.warn("Custom domain model ALREADY EXISTS: %s" % (domain_model)) except ImportError: domain_model = new_custom_domain_model(type_key, domain_iface, custom_archetype_key) # type_info entry ti = TI(workflow_key, domain_iface, domain_model, archetype_model) ti.custom = True TYPE_REGISTRY.append((type_key, ti)) log.info("Registered custom type [%s]: %s" % (custom_archetype_key, type_key)) return type_key, ti
def output_features(): """ provides a list of features """ from bungeni.capi import capi from zope.dottedname.resolve import resolve from bungeni.alchemist.catalyst import MODEL_MODULE from bungeni.utils import naming from bungeni.feature.feature import get_feature_cls # get a list of available types in a list li_available_types = [] for type_key, ti in capi.iter_type_info(): li_available_types.append(type_key) li_features = [] li_features.append("<features>") li_unique_features = [] for type_key, ti in capi.iter_type_info(): obj = resolve("%s.%s" % (MODEL_MODULE.__name__, naming.model_name(type_key))) if len(obj.available_dynamic_features) > 0: for dyn_feature in obj.available_dynamic_features: # check if feature is a type li_unique_features.append('%s' % dyn_feature) li_unique_features = list(set(li_unique_features)) for f in li_unique_features: fcls = get_feature_cls(f) li_features.append(' <feature name="%s" >' % f) if fcls.depends_on: li_features.append(' <depends>') for depends in fcls.depends_on: li_features.append(' <depend>%s</depend>' % depends) li_features.append(' </depends>') if fcls.feature_parameters is not None: if len(fcls.feature_parameters) > 0: li_features.append(' <params>') for key, val in fcls.feature_parameters.iteritems(): li_features.append(' <param name="%s">' % key) for key2, val2 in val.iteritems(): li_features.append(' <%(name)s>%(value)s</%(name)s>' % { "name": key2, "value": val2 }) li_features.append(' </param>') li_features.append(' </params>') li_features.append(' </feature>') li_features.append("</features>") print "\n".join(li_features).encode("utf-8") return "\n".join(li_features).encode("utf-8")
def get_feature_cls(feature_name): """Get the Feature implementation class by the feature name. All retrieval of Feature classes MUST be done vis this utility. """ feature_cls_name = naming.model_name(feature_name) try: return globals()[feature_cls_name] except KeyError: feature_module_name = "feature_%s" % (feature_name) feature_module = __import__("bungeni.feature.%s" % (feature_module_name), fromlist=[feature_module_name]) return getattr(feature_module, feature_cls_name)
def register_new_custom_type(type_key, workflow_key, custom_archetype_key, sys_archetype_key ): """Retrieve (create if needed) a domain interface and model for type_key, and register as new entry on TYPE_REGISTER. """ archetype_model = resolve("%s.%s" % ( MODEL_MODULE.__name__, naming.model_name(custom_archetype_key))) # validate that custom archetype uses correct system archetype if custom_archetype_key != sys_archetype_key: sys_archetype_model = resolve("%s.%s" % ( MODEL_MODULE.__name__, naming.model_name(sys_archetype_key))) assert issubclass(archetype_model, sys_archetype_model), \ "Custom archetype %r for type %r is not a sub-type of %r." % ( custom_archetype_key, type_key, sys_archetype_key) # generate custom domain interface domain_iface_name = naming.model_interface_name(type_key) try: domain_iface = resolve("%s.%s" % (INTERFACE_MODULE.__name__, domain_iface_name)) log.warn("Custom interface ALREADY EXISTS: %s" % (domain_iface)) except ImportError: domain_iface = new_custom_domain_interface(type_key, domain_iface_name) # generate custom domain_model domain_model_name = naming.model_name(type_key) try: domain_model = resolve("%s.%s" % (MODEL_MODULE.__name__, domain_model_name)) log.warn("Custom domain model ALREADY EXISTS: %s" % (domain_model)) except ImportError: domain_model = new_custom_domain_model(type_key, domain_iface, custom_archetype_key) # type_info entry ti = TI(workflow_key, domain_iface, domain_model, archetype_model) ti.custom = True TYPE_REGISTRY.append((type_key, ti)) log.info("Registered custom type [%s]: %s" % (custom_archetype_key, type_key)) return type_key, ti
def output_features(): """ provides a list of features """ from bungeni.capi import capi from zope.dottedname.resolve import resolve from bungeni.alchemist.catalyst import MODEL_MODULE from bungeni.utils import naming from bungeni.feature.feature import get_feature_cls # get a list of available types in a list li_available_types = [] for type_key, ti in capi.iter_type_info(): li_available_types.append(type_key) li_features = [] li_features.append("<features>") li_unique_features = [] for type_key, ti in capi.iter_type_info(): obj = resolve("%s.%s" % (MODEL_MODULE.__name__, naming.model_name(type_key))) if len(obj.available_dynamic_features) > 0: for dyn_feature in obj.available_dynamic_features: # check if feature is a type li_unique_features.append('%s' % dyn_feature) li_unique_features = list(set(li_unique_features)) for f in li_unique_features: fcls = get_feature_cls(f) li_features.append(' <feature name="%s" >' % f) if fcls.depends_on: li_features.append(' <depends>') for depends in fcls.depends_on: li_features.append(' <depend>%s</depend>' % depends) li_features.append(' </depends>') if fcls.feature_parameters is not None: if len(fcls.feature_parameters) > 0 : li_features.append(' <params>') for key, val in fcls.feature_parameters.iteritems(): li_features.append(' <param name="%s">' % key) for key2, val2 in val.iteritems(): li_features.append(' <%(name)s>%(value)s</%(name)s>' % {"name": key2, "value": val2 } ) li_features.append(' </param>') li_features.append(' </params>') li_features.append(' </feature>') li_features.append("</features>") print "\n".join(li_features).encode("utf-8") return "\n".join(li_features).encode("utf-8")
def output_features_by_type(): """ provides a list of features per type """ from bungeni.capi import capi from zope.dottedname.resolve import resolve from bungeni.alchemist.catalyst import MODEL_MODULE from bungeni.utils import naming # get a list of available types in a list li_available_types = [] for type_key, ti in capi.iter_type_info(): li_available_types.append(type_key) li_features = [] li_features.append("<featuresByType>") for type_key, ti in capi.iter_type_info(): obj = resolve("%s.%s" % (MODEL_MODULE.__name__, naming.model_name(type_key))) if len(obj.available_dynamic_features) > 0: li_features.append(' <features for="%s">' % type_key) for dyn_feature in obj.available_dynamic_features: workflow = False # check if feature is a type if dyn_feature in li_available_types: # feature is a type, so it has a workflow workflow = True li_features.append( ' <feature name="%(name)s" workflow="%(wf)s" />' % { "name": dyn_feature, "wf": workflow }) li_features.append(" </features>") li_features.append("</featuresByType>") return "\n".join(li_features).encode("utf-8")
def get_vp_kls(extended_type): kls_name = naming.model_name(extended_type) return getattr(MODEL_MODULE.vp, kls_name)
def _load(workflow_name, workflow): """ (workflow_name:str, workflow:etree_doc) -> Workflow """ workflow_title = xas(workflow, "title") naming.MSGIDS.add(workflow_title) workflow_description = xas(workflow, "description") naming.MSGIDS.add(workflow_description) transitions = [] states = [] note = xas(workflow, "note") # initial_state, in XML indicated with a transition.@source="" initial_state = None ZCML_PROCESSED = bool(workflow_name in ZCML_WORKFLOWS_PROCESSED) if not ZCML_PROCESSED: ZCML_WORKFLOWS_PROCESSED.add(workflow_name) ZCML_LINES.append(ZCML_INDENT) ZCML_LINES.append(ZCML_INDENT) ZCML_LINES.append("%s<!-- %s -->" % (ZCML_INDENT, workflow_name)) def validate_id(id, tag): """Ensure that ID values are unique within the same XML doc scope. """ m = "Invalid element <%s> id=%r in workflow %r" % (tag, id, workflow_name) assert id not in validate_id.wuids, "%s -- id not unique in workflow document" % (m) validate_id.wuids.add(id) validate_id.wuids = set() # unique IDs in this XML workflow file def get_from_state(state_id): if state_id is None: return for state in states: if state.id == state_id: return state raise ValueError("Invalid value: permissions_from_state=%r" % (state_id)) def check_not_global_grant(pid, role): # ensure global and local assignments are distinct # (global: global_pid_roles, workflow_name) global_proles = global_pid_roles.get(pid, "") assert role not in global_proles, ("Workflow [%s] may not mix " "global and local granting of a same permission [%s] to a " "same role [%s].") % (workflow_name, pid, role) # permission_actions -> permissions for this type for (key, permission_action) in capi.schema.qualified_permission_actions( workflow_name, xas(workflow, "permission_actions", "").split() ): pid = "bungeni.%s.%s" % (key, permission_action) title = "%s %s" % ( permission_action, naming.split_camel(naming.model_name(key))) # !+ add to a Workflow.defines_permissions list ZCML_LINES.append( '%s<permission id="%s" title="%s" />' % (ZCML_INDENT, pid, title)) provideUtility(Permission(pid), IPermission, pid) # global grants global_pid_roles = {} # {pid: [role]} global_grants = get_permissions_from_allows(workflow_name, workflow) for (setting, pid, role) in global_grants: # for each global permission, build list of roles it is set to global_pid_roles.setdefault(pid, []).append(role) assert setting and pid and role, \ "Global grant must specify valid permission/role" #!+RNC # !+ add to a Workflow.global_grants list ZCML_LINES.append( '%s<grant permission="%s" role="%s" />' % (ZCML_INDENT, pid, role)) # no real need to check that the permission and role of a global grant # are properly registered in the system -- an error should be raised # by the zcml if either is not defined. rpm.grantPermissionToRole(pid, role, check=False) for perm, roles in global_pid_roles.items(): # assert roles mix limitations for state permissions assert_distinct_permission_scopes(perm, roles, workflow_name, "global grants") # all workflow features (enabled AND disabled) workflow_features = load_features(workflow_name, workflow) enabled_feature_names = [None] + [ f.name for f in workflow_features if f.enabled ] # !+EVENT_FEATURE_TYPES add each as enabled_feature_names, or extend the # facet quailifer by feature_name(sub_type_key).facet_ref? # workflow facets workflow_facets = load_facets(workflow_name, workflow) # states for s in workflow.iterchildren("state"): # @id state_id = xas(s, "id") assert state_id, "Workflow State must define @id" #!+RNC validate_id(state_id, "state") # @actions - transition-to-state actions state_actions = [] for action_name in xas(s, "actions", "").split(): state_actions.append(capi.get_workflow_action(action_name)) # @permissions_from_state permissions = [] # [ tuple(bool:int, permission:str, role:str) ] # state.@permissions_from_state : to reduce repetition and enhance # maintainibility of workflow XML files, a state may inherit ALL # permissions defined by the specified state. NO other permissions # may be specified by this state. from_state = get_from_state(xas(s, "permissions_from_state")) parent_permissions = xab(s, "parent_permissions") if parent_permissions: pass # no own permission definitions allowed elif from_state: # assimilate (no more no less) the state's permissions !+tuple, use same? permissions[:] = from_state.permissions else: used_facets_fq = resolve_state_facets( workflow_name, workflow_facets, enabled_feature_names, s) # assimilate permissions from facets from None and all enabled features def add_facet_permissions(facet): for perm in facet.permissions: check_add_assign_permission(workflow_name, permissions, perm) check_not_global_grant(perm[1], perm[2]) for (feature_name, qtk) in used_facets_fq: # debug check that feature is enabled assert feature_name in enabled_feature_names facet = used_facets_fq[(feature_name, qtk)] if facet is not None: add_facet_permissions(facet) # states state_title = xas(s, "title") naming.MSGIDS.add(state_title) states.append( State(state_id, state_title, xas(s, "note"), state_actions, permissions, parent_permissions, xab(s, "obsolete"), ) ) STATE_IDS = [ s.id for s in states ] # transitions for t in workflow.iterchildren("transition"): title = xas(t, "title") naming.MSGIDS.add(title) # sources, empty string -> initial_state sources = t.get("source").split() or [initial_state] assert len(sources) == len(set(sources)), \ "Transition contains duplicate sources [%s]" % (sources) for source in sources: if source is not initial_state: assert source in STATE_IDS, \ "Unknown transition source state [%s]" % (source) # destination must be a valid state destination = t.get("destination") assert destination in STATE_IDS, \ "Unknown transition destination state [%s]" % (destination) # optionals -- only set on kw IFF explicitly defined kw = {} for i in TRANS_ATTRS_OPTIONALS: val = xas(t, i) if not val: # we let setting of defaults be handled downstream continue kw[i] = val # data up-typing # # trigger if "trigger" in kw: kw["trigger"] = trigger_value_map[kw["trigger"]] # roles -> permission - one-to-one per transition roles = capi.schema.qualified_roles(kw.pop("roles", "")) if not is_zcml_permissionable(t): assert not roles, "Workflow [%s] - non-permissionable transition " \ "does not allow @roles [%s]." % (workflow_name, roles) #!+RNC kw["permission"] = None # None -> CheckerPublic # !+CAN_EDIT_AS_DEFAULT_TRANSITION_PERMISSION(mr, oct-2011) this feature # is functional (uncomment following elif clause) but as yet not enabled. # # Advantage would be that it would be easier to keep transitions # permissions in sync with object permissions (set in state) as the # majority of transition require exactly this as privilege; for the # occassional transition needing a different privilege, the current # transition.@roles mechanism may be used to make this explicit. # # Need to consider implications further; the zope_principal_role_map db # table, that caches contextual roles for principals, should probably # first be reworked to be db-less (as for zope_role_permission_map). # #elif not roles: # # then as fallback transition permission use can modify object # kw["permission"] = "bungeni.%s.Edit" % (workflow_name) # fallback permission else: # Dedicated permission for XML multi-source transition. # Given that, irrespective of how sources are grouped into # multi-source XML <transition> elements, there may be only *one* # path from any given *source* to any given *destination* state, # it suffices to use only the first source element + the destination # to guarantee a unique identifier for an XML transition element. # # Note: the "-" char is not allowed within a permission id # (so we use "." also here). # tid = "%s.%s" % (sources[0] or "", destination) pid = "bungeni.%s.wf.%s" % (workflow_name, tid) if not ZCML_PROCESSED: zcml_transition_permission(pid, title, roles) # remember list of roles from xml kw["_roles"] = roles kw["permission"] = pid # python resolvables if "condition" in kw: kw["condition"] = capi.get_workflow_condition(kw["condition"]) # numeric if "order" in kw: kw["order"] = float(kw["order"]) # ValueError if not numeric # bool if "require_confirmation" in kw: try: kw["require_confirmation"] = misc.as_bool(kw["require_confirmation"]) assert kw["require_confirmation"] is not None #!+RNC except: raise ValueError("Invalid transition value " '[require_confirmation="%s"]' % ( t.get("require_confirmation"))) # multiple-source transitions are really multiple "transition paths" for source in sources: args = (title, source, destination) transitions.append(Transition(*args, **kw)) log.debug("[%s] adding transition [%s-%s] [%s]" % ( workflow_name, source or "", destination, kw)) return Workflow(workflow_name, workflow_features, workflow_facets, states, transitions, global_grants, workflow_title, workflow_description, note)
def catalyse_system_descriptors(module): """Catalyze system descriptor classes (with by-name-convention associated model class) in specified module. Called when ui.descriptor is initially imported, so before descriptors for custom types have been created (that happens on first call to localization.localize_descriptors on application created event). !+CATALYSE_SYSTEM_DESCRIPTORS(mr, feb-2013) drop this, reworking it into catalysing on first time to localize each descriptor. """ import sys import inspect from bungeni.alchemist.descriptor import IModelDescriptor from bungeni.capi import capi from bungeni.ui.utils import debug def descriptor_classes(): """A generator of descriptor classes in this module, preserving the order of definition. """ # dir() returns names in alphabetical order decorated = [] for key in dir(module): cls = getattr(module, key) try: assert IModelDescriptor.implementedBy(cls) # we decorate with the source code line number for the cls decorated.append((inspect.getsourcelines(cls)[1], cls)) except (TypeError, AttributeError, AssertionError): debug.log_exc(sys.exc_info(), log_handler=log.debug) # we yield each cls in order of definition for cls in [ cls for (line_num, cls) in sorted(decorated) ]: yield cls def is_model_mapped(domain_model): # try get mapper to force UnmappedClassError try: orm.class_mapper(domain_model) return True except orm.exc.UnmappedClassError: # unmapped class e.g. Version return False for descriptor_model in descriptor_classes(): descriptor_name = descriptor_model.__name__ type_key = naming.type_key("descriptor_class_name", descriptor_name) # Associate each descriptor to the dedicated domain type via naming # convention, and only catalyse (descriptor, model) pairs # for which the domain type is mapped. Otherwise, ignore. domain_model = getattr(MODEL_MODULE, naming.model_name(type_key), None) if not (domain_model and is_model_mapped(domain_model)): log.warn("Not catalysing: %s", descriptor_name) continue # type_info, register descriptor_model, domain_model ti = capi.get_type_info(type_key) utils.inisetattr(ti, "domain_model", domain_model) utils.inisetattr(ti, "descriptor_model", descriptor_model) # catalyse each (domain_model, descriptor_model) pair catalyse(ti) # !+remove? m = "\n\nDone all setup of system types... running with:\n\n%s\n\n" % ( "\n\n".join(sorted( [ "%s: %s" % (key, ti) for key, ti in capi.iter_type_info() ]) )) log.debug(m)
def model_title(type_key): return naming.split_camel(naming.model_name(type_key))
def retrieve_domain_model(type_key): """Infer and retrieve the target domain model class from the type key. Raise Attribute error if not defined on domain. """ return resolve("%s.%s" % (MODEL_MODULE.__name__, naming.model_name(type_key)))
def register_new_custom_type(type_key, sys_archetype_key, custom_archetype_key, workflow_key, descriptor_key, label, container_label): """Retrieve (create if needed) a domain interface and model for type_key, and register as new entry on TYPE_REGISTER. """ if custom_archetype_key is None: custom_archetype_key = sys_archetype_key archetype_model = resolve( "%s.%s" % (MODEL_MODULE.__name__, naming.model_name(custom_archetype_key))) # validate that custom archetype uses correct system archetype if custom_archetype_key != sys_archetype_key: sys_archetype_model = resolve( "%s.%s" % (MODEL_MODULE.__name__, naming.model_name(sys_archetype_key))) assert issubclass(archetype_model, sys_archetype_model), \ "Custom archetype %r for type %r is not a sub-type of %r." % ( custom_archetype_key, type_key, sys_archetype_key) # generate custom domain interface domain_iface_name = naming.model_interface_name(type_key) try: domain_iface = resolve("%s.%s" % (INTERFACE_MODULE.__name__, domain_iface_name)) log.warn("Custom interface ALREADY EXISTS: %s", domain_iface) except ImportError: domain_iface = new_custom_domain_interface(type_key, domain_iface_name, archetype_model) # generate custom domain_model domain_model_name = naming.model_name(type_key) try: domain_model = resolve("%s.%s" % (MODEL_MODULE.__name__, domain_model_name)) log.warn("Custom domain model ALREADY EXISTS: %s", domain_model) except ImportError: domain_model = new_custom_domain_model(type_key, domain_iface, custom_archetype_key) if workflow_key is None: workflow_key = type_key if descriptor_key is None: descriptor_key = type_key # ILegislativeContent === sys_archetype_key=="doc" # Provide convenience marker to easily distinguish between # "system super archetype" and "system sub archetype", specifically # between Doc and Event, i.e. we want to mark all types implementing # IDoc but not IEvent (that also always implement IDoc). if sys_archetype_key == "doc": if not interfaces.ILegislativeContent.implementedBy(domain_model): classImplements(domain_model, interfaces.ILegislativeContent) # !+MENUITEM_TITLE check uniqueness constraint on type labels -- due to # zope.componenet registration issues on registering multiple menuitems on # same (interface, title) pairs, may be changed in future... possible # alternative is to infer a sub-interface to distinguish between "same" pairs if label is not None: for tk, ti in _iter(): assert not label == ti.label, \ "Label %r for custom type %r is not unique (see type %r)" % ( label, type_key, tk) # type_info entry ti = TI(type_key, workflow_key, domain_iface, domain_model, archetype_model) ti.descriptor_key = descriptor_key ti.label = label ti.container_label = container_label ti.custom = True ti.custom_archetype_key = custom_archetype_key ti.sys_archetype_key = sys_archetype_key TYPE_REGISTRY.append(ti) log.info("Registered custom type [%s]: %s", custom_archetype_key, type_key) return type_key, ti
def register_new_custom_type(type_key, sys_archetype_key, custom_archetype_key, workflow_key, descriptor_key, label, container_label ): """Retrieve (create if needed) a domain interface and model for type_key, and register as new entry on TYPE_REGISTER. """ if custom_archetype_key is None: custom_archetype_key = sys_archetype_key archetype_model = resolve("%s.%s" % ( MODEL_MODULE.__name__, naming.model_name(custom_archetype_key))) # validate that custom archetype uses correct system archetype if custom_archetype_key != sys_archetype_key: sys_archetype_model = resolve("%s.%s" % ( MODEL_MODULE.__name__, naming.model_name(sys_archetype_key))) assert issubclass(archetype_model, sys_archetype_model), \ "Custom archetype %r for type %r is not a sub-type of %r." % ( custom_archetype_key, type_key, sys_archetype_key) # generate custom domain interface domain_iface_name = naming.model_interface_name(type_key) try: domain_iface = resolve("%s.%s" % (INTERFACE_MODULE.__name__, domain_iface_name)) log.warn("Custom interface ALREADY EXISTS: %s", domain_iface) except ImportError: domain_iface = new_custom_domain_interface(type_key, domain_iface_name, archetype_model) # generate custom domain_model domain_model_name = naming.model_name(type_key) try: domain_model = resolve("%s.%s" % (MODEL_MODULE.__name__, domain_model_name)) log.warn("Custom domain model ALREADY EXISTS: %s", domain_model) except ImportError: domain_model = new_custom_domain_model(type_key, domain_iface, custom_archetype_key) if workflow_key is None: workflow_key = type_key if descriptor_key is None: descriptor_key = type_key # ILegislativeContent === sys_archetype_key=="doc" # Provide convenience marker to easily distinguish between # "system super archetype" and "system sub archetype", specifically # between Doc and Event, i.e. we want to mark all types implementing # IDoc but not IEvent (that also always implement IDoc). if sys_archetype_key == "doc": if not interfaces.ILegislativeContent.implementedBy(domain_model): classImplements(domain_model, interfaces.ILegislativeContent) # !+MENUITEM_TITLE check uniqueness constraint on type labels -- due to # zope.componenet registration issues on registering multiple menuitems on # same (interface, title) pairs, may be changed in future... possible # alternative is to infer a sub-interface to distinguish between "same" pairs if label is not None: for tk, ti in _iter(): assert not label == ti.label, \ "Label %r for custom type %r is not unique (see type %r)" % ( label, type_key, tk) # type_info entry ti = TI(type_key, workflow_key, domain_iface, domain_model, archetype_model) ti.descriptor_key = descriptor_key ti.label = label ti.container_label = container_label ti.custom = True ti.custom_archetype_key = custom_archetype_key ti.sys_archetype_key = sys_archetype_key TYPE_REGISTRY.append(ti) log.info("Registered custom type [%s]: %s", custom_archetype_key, type_key) return type_key, ti
def setUp(self): # register translations #import zope.i18n.zcml #zope.i18n.zcml.registerTranslations(getConfigContext(), # capi.get_path_for("translations", "bungeni")) # !+ZCML_PYTHON(mr, apr-2011) above registerTranslations() in python # does not work, as subsequent utility lookup fails. We workaround it # by executing the following parametrized bit of ZCML: from zope.configuration import xmlconfig xmlconfig.string(""" <configure xmlns="http://namespaces.zope.org/zope" xmlns:i18n="http://namespaces.zope.org/i18n"> <include package="zope.i18n" file="meta.zcml" /> <i18n:registerTranslations directory="%s" /> </configure> """ % (capi.get_path_for("translations", "bungeni"))) sm = site.LocalSiteManager(self.context) self.context.setSiteManager(sm) from bungeni.core import language from bungeni.ui import z3evoque z3evoque.set_get_gettext() z3evoque.setup_evoque() z3evoque.domain.set_on_globals("devmode", common.has_feature("devmode")) z3evoque.domain.set_on_globals("absoluteURL", url.absoluteURL) z3evoque.domain.set_on_globals("get_section_name", url.get_section_name) z3evoque.domain.set_on_globals("get_base_direction", language.get_base_direction) z3evoque.domain.set_on_globals("is_rtl", language.is_rtl) # !+ where is the view name for the app root (slash) set? # CONVENTION: the action of each site top-section is made to point # directly the primary sub-section (the INDEX) that it contains. # EXCEPTION: the "/", when logged in, is redirected to "/workspace/pi" self.context["bungeni"] = AkomaNtosoSection( title=_(u"Bungeni"), description=_(u"Current parliamentary activity"), default_name="bung", # !+NAMING(mr, jul-2011) bung?!? ) # top-level sections workspace = self.context["workspace"] = WorkspaceSection( title=_("section_workspace", default=u"Workspace"), description=_(u"Current parliamentary activity"), default_name="my-documents", ) alsoProvides(workspace, interfaces.ISearchableSection) workspace["my-documents"] = WorkspaceSection( title=_("section_workspace_documents", default=u"my documents"), description=_(u"my documents workspace section"), default_name="inbox", marker=interfaces.IWorkspaceDocuments, ) for tab in capi.workspace_tabs: workspace["my-documents"][tab] = WorkspaceContainer( tab_type=tab, title=tab, marker=interfaces.IWorkspaceTab) ws_uc = workspace["under-consideration"] = WorkspaceSection( title=_(u"Under consideration"), description=_(u"Documents under consideration"), default_name="documents", marker=interfaces.IWorkspaceUnderConsideration) ws_uc["documents"] = WorkspaceUnderConsiderationContainer( name="documents", title=_(u"Under consideration"), description=_(u"Documents under consideration"), marker=interfaces.IWorkspaceTrackedDocuments) ws_uc["tracked-documents"] = WorkspaceTrackedDocumentsContainer( name="tracked documents", title=_(u"tracked documents"), description=_(u"tracked documents")) ws_sched = workspace["scheduling"] = Section( title=_("section_scheduling", default=u"Scheduling"), description=_(u"Workspace Scheduling"), default_name="index", marker=interfaces.IWorkspaceScheduling) ws_sched["committees"] = QueryContent( # !+CUSTOM container_getter(get_chamber_for_context, "committees"), title=_("section_scheduling_committees", default=u"Committees"), description=_(u"Committee schedules")) ws_sched["documents"] = WorkspaceSchedulableContainer( name=_(u"schedulable items"), title=_(u"schedulable items"), description=_(u"documents available for scheduling")) ws_sched["sittings"] = QueryContent( # !+FEATURE container_getter(get_chamber_for_context, "sittings"), title=_("section_scheduling_sittings", default=u"Sittings"), description=_(u"Plenary Sittings")) ws_sched["sessions"] = QueryContent( container_getter(get_chamber_for_context, "sessions"), title=_("section_scheduling_sessions", default=u"Sessions"), description=_(u"Plenary Sessions")) ws_sched["venues"] = QueryContent(container_getter( get_chamber_for_context, "venues"), title=_("section_scheduling_venues", default=u"Venues"), description=_(u"Venues")) ws_sched["publications"] = QueryContent( container_getter(get_chamber_for_context, "publications"), title=_("section_scheduling_publications", default=u"Publications"), description=_(u"Publications")) # !+CALENDAR_DOC_TYPES finish off setup_customization_ui steps that need # application sections to be created... import bungeni.feature.ui for calendar_doc_type_key in bungeni.feature.ui.CALENDAR_DOC_TYPE_KEYS: calendar_doc_ti = capi.get_type_info(calendar_doc_type_key) container_property_name = naming.plural(calendar_doc_type_key) # add section containers to workspace/scheduling ws_sched[container_property_name] = QueryContent( container_getter(get_chamber_for_context, container_property_name), title=_("section_scheduling_${container_property_name}", mapping={ "container_property_name": container_property_name }, default=calendar_doc_ti.container_label), description=_(u"Manage ${container_label}", mapping={ "container_label": calendar_doc_ti.container_label })) workspace["groups"] = WorkspaceSection( title=_("section_groups", default=u"My groups"), description=_(u"My bungeni groups"), default_name="my-groups", marker=interfaces.IWorkspaceGroups) workspace["groups"]["my-groups"] = WorkspaceGroupsContainer( name="my-groups", title=_(u"My groups"), description=_(u"Groups that I am a member of")) #!+TIMING #!+AUTO CONTAINERS SCHEDULING(mb, April-2012) # type_info missing container name for type_key, ti in capi.iter_type_info(): if IScheduleContent.implementedBy(ti.domain_model): container_property_name = naming.plural(type_key) container_name = naming.model_name(container_property_name) if not ws_sched.has_key(container_property_name): ws_sched[container_property_name] = QueryContent( container_getter(get_chamber_for_context, container_property_name), title=container_name, description=container_name) ########## # Admin User Interface # Administration section #!+SECURITY(miano. nov-2010) Admin section now uses AdminSection # container that is identical to Section, only difference is that # traversing though it requires zope.ManageSite permission as defined # in core/configure.zcml admin = self.context["admin"] = AdminSection( title=_(u"Administration"), description=_(u"Manage bungeni settings"), default_name="admin-index", marker=IBungeniAdmin) admin["email-settings"] = Section( title=_(u"email settings"), description=_(u"manage email settings"), marker=IBungeniAdmin, default_name="email-settings") admin["search-settings"] = Section( title=_(u"search settings"), description=_(u"manage bungeni email settings"), marker=IBungeniAdmin, default_name="search-settings") admin["registry-settings"] = Section( title=_(u"registry settings"), description=_(u"manage registry settings"), marker=IBungeniAdmin, default_name="registry-settings") admin["serialization-manager"] = Section( title=_(u"serialization manager"), description=_(u"batch serialization of content"), marker=IBungeniAdmin, default_name="serialization-manager") content = admin["content"] = Section( title=_(u"Content"), description=_(u"browse bungeni content"), marker=IBungeniAdmin, default_name="browse-admin") alsoProvides(content, interfaces.ISearchableSection) # !+CUSTOM form descriptor container on legislature content[u"legislatures"] = domain.LegislatureContainer() ''' !+LEGISLATURE requires that all chamber/government/joint_committee groups be created *under* the legislature in admin (or use demo data dump from circa r11500 or newer). content[u"chambers"] = domain.ChamberContainer() to_locatable_container(domain.Chamber, content[u"chambers"]) content[u"governments"] = domain.GovernmentContainer() to_locatable_container(domain.Government, content[u"governments"]) content[u"joint-committees"] = domain.JointCommitteeContainer() to_locatable_container(domain.JointCommittee, content["joint-committees"]) ''' content[u"users"] = domain.UserContainer() to_locatable_container(domain.User, content[u"users"]) # !+/CUSTOM api = self.context[u"api"] = APISection( title=_(u"Bungeni API"), description=_(u"Bungeni REST API"), default_name="index", ) api[u"workspace"] = copy.deepcopy(workspace) api[u"users"] = copy.deepcopy(content[u"users"]) self.context["oauth"] = OAuthSection( title=_(u"Bungeni OAuth API"), description=_(u"Bungeni OAuth API"), default_name="index", ) admin[u"applications"] = domain.OAuthApplicationContainer() to_locatable_container(domain.OAuthApplication, admin[u"applications"])
def catalyse_system_descriptors(module): """Catalyze system descriptor classes (with by-name-convention associated model class) in specified module. Called when ui.descriptor is initially imported, so before descriptors for custom types have been created (that happens on first call to localization.localize_descriptors on application created event). !+CATALYSE_SYSTEM_DESCRIPTORS(mr, feb-2013) drop this, reworking it into catalysing on first time to localize each descriptor. """ import sys import inspect from bungeni.alchemist.descriptor import IModelDescriptor from bungeni.models import domain from bungeni.capi import capi from bungeni.ui.utils import debug def descriptor_classes(): """A generator of descriptor classes in this module, preserving the order of definition. """ # dir() returns names in alphabetical order decorated = [] for key in dir(module): cls = getattr(module, key) try: assert IModelDescriptor.implementedBy(cls) # we decorate with the source code line number for the cls decorated.append((inspect.getsourcelines(cls)[1], cls)) except (TypeError, AttributeError, AssertionError): debug.log_exc(sys.exc_info(), log_handler=log.debug) # we yield each cls in order of definition for cls in [ cls for (line_num, cls) in sorted(decorated) ]: yield cls def is_model_mapped(domain_model): # try get mapper to force UnmappedClassError try: orm.class_mapper(domain_model) return True except orm.exc.UnmappedClassError: # unmapped class e.g. Address, Version return False for descriptor_model in descriptor_classes(): descriptor_name = descriptor_model.__name__ type_key = naming.type_key("descriptor_class_name", descriptor_name) # Associate each descriptor to the dedicated domain type via naming # convention, and only catalysze (descriptor, model) pairs # for which the domain type is mapped. Otherwise, ignore. domain_model = getattr(domain, naming.model_name(type_key), None) if not (domain_model and is_model_mapped(domain_model)): log.warn("Not catalysing: %s", descriptor_name) continue # type_info, register descriptor_model, domain_model ti = capi.get_type_info(type_key) utils.inisetattr(ti, "domain_model", domain_model) utils.inisetattr(ti, "descriptor_model", descriptor_model) # catalyse each (domain_model, descriptor_model) pair catalyse(ti) # !+remove? m = "\n\nDone all setup of system types... running with:\n\n%s\n\n" % ( "\n\n".join(sorted( [ "%s: %s" % (key, ti) for key, ti in capi.iter_type_info() ]) )) log.debug(m)
def setup_customization_ui(): """Called from ui.app.on_wsgi_application_created_event -- must be called late, at least as long as there other ui zcml directives (always executed very late) that need to have been executed prior to this e.g. creation of specific menus such as "context_actions". """ def register_menu_item(type_key, privilege, title, for_, action, menu="context_actions", order=10, layer="bungeni.ui.interfaces.IBungeniSkin" ): naming.MSGIDS.add(title) # for i18n extraction UI_ZC_DECLS.append(register_menu_item.TMPL.format(**locals())) register_menu_item.TMPL = """ <browser:menuItem menu="{menu}" for="{for_}" action="{action}" title="{title}" order="{order}" permission="bungeni.{type_key}.{privilege}" layer="{layer}" />""" def register_form_view(type_key, privilege, name, for_, class_, layer="bungeni.ui.interfaces.IBungeniSkin" ): UI_ZC_DECLS.append(register_form_view.TMPL.format(**locals())) register_form_view.TMPL = """ <browser:page name="{name}" for="{for_}" class="{class_}" permission="bungeni.{type_key}.{privilege}" layer="{layer}" />""" UI_ZC_DECLS[:] = [] # we assume that non-custom types have already been set up as needed for type_key, ti in capi.iter_type_info(scope="custom"): UI_ZC_DECLS.append(""" <!-- {type_key} -->""".format(type_key=type_key)) type_title = naming.split_camel(naming.model_name(type_key)) # model interface is defined, but container interface is not yet model_interface_qualname = naming.qualname(ti.interface) container_interface_qualname = "bungeni.models.interfaces.%s" % ( naming.container_interface_name(type_key)) # generic forms (independent of any feature) # add register_form_view(type_key, "Add", "add", container_interface_qualname, "bungeni.ui.forms.common.AddForm") # view register_form_view(type_key, "View", "view", model_interface_qualname, "bungeni.ui.forms.common.DisplayForm") # edit !+DiffEditForm prior to r10032, doc-archetyped types were being # *declared* to use bungeni.ui.forms.forms.DiffEditForm, but this # is not the edit view tht was actually being used! register_form_view(type_key, "Edit", "edit", model_interface_qualname, "bungeni.ui.forms.common.EditForm") # delete register_form_view(type_key, "Delete", "delete", model_interface_qualname, "bungeni.ui.forms.common.DeleteForm") # plone content menu (for custom types) # !+ doc-types were previously being layered on IWorkspaceOrAdminSectionLayer # !+ there was previously no reg for IReportConatiner and one of the member # containers, plus there was inconsistency in permission for # IOfficeMemberContainer (was bungeni.office.Add). register_menu_item(type_key, "Add", "Add %s..." % (type_title), container_interface_qualname, "./add", menu="plone_contentmenu", layer="bungeni.ui.interfaces.IAdminSectionLayer") # workspace if ti.workflow.has_feature("workspace"): log.debug("Setting up UI for feature %r for type %r", "workspace", type_key) # add menu item # !+workspace_feature_add(mr, oct-2012) note that an enabled # workspace feature also implies "add" functionality for the type first_tab = capi.workspace_tabs[0] action = "../../{first_tab}/add_{k}".format(first_tab=first_tab, k=type_key) register_menu_item(type_key, "Add", type_title, "*", action, menu="workspace_add_parliamentary_content", order=7) # add menu item -> for admin ?! # !+ why a duplicated (almost identical) menu item for admin? # !+ criteria here is having workspace enabled... but, cirterion # should be simply that of being "parliamentary"? Do we need to # formalize this distinction? # !+ need a formal "container attribute" naming convention! action = "{k}/add".format(k=naming.plural(type_key)) register_menu_item(type_key, "Add", type_title, "*", action, menu="context_add_parliamentary_content", order=7) # edit menu item # !+ edit/delete used to be on layer="bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer" title = "Edit {t}".format(t=type_title) register_menu_item(type_key, "Edit", title, model_interface_qualname, "edit", menu="context_actions", order=10) # delete menu item title = "Delete {t}".format(t=type_title) register_menu_item(type_key, "Delete", title, model_interface_qualname, "delete", menu="context_actions", order=99) # add view name = "add_{type_key}".format(type_key=type_key) register_form_view(type_key, "Add", name, "bungeni.core.interfaces.IWorkspaceTab", "bungeni.ui.workspace.WorkspaceAddForm") # events if ti.workflow.has_feature("event"): log.debug("Setting up UI for feature %r for type %r", "event", type_key) title = "Add {t} Event".format(t=type_title) register_menu_item("event", "Add", title, model_interface_qualname, "./events/add", menu="additems", order=21, layer="bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer") # address if ti.workflow.has_feature("address"): log.debug("Setting up UI for feature %r for type %r", "address", type_key) if issubclass(ti.domain_model, domain.Group): title = "Add {t} Address".format(t=type_title) #layer="bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer" # add address in the "add items..." menu register_menu_item("address", "Add", title, model_interface_qualname, "./addresses/add", menu="additems", order=80) elif issubclass(ti.domain_model, domain.User): # !+ User not a custom type (so should never pass here) assert False, "Type %s may not be a custom type" % (ti.domain_model)
def get_feature_interface(feature_name): return getattr(interfaces, "IFeature%s" % naming.model_name(feature_name))
def display_name(cls): cls_name = naming.model_name( naming.type_key("descriptor_class_name", cls.__name__)) return naming.split_camel(cls_name) # !+unicode
def setUp(self): # register translations #import zope.i18n.zcml #zope.i18n.zcml.registerTranslations(getConfigContext(), # capi.get_path_for("translations", "bungeni")) # !+ZCML_PYTHON(mr, apr-2011) above registerTranslations() in python # does not work, as subsequent utility lookup fails. We workaround it # by executing the following parametrized bit of ZCML: from zope.configuration import xmlconfig xmlconfig.string(""" <configure xmlns="http://namespaces.zope.org/zope" xmlns:i18n="http://namespaces.zope.org/i18n"> <include package="zope.i18n" file="meta.zcml" /> <i18n:registerTranslations directory="%s" /> </configure> """ % (capi.get_path_for("translations", "bungeni"))) sm = site.LocalSiteManager(self.context) self.context.setSiteManager(sm) from bungeni.core import language from bungeni.ui import z3evoque z3evoque.set_get_gettext() z3evoque.setup_evoque() z3evoque.domain.set_on_globals("devmode", common.has_feature("devmode")) z3evoque.domain.set_on_globals("absoluteURL", url.absoluteURL) z3evoque.domain.set_on_globals("get_section_name", url.get_section_name) z3evoque.domain.set_on_globals("get_base_direction", language.get_base_direction) z3evoque.domain.set_on_globals("is_rtl", language.is_rtl) # !+ where is the view name for the app root (slash) set? # CONVENTION: the action of each site top-section is made to point # directly the primary sub-section (the INDEX) that it contains. # EXCEPTION: the "/", when logged in, is redirected to "/workspace/pi" self.context["bungeni"] = AkomaNtosoSection( title=_(u"Bungeni"), description=_(u"Current parliamentary activity"), default_name="bung", # !+NAMING(mr, jul-2011) bung?!? ) # top-level sections workspace = self.context["workspace"] = WorkspaceSection( title=_("section_workspace", default=u"Workspace"), description=_(u"Current parliamentary activity"), default_name="my-documents", ) alsoProvides(workspace, interfaces.ISearchableSection) workspace["my-documents"] = WorkspaceSection( title=_("section_workspace_documents", default=u"my documents"), description=_(u"my documents workspace section"), default_name="inbox", marker=interfaces.IWorkspaceDocuments, ) for tab in capi.workspace_tabs: workspace["my-documents"][tab] = WorkspaceContainer( tab_type=tab, title=tab, marker=interfaces.IWorkspaceTab ) ws_uc = workspace["under-consideration"] = WorkspaceSection( title=_(u"Under consideration"), description=_(u"Documents under consideration"), default_name="documents", marker=interfaces.IWorkspaceUnderConsideration) ws_uc["documents"] = WorkspaceUnderConsiderationContainer( name="documents", title=_(u"Under consideration"), description=_(u"Documents under consideration"), marker=interfaces.IWorkspaceTrackedDocuments) ws_uc["tracked-documents"] = WorkspaceTrackedDocumentsContainer( name="tracked documents", title=_(u"tracked documents"), description=_(u"tracked documents")) ws_sched = workspace["scheduling"] = Section( title=_("section_scheduling", default=u"Scheduling"), description=_(u"Workspace Scheduling"), default_name="index", marker=interfaces.IWorkspaceScheduling) ws_sched["committees"] = QueryContent( # !+CUSTOM container_getter(get_chamber_for_context, "committees"), title=_("section_scheduling_committees", default=u"Committees"), description=_(u"Committee schedules")) ws_sched["documents"] = WorkspaceSchedulableContainer( name=_(u"schedulable items"), title=_(u"schedulable items"), description=_(u"documents available for scheduling")) ws_sched["sittings"] = QueryContent( # !+FEATURE container_getter(get_chamber_for_context, "sittings"), title=_("section_scheduling_sittings", default=u"Sittings"), description=_(u"Plenary Sittings")) ws_sched["sessions"] = QueryContent( container_getter(get_chamber_for_context, "sessions"), title=_("section_scheduling_sessions", default=u"Sessions"), description=_(u"Plenary Sessions")) ws_sched["venues"] = QueryContent( container_getter(get_chamber_for_context, "venues"), title=_("section_scheduling_venues", default=u"Venues"), description=_(u"Venues")) ws_sched["publications"] = QueryContent( container_getter(get_chamber_for_context, "publications"), title=_("section_scheduling_publications", default=u"Publications"), description=_(u"Publications")) # !+CALENDAR_DOC_TYPES finish off setup_customization_ui steps that need # application sections to be created... import bungeni.feature.ui for calendar_doc_type_key in bungeni.feature.ui.CALENDAR_DOC_TYPE_KEYS: calendar_doc_ti = capi.get_type_info(calendar_doc_type_key) container_property_name = naming.plural(calendar_doc_type_key) # add section containers to workspace/scheduling ws_sched[container_property_name] = QueryContent( container_getter(get_chamber_for_context, container_property_name), title=_("section_scheduling_${container_property_name}", mapping={"container_property_name": container_property_name}, default=calendar_doc_ti.container_label), description=_(u"Manage ${container_label}", mapping={"container_label": calendar_doc_ti.container_label})) workspace["groups"] = WorkspaceSection( title=_("section_groups", default=u"My groups"), description=_(u"My bungeni groups"), default_name="my-groups", marker=interfaces.IWorkspaceGroups) workspace["groups"]["my-groups"] = WorkspaceGroupsContainer( name="my-groups", title=_(u"My groups"), description=_(u"Groups that I am a member of")) #!+TIMING #!+AUTO CONTAINERS SCHEDULING(mb, April-2012) # type_info missing container name for type_key, ti in capi.iter_type_info(): if IScheduleContent.implementedBy(ti.domain_model): container_property_name = naming.plural(type_key) container_name = naming.model_name(container_property_name) if not ws_sched.has_key(container_property_name): ws_sched[container_property_name] = QueryContent( container_getter(get_chamber_for_context, container_property_name), title=container_name, description=container_name) ########## # Admin User Interface # Administration section #!+SECURITY(miano. nov-2010) Admin section now uses AdminSection # container that is identical to Section, only difference is that # traversing though it requires zope.ManageSite permission as defined # in core/configure.zcml admin = self.context["admin"] = AdminSection( title=_(u"Administration"), description=_(u"Manage bungeni settings"), default_name="admin-index", marker=IBungeniAdmin) admin["email-settings"] = Section( title=_(u"email settings"), description=_(u"manage email settings"), marker=IBungeniAdmin, default_name="email-settings") admin["search-settings"] = Section( title=_(u"search settings"), description=_(u"manage bungeni email settings"), marker=IBungeniAdmin, default_name="search-settings") admin["registry-settings"] = Section( title=_(u"registry settings"), description=_(u"manage registry settings"), marker=IBungeniAdmin, default_name="registry-settings") admin["serialization-manager"] = Section( title=_(u"serialization manager"), description=_(u"batch serialization of content"), marker=IBungeniAdmin, default_name="serialization-manager") content = admin["content"] = Section( title=_(u"Content"), description=_(u"browse bungeni content"), marker=IBungeniAdmin, default_name="browse-admin") alsoProvides(content, interfaces.ISearchableSection) # !+CUSTOM form descriptor container on legislature content[u"legislatures"] = domain.LegislatureContainer() ''' !+LEGISLATURE requires that all chamber/government/joint_committee groups be created *under* the legislature in admin (or use demo data dump from circa r11500 or newer). content[u"chambers"] = domain.ChamberContainer() to_locatable_container(domain.Chamber, content[u"chambers"]) content[u"governments"] = domain.GovernmentContainer() to_locatable_container(domain.Government, content[u"governments"]) content[u"joint-committees"] = domain.JointCommitteeContainer() to_locatable_container(domain.JointCommittee, content["joint-committees"]) ''' content[u"users"] = domain.UserContainer() to_locatable_container(domain.User, content[u"users"]) # !+/CUSTOM api = self.context[u"api"] = APISection( title=_(u"Bungeni API"), description=_(u"Bungeni REST API"), default_name="index", ) api[u"workspace"] = copy.deepcopy(workspace) api[u"users"] = copy.deepcopy(content[u"users"]) self.context["oauth"] = OAuthSection( title=_(u"Bungeni OAuth API"), description=_(u"Bungeni OAuth API"), default_name="index", ) admin[u"applications"] = domain.OAuthApplicationContainer() to_locatable_container(domain.OAuthApplication, admin[u"applications"])