diff --git a/bundles/core.rst b/bundles/core.rst deleted file mode 100644 index ac56b764..00000000 --- a/bundles/core.rst +++ /dev/null @@ -1,596 +0,0 @@ -.. index:: - single: Core; Bundles - single: CoreBundle - -CoreBundle -========== - -.. include:: _outdate-caution.rst.inc - -This is the `CoreBundle`_ for the Symfony2 content management framework. This -bundle provides common functionality, helpers and utilities for the other CMF -bundles. - -One of the provided features is an interface and implementation of a publish -workflow checker with an accompanying interface that models can implement that -want to support this checker. - -Furthermore it provides a twig helper exposing several useful functions for -twig templates to interact with PHPCR-ODM documents. - -.. index:: CoreBundle, PHPCR, ODM, publish workflow - -Configuration -------------- - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - cmf_core: - document_manager_name: ~ # used for the twig functions to fetch documents - publish_workflow: - enabled: true - view_non_published_role: ROLE_CAN_VIEW_NON_PUBLISHED - request_listener: true - - .. code-block:: xml - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('cmf_core', array( - 'document_manager_name' => null, - 'publish_workflow' => array( - 'enabled' => true, - 'view_non_published_role' => '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 ----------------- - -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 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 -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. - -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` -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 ``ROLE_CAN_VIEW_NON_PUBLISHED``. - -.. configuration-block:: - - .. 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:: - - use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishWorkflowChecker; - - // 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. even if the current role would allow - // to see the document, this will still return false if the documet is not - // published - if ($publishWorkflowChecker->isGranted( - PublishWorkflowChecker::VIEW_ANONYMOUS_ATTRIBUTE, - $document - )) { - // ... - } - -.. _bundle-core-publish_workflow-twig_function: - -To check publication in a template, use the twig function ``cmf_is_published``: - -.. configuration-block:: - - .. 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)) : ?> - - - - - isGranted('VIEW', $page)) : ?> - - - -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 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 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. - -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`` or ``ACCESS_DENIED``, otherwise they return -``ACCESS_ABSTAIN``. - -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 -................ - -This voter checks on the ``PublishableReadInterface`` 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 ``PublishTimePeriodReadInterface`` 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: - 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; - - $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 ` 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. - -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 ``PublishableReadInterface`` resp. -``PublishTimePeriodReadInterface`` you models instead need to implement the -``PublishableInterface`` and / or ``PublishTimePeriodInterface``. - -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: - cmf_core.admin_extension.publish_workflow.publishable: - implements: - - Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishableInterface - cmf_core.admin_extension.publish_workflow.time_period: - implements: - - Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishTimePeriodInterface - - .. code-block:: xml - - - - - - - Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishableInterface - - - - - Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishTimePeriodInterface - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('sonata_admin', array( - 'extensions' => array( - 'cmf_core.admin_extension.publish_workflow.publishable' => array( - 'implements' => array( - 'Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishableInterface', - ), - ), - 'cmf_core.admin_extension.publish_workflow.time_period' => array( - 'implements' => array( - 'Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishTimePeriodInterface', - ), - ), - ), - )); - -See the `Sonata Admin extension documentation`_ for more information. - -Dependency Injection Tags -------------------------- - -cmf_request_aware -~~~~~~~~~~~~~~~~~ - -If you have services that need the request (e.g. for the publishing workflow -or current menu item voters), you can tag them with ``cmf_request_aware`` to -have a kernel listener inject the request. Any class used in such a tagged -service must have the ``setRequest`` method or you will get a fatal error:: - - use Symfony\Component\HttpFoundation\Request; - - class MyClass - { - private $request; - - public function setRequest(Request $request) - { - $this->request = $request; - } - } - -.. note:: - - You should only use this tag on services that will be needed on every - request. If you use this tag excessively you will run into performance - issues. For seldom used services, you can inject the container in the - service definition and call ``$this->container->get('request')`` in your - code when you actually need the request. - -For Symfony 2.3, this tag is automatically translated to a -`synchronized service`_ but as Symfony 2.2 does not have that feature, you can -use this tag for bundles that you want to be able to use with Symfony 2.2. In -custom applications that run with Symfony 2.3, there is no need for this tag, -just use the synchronized service feature. - -cmf_published_voter -~~~~~~~~~~~~~~~~~~~ - -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. - -This tag has the attribute *priority*. The lower the priority number, the -earlier the voter gets to vote. - -Templating ----------- - -Twig -~~~~ - -The core bundle contains a Twig extension that provides a set of useful -functions for your templates. The functions respect the -:ref:`publish workflow ` if it is - -* **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, see - :ref:`cmf_is_published `. -* **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 - the child nodes of the provided parent -* **cmf_next**: returns the next document by examining the child nodes of the - provided parent -* **cmf_next_linkable**: returns the next linkable document by examining the - child nodes of the provided parent -* **cmf_child**: returns a child documents of the provided parent document and - child node -* **cmf_children**: returns an array of all the children documents of the - provided parent -* **cmf_linkable_children**: returns an array of all the linkable children - documents of the provided parent -* **cmf_descendants**: returns an array of all descendants paths of the - provided parent -* **cmf_document_locales**: gets the locales of the provided document -* **cmf_nodename**: returns the node name of the provided document -* **cmf_parent_path**: returns the parent path of the provided document -* **cmf_path**: returns the path of the provided document - -.. code-block:: jinja - - {% set page = cmf_find('/some/path') %} - - {% if cmf_is_published(page) %} - {% set prev = cmf_prev(page) %} - {% if prev %} - prev - {% endif %} - - {% set next = cmf_next(page) %} - {% if next %} - next - {% endif %} - - {% for news in cmf_children(page)|reverse %} -
  • {{ news.title }} ({{ news.publishStartDate | date('Y-m-d') }})
  • - {% endfor %} - - {% if 'de' in cmf_document_locales(page) %} - DE - {% endif %} - {% if 'fr' in cmf_document_locales(page) %} - FR - {% endif %} - {% endif %} - -PHP -~~~ - -The bundle also provides a templating helper to use in PHP templates, it -contains the following methods: - -* **find**: returns the document for the provided path -* **findMany**: returns an array of documents for the provided paths -* **isPublished**: checks if the provided document is published -* **getPrev**: returns the previous document by examining the child nodes of - the provided parent -* **getPrevLinkable**: returns the previous linkable document by examining - the child nodes of the provided parent -* **getNext**: returns the next document by examining the child nodes of the - provided parent -* **getNextLinkable**: returns the next linkable document by examining the - child nodes of the provided parent -* **getChild**: returns a child documents of the provided parent document and - child node -* **getChildren**: returns an array of all the children documents of the - provided parent -* **getLinkableChildren**: returns an array of all the linkable children - documents of the provided parent -* **getDescendants**: returns an array of all descendants paths of the - provided parent -* **getLocalesFor**: gets the locales of the provided document -* **getNodeName**: returns the node name of the provided document -* **getParentPath**: returns the parent path of the provided document -* **getPath**: returns the path of the provided document - -.. code-block:: php - - find('/some/path') ?> - - isPublished($page) : ?> - getPrev($page) ?> - - prev - - - getNext($page) ?> - - - next - - - - getChildren($page)) as $news) : ?> -
  • - getTitle() ?> - (getPublishStartDate()) ?>) -
  • - - - getLocalesFor($page))) : ?> - DE - - getLocalesFor($page))) : ?> - FR - - - -.. _`CoreBundle`: https://github.com/symfony-cmf/CoreBundle#readme -.. _`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 -.. _`Sonata Admin extension documentation`: http://sonata-project.org/bundles/admin/master/doc/reference/extensions.html -.. _`synchronized service`: http://symfony.com/doc/current/cookbook/service_container/scopes.html#using-a-synchronized-service diff --git a/bundles/core/dependency_injection_tags.rst b/bundles/core/dependency_injection_tags.rst new file mode 100644 index 00000000..dc135356 --- /dev/null +++ b/bundles/core/dependency_injection_tags.rst @@ -0,0 +1,52 @@ +.. index:: + single: Dependency Injection Tags; CoreBundle + +Dependency Injection Tags +------------------------- + +cmf_request_aware +~~~~~~~~~~~~~~~~~ + +If you have services that need the request (e.g. for the publishing workflow +or current menu item voters), you can tag them with ``cmf_request_aware`` to +have a kernel listener inject the request. Any class used in such a tagged +service must have the ``setRequest`` method or you will get a fatal error:: + + use Symfony\Component\HttpFoundation\Request; + + class MyClass + { + private $request; + + public function setRequest(Request $request) + { + $this->request = $request; + } + } + +.. caution:: + + You should only use this tag on services that will be needed on every + request. If you use this tag excessively you will run into performance + issues. For seldom used services, you can inject the container in the + service definition and call ``$this->container->get('request')`` in your + code when you actually need the request. + +For Symfony 2.3, this tag is automatically translated to a +`synchronized service`_ but Symfony 2.2 does not have that feature, so you can +use this tag for bundles that you want to be able to work with Symfony 2.2. In +custom applications that run with Symfony 2.3, there is no need for this tag, +just use the synchronized service feature. + +cmf_published_voter +~~~~~~~~~~~~~~~~~~~ + +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. + +This tag has the attribute ``priority``. The lower the priority number, the +earlier the voter gets to vote. + +.. _`synchronized service`: http://symfony.com/doc/current/cookbook/service_container/scopes.html#using-a-synchronized-service diff --git a/bundles/core/index.rst b/bundles/core/index.rst new file mode 100644 index 00000000..7dbcdbdf --- /dev/null +++ b/bundles/core/index.rst @@ -0,0 +1,11 @@ +CoreBundle +========== + +.. toctree:: + :maxdepth: 2 + + introduction + publish_workflow + dependency_injection_tags + templating + multilang diff --git a/bundles/core/introduction.rst b/bundles/core/introduction.rst new file mode 100644 index 00000000..bbcff467 --- /dev/null +++ b/bundles/core/introduction.rst @@ -0,0 +1,36 @@ +.. index:: + single: Core; Bundles + +CoreBundle +========== + + This bundle provides common functionality, helpers and utilities for the + other CMF bundles. + +One of the provided features is an interface and implementation of a publish +workflow checker with an accompanying interface that models can implement if +they want to support this checker. + +Furthermore, it provides a twig helper exposing several useful functions for +twig templates to interact with PHPCR-ODM documents. + +Finally, most of its configuration settings are automatically applied as +defaults for most of the other CMF Bundles. + +Installation +------------ + +You can install the bundle in 2 different ways: + +* Use the official Git repository (https://github.com/symfony-cmf/CoreBundle); +* Install it via Composer (``symfony-cmf/core-bundle`` on `Packagist`_). + +Sections +-------- + +* :doc:`publish_workflow` +* :doc:`dependency_injection_tags` +* :doc:`templating` +* :doc:`multilang` + +.. _`Packagist`: https://packagist.org/packages/symfony-cmf/core-bundle diff --git a/bundles/core/multilang.rst b/bundles/core/multilang.rst new file mode 100644 index 00000000..762d2496 --- /dev/null +++ b/bundles/core/multilang.rst @@ -0,0 +1,62 @@ +.. index:: + single: Multi-Language; CoreBundle + +Multi-language support +---------------------- + +Editing Locale Information: Translatable Sonata Admin Extension +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Several bundles provide translatable model classes that implement +``TranslatableInterface``. This extension adds a locale field +to the given SonataAdminBundle forms. + +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: + cmf_core.admin_extension.translatable: + implements: + - Symfony\Cmf\Bundle\CoreBundle\Translatable\TranslatableInterface + + .. code-block:: xml + + + + + + + + + + Symfony\Cmf\Bundle\CoreBundle\Translatable\TranslatableInterface + + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('sonata_admin', array( + // ... + 'extensions' => array( + 'cmf_core.admin_extension.translatable' => array( + 'implements' => array( + 'Symfony\Cmf\Bundle\CoreBundle\Translatable\TranslatableInterface', + ), + ), + ), + )); + +See the `Sonata Admin extension documentation`_ for more information. + +.. _`Sonata Admin extension documentation`: http://sonata-project.org/bundles/admin/master/doc/reference/extensions.html diff --git a/bundles/core/publish_workflow.rst b/bundles/core/publish_workflow.rst new file mode 100644 index 00000000..09a0f6fe --- /dev/null +++ b/bundles/core/publish_workflow.rst @@ -0,0 +1,354 @@ +.. index:: + single: Core; Bundles; publish workflow + +.. _bundle-core-publish_workflow: + +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 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 CoreBundle +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. + +.. tip:: + + A good introduction to the Symfony core security can be found in the + `Security Chapter`_ of 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` +of the Symfony Security component. The method to check publication is +:method:`Symfony\\Component\\Security\\Core\\SecurityContextInterface::isGranted`, +same as with the security context. + +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. + +Currently 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 ``ROLE_CAN_VIEW_NON_PUBLISHED``. + +.. configuration-block:: + + .. 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:: + + use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishWorkflowChecker; + + // 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. even if the current role would allow + // to see the document, this will still return false if the documet is not + // published + if ($publishWorkflowChecker->isGranted( + PublishWorkflowChecker::VIEW_ANONYMOUS_ATTRIBUTE, + $document + ) + ) { + // ... + } + +.. _bundle-core-publish_workflow-twig_function: + +To check publication in a template, use the twig function ``cmf_is_published``: + +.. configuration-block:: + + .. 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:: html+php + + + isPublished($page)) : ?> + + + + + isGranted('VIEW', $page)) : ?> + + + +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 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 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. + +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 must implement the +:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`. +A content object will be passed and it has to decide whether it is published +according to its rules. The CoreBundle provides a couple of generic voters +(`PublishableVoter`_ and `TimePeriodVoter`_) 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`` or +``ACCESS_DENIED``, otherwise they return ``ACCESS_ABSTAIN``. + +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 +................ + +This voter checks on the ``PublishableReadInterface`` 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 ``PublishTimePeriodReadInterface`` 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: + 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; + + $container + ->register( + 'acme.security.publishable_voter', + '%acme.security.publishable_voter.class%' + ) + ->addTag('cmf_published_voter', array('priority' => 30)) + ; + +The workflow checker will create an +:class:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\AnonymousToken` on +the fly if the securty context has none. This means that 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 they +should not call it to avoid triggering an exception. If a voter only gives +access if the 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. + +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 ``PublishableReadInterface`` resp. +``PublishTimePeriodReadInterface`` you models instead need to implement the +``PublishableInterface`` and / or ``PublishTimePeriodInterface``. + +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: + cmf_core.admin_extension.publish_workflow.publishable: + implements: + - Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishableInterface + cmf_core.admin_extension.publish_workflow.time_period: + implements: + - Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishTimePeriodInterface + + .. code-block:: xml + + + + + + + + + Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishableInterface + + + + + + Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishTimePeriodInterface + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('sonata_admin', array( + // ... + 'extensions' => array( + 'cmf_core.admin_extension.publish_workflow.publishable' => array( + 'implements' => array( + 'Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishableInterface', + ), + ), + 'cmf_core.admin_extension.publish_workflow.time_period' => array( + 'implements' => array( + 'Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishTimePeriodInterface', + ), + ), + ), + )); + +See the `Sonata Admin extension documentation`_ for more information. + +.. _`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 +.. _`Sonata Admin extension documentation`: http://sonata-project.org/bundles/admin/master/doc/reference/extensions.html diff --git a/bundles/core/templating.rst b/bundles/core/templating.rst new file mode 100644 index 00000000..828f205b --- /dev/null +++ b/bundles/core/templating.rst @@ -0,0 +1,160 @@ +.. index:: + single: Templating; CoreBundle + +Templating +---------- + +The CoreBundle also provides a lot of functions to use in templates. + +Twig +~~~~ + +The CoreBundle contains a Twig extension that provides a set of useful +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 +* **cmf_is_published**: checks if the provided document is published, see + :ref:`cmf_is_published `. +* **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 + the child nodes of the provided parent +* **cmf_next**: returns the next document by examining the child nodes of the + provided parent +* **cmf_next_linkable**: returns the next linkable document by examining the + child nodes of the provided parent +* **cmf_child**: returns a child documents of the provided parent document and + child node +* **cmf_children**: returns an array of all the children documents of the + provided parent +* **cmf_linkable_children**: returns an array of all the linkable children + documents of the provided parent +* **cmf_descendants**: returns an array of all descendants paths of the + provided parent +* **cmf_document_locales**: gets the locales of the provided document +* **cmf_nodename**: returns the node name of the provided document +* **cmf_parent_path**: returns the parent path of the provided document +* **cmf_path**: returns the path of the provided document + +An example of these functions can be: + +.. code-block:: html+jinja + + {% set page = cmf_find('/some/path') %} + + {% if cmf_is_published(page) %} + {% set prev = cmf_prev(page) %} + {% if prev %} + prev + {% endif %} + + {% set next = cmf_next(page) %} + {% if next %} + next + {% endif %} + + {% for news in cmf_children(page)|reverse %} +
  • {{ news.title }} ({{ news.publishStartDate | date('Y-m-d') }})
  • + {% endfor %} + + {% if 'de' in cmf_document_locales(page) %} + DE + {% endif %} + {% if 'fr' in cmf_document_locales(page) %} + FR + {% endif %} + {% endif %} + +PHP +~~~ + +The bundle also provides a templating helper to use in PHP templates, it +contains the following methods: + +* **find**: returns the document for the provided path +* **findMany**: returns an array of documents for the provided paths +* **isPublished**: checks if the provided document is published +* **getPrev**: returns the previous document by examining the child nodes of + the provided parent +* **getPrevLinkable**: returns the previous linkable document by examining + the child nodes of the provided parent +* **getNext**: returns the next document by examining the child nodes of the + provided parent +* **getNextLinkable**: returns the next linkable document by examining the + child nodes of the provided parent +* **getChild**: returns a child documents of the provided parent document and + child node +* **getChildren**: returns an array of all the children documents of the + provided parent +* **getLinkableChildren**: returns an array of all the linkable children + documents of the provided parent +* **getDescendants**: returns an array of all descendants paths of the + provided parent +* **getLocalesFor**: gets the locales of the provided document +* **getNodeName**: returns the node name of the provided document +* **getParentPath**: returns the parent path of the provided document +* **getPath**: returns the path of the provided document + +An example of these functions can be: + +.. code-block:: html+php + + find('/some/path') ?> + + isPublished($page) : ?> + getPrev($page) ?> + + prev + + + getNext($page) ?> + + + next + + + + getChildren($page)) as $news) : ?> +
  • + getTitle() ?> + (getPublishStartDate()) ?>) +
  • + + + getLocalesFor($page))) : ?> + DE + + getLocalesFor($page))) : ?> + FR + + diff --git a/bundles/index.rst b/bundles/index.rst index cf592770..99e89a47 100644 --- a/bundles/index.rst +++ b/bundles/index.rst @@ -7,7 +7,7 @@ Bundles block/index blog content - core + core/index create phpcr_odm media diff --git a/bundles/map.rst.inc b/bundles/map.rst.inc index 65b5e175..9c402759 100644 --- a/bundles/map.rst.inc +++ b/bundles/map.rst.inc @@ -14,9 +14,13 @@ * :doc:`content` -* **CoreBundle** +* :doc:`block/index` - * :doc:`core` + * :doc:`core/introduction` + * :doc:`core/publish_workflow` + * :doc:`core/dependency_injection_tags` + * :doc:`core/templating` + * :doc:`core/multilang` * **CreateBundle** diff --git a/index.rst b/index.rst index 0f1a642f..48f982d9 100644 --- a/index.rst +++ b/index.rst @@ -97,7 +97,7 @@ to do it? In this case the reference is the right place for you. bundles/block/index bundles/blog bundles/content - bundles/core + bundles/core/index bundles/create bundles/phpcr_odm bundles/media diff --git a/reference/configuration/core.rst b/reference/configuration/core.rst new file mode 100644 index 00000000..e6cd73f5 --- /dev/null +++ b/reference/configuration/core.rst @@ -0,0 +1,284 @@ +CoreBundle Configuration +======================== + +The CoreBundle provides infrastructure for other CMF bundles and can be configured +under the ``cmf_core`` key in your application configuration. When using +XML, you can use the ``http://cmf.symfony.com/schema/dic/core`` namespace. + +Configuration +------------- + +.. _config-core-persistence: + +persistence +~~~~~~~~~~~ + +phpcr +..... + +This defines the persistence driver. The default configuration of persistence +is the following configuration: + +.. configuration-block:: + + .. code-block:: yaml + + cmf_core: + persistence: + phpcr: + enabled: false + basepath: /cms + manager_registry: doctrine_phpcr + manager_name: ~ + use_sonata_admin: auto + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + $container->loadFromExtension('cmf_core', array( + 'persistence' => array( + 'phpcr' => array( + 'enabled' => false, + 'basepath' => '/cms/simple', + 'manager_registry' => 'doctrine_phpcr', + 'manager_name' => null, + 'use_sonata_admin' => 'auto', + ), + ), + )); + + +enabled +""""""" + +**type**: ``boolean`` **default**: ``false`` + +If ``true``, PHPCR is enabled in the service container. + +Enabling this setting will also automatically enable the equivalent setting in the following Bundles: + +* :doc:`BlockBundle <../../bundles/block/introduction>` +* :doc:`ContentBundle <../../bundles/content>` +* :doc:`CreateBundle <../../bundles/create>` +* :doc:`MediaBundle <../../bundles/media>` +* :doc:`MenuBundle <../../bundles/menu>` +* :doc:`RoutingBundle <../../bundles/routing/introduction>` +* :doc:`SearchBundle <../../bundles/search>` +* :doc:`SimpleCmsBundle <../../bundles/simple_cms>` +* :doc:`TreeBrowserCmsBundle <../../bundles/tree_browser>` + +PHPCR can be enabled by multiple ways such as: + +.. configuration-block:: + + .. code-block:: yaml + + phpcr: ~ # use default configuration + # or + phpcr: true # straight way + # or + phpcr: + manager: ... # or any other option under 'phpcr' + + .. code-block:: xml + + + + + + + true + + + + + + .. code-block:: php + + $container->loadFromExtension('cmf_core', array( + // ... + 'persistence' => array( + 'phpcr' => null, // use default configuration + // or + 'phpcr' => true, // straight way + // or + 'phpcr' => array( + 'manager' => '...', // or any other option under 'phpcr' + ), + ), + )); + +basepath +"""""""" + +**type**: ``string`` **default**: ``/cms/`` + +The basepath for CMS documents in the PHPCR tree. + +Enabling this setting will also automatically enable the equivalent settings in the following Bundles: + +* :doc:`BlockBundle <../../bundles/block/introduction>` +* :doc:`ContentBundle <../../bundles/content>` +* :doc:`MediaBundle <../../bundles/media>` +* :doc:`MenuBundle <../../bundles/menu>` +* :doc:`RoutingBundle <../../bundles/routing/introduction>` +* :doc:`SearchBundle <../../bundles/search>` +* :doc:`SimpleCmsBundle <../../bundles/simple_cms>` + +manager_registry +"""""""""""""""" + +**type**: ``string`` **default**: ``doctrine_phpcr`` + +Enabling this setting will also automatically enable the equivalent settings in the following Bundles: + +* :doc:`SearchBundle <../../bundles/search>` +* :doc:`SimpleCmsBundle <../../bundles/simple_cms>` + +manager_name +"""""""""""" + +**type**: ``string`` **default**: ``null`` + +The name of the Doctrine Manager to use. ``null`` tells the manager registry to +retrieve the default manager. + +Enabling this setting will also automatically enable the equivalent setting in the following Bundles: + +* :doc:`BlockBundle <../../bundles/block/introduction>` +* :doc:`MediaBundle <../../bundles/media>` +* :doc:`MenuBundle <../../bundles/menu>` +* :doc:`RoutingBundle <../../bundles/routing/introduction>` +* :doc:`SearchBundle <../../bundles/search>` +* :doc:`SimpleCmsBundle <../../bundles/simple_cms>` + +use_sonata_admin +"""""""""""""""" + +**type**: ``enum`` **valid values**: ``true|false|auto`` **default**: ``auto`` + +If ``true``, the admin classes for SimpleCmsBundle pages are activated. If set +to ``auto``, the admin services are activated only if the +SonataPhpcrAdminBundle is present. + +Enabling this setting will also automatically enable the equivalent setting in the following Bundles: + +* :doc:`BlockBundle <../../bundles/block/introduction>` +* :doc:`ContentBundle <../../bundles/content>` +* :doc:`MenuBundle <../../bundles/menu>` +* :doc:`RoutingBundle <../../bundles/routing/introduction>` +* :doc:`SimpleCmsBundle <../../bundles/simple_cms>` + +.. _config-core-multilang: + +multilang +~~~~~~~~~ + +This configures if multiple language mode should be activated. Specifically this +enables the ``TranslatableExtension`` for ``SonataAdminBundle``. + +Enabling this setting will also automatically enable the equivalent setting in the following Bundles: + +* :doc:`RoutingBundle <../../bundles/routing/introduction>` +* :doc:`SimpleCmsBundle <../../bundles/simple_cms>` + +.. configuration-block:: + + .. code-block:: yaml + + cmf_core: + multilang: + locales: [en, fr] + + .. code-block:: xml + + + + + + + en + fr + + + + + .. code-block:: php + + $container->loadFromExtension('cmf_core', array( + 'multilang' => array( + 'locales' => array( + 'en', + 'fr', + ), + ), + )); + +locales +....... + +**type**: ``array`` **default**: ``null`` + +This define languages that can be used. + +publish_workflow +~~~~~~~~~~~~~~~~ + +This configures if the publish workflow should be enabled, which service to use +and what role may view not yet published content. The request listener ensures +only published routes and content can be accessed. + +.. configuration-block:: + + .. code-block:: yaml + + cmf_core: + publish_workflow: + enabled: true + checker_service: cmf_core.publish_workflow.checker.default + view_non_published_role: ROLE_CAN_VIEW_NON_PUBLISHED + request_listener: true + + .. code-block:: xml + + + + + + + + + + .. code-block:: php + + $container->loadFromExtension('cmf_core', array( + 'publish_workflow' => array( + 'enabled' => true, + 'checker_service' => 'cmf_core.publish_workflow.checker.default', + 'view_non_published_role' => 'ROLE_CAN_VIEW_NON_PUBLISHED', + 'request_listener' => true, + ), + )); \ No newline at end of file diff --git a/reference/configuration/routing.rst b/reference/configuration/routing.rst index cc60b8b8..42afe5e9 100644 --- a/reference/configuration/routing.rst +++ b/reference/configuration/routing.rst @@ -221,7 +221,7 @@ enabled If ``true``, PHPCR is enabled in the service container. -If the :doc:`CoreBundle <../../bundles/core>` is registered, this will default to +If the :doc:`CoreBundle <../../bundles/core/index>` is registered, this will default to the value of ``cmf_core.persistence.phpcr.enabled``. manager_name @@ -231,7 +231,7 @@ manager_name The name of the Doctrine Manager to use. -If the :doc:`CoreBundle <../../bundles/core>` is registered, this will default to +If the :doc:`CoreBundle <../../bundles/core/index>` is registered, this will default to the value of ``cmf_core.persistence.phpcr.manager_name``. route_basepath @@ -241,7 +241,7 @@ route_basepath The basepath for routes in the PHPCR tree. -If the :doc:`CoreBundle <../../bundles/core>` is registered, this will default to +If the :doc:`CoreBundle <../../bundles/core/index>` is registered, this will default to ``%cmf_core.persistence.phpcr.basepath%/routes``. content_basepath @@ -251,7 +251,7 @@ content_basepath The basepath for content objects in the PHPCR tree. -If the :doc:`CoreBundle <../../bundles/core>` is registered, this will default to +If the :doc:`CoreBundle <../../bundles/core/index>` is registered, this will default to ``%cmf_core.persistence.phpcr.basepath%/content``. use_sonata_admin @@ -263,7 +263,7 @@ If ``true``, the admin classes for the routing are activated on the sonata admin panel. If set to ``auto``, the admin services are activated only if the SonataPhpcrAdminBundle is present. -If the :doc:`CoreBundle <../../bundles/core>` is registered, this will default to the value +If the :doc:`CoreBundle <../../bundles/core/index>` is registered, this will default to the value of ``cmf_core.persistence.phpcr.use_sonata_admin``. orm @@ -359,5 +359,5 @@ locales To enable multilanguage, set the valid locales in this option. -If the :doc:`CoreBundle <../../bundles/core>` is registered, this will default to the value +If the :doc:`CoreBundle <../../bundles/core/index>` is registered, this will default to the value of ``cmf_core.locales``. diff --git a/reference/configuration/simple-cms.rst b/reference/configuration/simple-cms.rst index e46f74f6..efdd381c 100644 --- a/reference/configuration/simple-cms.rst +++ b/reference/configuration/simple-cms.rst @@ -83,7 +83,7 @@ enabled If ``true``, PHPCR is enabled in the service container. -If the :doc:`CoreBundle <../../bundles/core>` is registered, this will default to +If the :doc:`CoreBundle <../../bundles/core/index>` is registered, this will default to the value of ``cmf_core.persistence.phpcr.enabled``. PHPCR can be enabled by multiple ways such as: @@ -134,7 +134,7 @@ basepath The basepath for CMS documents in the PHPCR tree. -If the :doc:`CoreBundle <../../bundles/core>` is registered, this will default to +If the :doc:`CoreBundle <../../bundles/core/index>` is registered, this will default to the value of ``%cmf_core.persistence.phpcr.basepath%/simple``. manager_registry @@ -142,7 +142,7 @@ manager_registry **type**: ``string`` **default**: ``doctrine_phpcr`` -If the :doc:`CoreBundle <../../bundles/core>` is registered, this will default to +If the :doc:`CoreBundle <../../bundles/core/index>` is registered, this will default to the value of ``cmf_core.persistence.phpcr.manager_registry``. manager_name @@ -153,7 +153,7 @@ manager_name The name of the Doctrine Manager to use. ``null`` tells the manager registry to retrieve the default manager. -If the :doc:`CoreBundle <../../bundles/core>` is registered, this will default to +If the :doc:`CoreBundle <../../bundles/core/index>` is registered, this will default to the value of ``cmf_core.persistence.phpcr.manager_name``. document_class @@ -172,7 +172,7 @@ If ``true``, the admin classes for SimpleCmsBundle pages are activated. If set to ``auto``, the admin services are activated only if the SonataPhpcrAdminBundle is present. -If the :doc:`CoreBundle <../../bundles/core>` is registered, this will default to the value +If the :doc:`CoreBundle <../../bundles/core/index>` is registered, this will default to the value of ``cmf_core.persistence.phpcr.use_sonata_admin``. sonata_admin.sort @@ -338,5 +338,5 @@ locales This define languages that can be used. -If the :doc:`CoreBundle <../../bundles/core>` is registered, this will default to +If the :doc:`CoreBundle <../../bundles/core/index>` is registered, this will default to the value of ``cmf_core.multilang.locales``. diff --git a/reference/index.rst b/reference/index.rst index 4340ecb6..cc3395d8 100644 --- a/reference/index.rst +++ b/reference/index.rst @@ -4,6 +4,7 @@ Reference .. toctree:: :hidden: + configuration/core.rst configuration/routing.rst .. include:: map.rst.inc diff --git a/reference/map.rst.inc b/reference/map.rst.inc index 68e23457..3d941357 100644 --- a/reference/map.rst.inc +++ b/reference/map.rst.inc @@ -1,3 +1,4 @@ * **Configuration** + * :doc:`configuration/core` * :doc:`configuration/routing`