def __delitem__(self, item, flush=True): """Delete a value from the container using the key.""" if isinstance(item, string_types): item = self[item] if item.__parent_uri__ == self.__uri__: if isinstance(item, BaseContainer): for key in item.keys(): item.__delitem__(key, False) get_current_registry().notify( ptah.events.ContentDeletingEvent(item)) name = item.__name__ if self._v_keys: self._v_keys.remove(name) if self._v_items and name in self._v_items: del self._v_items[name] if item in Session: try: Session.delete(item) if flush: Session.flush() except: pass return raise KeyError(item)
def __setitem__(self, key, item): """Set a new item in the container.""" if not isinstance(item, BaseContent): raise ValueError("Content object is required") if item.__uri__ == self.__uri__: raise ValueError("Can't set to it self") parents = [p.__uri__ for p in load_parents(self)] if item.__uri__ in parents: raise TypeError("Can't itself to chidlren") if key in self.keys(): raise KeyError(key) if item.__parent_uri__ is None: event = ptah.events.ContentAddedEvent(item) else: event = ptah.events.ContentMovedEvent(item) item.__name__ = key item.__parent__ = self item.__parent_uri__ = self.__uri__ item.__path__ = '%s%s/'%(self.__path__, key) if item not in Session: Session.add(item) # temporary keys if not self._v_items: self._v_items = {key: item} else: self._v_items[key] = item if key not in self._v_keys: self._v_keys.append(key) # recursevly update children paths def update_path(container): path = container.__path__ for item in container.values(): item.__path__ = '%s%s/'%(path, item.__name__) if isinstance(item, BaseContainer): update_path(item) if isinstance(item, BaseContainer): update_path(item) get_current_registry().notify(event)
def __setitem__(self, key, item): """Set a new item in the container.""" if not isinstance(item, BaseContent): raise ValueError("Content object is required") if item.__uri__ == self.__uri__: raise ValueError("Can't set to it self") parents = [p.__uri__ for p in load_parents(self)] if item.__uri__ in parents: raise TypeError("Can't itself to chidlren") if key in self.keys(): raise KeyError(key) if item.__parent_uri__ is None: event = ptah.events.ContentAddedEvent(item) else: event = ptah.events.ContentMovedEvent(item) item.__name__ = key item.__parent__ = self item.__parent_uri__ = self.__uri__ item.__path__ = '%s%s/' % (self.__path__, key) if item not in Session: Session.add(item) # temporary keys if not self._v_items: self._v_items = {key: item} else: self._v_items[key] = item if key not in self._v_keys: self._v_keys.append(key) # recursevly update children paths def update_path(container): path = container.__path__ for item in container.values(): item.__path__ = '%s%s/' % (path, item.__name__) if isinstance(item, BaseContainer): update_path(item) if isinstance(item, BaseContainer): update_path(item) get_current_registry().notify(event)
def register_type_impl( config, cls, tinfo, name, fieldset, permission = ptah.NOT_ALLOWED, fieldNames=None, **kw): # generate schema if fieldset is None: fieldset = ptah.generate_fieldset( cls, fieldNames=fieldNames, namesFilter=names_filter) log.info("Generating fieldset for %s content type.", cls) if 'global_allow' not in kw and not issubclass(cls, Content): kw['global_allow'] = False tinfo.__dict__.update(kw) if fieldset is not None: tinfo.fieldset = fieldset tinfo.cls = cls tinfo.permission = permission config.get_cfg_storage(TYPES_DIR_ID)[tinfo.__uri__] = tinfo # sql query for content resolver cls.__uri_sql_get__ = ptah.QueryFreezer( lambda: Session.query(cls) \ .filter(cls.__uri__ == sqla.sql.bindparam('uri'))) # build cms actions build_class_actions(cls)
class BlobStorage(object): """ simple blob storage """ _sql_get = ptah.QueryFreezer(lambda: Session.query(Blob).filter( Blob.__uri__ == sqla.sql.bindparam('uri'))) _sql_get_by_parent = ptah.QueryFreezer(lambda: Session.query(Blob).filter( Blob.__parent_uri__ == sqla.sql.bindparam('parent'))) def create(self, parent=None): blob = Blob(__parent__=parent) Session.add(blob) Session.flush() return blob def add(self, data, parent=None, **metadata): blob = self.create(parent) data.seek(0) blob.data = data.read() blob.updateMetadata(**metadata) data.seek(0, os.SEEK_END) blob.size = data.tell() return blob def get(self, uri): """SQL Blob resolver""" return self._sql_get.first(uri=uri) def getByParent(self, parent): return self._sql_get_by_parent.first(parent=parent) def replace(self, uri, data, **metadata): # pragma: no cover pass def remove(self, uri): # pragma: no cover pass
def __call__(self, request=None): root = self._sql_get_root.first(name=self.name, type=self.type.__uri__) if root is None: root = self.type.create(title=self.title) root.__name_id__ = self.name root.__path__ = '/%s/' % root.__uri__ Session.add(root) Session.flush() root.__root_path__ = self.path root.__parent__ = policy = self.policy(request) root.__default_root__ = self.default_root if request is not None: set_policy(policy) request.root = root if self.parent_factory: policy.__parent__ = self.parent_factory() return root
def __call__(self, request=None): root = self._sql_get_root.first(name=self.name, type=self.type.__uri__) if root is None: root = self.type.create(title=self.title) root.__name_id__ = self.name root.__path__ = '/%s/'%root.__uri__ Session.add(root) Session.flush() root.__root_path__ = self.path root.__parent__ = policy = self.policy(request) root.__default_root__ = self.default_root if request is not None: set_policy(policy) request.root = root if self.parent_factory: policy.__parent__ = self.parent_factory() return root
def __init__(self, cls, path='', name='', title='', policy=ApplicationPolicy, default_root=None, parent_factory=None, config=None): self.id = '-'.join(part for part in path.split('/') if part) self.path = path if path.endswith('/') else '%s/' % path self.name = name self.title = title self.default_root = default_root if (self.path == '/') and default_root is None: self.default_root = True self.cls = cls self.type = cls.__type__ self.policy = policy self.parent_factory = parent_factory discr = (APPFACTORY_ID, path) intr = ptah.config.Introspectable(APPFACTORY_ID, discr, name, APPFACTORY_ID) intr['id'] = self.id intr['factory'] = self if config is not None: ptah.config.get_cfg_storage( APPFACTORY_ID, registry=config.registry)[self.id] = self info = ptah.config.DirectiveInfo() info.attach( ptah.config.Action( lambda cfg: cfg.get_cfg_storage(APPFACTORY_ID)\ .update({self.id: self}), discriminator=discr, introspectables=(intr,)) ) self._sql_get_root = ptah.QueryFreezer( lambda: Session.query(cls)\ .filter(sqla.sql.and_( cls.__name_id__ == sqla.sql.bindparam('name'), cls.__type_id__ == sqla.sql.bindparam('type'))))
def __init__(self, cls, path='', name='', title='', policy = ApplicationPolicy, default_root = None, parent_factory = None, config=None): self.id = '-'.join(part for part in path.split('/') if part) self.path = path if path.endswith('/') else '%s/'%path self.name = name self.title = title self.default_root = default_root if (self.path == '/') and default_root is None: self.default_root = True self.cls = cls self.type = cls.__type__ self.policy = policy self.parent_factory = parent_factory discr = (APPFACTORY_ID, path) intr = ptah.config.Introspectable( APPFACTORY_ID, discr, name, APPFACTORY_ID) intr['id'] = self.id intr['factory'] = self if config is not None: ptah.config.get_cfg_storage( APPFACTORY_ID, registry=config.registry)[self.id] = self info = ptah.config.DirectiveInfo() info.attach( ptah.config.Action( lambda cfg: cfg.get_cfg_storage(APPFACTORY_ID)\ .update({self.id: self}), discriminator=discr, introspectables=(intr,)) ) self._sql_get_root = ptah.QueryFreezer( lambda: Session.query(cls)\ .filter(sqla.sql.and_( cls.__name_id__ == sqla.sql.bindparam('name'), cls.__type_id__ == sqla.sql.bindparam('type'))))
def create(self, parent=None): blob = Blob(__parent__=parent) Session.add(blob) Session.flush() return blob
def __call__(self, request, queries=_path_queries): environ = request.environ context = root = self.root if root.__default_root__ and 'bfg.routes.route' in environ: return ResourceTreeTraverser(root)(request) path = '/%s/'%'/'.join(traversal_path(environ.get('PATH_INFO','/'))) vroot_tuple = () l_path = len(root.__root_path__) # build paths for all parents in content hierarchy idx = 0 paths = {} current = root.__path__ for sec in path[l_path:].split('/'): if sec: current = '%s%s/'%(current, sec) paths[str(idx)] = current idx += 1 if idx: if idx not in queries: bindparams = [sql.bindparam(str(p)) for p in range(idx)] queries[idx] = ptah.QueryFreezer( lambda: Session.query(BaseContent)\ .filter(BaseContent.__path__.in_(bindparams)) .order_by(sql.desc(BaseContent.__path__))) parents = queries[idx].all(**paths) else: parents = [] if parents: # set __parent__ parents[-1].__parent__ = root for idx in range(len(parents)-1): parents[idx].__parent__ = parents[idx+1] context = parents[0] node = context.__path__[len(root.__path__):] leaf = path[l_path+len(node):].split('/') leaf, subpath = leaf[0], leaf[1:] return {'context': context, 'view_name': leaf, 'subpath': subpath, 'traversed': traversal_path(node), 'virtual_root': root, 'virtual_root_path': vroot_tuple, 'root': root} else: vpath_tuple = () leaf = path[l_path:].split('/') leaf, subpath = leaf[0], leaf[1:] return {'context': context, 'view_name': leaf, 'subpath': subpath, 'traversed': vpath_tuple, 'virtual_root': root, 'virtual_root_path': (), 'root': root}
class BaseContent(Node): """ Base class for content objects. A content class should inherit from `Content` to participate in content hierarchy traversal. .. attribute:: __path__ A string used by the :py:class:`ptah.cms.ContentTraverser` which is used for efficient resolution of URL structure to content models. This is internal implementation and manually editing it can break your hierarchy. .. attribute:: __name__ This is the identifier in a container if you are using containment and hierarchies. .. attribute:: title Content title which is editable by end user. .. attribute:: description Content description which is editable by end user. .. attribute:: view A URI which can be resolved with :py:func:`ptah.resolve` function which represents the 'default' view for content. Akin to index.html or default.php in Apache. .. attribute:: created Content creation time which is set by :py:func:`ptah.cms.content.createdHandler` during object creation. :type: :py:class:`datetime.datetime` .. attribute:: modified Content modification time which is set by :py:func:`ptah.cms.content.modifiedHandler` during object modification. :type: :py:class:`datetime.datetime` .. attribute:: effective :type: :py:class:`datetime.datetime` or None .. attribute:: expires :type: :py:class:`datetime.datetime` or None .. attribute:: creators a :py:class:`ptah.JsonListType` which contains sequence of users. Using principal URIs is a good idea. .. attribute:: subjects a :py:class:`ptah.JsonListType` which contains sequence of subjects. Holding a sequence of URIs could resolve to subject objects. Or you can use strings. .. attribute: publisher a Unicode string which should identify the publisher. .. attribute: contributors a :py:class:`ptah.JsonListType` which contains sequence of contributors. You could keep a sequence of principal URIs. """ __tablename__ = 'ptah_content' __id__ = sqla.Column('id', sqla.Integer, sqla.ForeignKey('ptah_nodes.id'), primary_key=True) __path__ = sqla.Column('path', sqla.Unicode, default=text_type('')) __name_id__ = sqla.Column('name', sqla.Unicode(255)) title = sqla.Column(sqla.Unicode, default=text_type('')) description = sqla.Column(sqla.Unicode, default=text_type(''), info={ 'missing': '', 'field_type': 'textarea' }) view = sqla.Column(sqla.Unicode, default=text_type('')) created = sqla.Column(sqla.DateTime) modified = sqla.Column(sqla.DateTime) effective = sqla.Column(sqla.DateTime) expires = sqla.Column(sqla.DateTime) creators = sqla.Column(ptah.JsonListType(), default=[]) subjects = sqla.Column(ptah.JsonListType(), default=[]) publisher = sqla.Column(sqla.Unicode, default=text_type('')) contributors = sqla.Column(ptah.JsonListType(), default=[]) # sql queries _sql_get = ptah.QueryFreezer(lambda: Session.query(BaseContent).filter( BaseContent.__uri__ == sqla.sql.bindparam('uri'))) _sql_get_in_parent = ptah.QueryFreezer( lambda: Session.query(BaseContent).filter( BaseContent.__name_id__ == sqla.sql.bindparam('key')).filter( BaseContent.__parent_uri__ == sqla.sql.bindparam('parent'))) _sql_parent = ptah.QueryFreezer(lambda: Session.query(BaseContent).filter( BaseContent.__uri__ == sqla.sql.bindparam('parent'))) def __init__(self, **kw): super(BaseContent, self).__init__(**kw) if self.__name__ and self.__parent__ is not None: self.__path__ = '%s%s/' % (self.__parent__.__path__, self.__name__) @hybrid_property def __name__(self): return self.__name_id__ @__name__.setter def __name__(self, value): self.__name_id__ = value def __resource_url__(self, request, info): return '%s%s' % (request.root.__root_path__, self.__path__[len(request.root.__path__):]) @action(permission=DeleteContent) def delete(self): parent = self.__parent__ if parent is None: parent = self.__parent_ref__ if parent is None: raise Error("Can't find parent") del parent[self] @action(permission=ModifyContent) def update(self, **data): if self.__type__: tinfo = self.__type__ for field in tinfo.fieldset.fields(): val = data.get(field.name, field.default) if val is not ptah.form.null: setattr(self, field.name, val) get_current_registry().notify( ptah.events.ContentModifiedEvent(self)) def _extra_info(self, info): if self.__type__: fieldset = self.__type__.fieldset.bind() for field in fieldset.fields(): val = getattr(self, field.name, field.default) info[field.name] = field.serialize(val) info['view'] = self.view info['created'] = self.created info['modified'] = self.modified info['effective'] = self.effective info['expires'] = self.expires def info(self): info = super(BaseContent, self).info() info['__name__'] = self.__name__ info['__content__'] = True info['__container__'] = False self._extra_info(info) return info
class BaseContainer(BaseContent): """ Content container implementation. """ _v_keys = None _v_keys_loaded = False _v_items = None _sql_keys = ptah.QueryFreezer( lambda: Session.query(BaseContent.__name_id__).filter( BaseContent.__parent_uri__ == sqla.sql.bindparam('uri'))) _sql_values = ptah.QueryFreezer(lambda: Session.query(BaseContent).filter( BaseContent.__parent_uri__ == sqla.sql.bindparam('uri'))) def keys(self): """Return an list of the keys in the container.""" if self._v_keys_loaded: return self._v_keys else: if self._v_keys is None: self._v_keys = [ n for n, in self._sql_keys.all(uri=self.__uri__) ] self._v_keys_loaded = True return self._v_keys def get(self, key, default=None): """Get a value for a key The default is returned if there is no value for the key. """ if self._v_items and key in self._v_items: return self._v_items[key] item = self._sql_get_in_parent.first(key=key, parent=self.__uri__) if item is not None: item.__parent__ = self if not self._v_items: self._v_items = {key: item} else: self._v_items[key] = item return item def values(self): """Return an list of the values in the container.""" if self._v_keys_loaded and self._v_items: if len(self._v_items) == len(self._v_keys): return self._v_items.values() values = [] old_items = self._v_items self._v_keys = keys = [] self._v_items = items = {} for item in self._sql_values.all(uri=self.__uri__): item.__parent__ = self items[item.__name__] = item keys.append(item.__name__) values.append(item) if old_items: for name, item in old_items.items(): if name not in items: keys.append(name) items[name] = item values.append(item) return values def items(self): """Return the items of the container.""" for item in self.values(): yield item.__name__, item def __contains__(self, key): """Tell if a key exists in the mapping.""" return key in self.keys() def __getitem__(self, key): """Get a value for a key A KeyError is raised if there is no value for the key. """ if self._v_items and key in self._v_items: return self._v_items[key] try: item = self._sql_get_in_parent.one(key=key, parent=self.__uri__) item.__parent__ = self if not self._v_items: self._v_items = {key: item} else: self._v_items[key] = item return item except sqla.orm.exc.NoResultFound: raise KeyError(key) def __setitem__(self, key, item): """Set a new item in the container.""" if not isinstance(item, BaseContent): raise ValueError("Content object is required") if item.__uri__ == self.__uri__: raise ValueError("Can't set to it self") parents = [p.__uri__ for p in load_parents(self)] if item.__uri__ in parents: raise TypeError("Can't itself to chidlren") if key in self.keys(): raise KeyError(key) if item.__parent_uri__ is None: event = ptah.events.ContentAddedEvent(item) else: event = ptah.events.ContentMovedEvent(item) item.__name__ = key item.__parent__ = self item.__parent_uri__ = self.__uri__ item.__path__ = '%s%s/' % (self.__path__, key) if item not in Session: Session.add(item) # temporary keys if not self._v_items: self._v_items = {key: item} else: self._v_items[key] = item if key not in self._v_keys: self._v_keys.append(key) # recursevly update children paths def update_path(container): path = container.__path__ for item in container.values(): item.__path__ = '%s%s/' % (path, item.__name__) if isinstance(item, BaseContainer): update_path(item) if isinstance(item, BaseContainer): update_path(item) get_current_registry().notify(event) def __delitem__(self, item, flush=True): """Delete a value from the container using the key.""" if isinstance(item, string_types): item = self[item] if item.__parent_uri__ == self.__uri__: if isinstance(item, BaseContainer): for key in item.keys(): item.__delitem__(key, False) get_current_registry().notify( ptah.events.ContentDeletingEvent(item)) name = item.__name__ if self._v_keys: self._v_keys.remove(name) if self._v_items and name in self._v_items: del self._v_items[name] if item in Session: try: Session.delete(item) if flush: Session.flush() except: pass return raise KeyError(item) @action def create(self, tname, name, **params): tinfo = ptah.resolve(tname) if tinfo is None: raise NotFound('Type information is not found') tinfo.check_context(self) if '/' in name: raise Error("Names cannot contain '/'") if name.startswith(' '): raise Error("Names cannot starts with ' '") if name in self: raise Error("Name already in use.") self[name] = tinfo.create() content = self[name] content.update(**params) return content @action(permission=DeleteContent) def batchdelete(self, uris): """Batch delete""" raise NotImplements() # pragma: no cover def info(self): info = super(BaseContainer, self).info() info['__container__'] = True return info
def __call__(self, request, queries=_path_queries): environ = request.environ context = root = self.root if root.__default_root__ and 'bfg.routes.route' in environ: return ResourceTreeTraverser(root)(request) path = '/%s/' % '/'.join(traversal_path(environ.get('PATH_INFO', '/'))) vroot_tuple = () l_path = len(root.__root_path__) # build paths for all parents in content hierarchy idx = 0 paths = {} current = root.__path__ for sec in path[l_path:].split('/'): if sec: current = '%s%s/' % (current, sec) paths[str(idx)] = current idx += 1 if idx: if idx not in queries: bindparams = [sql.bindparam(str(p)) for p in range(idx)] queries[idx] = ptah.QueryFreezer( lambda: Session.query(BaseContent)\ .filter(BaseContent.__path__.in_(bindparams)) .order_by(sql.desc(BaseContent.__path__))) parents = queries[idx].all(**paths) else: parents = [] if parents: # set __parent__ parents[-1].__parent__ = root for idx in range(len(parents) - 1): parents[idx].__parent__ = parents[idx + 1] context = parents[0] node = context.__path__[len(root.__path__):] leaf = path[l_path + len(node):].split('/') leaf, subpath = leaf[0], leaf[1:] return { 'context': context, 'view_name': leaf, 'subpath': subpath, 'traversed': traversal_path(node), 'virtual_root': root, 'virtual_root_path': vroot_tuple, 'root': root } else: vpath_tuple = () leaf = path[l_path:].split('/') leaf, subpath = leaf[0], leaf[1:] return { 'context': context, 'view_name': leaf, 'subpath': subpath, 'traversed': vpath_tuple, 'virtual_root': root, 'virtual_root_path': (), 'root': root }