-
Notifications
You must be signed in to change notification settings - Fork 158
documentation for publish workflow #171
Changes from 1 commit
eb8c14a
88a48ba
c60d5ea
df62e7f
5dc3597
179b5c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
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" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
.. code-block:: php | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
$container->loadFromExtension('cmf_core', array( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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()``. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. double backticks on the end should be single There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
^^^^^^^^^^^^^^^^ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
------------------------- | ||
|
||
|
@@ -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 | ||
|
@@ -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 |
There was a problem hiding this comment.
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
?There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not use
~
?