From eb8c14abe834e88906a29479edd1d5a847913ec2 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Wed, 26 Jun 2013 09:52:55 +0200 Subject: [PATCH 1/5] documentation for publish workflow --- bundles/core.rst | 218 +++++++++++++++++++++++++++++++++++++++++--- bundles/routing.rst | 2 + 2 files changed, 205 insertions(+), 15 deletions(-) diff --git a/bundles/core.rst b/bundles/core.rst index 5f50345b..a1a28bcb 100644 --- a/bundles/core.rst +++ b/bundles/core.rst @@ -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 - + document-manager-name="null"> + + + + .. code-block:: php + + $container->loadFromExtension('cmf_core', array( + '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 +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 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +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()``. + +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 `, routes and +the main content provided by the +:ref:`DynamicRouter ` 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 +~~~~~~~~~~~~~~ + +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 +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 +``ACCESS_ABSTAIN``. + +You can also implement your :ref:`own voters ` for +additional publication behaviour. + +PublishableVoter +^^^^^^^^^^^^^^^^ + +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; + +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 + services: + acme.security.publishable_voter: + class: %my_namespace.security.publishable_voter.class% + tags: + - { name: cmf_published_voter, priority: 30 } + + .. code-block:: xml + + + + + + + .. 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); + + +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 ` 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 `. 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 ` 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 \ No newline at end of file diff --git a/bundles/routing.rst b/bundles/routing.rst index d2ebd8dc..ef2b3b34 100644 --- a/bundles/routing.rst +++ b/bundles/routing.rst @@ -97,6 +97,8 @@ will look like this See also official Symfony2 `documentation for DependencyInjection tags`_ +.. _bundles-routing-dynamic_router: + Dynamic Router -------------- From 88a48baa494bda5f1751779da67294aece401434 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sun, 30 Jun 2013 22:45:10 +0200 Subject: [PATCH 2/5] cleanups --- bundles/core.rst | 104 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 28 deletions(-) diff --git a/bundles/core.rst b/bundles/core.rst index a1a28bcb..91b7bac8 100644 --- a/bundles/core.rst +++ b/bundles/core.rst @@ -26,7 +26,7 @@ Configuration document_manager_name: null # used for the twig functions to fetch documents publish_workflow: enabled: true - view_non_published_role: CAN_VIEW_NON_PUBLISHED + view_non_published_role: ROLE_CAN_VIEW_NON_PUBLISHED request_listener: true .. code-block:: xml @@ -36,7 +36,7 @@ Configuration document-manager-name="null"> @@ -47,7 +47,7 @@ Configuration 'document_manager_name' => null, 'publish_workflow' => array( 'enabled' => true, - 'view_non_published_role' => 'CAN_VIEW_NON_PUBLISHED', + 'view_non_published_role' => 'ROLE_CAN_VIEW_NON_PUBLISHED', 'request_listener' => true, ), )); @@ -61,7 +61,7 @@ Publish Workflow ---------------- 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 +site. This is similar to the `Symfony2 security component`_. But contrary to the 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`_). @@ -71,8 +71,11 @@ 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 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +A good introduction to the Symfony core security is the `Security Chapter`_ in +the Symfony2 book. + +Check if content is published +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Bundle provides the ``cmf_core.publish_workflow.checker`` service which implements the :class:`Symfony\\Component\\Security\\Core\\SecurityContextInterface` @@ -92,18 +95,65 @@ 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``. +given to editors. The default name of the role is ``ROLE_CAN_VIEW_NON_PUBLISHED``. -.. code-block:: php +.. configuration-block:: - $publishWorkflowChecker = $container->get('cmf_core.publish_workflow.checker'); + .. code-block:: yaml + + # app/config/security.yml + security: + role_hierarchy: + ROLE_EDITOR: ROLE_CAN_VIEW_NON_PUBLISHED + + .. code-block:: xml + + + + ROLE_CAN_VIEW_NON_PUBLISHED + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'role_hierarchy' => array( + 'ROLE_EDITOR' => 'ROLE_CAN_VIEW_NON_PUBLISHED', + ), + )); + +Once a user with ``ROLE_EDITOR`` is logged in - meaning there is a firewall in place for the path +in question - he will have the permission to view unpublished content as well:: - // if to ignore the role when deciding if to consider the document as published - $ignoreRole = false; + use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishWorkflowChecker; - if ($publishWorkflowChecker->isGranted('VIEW', $document)) { + // check if current user is allowed to see this document + $publishWorkflowChecker = $container->get('cmf_core.publish_workflow.checker'); + if ($publishWorkflowChecker->isGranted(PublishWorkflowChecker::VIEW_ATTRIBUTE, $document)) { // ... } + // check if the document is published, do not make an exception if current + // user would have the right to see this content + if ($publishWorkflowChecker->isGranted(PublishWorkflowChecker::VIEW_ANONYMOUS_ATTRIBUTE, $document)) { + // ... + } + +To check publication in a template, use the twig function ``cmf_is_published``: + +.. code-block:: jinja + + {# check if document is published, regardless of current users role #} + {% if cmf_is_published(page) %} + {# output the document #} + {% endif %} + + {# check if current logged in user is allowed to view the document either + because it is published or because the current user may view unpublished + documents. + #} + {% if is_granted('VIEW', page) %} + {# output the document #} + {% endif %} Code that loads content should do the publish checks. Note that the twig functions already check for publication. Thanks to a @@ -129,13 +179,13 @@ 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 +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 +according to its rules. The CoreBundle` provides a couple of generic voters 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 @@ -145,15 +195,15 @@ You can also implement your :ref:`own voters @@ -199,10 +247,8 @@ you can lower the priority of those voters. 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); - + $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 @@ -263,8 +309,8 @@ service must have the ``setRequest`` method or you will get a fatal error:: cmf_published_voter ~~~~~~~~~~~~~~~~~~~ -Used to activate voters for the -:ref:`publish workflow `. Tagging a service with +Used to activate :ref:`custom voters ` for the +:ref:`publish workflow ` . Tagging a service with ``cmf_published_voter`` integrates it into the access decision of the publish workflow. @@ -330,5 +376,7 @@ enabled. {% 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 \ No newline at end of file +.. _`Symfony2 security component`: http://www.symfony.com/doc/current/components/security/index.html +.. _`Symfony2 Authorization`: http://www.symfony.com/doc/current/components/security/authorization.html +.. _`Security Chapter`: http://www.symfony.com/doc/current/book/security.html +.. _`ACL checks`: http://www.symfony.com/doc/current/cookbook/security/acl.html \ No newline at end of file From c60d5ea86648e1eaf39e2215836cfe5a659283f8 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sat, 13 Jul 2013 11:40:39 +0200 Subject: [PATCH 3/5] some clarifications --- bundles/core.rst | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/bundles/core.rst b/bundles/core.rst index 91b7bac8..b3d21c98 100644 --- a/bundles/core.rst +++ b/bundles/core.rst @@ -62,7 +62,7 @@ Publish Workflow 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 -security check, the publish check can be executed even when no firewall is in +security context, 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 @@ -162,14 +162,16 @@ the main content provided by the :ref:`DynamicRouter ` 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 +It is possible to set the security 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 +is created on the fly. + +If you check for ``VIEW`` and not ``VIEW_ANONYMOUS``, the first check is +whether the security context knows the current user and if that user is granted +the bypass role. If so, access is granted, otherwise the decision is delegated to a :class:`Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager` which calls all voters with the requested attributes, the object and the token. @@ -185,14 +187,18 @@ 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 +according to its rules. The CoreBundle provides a couple of generic voters 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 ``ACCESS_ABSTAIN``. -You can also implement your :ref:`own voters ` for -additional publication behaviour. +As voting is unanimous, each voter returns ``ACCESS_GRANTED`` if its criteria +is met, but if a single voter returns ``ACCESS_DENIED``, the content is +considered not published. + +You can also implement your :ref:`own voters ` +for additional publication behaviour. PublishableVoter ................ @@ -326,7 +332,10 @@ enabled. * **cmf_find**: returns the document for the provided path * **cmf_find_many**: returns an array of documents for the provided paths -* **cmf_is_published**: checks if the provided document is published +* **cmf_is_published**: checks if the provided document is published, + regardless of an eventual bypass permission of the current user. If you need + the bypass role, you will have a firewall configured for the route the + template is rendered in and can simply use ``{{ is_granted('VIEW', document) }}`` * **cmf_prev**: returns the previous document by examining the child nodes of the provided parent * **cmf_prev_linkable**: returns the previous linkable document by examining From df62e7f1fccb51a094d5d8c2045f7c047c7dbe1b Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sat, 13 Jul 2013 11:55:35 +0200 Subject: [PATCH 4/5] add admin extension doc --- bundles/core.rst | 68 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/bundles/core.rst b/bundles/core.rst index b3d21c98..b89c8fe4 100644 --- a/bundles/core.rst +++ b/bundles/core.rst @@ -281,6 +281,71 @@ 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. +Editing publication information: Publish Workflow Sonata Admin Extension +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There is a write interface for each publish workflow too, defining setter +methods. The core bundle provides extensions for SonataAdminBundle to easily +add editing of the publish workflow fields to all or selected admins. + +Instead of implementing ``PublishableInterface`` resp. +``PublishTimePeriodInterface`` you models instead need to implement the +``PublishableWriteInterface`` and / or ``PublishTimePeriodWriteInterface``. + +To enable the extensions in your admin classes, simply define the extension +configuration in the ``sonata_admin`` section of your project configuration: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + sonata_admin: + # ... + extensions: + symfony_cmf_core.publish_workflow.admin_extension.publishable: + implements: + - Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishableWriteInterface + symfony_cmf_core.publish_workflow.admin_extension.time_period: + implements: + - Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishTimePeriodWriteInterface + + .. code-block:: xml + + + + + + + + + Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishableWriteInterface + + + + + Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishTimePeriodWriteInterface + + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('sonata_admin', array( + 'extensions' => array( + 'symfony_cmf_core.admin_extension.publish_workflow' => array( + 'implements' => array( + 'Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishWorkflowInterface', + ), + ), + ), + )); + +See the `Sonata Admin extension documentation`_ for more information. + Dependency Injection Tags ------------------------- @@ -388,4 +453,5 @@ enabled. .. _`Symfony2 security component`: http://www.symfony.com/doc/current/components/security/index.html .. _`Symfony2 Authorization`: http://www.symfony.com/doc/current/components/security/authorization.html .. _`Security Chapter`: http://www.symfony.com/doc/current/book/security.html -.. _`ACL checks`: http://www.symfony.com/doc/current/cookbook/security/acl.html \ No newline at end of file +.. _`ACL checks`: http://www.symfony.com/doc/current/cookbook/security/acl.html +.. _`Sonata Admin extension documentation`: http://sonata-project.org/bundles/admin/master/doc/reference/extensions.html \ No newline at end of file From 5dc3597d61b3ccbd1f79de74e3df68718ce3a15a Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sun, 14 Jul 2013 16:02:00 +0200 Subject: [PATCH 5/5] cleaning up publish workflow after feedback --- bundles/core.rst | 109 ++++++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 44 deletions(-) diff --git a/bundles/core.rst b/bundles/core.rst index b89c8fe4..6b58fed6 100644 --- a/bundles/core.rst +++ b/bundles/core.rst @@ -23,7 +23,7 @@ Configuration # app/config/config.yml cmf_core: - document_manager_name: null # used for the twig functions to fetch documents + document_manager_name: ~ # used for the twig functions to fetch documents publish_workflow: enabled: true view_non_published_role: ROLE_CAN_VIEW_NON_PUBLISHED @@ -32,18 +32,19 @@ Configuration .. code-block:: xml - - + .. code-block:: php - $container->loadFromExtension('cmf_core', array( + // app/config/config.php + $container->loadFromExtension('cmf_core', array( 'document_manager_name' => null, 'publish_workflow' => array( 'enabled' => true, @@ -61,7 +62,7 @@ Publish Workflow ---------------- 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 +site. This is similar to the `Symfony2 Security component`_. But contrary to the security context, the publish check can be executed even when no firewall is in place and the security context thus has no token (see `Symfony2 Authorization`_). @@ -74,14 +75,14 @@ publication. A good introduction to the Symfony core security is the `Security Chapter`_ in the Symfony2 book. -Check if content is published +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()``. +:method:`Symfony\\Component\\Security\\Core\\SecurityContextInterface::isGranted`. 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. @@ -109,7 +110,7 @@ given to editors. The default name of the role is ``ROLE_CAN_VIEW_NON_PUBLISHED` .. code-block:: xml - + ROLE_CAN_VIEW_NON_PUBLISHED @@ -118,7 +119,7 @@ given to editors. The default name of the role is ``ROLE_CAN_VIEW_NON_PUBLISHED` // app/config/security.php $container->loadFromExtension('security', array( 'role_hierarchy' => array( - 'ROLE_EDITOR' => 'ROLE_CAN_VIEW_NON_PUBLISHED', + 'ROLE_EDITOR' => 'ROLE_CAN_VIEW_NON_PUBLISHED', ), )); @@ -138,22 +139,43 @@ in question - he will have the permission to view unpublished content as well:: // ... } +.. _bundle-core-publish_workflow-twig_function: + To check publication in a template, use the twig function ``cmf_is_published``: -.. code-block:: jinja +.. configuration-block:: - {# check if document is published, regardless of current users role #} - {% if cmf_is_published(page) %} - {# output the document #} - {% endif %} + .. code-block:: jinja + + {# check if document is published, regardless of current users role #} + {% if cmf_is_published(page) %} + {# ... output the document #} + {% endif %} + + {# + check if current logged in user is allowed to view the document either + because it is published or because the current user may view unpublished + documents. + #} + {% if is_granted('VIEW', page) %} + {# ... output the document #} + {% endif %} + + .. code-block:: php + + + isPublished($page)) : ?> + + - {# check if current logged in user is allowed to view the document either - because it is published or because the current user may view unpublished - documents. - #} - {% if is_granted('VIEW', page) %} - {# output the document #} - {% endif %} + + isGranted('VIEW', $page)) : ?> + + Code that loads content should do the publish checks. Note that the twig functions already check for publication. Thanks to a @@ -190,7 +212,7 @@ 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 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 +``ACCESS_GRANTED`` or ``ACCESS_DENIED``, otherwise they return ``ACCESS_ABSTAIN``. As voting is unanimous, each voter returns ``ACCESS_GRANTED`` if its criteria @@ -253,8 +275,13 @@ you can lower the priority of those voters. use Symfony\Component\DependencyInjection\Definition; - $container->register('acme.security.publishable_voter', '%acme.security.publishable_voter.class%') - ->addTag('cmf_published_voter', array('priority' => 30)); + $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 @@ -313,22 +340,18 @@ configuration in the ``sonata_admin`` section of your project configuration: .. code-block:: xml - - - - - - - Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishableWriteInterface - - - - - Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishTimePeriodWriteInterface - - - - + + + + + Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishableWriteInterface + + + + + Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishTimePeriodWriteInterface + + .. code-block:: php @@ -397,10 +420,8 @@ enabled. * **cmf_find**: returns the document for the provided path * **cmf_find_many**: returns an array of documents for the provided paths -* **cmf_is_published**: checks if the provided document is published, - regardless of an eventual bypass permission of the current user. If you need - the bypass role, you will have a firewall configured for the route the - template is rendered in and can simply use ``{{ is_granted('VIEW', document) }}`` +* **cmf_is_published**: checks if the provided document is published, see + :ref:`bundle-core-publish_workflow-twig_function`. * **cmf_prev**: returns the previous document by examining the child nodes of the provided parent * **cmf_prev_linkable**: returns the previous linkable document by examining