Skip to content
This repository has been archived by the owner on Sep 16, 2021. It is now read-only.

Sitemap generation #196

Merged
merged 1 commit into from
Feb 17, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Changelog
=========

* **2015-14-02**: implement sitemap generation
* **2015-14-02**: [BC BREAK] Changed method visibility from private to public of SeoPresentation#getSeoMetadata()
* **2014-10-04**: Custom exception controller for error handling

1.1.0-RC3
Expand Down
2 changes: 2 additions & 0 deletions CmfSeoBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Doctrine\Bundle\PHPCRBundle\DependencyInjection\Compiler\DoctrinePhpcrMappingsPass;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass;

use Symfony\Cmf\Bundle\SeoBundle\DependencyInjection\Compiler\RegisterUrlInformationProviderPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

Expand All @@ -26,6 +27,7 @@ public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new RegisterExtractorsPass());
$container->addCompilerPass(new RegisterSuggestionProviderPass());
$container->addCompilerPass(new RegisterUrlInformationProviderPass());

$this->buildPhpcrCompilerPass($container);
$this->buildOrmCompilerPass($container);
Expand Down
103 changes: 103 additions & 0 deletions Controller/SitemapController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

/*
* This file is part of the Symfony CMF package.
*
* (c) 2011-2014 Symfony CMF
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's 2015 now :) (and as this file was added in 2015, it should officialy say "(c) 2015 Symfony CMF")

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we chage that in all files on master?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah and all bundles...

*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Cmf\Bundle\SeoBundle\Controller;

use Symfony\Cmf\Bundle\SeoBundle\Model\UrlInformation;
use Symfony\Cmf\Bundle\SeoBundle\Sitemap\UrlInformationProviderInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Templating\EngineInterface;

/**
* Controller to handle requests for sitemap.
*
* @author Maximilian Berghoff <[email protected]>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requires empty line

*/
class SitemapController
{
/**
* @var UrlInformationProviderInterface
*/
private $urlProvider;
/**
* @var EngineInterface
*/
private $templating;

/**
* @var array
*/
private $templates;

/**
* You should provide templates for html and xml.
*
* Json is serialized by default, but can be customized with a template
*
* @param UrlInformationProviderInterface $provider
* @param EngineInterface $templating
* @param array $templates Hash map with key being the format,
* value the name of the twig template
* to render the sitemap in that format
*/
public function __construct(
UrlInformationProviderInterface $provider,
EngineInterface $templating,
array $templates
) {
$this->urlProvider = $provider;
$this->templating = $templating;
$this->templates = $templates;
}

/**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have a short description?

* @param string $_format The format of the sitemap.
*
* @return Response
*/
public function indexAction($_format)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lsmith77 is there something to not need the underscore?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

afaik no

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, it's an internal parameter and as such, it is prefixed with an underscore. The only option is typehinting for a Request instance and doing $request->attributes->get('_format'). But I don't like that, what's wrong with this? :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dbu thought it should work without it, and i lost some hair in learning it.

{
$supportedFormats = array_merge(array('json'), array_keys($this->templates));
if (!in_array($_format, $supportedFormats)) {
$text = sprintf(
'Unknown format %s, use one of %s.',
$_format,
implode(', ', $supportedFormats)
);

return new Response($text, 406);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we could also allow other formats and remove the format requirement from the route. then you could start with isset (so that i can specify a template for json too), then try json and otherwise throw a 404 that this page is not found. with a message which formats are supported as you do below.


$urls = $this->urlProvider->getUrlInformation();
if (isset($this->templates[$_format])) {
return new Response($this->templating->render($this->templates[$_format], array('urls' => $urls)));
}

return $this->createJsonResponse($urls);
}

/**
* @param array|UrlInformation[] $urls
*
* @return JsonResponse
*/
private function createJsonResponse($urls)
{
$result = array();

foreach ($urls as $url) {
$result[] = $url->toArray();
}

return new JsonResponse($result);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we use the symfony serializer for this? or maybe the JMSSerializer, as there is a complex xml to be handled as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same answer for that dependency.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the symfony serializer is probably already required anyways. its problem is that it can't handle xml and json elegantly at the same time. but if we do the xml with a twig template we could here safe the toArray and delegate that to the serializer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided to keep that.

}
}
39 changes: 31 additions & 8 deletions DependencyInjection/CmfSeoExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Cmf\Bundle\RoutingBundle\Routing\DynamicRouter;
Expand Down Expand Up @@ -64,8 +65,6 @@ public function load(array $configs, ContainerBuilder $container)
$sonataBundles[] = 'SonataDoctrinePHPCRAdminBundle';

$this->loadPhpcr($config['persistence']['phpcr'], $loader, $container);

$loader->load('matcher_phpcr.xml');
}

if ($this->isConfigEnabled($container, $config['persistence']['orm'])) {
Expand All @@ -87,6 +86,10 @@ public function load(array $configs, ContainerBuilder $container)

$errorConfig = isset($config['error']) ? $config['error'] : array();
$this->loadErrorHandling($errorConfig, $container);

if ($this->isConfigEnabled($container, $config['sitemap'])) {
$this->loadSitemapHandling($config['sitemap'], $loader, $container);
}
}

/**
Expand Down Expand Up @@ -169,6 +172,9 @@ private function loadPhpcr($config, XmlFileLoader $loader, ContainerBuilder $con
$this->defaultAlternateLocaleProviderId = 'cmf_seo.alternate_locale.provider_phpcr';
}
}

$loader->load('matcher_phpcr.xml');
$loader->load('phpcr-sitemap.xml');
}

/**
Expand All @@ -189,13 +195,19 @@ private function loadAlternateLocaleProvider($config, ContainerBuilder $containe


if ($alternateLocaleProvider) {
$definition = $container->getDefinition('cmf_seo.event_listener.seo_content');
$definition
$alternateLocaleProviderDefinition = $container->findDefinition($alternateLocaleProvider);
$container
->findDefinition('cmf_seo.event_listener.seo_content')
->addMethodCall(
'setAlternateLocaleProvider',
array($alternateLocaleProviderDefinition)
);
$container
->findDefinition('cmf_seo.sitemap.phpcr_provider')
->addMethodCall(
'setAlternateLocaleProvider',
array($container->getDefinition($alternateLocaleProvider))
)
;
array($alternateLocaleProviderDefinition)
);
}
}

Expand All @@ -210,10 +222,21 @@ private function loadAlternateLocaleProvider($config, ContainerBuilder $containe
private function loadErrorHandling($config, ContainerBuilder $container)
{
foreach (array('parent', 'sibling') as $group) {
$remove = isset($config['enable_'.$group.'_provider']) && !$config['enable_'.$group.'_provider'] ? true : false;
$remove = isset($config['enable_'.$group.'_provider'])
&& !$config['enable_'.$group.'_provider'] ? true : false;
if ($container->has('cmf_seo.error.suggestion_provider.'.$group) && $remove) {
$container->removeDefinition('cmf_seo.error.suggestion_provider.'.$group);
}
}
}

private function loadSitemapHandling($config, XmlFileLoader $loader, ContainerBuilder $container)
{
$loader->load('sitemap.xml');

$container->setParameter(
$this->getAlias().'.sitemap.default_change_frequency',
$config['default_chan_frequency']
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

/*
* This file is part of the Symfony CMF package.
*
* (c) 2011-2014 Symfony CMF
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Cmf\Bundle\SeoBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Reference;

/**
* Compiler adds custom url information provider to the phpcr chain provider.
*
* To do so you need to tag them with "cmf_seo.sitemap.url_information_provider".
*
* @author Maximilian Berghoff <[email protected]>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add one line what this does (register services tagged with ... to the sitemap chain provider.)

*/
class RegisterUrlInformationProviderPass implements CompilerPassInterface
{
/**
* {@inheritDoc}
*
* @throws LogicException If a tagged service is not public.
*/
public function process(ContainerBuilder $container)
{
// feature not activated means nothing to add
if (!$container->hasDefinition('cmf_seo.sitemap.url_information_provider')) {
return;
}

$chainProviderDefinition = $container->getDefinition('cmf_seo.sitemap.url_information_provider');
$taggedServices = $container->findTaggedServiceIds('cmf_seo.sitemap.url_information_provider');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's rename the tag to cmf_seo.url_information_provider, because (a) it's less to type so DX++ and (b) it's more generic, you never know if you encounter more use cases for this


foreach ($taggedServices as $id => $attributes) {
$priority = null;
foreach ($attributes as $attribute) {
if (isset($attribute['priority'])) {
$priority = $attribute['priority'];
break;
}
}
$priority = $priority ?: 0;

$chainProviderDefinition->addMethodCall('addProvider', array(new Reference($id), $priority));
}
}
}
9 changes: 8 additions & 1 deletion DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public function getConfigTreeBuilder()
->addDefaultsIfNotSet()
->beforeNormalization()
->ifTrue( function ($v) { return is_scalar($v); })
->then( function ($v) {
->then(function ($v) {
return array('enabled' => $v);
})
->end()
Expand All @@ -85,6 +85,13 @@ public function getConfigTreeBuilder()
->scalarNode('enable_sibling_provider')->defaultValue(false)->end()
->end()
->end()
->arrayNode('sitemap')
->addDefaultsIfNotSet()
->canBeEnabled()
->children()
->scalarNode('default_chan_frequency')->defaultValue('always')->end()
->end()
->end()
->end()
;

Expand Down
Loading