Skip to content

Commit

Permalink
Merge branch 'master' of github.com:Pylons/substanced
Browse files Browse the repository at this point in the history
  • Loading branch information
mcdonc committed Jan 3, 2013
2 parents 44093d3 + 11481af commit 67c419a
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 35 deletions.
26 changes: 1 addition & 25 deletions TODO.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,31 +55,6 @@ Dump and Load
- Document ``sd_dump`` console script. Explain that there is no ``sd_load``,
as loading is more a development effort rather than an administrative one.

Events
++++++

- Explain event listener decorators and add_content_subscriber.

- Explain subscribe_acl_modified.

- Explain "moving" and "duplicating" flags on added and removed events.

Security
++++++++

- Explain that allowed indexes are reindexed when an object's ACL is changed.

- Explain change_acl.

- Explain that source-integral PrincipalToACLBearing relationships are set up
between an ACL-bearing object and the principals referred to within the ACL.

Initialization
++++++++++++++

- Explain __init__.py include-then-scan done by includeme and how to avoid
scanning.

Monitoring and Statistics
+++++++++++++++++++++++++

Expand Down Expand Up @@ -111,6 +86,7 @@ Misc

- Explain goggles ("@@")

- Explain undo tab

Can wait until after alpha 1
============================
Expand Down
27 changes: 27 additions & 0 deletions docs/cataloging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,30 @@ Hopefully soon we'll make this registration bit a bit less verbose. But in any
case, once this is done, whenever an object is added to the system, a value
(the result of the ``freaky()`` method of the catalog view) will be indexed in
the ``freaky`` field index.

Allowed Index and Security
--------------------------

The Substance D system catalog at
:class:`substanced.catalog.system.SystemCatalogFactory`
contains a number of default indexes, including an ``Allowed`` index.
Its job is to index security information to allow security-aware results
in queries.

In Substance D we index two permissions on each catalogued resource:
``view`` and ``sdi.view``. This allows us to constrain queries to the
system catalog based on whether the principal issuing the request has
either of those permissions on the matching resource.

To set the ACL in a way that helps keep track of all the contracts,
the helper function :func:`substanced.util.set_acl` can be used. For
example, the site root at :class:`substanced.root.Root` finishes with:

.. code-block:: python
set_acl(
self,
[(Allow, get_oid(admins), ALL_PERMISSIONS)],
registry=registry,
)
16 changes: 16 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
=============
Configuration
=============

While writing a Substance D application is very similar to writing a
Pyramid application, there are a few extra considerations to keep in
mind.

Scan and Include
================

When writing Pyramid applications, the Configurator supports
``config.include`` and ``config.scan`` Because of ordering
effects, do all your ``config.include`` calls before any of your
``config.scan`` calls.

108 changes: 108 additions & 0 deletions docs/content.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ You can convince the management interface that your particular resources are
content. To define a resource as content, you need to associate a resource
with a :term:`content type`.

.. _registering_content:

Registering Content
===================

Expand Down Expand Up @@ -567,3 +569,109 @@ Affecting the Tab Order for Management Views
The ``tab_order`` parameter overrides the mgmt_view tab settings,
for a content type, with a sequence of view names that should be
ordered (and everything not in the sequence, after.)
Handling Content Events
=======================
Adding and modifying data related to content is, thanks to the framework,
easy to do. Sometimes, though, you want to intervene and, for example,
perform some extra work when content resources are added. Substance D
has several framework events you can subscribe to using
:ref:`Pyramid events <pyramid:events_chapter>`.
The :py:mod:`substanced.events` module imports these events as interfaces
from :py:mod:`substanced.interfaces` and then provides decorator
subscribers as convenience for each:
- :py:class:`substanced.interfaces.IObjectAdded` as subscriber
``@subscriber_added``
- :py:class:`substanced.interfaces.IObjectWillBeAdded` as subscriber
``@subscriber_will_be_added``
- :py:class:`substanced.interfaces.IObjectRemoved` as subscriber
``@subscriber_removed``
- :py:class:`substanced.interfaces.IObjectWillBeRemoved` as subscriber
``@subscriber_will_be_removed``
- :py:class:`substanced.interfaces.IObjectModified` as subscriber
``@subscriber_modified``
- :py:class:`substanced.interfaces.IACLModified` as subscriber
``@subscriber_acl_modified``
- :py:class:`substanced.interfaces.IContentCreated` as subscriber
``@subscriber_created``
As an example, the
:py:func:`substanced.principal.subscribers.user_added` function is a
subscriber to the ``IObjectAdded`` event:
.. code-block:: python
@subscribe_added(IUser)
def user_added(event):
""" Give each user permission to change their own password."""
if event.loading: # fbo dump/load
return
user = event.object
registry = event.registry
set_acl(
user,
[(Allow, get_oid(user), ('sdi.view', 'sdi.change-password'))],
registry=registry,
)
As with the rest of Pyramid, you can do imperative configuration if you
don't like decorator-based configuration, using
``config.add_content_subscriber`` Both the declarative and imperative
forms result in :func:`substanced.event.add_content_subscriber`.
.. note::
While the event subscriber is de-coupled logically from the action
that triggers the event, both the action and the subscriber run
in the same transaction.
The ``IACLModified`` event (and ``@subscriber_acl_modified`` subscriber)
is used internally to Substance D to re-index information the system
catalog's ACL index. Substance D also uses this event to maintain
references between resources and principals. Substance D applications
can use this in different ways, for example recording a security audit
trail on security changes.
Sometimes when you perform operations on objects you don't want to
perform the standard events. For example, in folder contents you can
select a number of resources and move them to another folder. Normally
this would fire content change events that re-index the files. This is
fairly pointless: the content of the file hasn't changed.
If you looked at the interface for one of the content events,
you would see some extra information supported. For example, in
:py:class:`substanced.interfaces.IObjectWillBeAdded`:
.. code-block:: python
class IObjectWillBeAdded(IObjectEvent):
""" An event type sent when an before an object is added """
object = Attribute('The object being added')
parent = Attribute('The folder to which the object is being added')
name = Attribute('The name which the object is being added to the folder '
'with')
moving = Attribute('None or the folder from which the object being added '
'was moved')
loading = Attribute('Boolean indicating that this add is part of a load '
'(during a dump load process)')
duplicating = Attribute('The object being duplicated or ``None``')
``moving``, ``loading``, and ``duplicating`` are flags that can be set
on the event when certain actions are triggered. These help in cases
such as the one above: certain subscribers might want "flavors" of
standard events and, in some cases, handle the event in a different
way. This helps avoid lots of special-case events or the need for a
hierarchy of events.
Thus in the case above, the catalog subscriber can see that the changes
triggered by the event where in the special case of "moving". This can
be seen in :attr:`substanced.catalog.subscribers.object_added`.
46 changes: 38 additions & 8 deletions docs/folder_contents.rst
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,43 @@ of container. For example, :py:class:`substanced.catalog.Catalog`
is a content type that can hold only indexes. That is,it isn't meant to
hold any arbitrary kind of thing.

To tell the SDI what can be added inside a container content type,
add a ``__sdi_addable__`` method to your content type. This method is
passed the Substance D
:attr:`introspector <pyramid:pyramid.config.Configurator.introspector>`,
which you method can then use to see what content types are registered
in the site. Your ``__sdi_addable__`` method can perform some logic,
then return a filtered sequence.
To tell the SDI what can be added inside a container content type, add a
``__sdi_addable__`` method to your content type. This method is passed the
folder object representing the place the object might be added, and a Substance
D :term:`pyramid:introspectable` for a content type. When Substance D tries to
figure out whether an object is addable to a particular folder, it will call
the ``__sdi_addable__`` method of your folderish type once for each content
type.

The introspectable is a dictionary-like object which contains information about
the content type. The introspectable contains the following keys:

``meta``
A dictionary representing "meta" values passed to
:func:`~substanced.content.add_content_type`. For example, if you pass
``add_view='foo'`` to :func:`~substanced.content.add_content_type`, the
``meta`` of the content type will be ``{'add_view':'foo'}``.

``content_type``
The content type value passed to :func:`~substanced.content.add_content_type`.

``factory_type``
The ``factory_type`` value passed to
:func:`~substanced.content.add_content_type`.

``original_factory``
The original content factory (without any wrapping) passed to
:func:`~substanced.content.add_content_type`.

``factory``
The potentially wrapped content factory derived from the original factory in
:func:`~substanced.content.add_content_type`.

See :ref:`registering_content` for more information about content type
registration and what the above introspectable values mean.

Your ``__sdi_addable__`` method can perform some logic using the values it is
passed, and then it must return a filtered sequence.

As an example, the ``__sdi_addable__`` method on the ``Catalog``
filters out the kinds of things that can be added in a catalog.
Expand Down Expand Up @@ -327,4 +357,4 @@ have the correct permission (discussed above.) If our new button is
clicked, the form is posted with the ``form.reindex`` value in post
data. You can then make a ``@mgmt_view`` with
``request_param='form.reindex'`` in the declaration to handle the form
post when that button is clicked.
post when that button is clicked.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Narrative Documentation
workflows
evolution
folder_contents
configuration
retail

API Documentation
Expand Down
14 changes: 13 additions & 1 deletion docs/references.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
==========
References
----------
==========

Objects that live in the Substance D resource tree can be related to one
another using references.
Expand Down Expand Up @@ -196,3 +197,14 @@ interact with it at all, just assign and ask for attributes of your object.
The ``multireference_*`` variants are similar to the reference variants, but
they allow for more than one object on the "other side".

ACLs and Principal References
=============================

When an ACL is modified on a resource, a statement is being made about
a relationship between that resource and a principal or group of
principals. Wouldn't it be great if a reference was established,
allowing you to then see such connections in the SDI?

This is indeed exactly how Substance D behaves: a source-integral
PrincipalToACLBearing reference is set up between an ACL-bearing
resource and the principals referred to within the ACL.
2 changes: 1 addition & 1 deletion substanced/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ def upload(stream, mimetype_hint=False):
- A string containing a filename with an extension; the mimetype will
be derived from the extension in the filename.
- The constant :ref:`pyramid.file.USE_MAGIC`, which will derive the
- The constant :attr:`substanced.file.USE_MAGIC`, which will derive the
content type using the ``python-magic`` library based on the
stream's actual content.
"""
Expand Down

0 comments on commit 67c419a

Please sign in to comment.