Skip to content
This repository has been archived by the owner on Sep 16, 2021. It is now read-only.

documentation for publish workflow #171

Merged
merged 6 commits into from
Jul 14, 2013
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
218 changes: 203 additions & 15 deletions bundles/core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,38 +23,212 @@ Configuration

# app/config/config.yml
cmf_core:
document_manager_name: default
role: IS_AUTHENTICATED_ANONYMOUSLY # used by the publish workflow checker
document_manager_name: null # used for the twig functions to fetch documents
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be manager_name?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed it should, but its not. i'll do a separate PR once this change is merged.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rmsint: it would be consistent, yes. but unfortunately its like this in
the actual bundle atm. we should clean this up however. care to do a pr?
or open an issue so we don't forget?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I will do a PR on the CoreBundle to change it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not use ~?

publish_workflow:
enabled: true
view_non_published_role: CAN_VIEW_NON_PUBLISHED
request_listener: true

.. code-block:: xml

<!-- app/config/config.xml -->
<!-- role: used by the publish workflow checker -->
<config xmlns="http://symfony.com/schema/dic/core"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

symfony.com => cmf.symfony.com

document-manager-name="default"
role="IS_AUTHENTICATED_ANONYMOUSLY"
/>
document-manager-name="null">
<publish-workflow
enabled="true"
view-non-published-role="CAN_VIEW_NON_PUBLISHED"
request-listener="true"
/>
<config/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

</config>


.. code-block:: php

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// app/config/config.php

$container->loadFromExtension('cmf_core', array(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be indented one level back

'document_manager_name' => null,
'publish_workflow' => array(
'enabled' => true,
'view_non_published_role' => 'CAN_VIEW_NON_PUBLISHED',
'request_listener' => true,
),
));

The publish workflow is enabled by default. If you do not want to use it, you
can set ``cmf_core.publish_workflow.enabled: false`` to gain some performance.

.. _bundle-core-publish_workflow:

Publish Workflow Checker
------------------------
Publish Workflow
----------------

The Bundle provides a ``cmf_core.publish_workflow_checker`` service
which implements ``PublishWorkflowCheckerInterface``. This interface defines a
single method ``checkIsPublished()``.
The publish workflow system allows to control what content is available on the
site. This is similar to the Symfony2 security component. But contrary to the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest add a link to the security component docs

security check, the publish check can be executed even when no firewall is in
place and the security context thus has no token (see `Symfony2 Authorization`_).

The publish workflow is also tied into the security workflow: The core bundle
registers a security voter that forwards security checks to the publish
workflow. This means that if you always have a firewall, you can just use
the normal security context and the twig function ``is_granted`` to check for
publication.

Checking if content is published
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if Content is Published

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The Bundle provides the ``cmf_core.publish_workflow.checker`` service which
implements the :class:`Symfony\\Component\\Security\\Core\\SecurityContextInterface`
of the Symfony security component. The method to check publication is, like
with the security context,
:method:`Symfony\\Component\\Security\\Core\\SecurityContextInterface::isGranted()``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

double backticks on the end should be single

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and you must remove ()


This method is used as when doing `ACL checks`_: The first argument is the
desired action, the second the content object you want to do the action on.

In 1.0, the only actions supported by the default voters are ``VIEW`` and
``VIEW_ANONYMOUS``. Having the right to view means that the current user is
allowed to see this content either because it is published or because of his
specific permissions. In some contexts, your application might not want to
show unpublished content even to a privileged user so as not to confuse him.
For this, the "view anonymous" permission is used.

The workflow checker is configured with a role that is allowed to bypass
publication checks so that it can see unpublished content. This role should be
given to editors. The default name of the role is ``CAN_VIEW_NON_PUBLISHED``.

.. code-block:: php

$publishWorkflowChecker = $container->get('cmf_core.publish_workflow_checker');
$publishWorkflowChecker = $container->get('cmf_core.publish_workflow.checker');

// if to ignore the role when deciding if to consider the document as published
$ignoreRole = false;
if ($publishWorkflowChecker->checkIsPublished($document, $ignoreRole)) {

if ($publishWorkflowChecker->isGranted('VIEW', $document)) {
// ...
}

Code that loads content should do the publish checks. Note that the twig
functions already check for publication. Thanks to a
:ref:`request listener <bundle-core-workflow-request_listener>`, routes and
the main content provided by the
:ref:`DynamicRouter <bundles-routing-dynamic_router>` are checked automatically
as well.

It is possible to set the token explicitly on the workflow checker. But by
default, the checker will acquire the token from the default security context,
and if there is none (typically when there is no firewall in place for that
URL), an
:class:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\AnonymousToken`
is created on the fly. If we check for ``VIEW`` and not ``VIEW_ANONYMOUS``, it
checks whether there is a current user and if that user is granted the bypass
role. If so, access is granted, otherwise the decision is delegated to the
:class:`Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager`
which calls all voters with the requested attributes, the object and the token.

The decision manager is configured for an unanimous vote with "allow if all
abstain". This means a single voter saying ``ACCESS_DENIED`` is enough for
the content to be considered not published. If all voters abstain (for example
when the content in question does not implement any workflow features) the
content is still considered published.

Publish voters
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Publish Voters

~~~~~~~~~~~~~~

A voter has to implement the
:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`.
It will get passed a content object and has to decide whether it is published
according to its rules. The ``CoreBundle`` provides a couple of generic voters
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bundle names should not be included in a literal

that check the content for having an interface exposing the methods they need.
If the content implements the interface, they check the parameter and return
``ACCESS_GRANTED`` resp ``ACCESS_DENIED``, otherwise they return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like abbrevations in the docs

``ACCESS_ABSTAIN``.

You can also implement your :ref:`own voters <bundle-core-workflow_custom_voters>` for
additional publication behaviour.

PublishableVoter
^^^^^^^^^^^^^^^^
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use dots to follow our standards


This voter checks on the ``PublishableInterface`` which simply has a method to
return a boolean value.

* **isPublishable**: If the object should be considered for publication or not;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lasr items of a list should end with a dot

TimePeriodVoter
^^^^^^^^^^^^^^^

This voter checks on the ``PublishTimePeriodInterface`` which defines a start
and end date. A date may be null to indicate "always started" resp.
"never ending".

* **getPublishStartDate**: If non-null, the date from which the document
should start being published;
* **getPublishEndDate**: If non-null, the date from which the document
should stop being published.

.. _bundle-core-workflow_custom_voters:

Custom Voters
^^^^^^^^^^^^^

To build voters with custom logic, you need to implement
:class:`Symfony\\Component\\Security\\Core\\Authentication\\Voter\\VoterInterface`
and define a service with the tag ``cmf_published_voter``. This is similar
to the ``security.voter`` tag, but adds your voter to the publish workflow. As
with the security voters, you can specify a priority, though it is of limited
use as the access decision must be unanimous. If you have more expensive checks,
you can lower the priority of those voters.

.. configuration-block::

.. code-block:: yaml

# services.yml
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generalky, don't include filename comments for service config

services:
acme.security.publishable_voter:
class: %my_namespace.security.publishable_voter.class%
tags:
- { name: cmf_published_voter, priority: 30 }

.. code-block:: xml

<!-- services.xml -->
<service id="acme.security.publishable_voter" class="%acme.security.publishable_voter.class%">
<tag name="cmf_published_voter" priority="30"/>
</service>

.. code-block:: php

use Symfony\Component\DependencyInjection\Definition;

$definition = new Definition('%acme.security.publishable_voter.class%');
$definition->addTag('cmf_published_voter', array('priority' => 30));
$container->setDefinition('acme.security.publishable_voter', $definition);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer:

$container->register('acme.security.publishable_voter', '%acme.security.publishable_voter.class%')
    ->addTag('cmf_published_voter', array('priority' => 30)); 



As the workflow checker will create an
:class:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\AnonymousToken` on
the fly if the security context has none, voters must be able to handle this
situation when accessing the user. Also when accessing the security context,
they first must check if it has a token and otherwise not call it to avoid
triggering an exception. If a voter only gives access if there is a current
user fulfills some requirement, it simply has to return ``ACCESS_DENIED`` if
there is no current user.

.. _bundle-core-workflow-request_listener:

Publication Request Listener
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The :ref:`DynamicRouter <bundles-routing-dynamic_router>` places the route
object and the main content - if the route has a main content - into the
request attributes. Unless you disable the
``cmf_core.publish_workflow.request_listener``, this listener will listen
on all requests and check publication of both the route object and the main
content object.

This means that custom templates for ``templates_by_class`` and the controllers
of ``controllers_by_class`` need not check for publication explicitly as its
already done.

Dependency Injection Tags
-------------------------

Expand Down Expand Up @@ -86,11 +260,23 @@ service must have the ``setRequest`` method or you will get a fatal error::
service definition and call ``$this->container->get('request')`` in your
code when you actually need the request.

cmf_published_voter
~~~~~~~~~~~~~~~~~~~

Used to activate voters for the
:ref:`publish workflow <bundle-core-publish_workflow>`. Tagging a service with
``cmf_published_voter`` integrates it into the access decision of the publish
workflow.

This tag has the attribute *priority*. The lower the priority number, the
earlier the voter gets to vote.

Twig extension
--------------

Implements the following functions:
This provides a set of useful twig functions for your templates. The functions
respect the :ref:`publish workflow <bundle-core-publish_workflow>` if it is
enabled.

* **cmf_find**: returns the document for the provided path
* **cmf_find_many**: returns an array of documents for the provided paths
Expand Down Expand Up @@ -144,3 +330,5 @@ Implements the following functions:
{% endif %}

.. _`CoreBundle`: https://github.com/symfony-cmf/CoreBundle#readme
.. _`Symfony2 Authorization`: http://symfony.com/doc/current/components/security/authorization.html
.. _`ACL checks`: http://symfony.com/doc/current/cookbook/security/acl.html
2 changes: 2 additions & 0 deletions bundles/routing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ will look like this

See also official Symfony2 `documentation for DependencyInjection tags`_

.. _bundles-routing-dynamic_router:

Dynamic Router
--------------

Expand Down