The application model consists of nodes providing the application hierarchy, security declarations, UI configuration and node type information for authoring.
The base application node utilizes node and implements the following contracts:
node.interfaces.INode defines the basic tree information and containment API used for application model traversal.
node.interfaces.IAttributes extends the node by an attrs property
which holds the node data. We use a dedicated object holding the attributes
in order to separate node hierarchy information from actual node data in a
clean way.
cone.app.interfaces.ISecured extends the node by the __acl__
property. It is used by Pyramid at publishing time to manage access.
See Security documentation for more details.
cone.app.interfaces.IApplicationNode extends the node by application specific contracts.
- properties: Property containing
cone.app.IPropertiesimplementing object. The properties are supposed to provide UI configuration settings. See UI widgets for details.- metadata: Property containing
cone.app.IMetadataimplementing object. Metadata is used by different UI widgets to display node metadata.- nodeinfo: Property containing
cone.app.INodeInfoimplementing object. NodeInfo provides cardinality information and general node information which is primary needed for authoring operations.
The cone.app.model.BaseNode implements all required aspects for
representing an application node. It can be used as starting point when writing
application models.
from cone.app.model import BaseNode
class CustomNode(BaseNode):
"""Model related code goes here.
"""
A more advanced technique is to use the mechanisms provided by
plumber package directly for defining
different behaviors of a node. The IApplicationNode related interface
extensions are implemented as plumbing behaviors as well, and the BaseNode
class is just a combination of plumbing behaviors without extending anything on
the class directly.
from cone.app.model import AppNode
from node import behaviors
from plumber import plumbing
@plumbing(
behaviors.AppNode,
behaviors.AsAttrAccess,
behaviors.NodeChildValidate,
behaviors.Adopt,
behaviors.Nodespaces,
behaviors.Attributes,
behaviors.DefaultInit,
behaviors.Nodify,
behaviors.Lifecycle,
behaviors.OdictStorage)
class AdvancedNode(object)
"""The used plumbing behaviors on this class are the same as for
``cone.app.model.BaseNode``
"""
The cone.app.model.FactoryNode can be used to serve fixed children. The
factory node provides a factory attribute containing a dict, where the keys
represent the available children keys, and the values are callables which act
as factory for given key on first access.
E.g., the AppRoot node inherits from FactoryNode and is
used to serve the entry nodes of the application.
from cone.app.model import FactoryNode
class CustomFactoryNode(FactoryNode):
factories = {
'child_by_factory_function': self.child_factory_function,
'child_by_node_init_as_factory': BaseNode,
}
def child_factory_function(self):
return BaseNode()
The cone.app.AdapterNode can be used for publishing nodes of models where
the hierarchy differs from the one of the application model.
The adapter node by default acts as proxy for __iter__ and attrs, all
other functions refer to the underlying node.behaviors.OdictStorage of the
adapter node.
If an adapter node wants to publish the children of the adapted node, it must
not do this by just returning the children of the adapted node because the
application node hierarchy would get invalid. Thus it is required to adapt
them as well. Do this by overrwriting __getitem__.
from cone.app.model import AdapterNode
class AdaptedChildNode(AdapterNode):
pass
class CustomAdapterNode(AdapterNode):
def __getitem__(self, key):
try:
return self.storage[key]
except KeyError:
child_context = self.model[key]
child = AdaptedChildNode(child_context, key, self)
self.storage[key] = child
return child
cone.app.model.AppRoot derives from FactoryNode
and represents the application model root node.
This node gets instanciated only once on application startup. Every plugin
entry point registered with register_entry
gets written to the factories attribute of the root node.
Root node related settings from the .ini file are written to properties
respective metadata objects of the application root node.
The root node can be accessed either by calling self.root on application
model nodes or by using cone.app.get_root() utility.
from cone.app import get_root
root = get_root()
cone.app.model.AppSettings is like application root a factory node
initialized at application startup. Every settings node factory registered with
register_config gets written to the
factories attribute of the settings node.
The settings node provides relevant properties and metadata objects and
an __acl__ restricting access to the manager role.
The settings node is available at settings on application model root.
settings = get_root()['settings']
cone.app.model.AppEnvironment is a plumbing behavior implementing
node.interfaces.IApplicationEnvironment. It is useful for objects which
need to know about the current request and/or the current registry.
cone.app.model.CopySupport is a plumbing behavior for application model
nodes indicating that it’s children can be cut and copied, and that nodes from
another subtree can be pasted. Cut, copy and paste features are controlled by
supports_cut, supports_copy respective supports_paste flags. They
all default to True.
cone.app.model.NamespaceUUID is a plumbing behavior for application model
nodes and implements node.interfaces.IUUID. The uuid attribute is
implemented as read-only property which computes the UUID as uuid.uuid5
from a namespace and the node path. The uuid namespace can be overwritten
via uuid_namespace attribute.
cone.app.model.UUIDAttributeAware is a plumbing behavior and supposed to be
used to expose self.attrs['uuid'] at self.uuid.
Warning
EXPERIMENTAL - Subject to change.
cone.app.model.UUIDAsName is a plumbing behavior which provides
self.uuid at self.name. In conjunction with UUIDAttributeAware it
is possible to create application models where nodes are traversable by
persistent UUID.
self.set_uuid_for(node, override=False, recursiv=False) can be used to
recursively update UUID’s on copies of a node.
cone.app.model.Translation is a plumbing behavior for nodes
and implements node.interfaces.ITranslation. Using this behavior turns
the node into a container holding different translations for a specific string
or text. Accessing the value attribute returns the translated value by
current locale setting or the node name if no translation found for locale.
The Translation behavior derives from node.behaviors.Schema. The
default schema on translations is a cone.app.model.LanguageSchema instance.
This schema implementation reads the available languages set at
cone.available_languages in the application config file and provides them
as node.schema.Str fields.
cone.app.model.Properties can be used for any kind of property mapping.
The contract is described in cone.app.interfaces.IProperties. The
application node attributes properties and metadata promise to
provide an IProperties implementation.
Properties are accessed via python attribute access, but never raise an
AttributeError if property not exists, instead None is returned.
Available properties are provided by keys function.
Note
Although one Python ZEN rule says “Explicit is better than implicit”, the behavior is desired.
The reason is that IProperties objects are used to expect UI element
settings or metadata on application nodes.
When writing new UI elements supporting custom settings it’s not necessary to extend the properties objects all the time but just add the desired new setting to it.
The other way around a UI element accessing a missing setting property can
consider the UI element unconfigured/unavailable if expected setting is
None.
The downside of this strategy is that it’s necessary to be careful when defining setting names. They need to be explicit enough to avoid namespace clashes between UI widgets. A good practice is to prefix widget related settings by the related tile name.
>>> from cone.app.model import Properties
>>> props = Properties
>>> props.a = '1'
>>> props.b = '2'
>>> props.keys()
['a', 'b']
>>> assert(props.a == '1')
>>> assert(props.not_exists is None)
cone.app.model.ProtectedProperties object can be used to secure property
access by permissions. Properties with no permissions are always returned.
See Security documentation for more details about
permissions.
from cone.app.model import ProtectedProperties
Define the permission map. In this example, permission ‘view’ is required to access property ‘a’, and permission ‘edit’ is required to access property ‘b’.
permissions = {
'a': ['view'],
'b': ['edit'],
}
The model to check the permissions against.
model = BaseNode()
Property data.
data = {
'a': '1', # 'view' permission protected
'b': '2', # 'edit' permission protected
'c': '3', # unprotected
}
Initialize properties.
props = ProtectedProperties(model, permissions, data)
If a user does not have the required permission granted to access a specific
property, ProtectedProperties behaves as if this property is inexistent.
Note
Write access to properties is not protected at all.
cone.app.model.Metadada class inherits from cone.app.model.Properties
and adds the marker interface cone.app.interfaces.IMetadata. This object
is for cone.app.interfaces.IApplicationNode.metadata.
cone.app.model.XMLProperties is an IProperties implementation which
can be used to serialize/deserialize properties to XML files. Supported value
types are string, list, tuple, dict and datetime.datetime.
from cone.app.model import XMLProperties
file = '/path/to/file.xml'
props = XMLProperties(file)
props.a = '1'
props() # persist to file
cone.app.model.ConfigProperties is an IProperties implementation which
can be used to serialize/deserialize properties to .ini files.
Property values are handled as unicode strings and get UTF-8 encoded. It’s
possible to change the encoding by settings the encoding attribute.
By default the properties are stored in the properties section of the
.ini file. This can be configured by setting the properties_section
attribute.
The constructor expects the file path and an optional data dictionary containing initial properties as arguments.
from cone.app.model import ConfigProperties
props = ConfigProperties(
path='/path/to/file.ini',
data=dict(a=u'a')
)
props.b = u'b'
props() # persist to file
cone.app.model.NodeInfo class inherits from cone.app.model.Properties
and adds the marker interface cone.app.interfaces.INodeInfo.
NodeInfo provides cardinality information and general node information
which is primary needed for authoring operations. The following properties are
used:
cone.app.browser.authoring.default_addmodel_factory.cone.app by
default.NodeInfo objects are not instanciated directly, instead the
cone.app.model.node_info decorator is used to register node types.
from cone.app.model import BaseNode
from cone.app.model import node_info
@node_info(
name='custom_node',
title='Custom Node',
description='A Custom Node',
factory=None,
icon='ion-ios7-gear',
addables=['other_node'])
class CustomNode(BaseNode):
pass
The NodeInfo instance can be accessed either on the application model
nodes or with cone.app.model.get_node_info.
get_node_info returns None if node info by name not exists while
model.nodeinfo always returns a NodeInfo instance, regardless whether
there has been registered a dedicated one or not.
from cone.app.model import get_node_info
# lookup node info by utility function
info = get_node_info('custom_node')
# lookup node info from model
model = CustomNode()
info = model.nodeinfo
See Forms documentation for more details.