Skip to content

Commit

Permalink
Merge pull request #289 from ezsystems/root_location
Browse files Browse the repository at this point in the history
Implemented EZP-20286 - Multisite with single content repository
  • Loading branch information
lolautruche committed Apr 14, 2013
2 parents aa1556e + 1f933ff commit 1a97fb2
Show file tree
Hide file tree
Showing 15 changed files with 1,340 additions and 20 deletions.
80 changes: 80 additions & 0 deletions doc/specifications/multisite/design_routing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Multi site routing

## Introduction
Goal of this document is to design how to make it possible to have **multiple *content pools* inside a single content repository**.
This would give the possibility to build several websites which content is actually a *subtree* in the content repository.

This feature was already present in **eZ Publish 3.x/4.x** through the `PathPrefix`, `RootNode` and `IndexPage` settings.
The initial idea was to define a prefix to *hide* from the start of the URLAlias.
The main issue was that it was URI based, so if the URLAlias changed, the setting was obsolete.
It was also possible to define prefixes to exclude via the `PathPrefixExclude` setting.

This document explains how this feature is implemented in eZ Publish 5.1+.

## Concept
The main idea is to be able to define a **root location** for a given site, by its `locationId`.
This will will define in one go the *homepage* and the *path prefix* for the site.

It will still be possible to define exclusion for the *path prefix*, giving the possibility to make links to content outside the site subtree (e.g. *Media*, shared content between sites...).


## Configuration
End user configuration would look like:

```yaml
ezpublish:
system:
my_siteaccess:
content:
tree_root:
# Root locationId. Default is top locationId
location_id: 123
# Every URL aliases starting with those prefixes will be considered
# being outside of the subtree starting at root_location.
# Default value is an empty array.
# Prefixes are not case sensitive.
excluded_uri_prefixes:
- media
- users
```
## Routing & link generation
> **Note:** Only concerns the `UrlAliasRouter`

Defining a root location for the content subtree only affects routing and link generation.
In this regard, the root location will transparently add/remove a prefix to the requested URL alias.

> When generating a link to a location which is outside the content subtree, the URLAlias will be left as is.
> **A warning will be logged** as this is most likely due to a developer or content editor mistake.
>
> Note that no warning will be logged if the URLAlias is affected by one of the `excluded_uri_prefixes`.

It is also possible to exclude some URI prefixes from this behavior (see `excluded_uri_prefixes`),
a typical use case being 2 websites sharing the same product catalog.

### Example
Given the following content subtree:

```
.
├── website1
│   └── category1
│   └── my-cool-article
├── website2
│ └── the-truth-is-out-there
└── products
   ├── product1
└── product2
```

* Without defining a root location,
* `my-cool-article` will be accessible with `/website1/catory1/my-cool-article` URI.
* Links to `my-cool-article` will point to `/website1/category1/my-cool-article`.
* Defining `website1` as root location,
* `my-cool-article` will be accessible with `/category1/my-cool-article` URI.
* Links to `my-cool-article` will point to `/category1/my-cool-article`.
* Without defining `products` in `excluded_uri_prefixes`,
* `product1` won't be accessible, as it's not under `website1` subtree.
* Defining `products` in `excluded_uri_prefixes`,
* `product1` will be accessible with `/products/product1` URI.

Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ public function addSemanticConfig( NodeBuilder $nodeBuilder )
->booleanNode( 'view_cache' )->defaultValue( true )->end()
->booleanNode( 'ttl_cache' )->defaultValue( false )->end()
->scalarNode( 'default_ttl' )->info( 'Default value for TTL cache, in seconds' )->defaultValue( 60 )->end()
->arrayNode( 'tree_root' )
->canBeUnset()
->children()
->integerNode( 'location_id' )
->info( "Root locationId for routing and link generation.\nUseful for multisite apps with one repository." )
->isRequired()
->end()
->arrayNode( 'excluded_uri_prefixes' )
->info( "URI prefixes that are allowed to be outside the content tree\n(useful for content sharing between multiple sites).\nPrefixes are not case sensitive" )
->example( array( '/media/images', '/products' ) )
->end()
->end()
->end()
->end()
->end();
}
Expand All @@ -55,6 +68,15 @@ public function registerInternalConfig( array $config, ContainerBuilder $contain
$container->setParameter( "ezsettings.$sa.content.view_cache", $settings['content']['view_cache'] );
$container->setParameter( "ezsettings.$sa.content.ttl_cache", $settings['content']['ttl_cache'] );
$container->setParameter( "ezsettings.$sa.content.default_ttl", $settings['content']['default_ttl'] );

if ( isset( $settings['content']['tree_root'] ) )
{
$container->setParameter( "ezsettings.$sa.content.tree_root.location_id", $settings['content']['tree_root']['location_id'] );
if ( isset( $settings['content']['tree_root']['excluded_uri_prefixes'] ) )
{
$container->setParameter( "ezsettings.$sa.content.tree_root.excluded_uri_prefixes", $settings['content']['tree_root']['excluded_uri_prefixes'] );
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,14 @@ public function onKernelRequestRedirect( GetResponseEvent $event )
{
$siteaccess = $request->attributes->get( 'siteaccess' );
$semanticPathinfo = $request->attributes->get( 'semanticPathinfo' );
if ( $siteaccess instanceof SiteAccess && $siteaccess->matcher instanceof URILexer )
if (
$request->attributes->get( 'prependSiteaccessOnRedirect', true )
&& $siteaccess instanceof SiteAccess
&& $siteaccess->matcher instanceof URILexer
)
{
$semanticPathinfo = $siteaccess->matcher->analyseLink( $semanticPathinfo );
}

$event->setResponse(
new RedirectResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ parameters:
ezsettings.default.content.view_cache: true # Whether to use content view cache or not (Etag/Last-Modified based)
ezsettings.default.content.ttl_cache: false # Whether to use TTL Cache for content (i.e. Max-Age response header)
ezsettings.default.content.default_ttl: 60 # Default TTL cache value for content
ezsettings.default.content.tree_root.location_id: ~ # Root locationId for routing and link generation. Useful for multisite apps with one repository.
ezsettings.default.content.tree_root.excluded_uri_prefixes: [] # URI prefixes that are allowed to be outside the content tree

# Cache settings
ezsettings.default.http_cache.purge_servers: ["http://localhost/"] # Server(s) URL(s) that will be used for purging HTTP cache with PURGE requests.
Expand Down
2 changes: 1 addition & 1 deletion eZ/Bundle/EzPublishCoreBundle/Resources/config/routing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ services:

ezpublish.urlalias_generator:
class: %ezpublish.urlalias_generator.class%
arguments: [@ezpublish.api.repository.lazy, @router]
arguments: [@ezpublish.api.repository.lazy, @router, @?logger]
parent: ezpublish.url_generator.base

ezpublish.siteaccess.matcher_builder:
Expand Down
27 changes: 26 additions & 1 deletion eZ/Bundle/EzPublishCoreBundle/Routing/UrlAliasRouter.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class UrlAliasRouter extends BaseUrlAliasRouter
*/
protected $container;

protected $rootLocationId;

public function setContainer( ContainerInterface $container )
{
$this->container = $container;
Expand All @@ -36,11 +38,34 @@ protected function getConfigResolver()

public function matchRequest( Request $request )
{
$configResolver = $this->getConfigResolver();
// UrlAliasRouter might be disabled from configuration.
// An example is for running the admin interface: it needs to be entirely run through the legacy kernel.
if ( $this->getConfigResolver()->getParameter( 'url_alias_router' ) === false )
if ( $configResolver->getParameter( 'url_alias_router' ) === false )
throw new ResourceNotFoundException( "Config says to bypass UrlAliasRouter" );

$this->rootLocationId = $configResolver->getParameter( 'content.tree_root.location_id' );
$this->generator->setRootLocationId( $this->rootLocationId );
$this->generator->setExcludedUriPrefixes( $configResolver->getParameter( 'content.tree_root.excluded_uri_prefixes' ) );
return parent::matchRequest( $request );
}

/**
* Will return the right UrlAlias in regards to configured root location.
*
* @param string $pathinfo
* @return \eZ\Publish\API\Repository\Values\Content\URLAlias
*/
protected function getUrlAlias( $pathinfo )
{
if ( $this->rootLocationId === null || $this->generator->isUriPrefixExcluded( $pathinfo ) )
{
return parent::getUrlAlias( $pathinfo );
}

return $this
->getRepository()
->getURLAliasService()
->lookup( $this->generator->getPathPrefixByRootLocationId( $this->rootLocationId ) . $pathinfo );
}
}
Loading

0 comments on commit 1a97fb2

Please sign in to comment.