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