Example #1
0
def generate_container_class(ti):
    """Generate a zope3 container class for a domain model.
    """
    type_key = naming.polymorphic_identity(ti.domain_model)
    container_name = naming.container_class_name(type_key)
    container_iname = naming.container_interface_name(type_key)
    base_interfaces = (IAlchemistContainer,
                       )  # !+achetype.container_interface?

    # logging variables
    msg = (ti.domain_model.__name__, CONTAINER_MODULE.__name__, container_name)

    # container class - if we already have one, exit
    if getattr(CONTAINER_MODULE, container_name, None):
        log.info(
            "generate_container_class [model=%s] found container %s.%s, skipping",
            *msg)
        ti.container_class = getattr(CONTAINER_MODULE, container_name)
        return

    container_class = type(
        container_name, (AlchemistContainer, ),
        dict(_class=ti.domain_model, __module__=CONTAINER_MODULE.__name__))
    # set on CONTAINER_MODULE, register on type_info
    setattr(CONTAINER_MODULE, container_name, container_class)
    ti.container_class = container_class
    log.info("generate_container_class [model=%s] generated container %s.%s",
             *msg)

    # container interface - if we already have one, skip creation
    # !+ should always be newly created?
    container_iface = getattr(INTERFACE_MODULE, container_iname, None)
    msg = (ti.domain_model.__name__, CONTAINER_MODULE.__name__,
           container_iname)
    if container_iface is not None:
        assert issubclass(container_iface, IAlchemistContainer)
        log.info(
            "generate_container_class [model=%s] skipping container interface %s.%s for",
            *msg)
    else:
        container_iface = interface.interface.InterfaceClass(
            container_iname,
            bases=base_interfaces,
            __module__=INTERFACE_MODULE.__name__)
        # set on INTERFACE_MODULE, register on type_info
        setattr(INTERFACE_MODULE, container_iname, container_iface)
        ti.container_interface = container_iface
        log.info(
            "generate_container_class [model=%s] generated container interface %s.%s",
            *msg)

    # setup security
    for n, d in container_iface.namesAndDescriptions(all=True):
        protectName(container_class, n, "zope.Public")
    # apply implementedBy
    if not container_iface.implementedBy(container_class):
        interface.classImplements(container_class, container_iface)
Example #2
0
def generate_container_class(ti):
    """Generate a zope3 container class for a domain model.
    """
    type_key = naming.polymorphic_identity(ti.domain_model)
    container_name = naming.container_class_name(type_key)
    container_iname = naming.container_interface_name(type_key)
    base_interfaces = (IAlchemistContainer,)
    
    # logging variables
    msg = (ti.domain_model.__name__, CONTAINER_MODULE.__name__, container_name)
    
    # container class - if we already have one, exit                
    if getattr(CONTAINER_MODULE, container_name, None):
        log.info("generate_container_class [model=%s] found container %s.%s, skipping" % msg)
        ti.container_class = getattr(CONTAINER_MODULE, container_name)
        return
    
    container_class = type(container_name,
        (AlchemistContainer,),
        dict(_class=ti.domain_model, 
            __module__=CONTAINER_MODULE.__name__)
    )
    # set on CONTAINER_MODULE, register on type_info
    setattr(CONTAINER_MODULE, container_name, container_class)
    ti.container_class = container_class
    log.info("generate_container_class [model=%s] generated container %s.%s" % msg)
    
    # container interface - if we already have one, skip creation 
    # !+ should always be newly created?
    container_iface = getattr(INTERFACE_MODULE, container_iname, None)
    msg = (ti.domain_model.__name__, CONTAINER_MODULE.__name__, container_iname)
    if container_iface is not None:
        assert issubclass(container_iface, IAlchemistContainer)
        log.info("generate_container_class [model=%s] skipping container interface %s.%s for" % msg)
    else:
        container_iface = interface.interface.InterfaceClass(
            container_iname,
            bases=base_interfaces,
            __module__=INTERFACE_MODULE.__name__
        )
        # set on INTERFACE_MODULE, register on type_info
        setattr(INTERFACE_MODULE, container_iname, container_iface)
        ti.container_interface = container_iface
        log.info("generate_container_class [model=%s] generated container interface %s.%s" % msg)
    
    # setup security
    for n, d in container_iface.namesAndDescriptions(all=True):
        protectName(container_class, n, "zope.Public")
    # apply implementedBy
    if not container_iface.implementedBy(container_class):
        interface.classImplements(container_class, container_iface)
Example #3
0
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}"
            />"""

    def register_api_view(type_key, for_):
        UI_ZC_DECLS.append(register_api_view.TMPL.format(**locals()))

    register_api_view.TMPL = """
            <browser:page name="index"
                for="{for_}"
                class="bungeni.ui.api.APIObjectView"
                permission="bungeni.{type_key}.View"
                layer="bungeni.ui.interfaces.IBungeniAPILayer"
            />"""

    def model_title(type_key):
        return naming.split_camel(naming.model_name(type_key))

    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 = model_title(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", "index", model_interface_qualname,
                           "bungeni.ui.forms.common.DisplayForm")

        register_api_view(type_key, model_interface_qualname)
        # 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")
        if issubclass(ti.interface, IBungeniGroup):
            register_form_view(type_key, "Edit", "edit",
                               model_interface_qualname,
                               "bungeni.ui.forms.common.GroupEditForm")
        else:
            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)
            for event_type_key in ti.workflow.get_feature(
                    "event").params["types"]:
                if capi.has_type_info(event_type_key):
                    container_property_name = naming.plural(event_type_key)
                    # add menu item
                    title = "{t} {e}".format(t=type_title,
                                             e=model_title(event_type_key))
                    register_menu_item(
                        event_type_key,
                        "Add",
                        "Add %s" % (title),
                        model_interface_qualname,
                        "./%s/add" % (container_property_name),
                        menu="additems",
                        order=21,
                        layer=
                        "bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer")
                else:
                    log.warn(
                        'IGNORING feature "event" ref to disabled type %r',
                        event_type_key)

        # register other non-workspace menu items for custom types (only once)
        # custom events !+GET_ARCHETYPE
        if issubclass(ti.domain_model, domain.Event):
            # edit menu item
            register_menu_item(
                type_key,
                "Edit",
                "Edit {t}".format(t=type_title),
                model_interface_qualname,
                "edit",
                menu="context_actions",
                order=10,
                layer="bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer")
            # delete menu item
            register_menu_item(
                type_key,
                "Delete",
                "Delete {t}".format(t=type_title),
                model_interface_qualname,
                "delete",
                menu="context_actions",
                order=99,
                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)
Example #4
0
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}"
            />"""

    def register_api_view(type_key, for_):
        UI_ZC_DECLS.append(register_api_view.TMPL.format(**locals()))
    register_api_view.TMPL = """
            <browser:page name="index"
                for="{for_}"
                class="bungeni.ui.api.APIObjectView"
                permission="bungeni.{type_key}.View"
                layer="bungeni.ui.interfaces.IBungeniAPILayer"
            />"""

    def model_title(type_key):
        return naming.split_camel(naming.model_name(type_key))
    
    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 = model_title(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", "index", model_interface_qualname,
            "bungeni.ui.forms.common.DisplayForm")
        
        register_api_view(type_key, model_interface_qualname)
        # 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")
        if issubclass(ti.interface, IBungeniGroup):
            register_form_view(type_key, "Edit", "edit",
                model_interface_qualname,
                "bungeni.ui.forms.common.GroupEditForm")
        else:
            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)
            for event_type_key in ti.workflow.get_feature("event").params["types"]:
                if capi.has_type_info(event_type_key):
                    container_property_name = naming.plural(event_type_key)
                    # add menu item
                    title = "{t} {e}".format(t=type_title, e=model_title(event_type_key))
                    register_menu_item(event_type_key, "Add", "Add %s" %(title),
                        model_interface_qualname, 
                        "./%s/add" % (container_property_name), 
                        menu="additems", 
                        order=21,
                        layer="bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer")
                else:
                    log.warn('IGNORING feature "event" ref to disabled type %r', 
                        event_type_key)
        
        # register other non-workspace menu items for custom types (only once)
        # custom events !+GET_ARCHETYPE
        if issubclass(ti.domain_model, domain.Event):
            # edit menu item
            register_menu_item(type_key, "Edit", "Edit {t}".format(t=type_title),
                model_interface_qualname,
                "edit",
                menu="context_actions",
                order=10,
                layer="bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer")
            # delete menu item
            register_menu_item(type_key, "Delete", "Delete {t}".format(t=type_title),
                model_interface_qualname,
                "delete",
                menu="context_actions",
                order=99,
                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)
Example #5
0
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 model_title(type_key):
    #    return naming.split_camel(naming.model_name(type_key))
    
    # clear accumulation of generated zcml code -- but retain the same GLOBAL 
    # list variable instance
    UI_ZC_DECLS[:] = []
    
    # setup bungeni_custom resource
    UI_ZC_DECLS.append("""
    <browser:resourceDirectory name="reporting-static" 
        directory="%s/reporting/static" />""" % (capi.get_root_path()))
    
    # 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 = ti.label or ti.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")
        # api add
        register_form_view(type_key, "Add", "add", container_interface_qualname,
            "bungeni.ui.api.APIAddForm", "bungeni.ui.interfaces.IBungeniAPILayer")
        # view
        register_form_view(type_key, "View", "index", model_interface_qualname,
            "bungeni.ui.forms.common.DisplayForm")
        # api view
        register_form_view(type_key, "View", "index",
                model_interface_qualname,
                "bungeni.ui.api.APIObjectView",
                "bungeni.ui.interfaces.IBungeniAPILayer")
        # 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")
        if issubclass(ti.domain_model, domain.Group):
            # groups
            register_form_view(type_key, "Edit", "edit",
                model_interface_qualname,
                "bungeni.ui.forms.common.GroupEditForm")
        else:
            register_form_view(type_key, "Edit", "edit",
                model_interface_qualname,
                "bungeni.ui.forms.common.EditForm")
            register_form_view(type_key, "Edit", "edit",
                model_interface_qualname,
                "bungeni.ui.api.APIEditForm",
                "bungeni.ui.interfaces.IBungeniAPILayer")
        # 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
        register_menu_item(type_key, "Add", "Add %s..." % (type_title), 
            container_interface_qualname,
            "./add",
            menu="plone_contentmenu",
            layer="bungeni.ui.interfaces.IAdminSectionLayer")
        
        # group        
        if issubclass(ti.domain_model, domain.Group):
            if ti.workflow.has_feature("sitting"):
                # !+CHAMBER_SITTING clarify/regularize for chamber (e.g. can
                # already add an agenda item via workspace menus, etc).
                # add sitting
                register_menu_item("sitting", "Add", "Add sitting...",
                    model_interface_qualname,
                    "./sittings/add",
                    menu="additems", 
                    order=40,
                    layer="bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer")
                # add calendar "createable" add doc menu items
                sitting_feature = ti.workflow.get_feature("sitting")
                for calendar_doc_type_key in sitting_feature.get_param("calendar_doc_types"):
                    CALENDAR_DOC_TYPE_KEYS.add(calendar_doc_type_key)
                    calendar_doc_ti = capi.get_type_info(calendar_doc_type_key)
                    container_property_name = naming.plural(calendar_doc_type_key)
                    register_menu_item(calendar_doc_type_key, 
                        "Add",
                        "Add %s..." % (calendar_doc_ti.label), #!+MENUITEM_TITLE
                        model_interface_qualname,
                        "./%s/add" % (container_property_name),
                        menu="additems", 
                        order=41,
                        layer="bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer")
                
                # group CalendarView
                register_form_view(type_key, "View", "schedule",
                    model_interface_qualname,
                    "bungeni.ui.calendar.browser.CalendarView")
        
        # member
        if issubclass(ti.domain_model, domain.GroupMember):
            group_ti = capi.get_type_info(ti.within_type_key)
            group_model_interface_qualname = naming.qualname(group_ti.interface)
            # add
            register_menu_item(type_key, "Add", "Add %s..." % (type_title), 
                group_model_interface_qualname,
                "./%s/add" % (naming.plural(type_key)),
                menu="additems", 
                order=61,
                layer="bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer")
        
        # group, member
        if issubclass(ti.domain_model, (domain.Group, domain.GroupMember)):
            # edit
            register_menu_item(type_key, "Edit", "Edit %s..." % (type_title), 
                model_interface_qualname,
                "edit",
                menu="context_actions", 
                order=10,
                layer="bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer")
            # delete
            register_menu_item(type_key, "Delete", "Delete %s..." % (type_title), 
                model_interface_qualname,
                "delete",
                menu="context_actions", 
                order=99,
                layer="bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer")
        
        # create/register custom container viewlets
        # !+descriptor/restore.py ti.descriptor_model is None when running this utility
        if ti.descriptor_model:
            for i, ic in enumerate(ti.descriptor_model.info_containers):
                if ic.viewlet:
                    sfv_cls = new_container_sub_form_viewlet_cls(type_key, ic, i)
                    register_container_viewlet(
                        type_key, ic.viewlet_name, model_interface_qualname)
        
        # workspace
        if ti.workflow.has_feature("workspace"):
            log.debug("Setting up UI for feature %r for type %r", "workspace", type_key)
            
            # add menu item
            # !+AUTO_UI_ZCML_MENU_ITEMS: workspace_add_parliamentary_content
            # !+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_verb = "add_{k}".format(k=type_key)
            action = "../../{first_tab}/{action_verb}".format(
                first_tab=first_tab, action_verb=action_verb)
            # !+MENUITEM_TITLE_r11350(mr, jun-2014) am switching the title of these 
            # two menuItems to use the type_key instead of the ti label, as it seems 
            # ZCML does not allow to have a same @title value for multiple menuItem 
            # declarations for the same menu + target interface (even if @action,
            # @permission are different).
            # bungeni.models.interfaces.IGroup, bungeni.core.interfaces.IWorkspaceContainer
            register_menu_item(type_key, "Add", 
                type_title, # !+MENUITEM_TITLE
                "bungeni.models.interfaces.ISUBMENU_workspace_add_parliamentary_content",
                action,
                menu="workspace_add_parliamentary_content",
                filter_="python: context.is_type_workspaced(%r)" % (type_key),
                order=7)
            
            ''' !+MENUITEM_TITLE_r11350(mr, jun-2014) -- disabling to monitor 
                if needed; re-enable or delete...
            # add menu item -> for admin ?!
            # !+AUTO_UI_ZCML_MENU_ITEMS: context_add_parliamentary_content
            # !+ why a duplicated (almost identical) menu item for admin?
            # !+ criteria here is having workspace enabled... but, criterion 
            # 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, 
                "bungeni.models.interfaces.IGroup",
                action,
                menu="context_add_parliamentary_content",
                filter_="python: context.is_type_workspaced(%r)" % (type_key),
                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")
            # api add
            register_form_view(type_key, "Add", name,
                "bungeni.core.interfaces.IWorkspaceTab",
                "bungeni.ui.api.APIWorkspaceAddForm",
                "bungeni.ui.interfaces.IBungeniAPILayer")
        
        # workspace add titles to strings for i18n
        for ws_tab in capi.workspace_tabs:
            naming.MSGIDS.add(("section_workspace_%s" % ws_tab, ws_tab))
        
        # events
        if ti.workflow.has_feature("event"):
            log.debug("Setting up UI for feature %r for type %r", "event", type_key)
            event_feature = ti.workflow.get_feature("event")
            for event_type_key in event_feature.get_param("types"):
                if capi.has_type_info(event_type_key):
                    container_property_name = naming.plural(event_type_key)
                    event_type_ti = capi.get_type_info(event_type_key)
                    # add menu item
                    title = "{t} {e}".format(
                        t=type_title, 
                        e=(event_type_ti.label or event_type_ti.type_key))
                    register_menu_item(event_type_key, "Add", "Add %s" %(title),
                        model_interface_qualname, 
                        "./%s/add" % (container_property_name), 
                        menu="additems",
                        order=21,
                        layer="bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer")
                else:
                    log.warn("IGNORING feature %r ref to disabled type %r", 
                        "event", event_type_key)
        
        # register other non-workspace menu items for custom types (only once)
        # custom events !+GET_ARCHETYPE !+ARCHETYPE_INSTANTIATABLE
        if issubclass(ti.domain_model, domain.Event):
            # edit menu item
            register_menu_item(type_key, "Edit", "Edit {t}".format(t=type_title),
                model_interface_qualname,
                "edit",
                menu="context_actions",
                order=10,
                layer="bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer")
            # delete menu item
            register_menu_item(type_key, "Delete", "Delete {t}".format(t=type_title),
                model_interface_qualname,
                "delete",
                menu="context_actions",
                order=99,
                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)
    
    # once-only processing
    for calendar_doc_type_key in CALENDAR_DOC_TYPE_KEYS:
        # !+CALENDAR_DOC_TYPES
        calendar_doc_ti = capi.get_type_info(calendar_doc_type_key)
        container_property_name = naming.plural(calendar_doc_type_key)
        # !+SchedulingContext why do they need to be different?
        # plenary scheduling
        register_menu_item(calendar_doc_type_key, 
            "Add", 
            "Add %s..." % (calendar_doc_ti.label), #!+MENUITEM_TITLE
            "bungeni.core.schedule.WorkspaceSchedulingContext",
            "./%s/add" % (container_property_name),
            menu="plone_contentmenu", 
            #order=41,
            layer="bungeni.ui.interfaces.IWorkspaceSectionLayer")
        # group (committee) scheduling 
        register_menu_item(calendar_doc_type_key, 
            "Add", 
            "Add %s..." % (calendar_doc_ti.label), #!+MENUITEM_TITLE
            "bungeni.core.schedule.GroupSchedulingContext",
            "./../%s/add" % (container_property_name),
            menu="plone_contentmenu", 
            #order=41,
            layer="bungeni.ui.interfaces.IWorkspaceOrAdminSectionLayer")