From 6d92c953d95c3c648c082af831179060bf82ae7b Mon Sep 17 00:00:00 2001 From: mkoosej Date: Mon, 18 Aug 2014 15:26:22 -0400 Subject: [PATCH] Add MenuOptionsExtension docs --- .gitignore | 1 + .gitmodules | 3 + _exts/sensio/__init__.py | 0 _exts/sensio/sphinx/__init__.py | 0 _exts/sensio/sphinx/configurationblock.py | 79 ------- _exts/sensio/sphinx/php.py | 88 -------- _exts/sensio/sphinx/phpcode.py | 111 --------- _exts/sensio/sphinx/refinclude.py | 55 ----- book/installation.rst | 17 +- book/routing.rst | 4 +- bundles/create/introduction.rst | 59 +++++ bundles/map.rst.inc | 8 +- bundles/menu/sonata_admin.rst | 108 +++++++++ bundles/routing_auto/conflict_resolvers.rst | 117 ++++++++++ bundles/routing_auto/customization.rst | 157 ------------- .../routing_auto/defunct_route_handlers.rst | 184 +++++++++++++++ bundles/routing_auto/exists_actions.rst | 67 ------ bundles/routing_auto/index.rst | 8 +- bundles/routing_auto/introduction.rst | 213 +++++------------- bundles/routing_auto/not_exists_actions.rst | 60 ----- bundles/routing_auto/providers.rst | 200 ---------------- bundles/routing_auto/token_providers.rst | 208 +++++++++++++++++ bundles/simple_cms/extending_page_class.rst | 2 +- bundles/simple_cms/index.rst | 1 + bundles/simple_cms/introduction.rst | 1 + bundles/simple_cms/menus.rst | 50 ++++ cookbook/creating_a_cms/auto-routing.rst | 121 +++------- .../creating_a_cms/content-to-controllers.rst | 40 +++- cookbook/creating_a_cms/getting-started.rst | 35 +-- cookbook/creating_a_cms/make-homepage.rst | 11 +- cookbook/creating_a_cms/sonata-admin.rst | 33 +-- cookbook/creating_a_cms/the-frontend.rst | 14 +- .../database/create_new_project_phpcr_odm.rst | 158 +++++++++++++ cookbook/index.rst | 1 + cookbook/map.rst.inc | 1 + reference/configuration/create.rst | 11 + reference/configuration/menu.rst | 62 +++++ reference/configuration/routing.rst | 31 ++- 38 files changed, 1201 insertions(+), 1118 deletions(-) create mode 100644 .gitmodules delete mode 100644 _exts/sensio/__init__.py delete mode 100644 _exts/sensio/sphinx/__init__.py delete mode 100644 _exts/sensio/sphinx/configurationblock.py delete mode 100644 _exts/sensio/sphinx/php.py delete mode 100644 _exts/sensio/sphinx/phpcode.py delete mode 100644 _exts/sensio/sphinx/refinclude.py create mode 100644 bundles/routing_auto/conflict_resolvers.rst delete mode 100644 bundles/routing_auto/customization.rst create mode 100644 bundles/routing_auto/defunct_route_handlers.rst delete mode 100644 bundles/routing_auto/exists_actions.rst delete mode 100644 bundles/routing_auto/not_exists_actions.rst delete mode 100644 bundles/routing_auto/providers.rst create mode 100644 bundles/routing_auto/token_providers.rst create mode 100644 bundles/simple_cms/menus.rst create mode 100644 cookbook/database/create_new_project_phpcr_odm.rst diff --git a/.gitignore b/.gitignore index 23e381f4..de033270 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ _build/ *.pyc +_exts diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..b3eb15b8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "_exts"] + path = _exts + url = http://github.com/fabpot/sphinx-php diff --git a/_exts/sensio/__init__.py b/_exts/sensio/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/_exts/sensio/sphinx/__init__.py b/_exts/sensio/sphinx/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/_exts/sensio/sphinx/configurationblock.py b/_exts/sensio/sphinx/configurationblock.py deleted file mode 100644 index 7269f192..00000000 --- a/_exts/sensio/sphinx/configurationblock.py +++ /dev/null @@ -1,79 +0,0 @@ -# -*- coding: utf-8 -*- -""" - :copyright: (c) 2010-2012 Fabien Potencier - :license: MIT, see LICENSE for more details. -""" - -from docutils.parsers.rst import Directive, directives -from docutils import nodes -from string import upper - -class configurationblock(nodes.General, nodes.Element): - pass - -class ConfigurationBlock(Directive): - has_content = True - required_arguments = 0 - optional_arguments = 0 - final_argument_whitespace = True - option_spec = {} - formats = { - 'html': 'HTML', - 'xml': 'XML', - 'php': 'PHP', - 'yaml': 'YAML', - 'jinja': 'Twig', - 'html+jinja': 'Twig', - 'jinja+html': 'Twig', - 'php+html': 'PHP', - 'html+php': 'PHP', - 'ini': 'INI', - 'php-annotations': 'Annotations', - } - - def run(self): - env = self.state.document.settings.env - - node = nodes.Element() - node.document = self.state.document - self.state.nested_parse(self.content, self.content_offset, node) - - entries = [] - for i, child in enumerate(node): - if isinstance(child, nodes.literal_block): - # add a title (the language name) before each block - #targetid = "configuration-block-%d" % env.new_serialno('configuration-block') - #targetnode = nodes.target('', '', ids=[targetid]) - #targetnode.append(child) - - innernode = nodes.emphasis(self.formats[child['language']], self.formats[child['language']]) - - para = nodes.paragraph() - para += [innernode, child] - - entry = nodes.list_item('') - entry.append(para) - entries.append(entry) - - resultnode = configurationblock() - resultnode.append(nodes.bullet_list('', *entries)) - - return [resultnode] - -def visit_configurationblock_html(self, node): - self.body.append(self.starttag(node, 'div', CLASS='configuration-block')) - -def depart_configurationblock_html(self, node): - self.body.append('\n') - -def visit_configurationblock_latex(self, node): - pass - -def depart_configurationblock_latex(self, node): - pass - -def setup(app): - app.add_node(configurationblock, - html=(visit_configurationblock_html, depart_configurationblock_html), - latex=(visit_configurationblock_latex, depart_configurationblock_latex)) - app.add_directive('configuration-block', ConfigurationBlock) diff --git a/_exts/sensio/sphinx/php.py b/_exts/sensio/sphinx/php.py deleted file mode 100644 index 9c84acc5..00000000 --- a/_exts/sensio/sphinx/php.py +++ /dev/null @@ -1,88 +0,0 @@ -# -*- coding: utf-8 -*- -""" - :copyright: (c) 2010-2012 Fabien Potencier - :license: MIT, see LICENSE for more details. -""" - -from sphinx import addnodes -from sphinx.domains import Domain, ObjType -from sphinx.locale import l_, _ -from sphinx.directives import ObjectDescription -from sphinx.domains.python import py_paramlist_re as js_paramlist_re -from sphinx.roles import XRefRole -from sphinx.util.nodes import make_refnode -from sphinx.util.docfields import Field, GroupedField, TypedField - -def setup(app): - app.add_domain(PHPDomain) - -class PHPXRefRole(XRefRole): - def process_link(self, env, refnode, has_explicit_title, title, target): - # basically what sphinx.domains.python.PyXRefRole does - refnode['php:object'] = env.temp_data.get('php:object') - if not has_explicit_title: - title = title.lstrip('\\') - target = target.lstrip('~') - if title[0:1] == '~': - title = title[1:] - ns = title.rfind('\\') - if ns != -1: - title = title[ns+1:] - if target[0:1] == '\\': - target = target[1:] - refnode['refspecific'] = True - return title, target - -class PHPDomain(Domain): - """PHP language domain.""" - name = 'php' - label = 'PHP' - # if you add a new object type make sure to edit JSObject.get_index_string - object_types = { - } - directives = { - } - roles = { - 'func': PHPXRefRole(fix_parens=True), - 'class': PHPXRefRole(), - 'data': PHPXRefRole(), - 'attr': PHPXRefRole(), - } - initial_data = { - 'objects': {}, # fullname -> docname, objtype - } - - def clear_doc(self, docname): - for fullname, (fn, _) in self.data['objects'].items(): - if fn == docname: - del self.data['objects'][fullname] - - def find_obj(self, env, obj, name, typ, searchorder=0): - if name[-2:] == '()': - name = name[:-2] - objects = self.data['objects'] - newname = None - if searchorder == 1: - if obj and obj + '\\' + name in objects: - newname = obj + '\\' + name - else: - newname = name - else: - if name in objects: - newname = name - elif obj and obj + '\\' + name in objects: - newname = obj + '\\' + name - return newname, objects.get(newname) - - def resolve_xref(self, env, fromdocname, builder, typ, target, node, - contnode): - objectname = node.get('php:object') - searchorder = node.hasattr('refspecific') and 1 or 0 - name, obj = self.find_obj(env, objectname, target, typ, searchorder) - if not obj: - return None - return make_refnode(builder, fromdocname, obj[0], name, contnode, name) - - def get_objects(self): - for refname, (docname, type) in self.data['objects'].iteritems(): - yield refname, refname, type, docname, refname, 1 diff --git a/_exts/sensio/sphinx/phpcode.py b/_exts/sensio/sphinx/phpcode.py deleted file mode 100644 index c9230a3f..00000000 --- a/_exts/sensio/sphinx/phpcode.py +++ /dev/null @@ -1,111 +0,0 @@ -# -*- coding: utf-8 -*- -""" - :copyright: (c) 2010-2012 Fabien Potencier - :license: MIT, see LICENSE for more details. -""" - -from docutils import nodes, utils - -from sphinx.util.nodes import split_explicit_title -from string import lower - -def php_namespace_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): - text = utils.unescape(text) - env = inliner.document.settings.env - base_url = env.app.config.api_url - has_explicit_title, title, namespace = split_explicit_title(text) - - try: - full_url = base_url % namespace.replace('\\', '/') + '.html' - except (TypeError, ValueError): - env.warn(env.docname, 'unable to expand %s api_url with base ' - 'URL %r, please make sure the base contains \'%%s\' ' - 'exactly once' % (typ, base_url)) - full_url = base_url + utils.escape(full_class) - if not has_explicit_title: - name = namespace.lstrip('\\') - ns = name.rfind('\\') - if ns != -1: - name = name[ns+1:] - title = name - list = [nodes.reference(title, title, internal=False, refuri=full_url, reftitle=namespace)] - pnode = nodes.literal('', '', *list) - return [pnode], [] - -def php_class_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): - text = utils.unescape(text) - env = inliner.document.settings.env - base_url = env.app.config.api_url - has_explicit_title, title, full_class = split_explicit_title(text) - - try: - full_url = base_url % full_class.replace('\\', '/') + '.html' - except (TypeError, ValueError): - env.warn(env.docname, 'unable to expand %s api_url with base ' - 'URL %r, please make sure the base contains \'%%s\' ' - 'exactly once' % (typ, base_url)) - full_url = base_url + utils.escape(full_class) - if not has_explicit_title: - class_name = full_class.lstrip('\\') - ns = class_name.rfind('\\') - if ns != -1: - class_name = class_name[ns+1:] - title = class_name - list = [nodes.reference(title, title, internal=False, refuri=full_url, reftitle=full_class)] - pnode = nodes.literal('', '', *list) - return [pnode], [] - -def php_method_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): - text = utils.unescape(text) - env = inliner.document.settings.env - base_url = env.app.config.api_url - has_explicit_title, title, class_and_method = split_explicit_title(text) - - ns = class_and_method.rfind('::') - full_class = class_and_method[:ns] - method = class_and_method[ns+2:] - - try: - full_url = base_url % full_class.replace('\\', '/') + '.html' + '#method_' + method - except (TypeError, ValueError): - env.warn(env.docname, 'unable to expand %s api_url with base ' - 'URL %r, please make sure the base contains \'%%s\' ' - 'exactly once' % (typ, base_url)) - full_url = base_url + utils.escape(full_class) - if not has_explicit_title: - title = method + '()' - list = [nodes.reference(title, title, internal=False, refuri=full_url, reftitle=full_class + '::' + method + '()')] - pnode = nodes.literal('', '', *list) - return [pnode], [] - -def php_phpclass_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): - text = utils.unescape(text) - has_explicit_title, title, full_class = split_explicit_title(text) - - full_url = 'http://php.net/manual/en/class.%s.php' % lower(full_class) - - if not has_explicit_title: - title = full_class - list = [nodes.reference(title, title, internal=False, refuri=full_url, reftitle=full_class)] - pnode = nodes.literal('', '', *list) - return [pnode], [] - -def php_phpfunction_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): - text = utils.unescape(text) - has_explicit_title, title, full_function = split_explicit_title(text) - - full_url = 'http://php.net/manual/en/function.%s.php' % lower(full_function.replace('_', '-')) - - if not has_explicit_title: - title = full_function - list = [nodes.reference(title, title, internal=False, refuri=full_url, reftitle=full_function)] - pnode = nodes.literal('', '', *list) - return [pnode], [] - -def setup(app): - app.add_config_value('api_url', {}, 'env') - app.add_role('namespace', php_namespace_role) - app.add_role('class', php_class_role) - app.add_role('method', php_method_role) - app.add_role('phpclass', php_phpclass_role) - app.add_role('phpfunction', php_phpfunction_role) diff --git a/_exts/sensio/sphinx/refinclude.py b/_exts/sensio/sphinx/refinclude.py deleted file mode 100644 index 0b1d7208..00000000 --- a/_exts/sensio/sphinx/refinclude.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -""" - :copyright: (c) 2010-2012 Fabien Potencier - :license: MIT, see LICENSE for more details. -""" - -from docutils.parsers.rst import Directive, directives -from docutils import nodes - -class refinclude(nodes.General, nodes.Element): - pass - -class RefInclude(Directive): - has_content = False - required_arguments = 1 - optional_arguments = 0 - final_argument_whitespace = False - option_spec = {} - - def run(self): - document = self.state.document - - if not document.settings.file_insertion_enabled: - return [document.reporter.warning('File insertion disabled', - line=self.lineno)] - - env = self.state.document.settings.env - target = self.arguments[0] - - node = refinclude() - node['target'] = target - - return [node] - -def process_refinclude_nodes(app, doctree, docname): - env = app.env - for node in doctree.traverse(refinclude): - docname, labelid, sectname = env.domaindata['std']['labels'].get(node['target'], - ('','','')) - - if not docname: - return [document.reporter.error('Unknown target name: "%s"' % node['target'], - line=self.lineno)] - - resultnode = None - dt = env.get_doctree(docname) - for n in dt.traverse(nodes.section): - if labelid in n['ids']: - node.replace_self([n]) - break - -def setup(app): - app.add_node(refinclude) - app.add_directive('include-ref', RefInclude) - app.connect('doctree-resolved', process_refinclude_nodes) diff --git a/book/installation.rst b/book/installation.rst index 38763458..dafdeaf9 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -217,10 +217,19 @@ Symfony CMF SE does not provide any admin tools to create new pages. If you are interested in adding an admin UI one solution can be found in :doc:`../cookbook/creating_a_cms/sonata-admin`. However, if all you want is a simple way to add new pages that you can then edit via the inline -editing, then you can use the SimpleCmsBundle ``page`` migrator. The Symfony -CMF SE ships with an example YAML file stored in -``app/Resources/data/pages/test.yml``. The contents of this file can be loaded -into the PHPCR database by calling: +editing, then you can use the SimpleCmsBundle ``page`` migrator. For example, +to add a page called "Testing", creating a file called +``app/Resources/data/pages/test.yml`` with the following contents: + +.. code-block:: yaml + + label: "Testing" + title: "Testing" + body: | +

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

+

Morbi eleifend, ipsum eget facilisis lacinia, lorem dui venenatis quam, at vulputate purus erat sit amet elit.

+ +The contents of this file can be loaded into the PHPCR database by calling: .. code-block:: bash diff --git a/book/routing.rst b/book/routing.rst index aa940db2..18ddc868 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -449,6 +449,8 @@ follows:: namespace Acme\DemoBundle\DataFixtures\PHPCR; use Doctrine\ODM\PHPCR\DocumentManager; + use Doctrine\Common\DataFixtures\FixtureInterface; + use Doctrine\Common\Persistence\ObjectManager; use Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route; use Symfony\Cmf\Bundle\ContentBundle\Doctrine\Phpcr\StaticContent; @@ -476,7 +478,7 @@ follows:: $route->setRequirement('id', '\d+'); $route->setDefault('id', 1); - $dm->persist($route), + $dm->persist($route); $dm->flush(); } } diff --git a/bundles/create/introduction.rst b/bundles/create/introduction.rst index 599130f9..138d08ed 100644 --- a/bundles/create/introduction.rst +++ b/bundles/create/introduction.rst @@ -519,6 +519,65 @@ configure the ``cmf_create.object_mapper_service_id``. CreatePHP would support specific mappers per RDFa type. If you need that, dig into the CreatePHP and CreateBundle and do a pull request to enable this feature. +Workflows +--------- + +.. versionadded:: 1.1 + Support for workflows was introduced in CreateBundle 1.1. + +CreateJS uses a REST api for creating, loading and changing content. To delete content +the HTTP method DELETE is used. Since deleting might be a more complex operation +than just removing the content form the storage (e.g. getting approval by another +editor) there is no simple delete button in the user frontend. Instead, CreateJS and +CreatePHP use "workflows" to implement that. This bundle comes with a simple implementation +of a workflow to delete content. To enable the workflow set the config option 'delete' to true. + +.. configuration-block:: + + .. code-block:: yaml + + cmf_create: + persistence: + phpcr: + delete: true + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + $container->loadFromExtension('cmf_create', array( + 'persistence' => array( + 'phpcr' => array( + 'delete' => true, + ), + ), + )); + +This results in the delete workflow being registered with CreatePHP and CreateJS so that +you can now delete content from the frontend. + +.. note:: + + The provided workflow supports PHPCR persistence only. It deletes the currently selected + content once you confirmed deletion in the frontend. If the currently selected property is + a property of the page the whole page is deleted. + +In a more complex setup you need to create your own workflow instance, register it with CreatePHP +and implement your logic in the workflows run method. + +Currently the bundle only supports delete workflows but that will change in the future. .. _`create.js`: http://createjs.org .. _`hallo.js`: http://hallojs.org diff --git a/bundles/map.rst.inc b/bundles/map.rst.inc index 2840cae9..d4ed2efe 100644 --- a/bundles/map.rst.inc +++ b/bundles/map.rst.inc @@ -50,10 +50,9 @@ library or they introduce a complete new concept. * :doc:`routing_auto/index` (in development) * :doc:`routing_auto/introduction` - * :doc:`routing_auto/providers` - * :doc:`routing_auto/exists_actions` - * :doc:`routing_auto/not_exists_actions` - * :doc:`routing_auto/customization` + * :doc:`routing_auto/token_providers` + * :doc:`routing_auto/conflict_resolvers` + * :doc:`routing_auto/defunct_route_handlers` * :doc:`search/index` @@ -90,6 +89,7 @@ bundle based on the key bundles. * :doc:`simple_cms/introduction` * :doc:`simple_cms/multilang` * :doc:`simple_cms/rendering` + * :doc:`simple_cms/menus` * :doc:`simple_cms/extending_page_class` Contributed Bundles diff --git a/bundles/menu/sonata_admin.rst b/bundles/menu/sonata_admin.rst index 970e4124..75f1ac73 100644 --- a/bundles/menu/sonata_admin.rst +++ b/bundles/menu/sonata_admin.rst @@ -121,6 +121,114 @@ configuration in the ``sonata_admin`` section of your project configuration: See the `Sonata Admin extension documentation`_ for more information. +MenuOptionInterface Sonata Admin Extension +------------------------------------------ + +This bundle provides an extension that allows user to edit different menu +options using the Sonata admin interface. + +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_menu.admin_extension.menu_options: + implements: + - Symfony\Cmf\Bundle\MenuBundle\Model\MenuOptionsInterface + + .. code-block:: xml + + + + + + + + + Symfony\Cmf\Bundle\MenuBundle\Model\MenuOptionsInterface + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('sonata_admin', array( + 'extensions' => array( + 'cmf_menu.admin_extension.menu_options' => array( + 'implements' => array( + 'Symfony\Cmf\Bundle\MenuBundle\Model\MenuOptionsInterface', + ), + ), + ), + )); + +See the `Sonata Admin extension documentation`_ for more information. + +These are the list of available options: + + * Display; + * Display children; + * Menu attributes (advanced); + * Label attributes (advanced); + * Children attributes (advanced); + * Link attributes (advanced). + +See the `KnpMenuBundle documentation`_ for more information about these +attributes. + +By default the only available options are **Display** and **Display Children**. +To enable the advaned options you need to add ``burgov/key-value-form-bundle`` +requirement in your ``composer.json`` and enable the advanced options in +your config file: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + cmf_menu: + admin_extensions: + menu_options: + advanced: true + + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('cmf_menu', array( + 'admin_extensions' => array( + 'menu_options' => array( + 'advanced' => true, + ), + ), + )); + .. _`Sonata Admin extension documentation`: http://sonata-project.org/bundles/admin/master/doc/reference/extensions.html .. _SonataDoctrinePHPCRAdminBundle: http://sonata-project.org/bundles/doctrine-phpcr-admin/master/doc/index.html .. _`configuring sonata admin`: http://sonata-project.org/bundles/doctrine-phpcr-admin/master/doc/reference/configuration.html +.. _`KnpMenuBundle documentation`: http://github.com/KnpLabs/KnpMenu/blob/master/doc/01-Basic-Menus.markdown#menu-attributes diff --git a/bundles/routing_auto/conflict_resolvers.rst b/bundles/routing_auto/conflict_resolvers.rst new file mode 100644 index 00000000..149cf0a1 --- /dev/null +++ b/bundles/routing_auto/conflict_resolvers.rst @@ -0,0 +1,117 @@ +.. index:: + single: Conflict Resolvers; RoutingAutoBundle + +Conflict Resolvers +================== + +Conflict resolvers are invoked when the system detects that a newly generated +route would conflict with a route already existing in the route repository. + +This section details the conflict resolvers which are provided by default. + +auto_increment +-------------- + +The ``auto_increment`` conflict resolver will add a numerical suffix to the +path, for example if ``my/path`` already exists, it would first become +``my/path-1`` and if that path *also* exists it will try ``my/path-2``, +``my/path-3`` and so on into infinity until it finds a path which *doesn't* +exist. + +.. configuration-block:: + + .. code-block:: yaml + + stdClass: + uri_schema: /cmf/blog + conflict_resolver: auto_increment + + .. code-block:: xml + + + + + + + +throw_exception +--------------- + +The ``throw_exception`` efficiently "resolves" conflicts by throwing exceptions. +This is the default action. + +.. configuration-block:: + + .. code-block:: yaml + + stdClass: + uri_schema: /cmf/blog + conflict_resolver: throw_exception + + .. code-block:: xml + + + + + + + +Creating a Custom Conflict Resolver +----------------------------------- + +To create a custom conflict resolver, you have to implement +``ConflictResolverInterface``. This interface requires a method called +``resolveConflict`` which has access to the ``UriContext``. It returns the new +route. + +The following example will append a unique string to the URI to resolve a +conflict:: + + namespace Symfony\Cmf\Component\RoutingAuto\ConflictResolver; + + use Symfony\Cmf\Component\RoutingAuto\ConflictResolverInterface; + use Symfony\Cmf\Component\RoutingAuto\UriContext; + use Symfony\Cmf\Component\RoutingAuto\Adapter\AdapterInterface; + + class UniqidConflictResolver implements ConflictResolverInterface + { + public function resolveConflict(UriContext $uriContext) + { + $uri = $uriContext->getUri(); + return sprintf('%s-%s', uniqid()); + } + } + +Conflict resolvers should be registered as services and tagged with +``cmf_routing_auto.conflict_resolver``: + +.. configuration-block:: + + .. code-block:: yaml + + services: + acme_cms.conflict_resolver.foobar: + class: Acme\CmsBundle\RoutingAuto\ConflictResolver\UniqidConflictResolver + tags: + - { name: cmf_routing_auto.conflict_resolver, alias: "uniqid"} + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + use Symfony\Component\DependencyInjection\Definition; + + $definition = new Definition('Acme\CmsBundle\RoutingAuto\ConflictResolver\UniqidConflictResolver'); + $definition->addTag('cmf_routing_auto.conflict_resolver', array('alias' => 'foobar')); + + $container->setDefinition('acme_cms.conflict_resolver.uniqid', $definition); diff --git a/bundles/routing_auto/customization.rst b/bundles/routing_auto/customization.rst deleted file mode 100644 index b293ebfe..00000000 --- a/bundles/routing_auto/customization.rst +++ /dev/null @@ -1,157 +0,0 @@ -.. index:: - single: Customization; RoutingAutoBundle - -Customization -------------- - -.. _routingauto_customization_pathproviders: - -Adding Path Providers -~~~~~~~~~~~~~~~~~~~~~ - -The goal of a ``PathProvider`` class is to add one or several path elements to -the route stack. For example, the following provider will add the path -``foo/bar`` to the route stack:: - - // src/Acme/CmsBundle/RoutingAuto/PathProvider/FoobarProvider.php - namespace Acme\CmsBundle\RoutingAuto\PathProvider; - - use Symfony\Cmf\Bundle\RoutingAutoBundle\AutoRoute\PathProviderInterface; - use Symfony\Cmf\Bundle\RoutingAutoBundle\AutoRoute\RouteStack; - - class FoobarProvider implements PathProviderInterface - { - public function providePath(RouteStack $routeStack) - { - $routeStack->addPathElements(array('foo', 'bar')); - } - } - -To use the path provider you must register it in the container and add the -``cmf_routing_auto.provider`` tag and set the **alias** accordingly: - -.. configuration-block:: - - .. code-block:: yaml - - services: - acme_cms.path_provider.foobar: - class: Acme\CmsBundle\RoutingAuto\PathProvider\FoobarProvider - scope: prototype - tags: - - { name: cmf_routing_auto.provider, alias: "foobar"} - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - use Symfony\Component\DependencyInjection\Definition; - - $definition = new Definition('Acme\CmsBundle\RoutingAuto\PathProvider\FoobarProvider'); - $definition->addTag('cmf_routing_auto.provider', array('alias' => 'foobar')); - $definition->setScope('prototype'); - - $container->setDefinition('acme_cms.path_provider.foobar', $definition); - -The ``FoobarProvider`` is now available as **foobar** in the routing auto -configuration. - -.. caution:: - - Both path providers and path actions need to be defined with a scope of - "prototype". This ensures that each time the auto routing system requests - the class a new one is given and you do not have any state problems. - -Adding Path Actions -~~~~~~~~~~~~~~~~~~~ - -In the auto routing system, a "path action" is an action to take if the path -provided by the "path provider" exists or not. - -You can add a path action by extending the ``PathActionInterface`` and -registering your new class correctly in the DI configuration. - -This is a very simple implementation from the bundle - it is used to throw an -exception when a path already exists:: - - namespace Symfony\Cmf\Bundle\RoutingAutoBundle\RoutingAuto\PathNotExists; - - use Symfony\Cmf\Bundle\RoutingAutoBundle\AutoRoute\PathActionInterface; - use Symfony\Cmf\Bundle\RoutingAutoBundle\AutoRoute\Exception\CouldNotFindRouteException; - use Symfony\Cmf\Bundle\RoutingAutoBundle\AutoRoute\RouteStack; - - class ThrowException implements PathActionInterface - { - public function init(array $options) - { - } - - public function execute(RouteStack $routeStack) - { - throw new CouldNotFindRouteException('/'.$routeStack->getFullPath()); - } - } - -The ``init()`` method configures the provider (throwing errors when required -options do not exists) and the ``execute()`` method executes the action. - -It is registered in the DI configuration as follows: - -.. configuration-block:: - - .. code-block:: yaml - - services: - cmf_routing_auto.not_exists_action.throw_exception: - class: Symfony\Cmf\Bundle\RoutingAutoBundle\RoutingAuto\PathNotExists\ThrowException - scope: prototype - tags: - - { name: cmf_routing_auto.not_exists_action, alias: "throw_exception"} - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - use Symfony\Component\DependencyInjection\Definition; - - $definition = new Definition('Symfony\Cmf\Bundle\RoutingAutoBundle\RoutingAuto\PathNotExists\ThrowException'); - $definition->addTag('cmf_routing_auto.provider', array('alias' => 'throw_exception')); - $definition->setScope('prototype'); - - $container->setDefinition('cmf_routing_auto.not_exists_action.throw_exception', $definition); - -Note the following: - -* **Scope**: Must *always* be set to *prototype*; -* **Tag**: The tag registers the service with the auto routing system, it can - be one of the following: - - * ``cmf_routing_auto.exists.action`` - if the action is to be used when a - path exists; - * ``cmf_routing_auto.not_exists.action`` - if the action is to be used when - a path does not exist; - -* **Alias**: The alias of the tag is the name by which you will reference this - action in the auto routing configuration. diff --git a/bundles/routing_auto/defunct_route_handlers.rst b/bundles/routing_auto/defunct_route_handlers.rst new file mode 100644 index 00000000..8111e907 --- /dev/null +++ b/bundles/routing_auto/defunct_route_handlers.rst @@ -0,0 +1,184 @@ +.. index:: + single: Customization; RoutingAutoBundle + +Defunct Route Handlers +====================== + +When an already-persisted document is updated and the URI generated by the +RoutingAuto system is changed, a *new* route is always created. Defunct route +handlers decide what to do with the *old* routes. + +remove +------ + +The remove handler will simply delete any old routes. This is the **default** +action. + +.. configuration-block:: + + .. code-block:: yaml + + stdClass: + uri_schema: /cmf/blog + defunct_route_handler: remove + + .. code-block:: xml + + + + + + + +.. note:: + + The above example is illustrative only. Removing old routes is the default + action, so it is not necessary to explicitly configure this handler. + +leave_redirect +-------------- + +The ``LeaveRedirectDefunctRouteHandler`` will automatically update old routes +to redirect the browser to the route which has replaced it. This effectively +means you can rename your objects without worrying about users receiving HTTP +404 responses. + +The handler will replace the old route with a ``RedirectRoute`` at the same +path. + +.. configuration-block:: + + .. code-block:: yaml + + stdClass: + uri_schema: /cmf/blog + defunct_route_handler: leave_redirect + + .. code-block:: xml + + + + + + + +For the redirect to work you will also need to configure a redirect controller +in the ``cmf_routing`` configuration: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + cmf_routing: + dynamic: + controllers_by_class: + Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\RedirectRoute: cmf_routing.redirect_controller:redirectAction + + .. code-block:: xml + + + + + + + + + cmf_routing.redirect_controller:redirectAction + + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('cmf_routing', array( + 'dynamic' => array( + 'controllers_by_class' => array( + 'Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\RedirectRoute' => 'cmf_routing.redirect_controller:redirectAction', + ), + ), + )); + +Creating a Custom Defunct Route Handler +--------------------------------------- + +To create a custom default route handler, you have to implement +``DefunctRouteHandlerInterface``. This requires a method ``handleDefunctRoutes()``. + +They are not alltogether trivial - the following handler removes old routes and is +the default handler:: + + namespace Symfony\Cmf\Component\RoutingAuto\DefunctRouteHandler; + + use Symfony\Cmf\Component\RoutingAuto\DefunctRouteHandlerInterface; + use Symfony\Cmf\Component\RoutingAuto\UriContextCollection; + use Symfony\Cmf\Component\RoutingAuto\Adapter\AdapterInterface; + + class RemoveDefunctRouteHandler implements DefunctRouteHandlerInterface + { + protected $adapter; + + public function __construct(AdapterInterface $adapter) + { + $this->adapter = $adapter; + } + + public function handleDefunctRoutes(UriContextCollection $uriContextCollection) + { + // get all routes that refer to the subject object + $referringAutoRouteCollection = $this->adapter->getReferringAutoRoutes( + $uriContextCollection->getSubjectObject() + ); + + foreach ($referringAutoRouteCollection as $referringAutoRoute) { + // if the route no longer exists + if (false === $uriContextCollection->containsAutoRoute($referringAutoRoute)) { + // get the new route + $newRoute = $uriContextCollection->getAutoRouteByTag($referringAutoRoute->getAutoRouteTag()); + + // migrate the children to the new route + $this->adapter->migrateAutoRouteChildren($referringAutoRoute, $newRoute); + // remove the old route + $this->adapter->removeAutoRoute($referringAutoRoute); + } + } + } + } + +All defunct route handlers should be tagged with +``cmf_routing_auto.defunct_route_handler``. The above class is registered as following: + +.. configuration-block:: + + .. code-block:: yaml + + services: + acme_cms.defunct_route_handler.foobar: + class: Acme\CmsBundle\RoutingAuto\DefunctRouteHandler\RemoveConflictResolver + tags: + - { name: cmf_routing_auto.defunct_route_handler, alias: "remove"} + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + use Symfony\Component\DependencyInjection\Definition; + + $definition = new Definition('Acme\CmsBundle\RoutingAuto\DefunctRouteHandler\RemoveConflictResolver'); + $definition->addTag('cmf_routing_auto.defunct_route_handler', array('alias' => 'foobar')); + + $container->setDefinition('acme_cms.defunct_route_handler.remove', $definition); diff --git a/bundles/routing_auto/exists_actions.rst b/bundles/routing_auto/exists_actions.rst deleted file mode 100644 index 476ce4f8..00000000 --- a/bundles/routing_auto/exists_actions.rst +++ /dev/null @@ -1,67 +0,0 @@ -.. index:: - single: Exists Actions; RoutingAutoBundle - -Path Exists Actions -------------------- - -These are the default actions available to take if the path provided by a -``path_provider`` already exists and so creating a new path would create a -conflict. - -auto_increment -~~~~~~~~~~~~~~ - -The ``auto_increment`` action will add a numerical suffix to the path, for -example ``my/path`` would first become ``my/path-1`` and if that path *also* -exists it will try ``my/path-2``, ``my/path-3`` and so on into infinity until -it finds a path which *doesn't* exist. - -This action should typically be used in the ``content_name`` builder unit to -resolve conflicts. Using it in the ``content_path`` builder chain would not -make much sense. - -.. configuration-block:: - - .. code-block:: yaml - - exists_action: auto_increment - - .. code-block:: xml - - - - .. code-block:: php - - array( - // ... - 'exists_action' => 'auto_increment', - ); - -use -~~~ - -The ``use`` action will simply take the existing path and use it. For example, -in a forum the builder unit must first determine the category path, ``/my-category``, -if this path exists (and it should) then it will be *used* in the stack. - -This action should typically be used in one of the content path builder units -to specify that it should use the existing route. On the other hand, using -this as the content name builder action should cause the old route to be -overwritten. - -.. configuration-block:: - - .. code-block:: yaml - - exists_action: use - - .. code-block:: xml - - - - .. code-block:: php - - array( - // ... - 'exists_action' => 'use', - ); diff --git a/bundles/routing_auto/index.rst b/bundles/routing_auto/index.rst index 1ddaf6b6..edf6d93b 100644 --- a/bundles/routing_auto/index.rst +++ b/bundles/routing_auto/index.rst @@ -5,7 +5,7 @@ RoutingAutoBundle :maxdepth: 2 introduction - providers - exists_actions - not_exists_actions - customization + token_providers + conflict_resolvers + defunct_route_handlers + diff --git a/bundles/routing_auto/introduction.rst b/bundles/routing_auto/introduction.rst index a8e4c61c..0a0bd6cf 100644 --- a/bundles/routing_auto/introduction.rst +++ b/bundles/routing_auto/introduction.rst @@ -5,10 +5,8 @@ RoutingAutoBundle ================= - The RoutingAutoBundle allows you to define automatically created routes for - documents. - -.. include:: ../_not-stable-caution.rst.inc +The RoutingAutoBundle allows you to automatically persist routes when +documents are persisted based on URI schemas and contextual information. This implies a separation of the ``Route`` and ``Content`` documents. If your needs are simple this bundle may not be for you and you should have a look at @@ -30,7 +28,7 @@ content documents - a category and the topics. These documents are called If you create a new category with the title "My New Category", the RoutingAutoBundle will automatically create the route ``/forum/my-new-cateogry``. For each new ``Topic`` it could create a route -like ``/forum/my-new-category/my-new-topic``. This URL resolves to a special +like ``/forum/my-new-category/my-new-topic``. This URI resolves to a special type of route that is called an *auto route*. By default, when you update a content document that has an auto route, the @@ -51,13 +49,13 @@ pattern ``/forum/my-new-forum/{topic}``, which could be handled by a controller. Why not just do that? #. By having a route for each page in the system, the application has a - knowledge of which URLs are accessible. This can be very useful, for + knowledge of which URIs are accessible. This can be very useful, for example, when specifying endpoints for menu items that are used when generating a site map; #. By separating the route from the content you allow the route to be customized independently of the content, for example, a topic may have - the same title as another topic but might need a different URL; -#. Separate route documents are translateable - this means you can have a URL + the same title as another topic but might need a different URI; +#. Separate route documents are translateable - this means you can have a URI for *each language*, "/welcome" and "/bienvenue" would each reference the same document in English and French respectively. This would be difficult if the slug was embedded in the content document; @@ -68,142 +66,59 @@ Why not just do that? Usage ----- -The diagram below shows a fictional URL for a forum topic. The first 6 elements -of the URL are called the *content path*. The last element is called the *content name*. +Imagine you have a fictional forum application and that you want to access the +forum topic with the following fictional URI: -.. image:: ../../_images/bundles/routing_auto_post_schema.png +- ``https://mywebsite.com/my-forum/drinks/coffee`` -The content path is further broken down into *path units* and *path elements*. A -path unit is a group of path elements and path elements are simply documents -in the PHPCR tree. +The RoutingAutoBundle uses a URI schema to define how routes are generated. A +schema for the above URI would look like this (the bundle does not care about +the host or protocol): -.. note:: +- ``/my-forum/{category}/{title}`` - Although path elements can be of any document class in this case, only - objects which extend the :class:`Symfony\\Component\\Routing\\Route` - object will be considered when matching a URL. +You can see that ``my-forum`` is static (it will not change) but that +``drinks`` has been replaced with ``{category}`` and ``coffee`` with +``{title}``. The replacements are called *tokens*. - The default behavior is to use ``Generic`` documents when generating a content - path, and these documents will result in a 404 when accessed directly. +The value for tokens are provided by *token providers*. -Internally, each path unit is built up by a *builder unit*. Builder units -contain one *path provider* class and two actions classes - one action to take -if the provided path exists in the PHPCR tree, the other if it does not. The -goal of each builder unit is to generate a path and then provide a route -object for each element in that path. +The schema, token providers, and other configurations (more on this later) are +contained within ``routing_auto.format`` files (currently ``xml`` and ``yaml`` are +supported). These files are contained either in your bundles +``Resources/config`` directory or in a custom location specified in +the bundle configuration. -The configuration for the example above could be as follows: +The configuration files for the above schema as applied to a ``Topic`` +document could be defined as follows: .. configuration-block:: .. code-block:: yaml - # app/config/config.yml - cmf_routing_auto: - mappings: - - Acme\ForumBundle\Document\Topic - content_path: - # corresponds first path unit in diagram: my-forum - forum_path: - provider: [specified, { path: my-form }] - exists_action: use - not_exists_action: create - - # corresponds second path unit in diagram: my-category - category_path: - provider: [content_object, { method: getCategory }] - exists_action: use - not_exists_action: throw_exception - - # corresponds to the content name: my-new-topic - content_name: - provider: [content_method, { method: getTitle }] - exists_action: [auto_increment, { pattern: -%d }] - not_exists_action: create + # src/Acme/ForumBundle/Resources/config/cmf_routing_auto.yml + Acme\ForumBundle\Document\Topic: + uri_schema: /my-forum/{category}/{title} + token_providers: + category: [content_method, { method: getCategoryTitle, slugify: true }] + title: [content_method, { method: getTitle }] # slugify is true by default .. code-block:: xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('cmf_routing_auto', array( - 'mappings' => array( - 'Acme\ForumBundle\Document\Topic' => array( - 'content_path' => array( - // corresponds first path unit in diagram: my-forum - 'forum_path' => array( - 'provider' => array('specified', array( - 'path' => 'my-forum', - )), - 'exists_action' => 'use', - 'not_exists_action' => 'create', - ), - - // corresponds second path unit in diagram: my-category - 'category_path' => array( - 'provider' => array('content_object', array( - 'method' => 'getCategory', - )), - 'exists_action' => 'use', - 'not_exists_action' => 'throw_exception', - ), - ), - - // corresponds to the content name: my-new-topic - 'content_name' => array( - 'provider' => array('content_method', array( - 'method' => 'getTitle', - )), - 'exists_action' => array('auto_increment', array( - 'pattern' => '-%d', - )), - 'not_exists_action' => 'create', - ), - ), - ), - )); + + + + + + + + + + + + + + The ``Topic`` document would then need to implement the methods named above as follows:: @@ -216,47 +131,39 @@ follows:: /** * Returns the category object associated with the topic. */ - public function getCategory() + public function getCategoryName() { - return $this->category; - } - - public function getPublishedDate() - { - return new \DateTime('2013/04/06'); + return 'Drinks'; } public function getTitle() { - return "My Topic Title"; + return 'Coffee'; } } -After persisting this object, the route will be created. Of course, you need to make -the properties editable and then you have a fully working routing system. +After persisting this object, the route will be created. You will of course +be wanting to return property values and not static strings, but you get the +idea. .. note:: Any mapping applied to an object will also apply to subclasses of that object. Imagine you have 2 documents, ``ContactPage`` and ``Page``, which both extend ``AbstractPage``. When you map the ``AbstractPage`` class, it - will be applied to both documents. - -Provided Providers and Action ------------------------------ - -The RoutingAutoBundle comes with a couple path providers and actions by -default. Read more about them in the other sections: + will be applied to both documents. You can also use the ``extend`` keyword + to achieve the same thing with objects which are not related. -* :doc:`providers` -* :doc:`exists_actions` -* :doc:`not_exists_actions` +This is just a basic example. You can also configure what should happen when +a route already exists (confict resolution) and what to do with old routes +when the generated URI is changed (defunct route handling). -Customization -------------- +Read more +--------- -Besides the default providers and actions, you can also create your own. Read more about -that in :doc:`customization`. +* :doc:`token_providers` +* :doc:`conflict_resolvers` +* :doc:`defunct_route_handlers` .. _`with composer`: http://getcomposer.org/ .. _`symfony-cmf/routing-auto-bundle`: https:/packagist.org/packages/symfony-cmf/routing-auto-bundle diff --git a/bundles/routing_auto/not_exists_actions.rst b/bundles/routing_auto/not_exists_actions.rst deleted file mode 100644 index 3ce12694..00000000 --- a/bundles/routing_auto/not_exists_actions.rst +++ /dev/null @@ -1,60 +0,0 @@ -.. index:: - single: Not Exists Actions; RoutingAutoBundle - -Path not Exists Actions ------------------------ - -These are the default actions available to take if the path provided by a -``path_provider`` does not exist. - -create -~~~~~~ - -The ``create`` action will create the path. - -.. configuration-block:: - - .. code-block:: yaml - - not_exists_action: create - - .. code-block:: xml - - - - .. code-block:: php - - array( - // ... - 'not_exists_action' => 'create', - ); - -.. note:: - - **Currently** all routes provided by the content path builder units will be - created as ``Generic`` documents, whilst the content name route will be - created as an ``AutoRoute`` document. - -throw_exception -~~~~~~~~~~~~~~~ - -This action will throw an exception if the route provided by the path provider -does not exist. You should take this action if you are sure that the route -*should* exist. - -.. configuration-block:: - - .. code-block:: yaml - - not_exists_action: throw_exception - - .. code-block:: xml - - - - .. code-block:: php - - array( - // ... - 'not_exists_action' => 'throw_exception', - ); diff --git a/bundles/routing_auto/providers.rst b/bundles/routing_auto/providers.rst deleted file mode 100644 index fb4fead3..00000000 --- a/bundles/routing_auto/providers.rst +++ /dev/null @@ -1,200 +0,0 @@ -.. index:: - single: Path Providers; RoutingAutoBundle - -Path Providers --------------- - -Path providers specify a target path which is used by the subsequent path -actions to provide the actual route documents. - -**Base** providers must be the first configured as the first builder in the -content path chain. This is because the paths that they provide correspond -directly to an existing path, i.e. they have an absolute reference. - -specified (base provider) -~~~~~~~~~~~~~~~~~~~~~~~~~ - -This is the most basic path provider and allows you to specify an exact -(fixed) path. - -Options -....... - -* ``path`` - **required** The path to provide. - -.. configuration-block:: - - .. code-block:: yaml - - provider: [specified, { path: this/is/a/path }] - - .. code-block:: xml - - - - - .. code-block:: php - - array( - // ... - 'provider' => array('specified', array('path' => 'this/is/a/path')), - ); - -.. caution:: - - You should never specifiy absolute paths in the auto route system. If the - builder unit is the first content path chain it is understood that it is - the base of an absolute path. - -content_object (base provider) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The content object provider will try and provide a path from an object -implementing ``RouteReferrersInterface`` provided by a designated method on the -content document. For example, if you have a ``Topic`` class, which has a -``getCategory`` method, using this provider you can tell the ``Topic`` auto route -to use the route of the category as a base. - -So basically, if your category document has a path of ``/this/is/my/category``, -you can use this path as the base of your ``Category`` auto-route. - -Options -....... - - - ``method``: **required** Method used to return the document whose route path you wish to use. - -.. configuration-block:: - - .. code-block:: yaml - - provider: [content_object, { method: getCategory }] - - .. code-block:: xml - - - - - .. code-block:: php - - array( - // ... - 'provider' => array('content_object', array('method' => 'getCategory')), - ); - -.. note:: - - At the time of writing translated objects are not supported. But a patch - is already created for this feature. - -content_method -~~~~~~~~~~~~~~ - -The ``content_method`` provider allows the content object (e.g. a forum -``Topic``) to specify a path using one of its methods. This is quite a powerful -method as it allows the content document to do whatever it can to produce the -route, the disadvantage is that your content document will have extra code in -it. - -Options -....... - -* ``method``: **required** Method used to return the route name/path/path elements. -* ``slugify``: If the return value should be slugified, default is ``true``. - -.. configuration-block:: - - .. code-block:: yaml - - provider: [content_method, { method: getTitle }] - - .. code-block:: xml - - - - - .. code-block:: php - - array( - // ... - 'provider' => array('content_method', array('method' => 'getTitle')), - ); - -This example will use the existing method "getTitle" of the ``Topic`` document -to retrieve the title. By default all strings are *slugified*. - -The method can return the path either as a single string, an array of path -elements or an object which can be converted into a string, as shown in the -following example:: - - class Topic - { - /* Using a string */ - public function getTitle() - { - return "This is a topic"; - } - - /* Using an array */ - public function getPathElements() - { - return array('this', 'is', 'a', 'path'); - } - - /* Using an object */ - public function getStringObject() - { - $object = ...; // an object which has a __toString() method - - return $object; - } - } - -content_datetime -~~~~~~~~~~~~~~~~ - -The ``content_datettime`` provider will provide a path from a ``DateTime`` -object provided by a designated method on the content document. - -.. configuration-block:: - - .. code-block:: yaml - - provider: [content_datetime, { method: getDate, date_format: Y/m/d }] - - .. code-block:: xml - - - - - .. code-block:: php - - array( - // ... - 'provider' => array('content_datetime', array( - 'method' => 'getDate', - 'date_format' => 'Y/m/d', - )), - ); - -.. note:: - - This method extends `content_method`_ and inherits the slugify feature. - Internally, it returns a string using the `DateTime->format()` method. This - means that you can specify your date in anyway you like and it will be - automatically slugified. Also, by adding path separators in the - ``date_format`` you are effectively creating routes for each date component - as slugify applies to **each element** of the path. - -Options -....... - -* ``method``: **required** Method used to return the route name/path/path - elements. -* ``slugify``: If the return value should be slugified, default is ``true``. -* ``date_format``: Any date format accepted by the `DateTime` class, default - ``Y-m-d``. diff --git a/bundles/routing_auto/token_providers.rst b/bundles/routing_auto/token_providers.rst new file mode 100644 index 00000000..38e7786b --- /dev/null +++ b/bundles/routing_auto/token_providers.rst @@ -0,0 +1,208 @@ +.. index:: + single: Path Providers; RoutingAutoBundle + +Token Providers +=============== + +Token providers provide values for the tokens specified in the URI schemas. +Such values can be derived form the object for which the route is being +generated or from the environment (e.g. you could use the current locale in +the route). + + +content_method +-------------- + +The ``content_method`` provider allows the content object (e.g. a forum +``Topic``) to specify a path using one of its methods. This is quite a powerful +method as it allows the content document to do whatever it can to produce the +route. + +.. configuration-block:: + + .. code-block:: yaml + + # src/Acme/ForumBundle/Resources/config/cmf_routing_auto.yml + Acme\ForumBundle\Document\Topic: + uri_schema: /my-forum/{title} + token_providers: + title: [content_method, { method: getTitle }] + + .. code-block:: xml + + + + + + + + + + + +This example will use the existing method "getTitle" of the ``Topic`` document +to retrieve the title. By default all strings are *slugified*. + +Options +~~~~~~~ + +``method`` + **required** Method used to return the route name/path/path elements. +``slugify`` + If the return value should be slugified, default is ``true``. + +content_datetime +---------------- + +The ``content_datetime`` provider will provide a path from a ``DateTime`` +object provided by a designated method on the content document. + +.. configuration-block:: + + .. code-block:: yaml + + # src/Acme/ForumBundle/Resources/config/cmf_routing_auto.yml + Acme\ForumBundle\Document\Topic: + uri_schema: /blog/{date}/my-post + token_providers: + date: [content_datetime, { method: getDate }] + + .. code-block: xml + + + + + + + + + + + +Options +~~~~~~~ + +``method`` + **required** Method used to return the route name/path/path elements. +``slugify`` + If the return value should be slugified, default is ``true``. +``date_format`` + Any date format accepted by the `DateTime` class, default ``Y-m-d``. + +content_locale +-------------- + +The ``content_locale`` provider will provide the locale (e.g. ``fr``, ``de``, +etc) from the subject object. It ultimately determines the locale from the +storage specific adapter - so it is dependent upon the adapter supporting this +feature. + +.. configuration-block:: + + .. code-block:: yaml + + # src/Acme/ForumBundle/Resources/config/cmf_routing_auto.yml + Acme\ForumBundle\Document\Topic: + uri_schema: /blog/{locale}/my-post + token_providers: + locale: [content_locale] + + .. code-block: xml + + + + + + + + + +Options +~~~~~~~ + +``method`` + **required** Method used to return the route name/path/path elements. +``slugify`` + If the return value should be slugified, default is ``true``. +``locale_format`` + Any locale format accepted by the `DateTime` class, default ``Y-m-d``. + +Creating a Custom Token Provider +-------------------------------- + +To create a custom token provider, you have to create a class which implements +``TokenProviderInterface``. This class requires a method called ``provideValue()`` +which returns the value of the token. It has access to the ``UriContext``, +which contains the current uri (``getUri()``), the subject object +(``getSubjectObject()``), the locale (``getLocale()``) and the auto route +(``getAutoRoute()``). + +The class also requires a method called ``configureOptions()``. This method can +configure any options using the `OptionsResolver component`_. + +The following token provider doesn't have any options and simply always returns +``'foobar'``:: + + // src/Acme/CmsBundle/RoutingAuto/PathProvider/FoobarTokenProvider.php + namespace Symfony\Cmf\Component\RoutingAuto\TokenProvider; + + use Symfony\Cmf\Component\RoutingAuto\TokenProviderInterface; + use Symfony\Component\OptionsResolver\OptionsResolverInterface; + use Symfony\Cmf\Component\RoutingAuto\UriContext; + + class FoobarTokenProvider implements TokenProviderInterface + { + /** + * {@inheritDoc} + */ + public function provideValue(UriContext $uriContext, $options) + { + return 'foobar'; + } + + /** + * {@inheritDoc} + */ + public function configureOptions(OptionsResolverInterface $optionsResolver) + { + } + } + +To use the path provider, you must register it in the container and add the +``cmf_routing_auto.token_provider`` tag and set the **alias** accordingly: + +.. configuration-block:: + + .. code-block:: yaml + + services: + acme_cms.token_provider.foobar: + class: Acme\CmsBundle\RoutingAuto\PathProvider\FoobarTokenProvider + tags: + - { name: cmf_routing_auto.token_provider, alias: "foobar" } + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + use Symfony\Component\DependencyInjection\Definition; + + $definition = new Definition('Acme\CmsBundle\RoutingAuto\PathProvider\FoobarTokenProvider'); + $definition->addTag('cmf_routing_auto.token_provider', array('alias' => 'foobar')); + + $container->setDefinition('acme_cms.token_provider.foobar', $definition); + +The ``FoobarTokenProvider`` is now available as **foobar** in the routing auto +configuration. + +.. _`OptionsResolver component`: http://symfony.com/doc/current/components/options_resolver.html diff --git a/bundles/simple_cms/extending_page_class.rst b/bundles/simple_cms/extending_page_class.rst index 9c87d70e..e1a30358 100644 --- a/bundles/simple_cms/extending_page_class.rst +++ b/bundles/simple_cms/extending_page_class.rst @@ -4,7 +4,7 @@ Extending the Page class ------------------------ -The default Page document (``Symfony\Cmf\Bundle\SimpleCmsBundle\Model\Page``) +The default Page document (``Symfony\Cmf\Bundle\SimpleCmsBundle\Doctrine\Phpcr\Page``) is relatively simple, shipping with a handful of the most common properties for building a typical page: title, body, tags, publish dates etc. diff --git a/bundles/simple_cms/index.rst b/bundles/simple_cms/index.rst index a3f42c00..0bfcf85e 100644 --- a/bundles/simple_cms/index.rst +++ b/bundles/simple_cms/index.rst @@ -7,4 +7,5 @@ SimpleCmsBundle introduction multilang rendering + menus extending_page_class diff --git a/bundles/simple_cms/introduction.rst b/bundles/simple_cms/introduction.rst index 13ff4034..eedb0678 100644 --- a/bundles/simple_cms/introduction.rst +++ b/bundles/simple_cms/introduction.rst @@ -35,6 +35,7 @@ Sections * :doc:`multilang` * :doc:`rendering` +* :doc:`menus` * :doc:`extending_page_class` .. _`Symfony CMF Standard Edition`: https://github.com/symfony-cmf/standard-edition diff --git a/bundles/simple_cms/menus.rst b/bundles/simple_cms/menus.rst new file mode 100644 index 00000000..48343187 --- /dev/null +++ b/bundles/simple_cms/menus.rst @@ -0,0 +1,50 @@ +.. index:: + single: Menus; SimpleCmsBundle + +Menus +----- + +You can use `Knp Menu Bundle`_ to render a menu of your SimpleCms pages. The default Page document +(``Symfony\Cmf\Bundle\SimpleCmsBundle\Doctrine\Phpcr\Page``) implements the ``Knp\Menu\NodeInterface`` +which allows for rendering them as a menu. + +.. configuration-block:: + + .. code-block:: jinja + + {{ knp_menu_render('/cms/simple/mypage') }} + + .. code-block:: html+php + + render('/cms/simple/mypage') ?> + +Menu options can be customized for each `Page` using the following public methods of the `Page`. + +setAttributes(array $attributes), setAttribute($name, $value) + Set one or more html attributes to be used when rendering the item (generally the
  • tag) + +setLabel($label) + Set the label text to be used + +setLabelAttributes($labelAttributes) + Set html attributes to be used when rendering the label + +setChildrenAttributes(array $attributes) + Set one or more html attributes to be used on the element containing the children (generally the
      tag) + +setLinkAttributes($linkAttributes) + Set html attributes to be used when rendering the link tag + +setDisplay($display) + Boolean which determins if the page should be included in menus + +setDisplayChildren($displayChildren) + Boolean which determines whether children should be added to the menu + +.. tip:: + + If you use Sonata Admin in your project you can edit the menu options + using the MenuOptionsExtension that comes with the menu bundle. For more + information on how to use it take a look at the :doc:`menu bundle documentation <../menu/sonata_admin>` + +.. _`Knp Menu Bundle`: https://github.com/KnpLabs/KnpMenuBundle diff --git a/cookbook/creating_a_cms/auto-routing.rst b/cookbook/creating_a_cms/auto-routing.rst index d9b80710..b6caef3c 100644 --- a/cookbook/creating_a_cms/auto-routing.rst +++ b/cookbook/creating_a_cms/auto-routing.rst @@ -1,7 +1,7 @@ Routing and Automatic Routing ----------------------------- -The routes (URLs) to your content will be automatically created and updated +The routes (URIs) to your content will be automatically created and updated using the RoutingAutoBundle. This bundle uses a configuration language to specify automatic creation of routes, which can be a bit hard to grasp the first time you see it. @@ -17,7 +17,7 @@ new route will be linked back to the target content: The paths above represent the path in the PHPCR-ODM document tree. In the next section you will define ``/cms/routes`` as the base path for routes, and subsequently -the contents will be available at the following URLs: +the contents will be available at the following URIs: * **Home**: ``http://localhost:8000/page/home`` * **About**: ``http://localhost:8000/page/about`` @@ -26,22 +26,8 @@ the contents will be available at the following URLs: Installation ~~~~~~~~~~~~ -Ensure that you have the following package installed: - -.. code-block:: javascript - - { - ... - require: { - ... - "symfony-cmf/routing-auto-bundle": "1.0.*@alpha" - }, - ... - } - -.. note:: - - You are installing the bleeding edge version of the routing-auto bundle. +Ensure that you installed the RoutingAutoBundle package as detailed in the :ref:`gettingstarted_installadditionbundles` +section. Enable the routing bundles to your kernel:: @@ -135,88 +121,53 @@ This will: Auto Routing Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~ -Create the following file in your applications configuration directory: +First you need to configure the auto routing bundle: .. code-block:: yaml - # app/config/routing_auto.yml cmf_routing_auto: - mappings: - Acme\BasicCmsBundle\Document\Page: - content_path: - pages: - provider: [specified, { path: /cms/routes/page }] - exists_action: use - not_exists_action: create - content_name: - provider: [content_method, { method: getTitle }] - exists_action: auto_increment - not_exists_action: create - - Acme\BasicCmsBundle\Document\Post: - content_path: - blog_path: - provider: [specified, { path: /cms/routes/post }] - exists_action: use - not_exists_action: create - date: - provider: [content_datetime, { method: getDate}] - exists_action: use - not_exists_action: create - content_name: - provider: [content_method, { method: getTitle }] - exists_action: auto_increment - not_exists_action: create + persistence: + phpcr: + enabled: true -This will configure the routing auto system to automatically create and update -route documents for both the ``Page`` and ``Post`` documents. +The above configures the RoutingAutoBundle to work with PHPCR-ODM. -In summary: +You can now proceed to mapping your documents, create the following in your +*bundles* configuration directory: -* The ``content_path`` key represents the parent path of the content, e.g. - ``/if/this/is/a/path`` then the ``content_path`` - represents ``/if/this/is/a``; -* Each element under ``content_path`` represents a section of the URL; -* The first element ``blog_path`` uses a *provider* which *specifies* a - path. If that path exists then it will do nothing; -* The second element uses the ``content_datetime`` provider, which will - use a ``DateTime`` object returned from the specified method on the - content object (the ``Post``) and create a path from it, e.g. - ``2013/10/13``; -* The ``content_name`` key represents the last part of the path, e.g. ``path`` - from ``/if/this/is/a/path``. +.. code-block:: yaml -Now you will need to include this configuration: + # src/Acme/BasicCmsBundle/Resources/config/cmf_routing_auto.yml + Acme\BasicCmsBundle\Document\Page: + uri_schema: /page/{title} + token_providers: + title: [content_method, { method: getTitle } ] -.. configuration-block:: - - .. code-block:: yaml + Acme\BasicCmsBundle\Document\Post: + uri_schema: /post/{date}/{title} + token_providers: + date: [content_datetime, { method: getDate } + title: [content_method, { method: getTitle }] - # app/config/config.yml - imports: - - { resource: routing_auto.yml } +.. note:: - .. code-block:: xml + RoutingAutoBundle mapping bundles are registered automatically when they are named + as above, you may alternatively explicitly declare from where the mappings should be loaded, + see the :doc:`../../bundles/routing_auto/index` documentation for more information. - - - - - - - - .. code-block:: php +This will configure the routing auto system to automatically create and update +route documents for both the ``Page`` and ``Post`` documents. - // src/Acme/BasicCmsBundle/Resources/config/config.php +In summary, for each class: - // ... - $this->import('routing_auto.yml'); +* We defined a ``uri_schema`` which defines the form of the URI which will be + generated. + * Within the schema you place ``{tokens}`` - placeholders for values provided by... +* Token providers provide values which will be substituted into the URI. Here + you use two different providers - ``content_date`` and ``content_method``. + Both will return dynamic values from the subject object itself. -and reload the fixtures: +Now reload the fixtures: .. code-block:: bash diff --git a/cookbook/creating_a_cms/content-to-controllers.rst b/cookbook/creating_a_cms/content-to-controllers.rst index 37a1deb3..8dd51869 100644 --- a/cookbook/creating_a_cms/content-to-controllers.rst +++ b/cookbook/creating_a_cms/content-to-controllers.rst @@ -1,7 +1,45 @@ Controllers and Templates ------------------------- -Go to the URL http://localhost:8000/page/home in your browser - this should be +Make your content route aware +............................. + +In the :doc:`getting-started` section you defined your `Post` and `Page` documents as +implementing the `RoutesReferrersReadInterface`. This interface enables the routing system +to retrieve routes which refer to the object implementing this interface, and this enables +the system to generate a URL (for example when you use ``{{ path(mydocument) }}`` in Twig). + +Earlier we did not have the RoutingBundle installed, so we could not add the mapping. + +Map the ``$routes`` property to contain a collection of all the routes which refer to this +document:: + + // src/AcmeBundle/BasicCmsBundle/Document/ContentTrait.php + + // ... + + trait ContentTrait + { + // ... + + /** + * @PHPCR\Referrers( + * referringDocument="Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route", + * referencedBy="content" + * ) + */ + protected $routes; + + // ... + } + +Now you can call the method ``getRoutes`` on either ``Page`` or ``Post`` and retrieve all the +routes which refer to that document ... pretty cool! + +Route requests to a controller +.............................. + +Go to the URL http://127.0.0.1:8000/page/home in your browser - this should be your page, but it says that it cannot find a controller. In other words it has found the *route referencing the page* for your page but Symfony does not know what to do with it. diff --git a/cookbook/creating_a_cms/getting-started.rst b/cookbook/creating_a_cms/getting-started.rst index 49bd11db..4268cb75 100644 --- a/cookbook/creating_a_cms/getting-started.rst +++ b/cookbook/creating_a_cms/getting-started.rst @@ -4,9 +4,11 @@ Getting Started Initializing the Project ~~~~~~~~~~~~~~~~~~~~~~~~ -First, follow the generic steps in :doc:`../../bundles/phpcr_odm/introduction` +First, follow the generic steps in :doc:`../database/create_new_project_phpcr_odm` to create a new project using the PHPCR-ODM. +.. _gettingstarted_installadditionbundles: + Install Additional Bundles .......................... @@ -23,19 +25,28 @@ section titled "installation". If you intend to complete the entire tutorial you can save some time by adding all of the required packages now. +.. note:: + + The routing-auto bundle is currently unstable, the package versions listed below are required + but are not **stable**. This means that this is a somewhat volatile combination and you should + think twice before deploying to production -- there will be a stable release soon. + +Please ensure that the packages below replace any packages already defined in your ``composer.json`` +file in the previous step. + .. code-block:: javascript { ... require: { ... - "symfony-cmf/routing-auto-bundle": "1.0.*@alpha", - "symfony-cmf/menu-bundle": "1.1.*", + "doctrine/phpcr-bundle": "1.0.0", + "jackalope/jackalope-doctrine-dbal": "1.1.0", + "symfony-cmf/routing-auto-bundle": "dev-master", + "symfony-cmf/menu-bundle": "1.2.*", "sonata-project/doctrine-phpcr-admin-bundle": "1.1.*", - "symfony-cmf/tree-browser-bundle": "1.1.*", + "symfony-cmf/tree-browser-bundle": "1.1.x-dev as 1.0", "doctrine/data-fixtures": "1.0.*", - - "doctrine/phpcr-odm": "1.1.*", "phpcr/phpcr-utils": "1.1.*", "doctrine/phpcr-bundle": "1.1.*", "symfony-cmf/routing-bundle": "1.2.*", @@ -110,7 +121,7 @@ to reduce code duplication:: protected $parent; /** - * @PHPCR\NodeName() + * @PHPCR\Nodename() */ protected $title; @@ -119,12 +130,6 @@ to reduce code duplication:: */ protected $content; - /** - * @PHPCR\Referrers( - * referringDocument="Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route", - * referencedBy="content" - * ) - */ protected $routes; public function getId() @@ -267,8 +272,8 @@ configuration: .. code-block:: xml - + homepage = $homepage; } + + public function setId($id) + { + $this->id = $id; + } } Initializing the Site Document @@ -85,6 +90,7 @@ node:: use Doctrine\Bundle\PHPCRBundle\Initializer\InitializerInterface; use PHPCR\Util\NodeHelper; use Doctrine\Bundle\PHPCRBundle\ManagerRegistry; + use Acme\BasicCmsBundle\Document\Site; class SiteInitializer implements InitializerInterface { @@ -102,7 +108,7 @@ node:: return; } - $site = new Acme\BasicCmsBundle\Document\Site(); + $site = new Site(); $site->setId($this->basePath); $dm->persist($site); $dm->flush(); @@ -178,12 +184,13 @@ follows: ->addTag('doctrine_phpcr.initializer', array('name' => 'doctrine_phpcr.initializer') ; -Now empty your repository and then reinitialize it: +Now empty your repository, reinitialize it and reload your fixtures: .. code-block:: bash $ php app/console doctrine:phpcr:node:remove /cms $ php app/console doctrine:phpcr:repository:init + $ php app/console doctrine:phpcr:fixtures:load and verify that the ``cms`` node has been created correctly, using the ``doctrine:phpcr:node:dump`` command with the ``props`` flag: diff --git a/cookbook/creating_a_cms/sonata-admin.rst b/cookbook/creating_a_cms/sonata-admin.rst index 1badbc65..ed852482 100644 --- a/cookbook/creating_a_cms/sonata-admin.rst +++ b/cookbook/creating_a_cms/sonata-admin.rst @@ -7,18 +7,9 @@ of the SonataDoctrinePHPCRAdminBundle_. Installation ~~~~~~~~~~~~ -Ensure that you have the following package installed: - -.. code-block:: javascript - - { - ... - require: { - ... - "sonata-project/doctrine-phpcr-admin-bundle": "1.1.*", - }, - ... - } +Ensure that you installed the ``sonata-project/doctrine-phpcr-admin-bundle`` +package as detailed in the :ref:`gettingstarted_installadditionbundles` +section. Enable the Sonata related bundles to your kernel:: @@ -146,7 +137,14 @@ and publish your assets (remove ``--symlink`` if you use Windows!): $ php app/console assets:install --symlink web/ -Great, now have a look at http://localhost:8000/admin/dashboard +Now start a local webserver: + +.. code-block:: bash + + $ php app/console server:run + + +That works? Great, now have a look at http://127.0.0.1:8000/admin/dashboard No translations? Uncomment the translator in the configuration file: @@ -475,6 +473,12 @@ Enable the CmfTreeBundle and the FOSJsRoutingBundle in your kernel:: } } +Now publish your assets again: + +.. code-block:: bash + + $ php app/console assets:install --symlink web/ + Routes used by the tree in the frontend are handled by the FOSJsRoutingBundle. The relevant routes are tagged with the ``expose`` flag, they are available automatically. However, you need to load the routes of the TreeBundle @@ -523,7 +527,8 @@ and the FOSJsRoutingBundle: return $collection; Add the tree block to the ``sonata_block`` configuration and tell sonata -admin to display the block: +admin to display the block (be careful to *add* to the existing configuration and +not to create another section!): .. configuration-block:: diff --git a/cookbook/creating_a_cms/the-frontend.rst b/cookbook/creating_a_cms/the-frontend.rst index 47ce8fe2..a5657241 100644 --- a/cookbook/creating_a_cms/the-frontend.rst +++ b/cookbook/creating_a_cms/the-frontend.rst @@ -8,18 +8,8 @@ using the Twig helper of the `KnpMenuBundle`_. Installation ............ -Ensure that the following package is installed: - -.. code-block:: javascript - - { - ... - require: { - ... - "symfony-cmf/menu-bundle": "1.1.*" - }, - ... - } +Ensure that you installed the ``symfony-cmf/menu-bundle`` package as detailed in the :ref:`gettingstarted_installadditionbundles` +section. Add the CMF `MenuBundle`_ and its dependency, `CoreBundle`_, to your kernel:: diff --git a/cookbook/database/create_new_project_phpcr_odm.rst b/cookbook/database/create_new_project_phpcr_odm.rst new file mode 100644 index 00000000..dc9a23a5 --- /dev/null +++ b/cookbook/database/create_new_project_phpcr_odm.rst @@ -0,0 +1,158 @@ +.. index:: + single: PHPCR-ODM, Creating a New Project + +Create a New Project with PHPCR-ODM +=================================== + +This article will show you how to create a new Symfony project from the +`Symfony Standard Edition`_ using PHPCR-ODM instead of (or in addition to) the +`Doctrine ORM`_. + +It is assumed that you have `installed composer`_. + +.. note:: + + This walkthrough is intended to get you off the ground quickly, for more + detailed documentation on integrating the PHPCR-ODM bundle see + the documentation for the :doc:`../../bundles/phpcr_odm/index`. + +General Instructions using Jackalope Doctrine DBAL +-------------------------------------------------- + +The `Jackalope`_ Doctrine DBAL backend will use `Doctrine DBAL`_ to store the +content repository. + +**Step 1**: Create a new Symfony project with composer based on the standard edition: + +.. code-block:: bash + + $ php composer.phar create-project symfony/framework-standard-edition / --no-install + +**Step 2**: Add the required packages to ``composer.json``: + +.. code-block:: javascript + + { + ... + "require": { + ... + "doctrine/phpcr-bundle": "1.0.0", + "doctrine/phpcr-odm": "1.0.*", + "jackalope/jackalope-doctrine-dbal": "1.0.0" + } + } + + + +**Step 3**: (*optional*) Remove the Doctrine ORM: + +* Remove the ``doctrine\orm`` package from ``composer.json``; +* Remove the ``orm`` section from ``app/config/config.yml``. + +**Step 4**: Add the DoctrinePHPCRBundle to the ``AppKernel``:: + + // app/AppKernel.php + + // ... + class AppKernel extends Kernel + { + public function registerBundles() + { + $bundles = array( + // ... + new Doctrine\Bundle\PHPCRBundle\DoctrinePHPCRBundle(), + ); + + // ... + } + } + +**Step 5**: Modify ``parameters.yml.dist``, adding the required PHPCR-ODM settings: + +.. code-block:: yaml + + # app/config/parameters.yml.dist + parameters: + # ... + phpcr_backend: + type: doctrinedbal + connection: default + phpcr_workspace: default + phpcr_user: admin + phpcr_pass: admin + +.. note:: + + You are modififying ``parameters.yml.dist`` and not ``paramaters.yml``. + This is because the Standard Edition will use this file as a template when + updating the configuration. + +**Step 6**: Add the Doctrine PHPCR configuration to the main application configuration: + +.. configuration-block:: + + .. code-block:: yaml + + # ... + doctrine_phpcr: + # configure the PHPCR session + session: + backend: "%phpcr_backend%" + workspace: "%phpcr_workspace%" + username: "%phpcr_user%" + password: "%phpcr_pass%" + # enable the ODM layer + odm: + auto_mapping: true + auto_generate_proxy_classes: "%kernel.debug%" + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + $container->loadFromExtension('doctrine_phpcr', array( + 'session' => array( + 'backend' => '%phpcr_backend%', + 'workspace' => '%phpcr_workspace%', + 'username' => '%phpcr_username%', + 'password' => '%phpcr_password%', + ), + 'odm' => array( + 'auto_mapping' => true, + 'auto_generate_proxy_classes' => '%kernel.debug%', + ), + )); + +**Step 7**: Run ``composer install``: + +.. code-block:: bash + + $ composer install + +After installing the packages composer will ask you to confirm or modify the +default parameters defined in ``parameters.yml.dist`` and then generate the +``parameters.yml`` file. + +Your should now be all set to start using PHPCR-ODM in your project! + +.. _`Symfony Standard Edition`: https://github.com/symfony/symfony-standard +.. _`Doctrine ORM`: https://github.com/doctrine/doctrine2 +.. _`Apache Jackrabbit`: https://jackrabbit.apache.org +.. _`Jackalope`: https://github.com/jackalope/jackalope +.. _`Doctrine DBAL`: https://github.com/doctrine/dbal +.. _`installed composer`: http://getcomposer.org/doc/00-intro.md#system-requirements diff --git a/cookbook/index.rst b/cookbook/index.rst index 56be9f7d..55ea4b4e 100644 --- a/cookbook/index.rst +++ b/cookbook/index.rst @@ -9,6 +9,7 @@ The Cookbook database/choosing_phpcr_implementation database/running_jackrabbit database/doctrine_cache + database/create_new_project_phpcr_odm editions/cmf_sandbox editions/cmf_core exposing_content_via_rest diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 9314d991..82265ff2 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -4,6 +4,7 @@ * :doc:`database/choosing_phpcr_implementation` * :doc:`database/running_jackrabbit` * :doc:`database/doctrine_cache` + * :doc:`database/create_new_project_phpcr_odm` * **Editions** diff --git a/reference/configuration/create.rst b/reference/configuration/create.rst index acd10270..954fce4b 100644 --- a/reference/configuration/create.rst +++ b/reference/configuration/create.rst @@ -86,6 +86,7 @@ persistence configuration has the following configuration: model_class: ~ controller_class: Symfony\Cmf\Bundle\CreateBundle\Controller\ImageController basepath: /cms/media + delete: false .. code-block:: xml @@ -96,6 +97,7 @@ persistence configuration has the following configuration: 'Symfony\Cmf\Bundle\CreateBundle\Controller\ImageController', 'basepath' => '/cms/media', ), + 'delete' => false, ), ), )); @@ -153,6 +156,14 @@ provided by the MediaBundle. If you need different image handling, you can either overwrite ``model_class`` and/or the ``controller_class``. +delete +"""""" +**type**: ``boolean`` **default**: ``false`` + +Set delete to true to enable the simple delete workflow. This allows to directly +delete content from the frontend. Be careful, there are no special checks once you confirm deletion +your content is gone. + Metadata Handling ~~~~~~~~~~~~~~~~~ diff --git a/reference/configuration/menu.rst b/reference/configuration/menu.rst index cf1764ed..10807a98 100644 --- a/reference/configuration/menu.rst +++ b/reference/configuration/menu.rst @@ -180,6 +180,68 @@ admin_recursive_breadcrumbs When editing a node, this setting will cause the Sonata admin breadcrumb to include ancestors of the node being edited. +Admin Extensions +---------------- + +The ``admin_extensions`` section contains the configurations of the admin extensions that comes with the menu bundle. + +menu_options +~~~~~~~~~~~~ + +You can configure the menu options extension in this sections. + +.. configuration-block:: + + .. code-block:: yaml + + cmf_menu: + # ... + cmf_menu: + admin_extensions: + menu_options: + enabled: auto + advanced: false + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + $container->loadFromExtension('cmf_menu', array( + 'admin_extensions' => array( + 'menu_options' => array( + 'enabled' => 'auto', + 'advanced' => false, + ), + ), + ), + )); + +enabled +""""""" +**type**: ``enum`` **valid values**: ``true|false|auto`` **default**: ``auto`` + +If ``true``, the admin extension is activated. If set to ``auto``, it will be +activated only if the SonataAdminBundle is present. + +advanced +"""""""" +**type**: ``boolean`` **default**: ``false`` + +if set to ``true`` it will expose the advanced options in the admin. +To enable this options you need ``BurgovKeyValueFormBundle`` + Voter ----- diff --git a/reference/configuration/routing.rst b/reference/configuration/routing.rst index 1bf69722..003e6235 100644 --- a/reference/configuration/routing.rst +++ b/reference/configuration/routing.rst @@ -321,6 +321,7 @@ phpcr content_basepath: /cms/content admin_basepath: /cms/routes use_sonata_admin: auto + enable_initializer: true .. code-block:: xml @@ -336,6 +337,7 @@ phpcr content-basepath="/cms/content" admin-basepath="/cms/routes" use-sonata-admin="auto" + enable_initializer="true" > /cms/routes /cms/simple @@ -351,15 +353,16 @@ phpcr 'dynamic' => array( 'persistence' => array( 'phpcr' => array( - 'enabled' => false, - 'manager_name' => null, - 'route_basepaths' => array( + 'enabled' => false, + 'manager_name' => null, + 'route_basepaths' => array( '/cms/routes', '/cms/simple', ) - 'content_basepath' => '/cms/content', - 'admin_basepath' => '/cms/routes', - 'use_sonata_admin' => 'auto', + 'content_basepath' => '/cms/content', + 'admin_basepath' => '/cms/routes', + 'use_sonata_admin' => 'auto', + 'enable_initializer' => true, ), ), ), @@ -424,6 +427,20 @@ SonataPhpcrAdminBundle is present. If the :doc:`CoreBundle <../../bundles/core/index>` is registered, this will default to the value of ``cmf_core.persistence.phpcr.use_sonata_admin``. +enable_initializer +****************** + +**type**: ``boolean`` **default**: ``true`` + +.. versionadded:: 1.3 + This configuration option was introduced in RoutingBundle 1.3. + +The bundle comes with an initializer that creates the nodes for the ``admin_basepath`` +automatically when initializing the repository or loading fixtures. Sometimes this +is not what you want, as the created node is of type 'Generic' and sometimes this +already needs to be a route (for the homepage). Set this to false to disable the +initializer when you create your nodes your self (e.g. using Alice_). + orm """ @@ -575,3 +592,5 @@ no locale in their static pattern get the ``auto_locale_pattern`` option set. This is ignored if there are no ``locales`` configured. It makes no sense to enable this option when ``match_implicit_locale`` is disabled. + +.. _Alice: https://github.com/nelmio/alice