This commit is contained in:
Kevin Adametz 2020-03-07 19:46:02 +01:00
parent a37785b391
commit 33458b2ca3
9915 changed files with 1247019 additions and 0 deletions

View file

@ -0,0 +1,2 @@
composer.lock
vendor/*

View file

@ -0,0 +1,18 @@
## 1.2.2 (2016-01-27)
* Added support for Symfony 3
## 1.2.1 (2015-08-12)
* Fixed the BlameListener
## 1.2.0 (2015-08-12)
Bugfixes:
* Fixed the handling of the directory validation of the uploadable extension
* Removed usage of APIs deprecated in Symfony 2.6+
Features:
* Marked the Gedmo extensions 2.4.0 release as supported (as well as any future 2.x release thanks to semver)

View file

@ -0,0 +1,32 @@
<?php
namespace Stof\DoctrineExtensionsBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class SecurityContextPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if ($container->has('security.token_storage')) {
$args = array(new Reference('security.token_storage'), new Reference('security.authorization_checker'));
} elseif ($container->has('security.context')) {
$args = array(new Reference('security.context'));
} else {
return; // SecurityBundle is not configured
}
$defs = array(
$container->findDefinition('stof_doctrine_extensions.event_listener.blame'),
$container->findDefinition('stof_doctrine_extensions.event_listener.logger'),
);
foreach ($defs as $def) {
foreach ($args as $argument) {
$def->addArgument($argument);
}
}
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace Stof\DoctrineExtensionsBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class ValidateExtensionConfigurationPass implements CompilerPassInterface
{
/**
* Validate the DoctrineExtensions DIC extension config.
*
* This validation runs in a discrete compiler pass because it depends on
* DBAL and ODM services, which aren't available during the config merge
* compiler pass.
*
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
$container->getExtension('stof_doctrine_extensions')->configValidate($container);
}
}

View file

@ -0,0 +1,148 @@
<?php
namespace Stof\DoctrineExtensionsBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
/**
* Generates the configuration tree.
*
* @return TreeBuilder
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('stof_doctrine_extensions');
$rootNode
->append($this->getVendorNode('orm'))
->append($this->getVendorNode('mongodb'))
->append($this->getClassNode())
->append($this->getUploadableNode())
->children()
->scalarNode('default_locale')
->cannotBeEmpty()
->defaultValue('en')
->end()
->booleanNode('translation_fallback')->defaultFalse()->end()
->booleanNode('persist_default_translation')->defaultFalse()->end()
->booleanNode('skip_translation_on_load')->defaultFalse()->end()
->end()
;
return $treeBuilder;
}
/**
* @param string $name
*/
private function getVendorNode($name)
{
$treeBuilder = new TreeBuilder();
$node = $treeBuilder->root($name);
$node
->useAttributeAsKey('id')
->prototype('array')
->children()
->scalarNode('translatable')->defaultFalse()->end()
->scalarNode('timestampable')->defaultFalse()->end()
->scalarNode('blameable')->defaultFalse()->end()
->scalarNode('sluggable')->defaultFalse()->end()
->scalarNode('tree')->defaultFalse()->end()
->scalarNode('loggable')->defaultFalse()->end()
->scalarNode('sortable')->defaultFalse()->end()
->scalarNode('softdeleteable')->defaultFalse()->end()
->scalarNode('uploadable')->defaultFalse()->end()
->scalarNode('reference_integrity')->defaultFalse()->end()
->end()
->end()
;
return $node;
}
private function getClassNode()
{
$treeBuilder = new TreeBuilder();
$node = $treeBuilder->root('class');
$node
->addDefaultsIfNotSet()
->children()
->scalarNode('translatable')
->cannotBeEmpty()
->defaultValue('Gedmo\Translatable\TranslatableListener')
->end()
->scalarNode('timestampable')
->cannotBeEmpty()
->defaultValue('Gedmo\\Timestampable\\TimestampableListener')
->end()
->scalarNode('blameable')
->cannotBeEmpty()
->defaultValue('Gedmo\\Blameable\\BlameableListener')
->end()
->scalarNode('sluggable')
->cannotBeEmpty()
->defaultValue('Gedmo\\Sluggable\\SluggableListener')
->end()
->scalarNode('tree')
->cannotBeEmpty()
->defaultValue('Gedmo\\Tree\\TreeListener')
->end()
->scalarNode('loggable')
->cannotBeEmpty()
->defaultValue('Gedmo\Loggable\LoggableListener')
->end()
->scalarNode('sortable')
->cannotBeEmpty()
->defaultValue('Gedmo\\Sortable\\SortableListener')
->end()
->scalarNode('softdeleteable')
->cannotBeEmpty()
->defaultValue('Gedmo\\SoftDeleteable\\SoftDeleteableListener')
->end()
->scalarNode('uploadable')
->cannotBeEmpty()
->defaultValue('Gedmo\\Uploadable\\UploadableListener')
->end()
->scalarNode('reference_integrity')
->cannotBeEmpty()
->defaultValue('Gedmo\\ReferenceIntegrity\\ReferenceIntegrityListener')
->end()
->end()
;
return $node;
}
private function getUploadableNode()
{
$treeBuilder = new TreeBuilder();
$node = $treeBuilder->root('uploadable');
$node
->addDefaultsIfNotSet()
->children()
->scalarNode('default_file_path')
->cannotBeEmpty()
->defaultNull()
->end()
->scalarNode('mime_type_guesser_class')
->cannotBeEmpty()
->defaultValue('Stof\\DoctrineExtensionsBundle\\Uploadable\\MimeTypeGuesserAdapter')
->end()
->scalarNode('default_file_info_class')
->cannotBeEmpty()
->defaultValue('Stof\\DoctrineExtensionsBundle\\Uploadable\\UploadedFileInfo')
->end()
->booleanNode('validate_writable_directory')->defaultTrue()->end()
->end()
;
return $node;
}
}

View file

@ -0,0 +1,144 @@
<?php
namespace Stof\DoctrineExtensionsBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class StofDoctrineExtensionsExtension extends Extension
{
private $entityManagers = array();
private $documentManagers = array();
public function load(array $configs, ContainerBuilder $container)
{
$processor = new Processor();
$configuration = new Configuration();
$config = $processor->processConfiguration($configuration, $configs);
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('listeners.xml');
$uploadableConfig = $config['uploadable'];
$container->setParameter('stof_doctrine_extensions.default_locale', $config['default_locale']);
$container->setParameter('stof_doctrine_extensions.default_file_path', $uploadableConfig['default_file_path']);
$container->setParameter('stof_doctrine_extensions.translation_fallback', $config['translation_fallback']);
$container->setParameter('stof_doctrine_extensions.persist_default_translation', $config['persist_default_translation']);
$container->setParameter('stof_doctrine_extensions.skip_translation_on_load', $config['skip_translation_on_load']);
$useTranslatable = false;
$useLoggable = false;
$useBlameable = false;
foreach ($config['orm'] as $name => $listeners) {
foreach ($listeners as $ext => $enabled) {
$listener = sprintf('stof_doctrine_extensions.listener.%s', $ext);
if ($enabled && $container->hasDefinition($listener)) {
$attributes = array('connection' => $name);
if ('translatable' === $ext) {
$useTranslatable = true;
// the translatable listener must be registered after others to work with them properly
$attributes['priority'] = -10;
} elseif ('loggable' === $ext) {
$useLoggable = true;
} elseif ('blameable' === $ext) {
$useBlameable = true;
} elseif ('uploadable' === $ext) {
$attributes['priority'] = -5;
}
$definition = $container->getDefinition($listener);
$definition->addTag('doctrine.event_subscriber', $attributes);
}
}
$this->entityManagers[$name] = $listeners;
}
foreach ($config['mongodb'] as $name => $listeners) {
foreach ($listeners as $ext => $enabled) {
$listener = sprintf('stof_doctrine_extensions.listener.%s', $ext);
if ($enabled && $container->hasDefinition($listener)) {
$attributes = array('connection' => $name);
if ('translatable' === $ext) {
$useTranslatable = true;
// the translatable listener must be registered after others to work with them properly
$attributes['priority'] = -10;
} elseif ('loggable' === $ext) {
$useLoggable = true;
} elseif ('blameable' === $ext) {
$useBlameable = true;
}
$definition = $container->getDefinition($listener);
$definition->addTag('doctrine_mongodb.odm.event_subscriber', $attributes);
}
}
$this->documentManagers[$name] = $listeners;
}
if ($useTranslatable) {
$container->getDefinition('stof_doctrine_extensions.event_listener.locale')
->setPublic(true)
->addTag('kernel.event_subscriber');
}
if ($useLoggable) {
$container->getDefinition('stof_doctrine_extensions.event_listener.logger')
->setPublic(true)
->addTag('kernel.event_subscriber');
}
if ($useBlameable) {
$container->getDefinition('stof_doctrine_extensions.event_listener.blame')
->setPublic(true)
->addTag('kernel.event_subscriber');
}
if ($uploadableConfig['default_file_path']) {
$container->getDefinition('stof_doctrine_extensions.listener.uploadable')
->addMethodCall('setDefaultPath', array($uploadableConfig['default_file_path']));
}
// Default FileInfoInterface class
$container->setParameter('stof_doctrine_extensions.uploadable.default_file_info.class', $uploadableConfig['default_file_info_class']);
$container->setParameter(
'stof_doctrine_extensions.uploadable.validate_writable_directory',
$uploadableConfig['validate_writable_directory']
);
if ($uploadableConfig['mime_type_guesser_class']) {
if (!class_exists($uploadableConfig['mime_type_guesser_class'])) {
$msg = 'Class "%s" configured to use as the mime type guesser in the Uploadable extension does not exist.';
throw new \InvalidArgumentException(sprintf($msg, $uploadableConfig['mime_type_guesser_class']));
}
$container->setParameter(
'stof_doctrine_extensions.uploadable.mime_type_guesser.class',
$uploadableConfig['mime_type_guesser_class']
);
}
foreach ($config['class'] as $listener => $class) {
$container->setParameter(sprintf('stof_doctrine_extensions.listener.%s.class', $listener), $class);
}
}
public function configValidate(ContainerBuilder $container)
{
foreach (array_keys($this->entityManagers) as $name) {
if (!$container->hasDefinition(sprintf('doctrine.dbal.%s_connection', $name))) {
throw new \InvalidArgumentException(sprintf('Invalid %s config: DBAL connection "%s" not found', $this->getAlias(), $name));
}
}
foreach (array_keys($this->documentManagers) as $name) {
if (!$container->hasDefinition(sprintf('doctrine_mongodb.odm.%s_document_manager', $name))) {
throw new \InvalidArgumentException(sprintf('Invalid %s config: document manager "%s" not found', $this->getAlias(), $name));
}
}
}
}

View file

@ -0,0 +1,76 @@
<?php
namespace Stof\DoctrineExtensionsBundle\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Gedmo\Blameable\BlameableListener;
/**
* BlameableListener
*
* @author David Buchmann <mail@davidbu.ch>
*/
class BlameListener implements EventSubscriberInterface
{
private $authorizationChecker;
private $tokenStorage;
/**
* @var BlameableListener
*/
private $blameableListener;
public function __construct(BlameableListener $blameableListener, $tokenStorage = null, AuthorizationCheckerInterface $authorizationChecker = null)
{
$this->blameableListener = $blameableListener;
// BC layer for Symfony 2.5 and older
if ($tokenStorage instanceof SecurityContextInterface) {
$this->tokenStorage = $this->authorizationChecker = $tokenStorage;
return;
}
if (null !== $tokenStorage && !$tokenStorage instanceof TokenStorageInterface) {
throw new \InvalidArgumentException(sprintf('The second argument of the %s constructor should be a Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface or a Symfony\Component\Security\Core\SecurityContextInterface or null.', __CLASS__));
}
$this->tokenStorage = $tokenStorage;
$this->authorizationChecker = $authorizationChecker;
}
/**
* Set the username from the security context by listening on core.request
*
* @param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
if (null === $this->tokenStorage || null === $this->authorizationChecker) {
return;
}
$token = $this->tokenStorage->getToken();
if (null !== $token && $this->authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
$this->blameableListener->setUserValue($token->getUser());
}
}
public static function getSubscribedEvents()
{
return array(
KernelEvents::REQUEST => 'onKernelRequest',
);
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace Stof\DoctrineExtensionsBundle\EventListener;
use Gedmo\Translatable\TranslatableListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* This listeners sets the current locale for the TranslatableListener
*
* @author Christophe COEVOET
*/
class LocaleListener implements EventSubscriberInterface
{
private $translatableListener;
public function __construct(TranslatableListener $translatableListener)
{
$this->translatableListener = $translatableListener;
}
/**
* Set the translation listener locale from the request.
*
* This method should be attached to the kernel.request event.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
$this->translatableListener->setTranslatableLocale($event->getRequest()->getLocale());
}
public static function getSubscribedEvents()
{
return array(
KernelEvents::REQUEST => 'onKernelRequest',
);
}
}

View file

@ -0,0 +1,73 @@
<?php
namespace Stof\DoctrineExtensionsBundle\EventListener;
use Gedmo\Loggable\LoggableListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
/**
* LoggableListener
*
* @author Christophe Coevoet <stof@notk.org>
*/
class LoggerListener implements EventSubscriberInterface
{
private $authorizationChecker;
private $tokenStorage;
private $loggableListener;
public function __construct(LoggableListener $loggableListener, $tokenStorage = null, AuthorizationCheckerInterface $authorizationChecker = null)
{
$this->loggableListener = $loggableListener;
// BC layer for Symfony 2.5 and older
if ($tokenStorage instanceof SecurityContextInterface) {
$this->tokenStorage = $this->authorizationChecker = $tokenStorage;
return;
}
if (null !== $tokenStorage && !$tokenStorage instanceof TokenStorageInterface) {
throw new \InvalidArgumentException(sprintf('The second argument of the %s constructor should be a Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface or a Symfony\Component\Security\Core\SecurityContextInterface or null.', __CLASS__));
}
$this->tokenStorage = $tokenStorage;
$this->authorizationChecker = $authorizationChecker;
}
/**
* Set the username from the security context by listening on core.request
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
if (null === $this->tokenStorage || null === $this->authorizationChecker) {
return;
}
$token = $this->tokenStorage->getToken();
if (null !== $token && $this->authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
$this->loggableListener->setUsername($token);
}
}
public static function getSubscribedEvents()
{
return array(
KernelEvents::REQUEST => 'onKernelRequest',
);
}
}

View file

@ -0,0 +1,19 @@
Copyright (c) 2010 Christophe Coevoet
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,17 @@
This bundle provides integration for
[DoctrineExtensions](https://github.com/Atlantic18/DoctrineExtensions) in
your Symfony2 Project.
[![Total Downloads](https://poser.pugx.org/stof/doctrine-extensions-bundle/downloads.png)](https://packagist.org/packages/stof/doctrine-extensions-bundle)
[![Latest Stable Version](https://poser.pugx.org/stof/doctrine-extensions-bundle/v/stable.png)](https://packagist.org/packages/stof/doctrine-extensions-bundle)
For documentation, see it [online](https://symfony.com/doc/master/bundles/StofDoctrineExtensionsBundle/index.html)
License: [MIT](LICENSE)
> Note:
>
> The master branch of the bundle is in sync with Symfony master branch which
> contains a BC break. If you are using a 2.0.x version of Symfony, you need
> to use the 1.0.x branch of the bundle.

View file

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="stof_doctrine_extensions.event_listener.locale.class">Stof\DoctrineExtensionsBundle\EventListener\LocaleListener</parameter>
<parameter key="stof_doctrine_extensions.event_listener.logger.class">Stof\DoctrineExtensionsBundle\EventListener\LoggerListener</parameter>
<parameter key="stof_doctrine_extensions.event_listener.blame.class">Stof\DoctrineExtensionsBundle\EventListener\BlameListener</parameter>
<parameter key="stof_doctrine_extensions.uploadable.manager.class">Stof\DoctrineExtensionsBundle\Uploadable\UploadableManager</parameter>
<parameter key="stof_doctrine_extensions.uploadable.mime_type_guesser.class" />
<parameter key="stof_doctrine_extensions.uploadable.default_file_info.class" />
</parameters>
<services>
<!-- Doctrine listeners -->
<service id="stof_doctrine_extensions.listener.timestampable" class="%stof_doctrine_extensions.listener.timestampable.class%" public="false">
<call method="setAnnotationReader">
<argument type="service" id="annotation_reader" />
</call>
</service>
<service id="stof_doctrine_extensions.listener.blameable" class="%stof_doctrine_extensions.listener.blameable.class%" public="false">
<call method="setAnnotationReader">
<argument type="service" id="annotation_reader" />
</call>
</service>
<service id="stof_doctrine_extensions.listener.tree" class="%stof_doctrine_extensions.listener.tree.class%" public="false">
<call method="setAnnotationReader">
<argument type="service" id="annotation_reader" />
</call>
</service>
<service id="stof_doctrine_extensions.listener.sluggable" class="%stof_doctrine_extensions.listener.sluggable.class%" public="false">
<call method="setAnnotationReader">
<argument type="service" id="annotation_reader" />
</call>
</service>
<service id="stof_doctrine_extensions.listener.translatable" class="%stof_doctrine_extensions.listener.translatable.class%" public="false">
<call method="setAnnotationReader">
<argument type="service" id="annotation_reader" />
</call>
<call method="setDefaultLocale">
<argument>%stof_doctrine_extensions.default_locale%</argument>
</call>
<call method="setTranslatableLocale">
<argument>%stof_doctrine_extensions.default_locale%</argument>
</call>
<call method="setTranslationFallback">
<argument>%stof_doctrine_extensions.translation_fallback%</argument>
</call>
<call method="setPersistDefaultLocaleTranslation">
<argument>%stof_doctrine_extensions.persist_default_translation%</argument>
</call>
<call method="setSkipOnLoad">
<argument>%stof_doctrine_extensions.skip_translation_on_load%</argument>
</call>
</service>
<service id="stof_doctrine_extensions.listener.loggable" class="%stof_doctrine_extensions.listener.loggable.class%" public="false">
<call method="setAnnotationReader">
<argument type="service" id="annotation_reader" />
</call>
</service>
<service id="stof_doctrine_extensions.listener.sortable" class="%stof_doctrine_extensions.listener.sortable.class%" public="false">
<call method="setAnnotationReader">
<argument type="service" id="annotation_reader" />
</call>
</service>
<service id="stof_doctrine_extensions.listener.softdeleteable" class="%stof_doctrine_extensions.listener.softdeleteable.class%" public="false">
<call method="setAnnotationReader">
<argument type="service" id="annotation_reader" />
</call>
</service>
<service id="stof_doctrine_extensions.listener.uploadable" class="%stof_doctrine_extensions.listener.uploadable.class%" public="false">
<argument type="service" id="stof_doctrine_extensions.uploadable.mime_type_guesser" />
<call method="setAnnotationReader">
<argument type="service" id="annotation_reader" />
</call>
<call method="setDefaultFileInfoClass">
<argument>%stof_doctrine_extensions.uploadable.default_file_info.class%</argument>
</call>
</service>
<service id="stof_doctrine_extensions.listener.reference_integrity" class="%stof_doctrine_extensions.listener.reference_integrity.class%" public="false">
<call method="setAnnotationReader">
<argument type="service" id="annotation_reader" />
</call>
</service>
<!-- Symfony listeners -->
<service id="stof_doctrine_extensions.event_listener.locale" class="%stof_doctrine_extensions.event_listener.locale.class%" public="false">
<argument type="service" id="stof_doctrine_extensions.listener.translatable" />
</service>
<service id="stof_doctrine_extensions.event_listener.logger" class="%stof_doctrine_extensions.event_listener.logger.class%" public="false">
<argument type="service" id="stof_doctrine_extensions.listener.loggable" />
</service>
<service id="stof_doctrine_extensions.event_listener.blame" class="%stof_doctrine_extensions.event_listener.blame.class%" public="false">
<argument type="service" id="stof_doctrine_extensions.listener.blameable" />
</service>
<!-- Uploadable -->
<service id="stof_doctrine_extensions.uploadable.mime_type_guesser" class="%stof_doctrine_extensions.uploadable.mime_type_guesser.class%" public="false" />
<service id="stof_doctrine_extensions.uploadable.manager" class="%stof_doctrine_extensions.uploadable.manager.class%">
<argument type="service" id="stof_doctrine_extensions.listener.uploadable" />
<argument>%stof_doctrine_extensions.uploadable.default_file_info.class%</argument>
</service>
</services>
</container>

View file

@ -0,0 +1,352 @@
StofDoctrineExtensionsBundle
============================
Provides integration for `DoctrineExtensions`_ for your Symfony2 Project.
Features
--------
This bundle allows to easily use `DoctrineExtensions`_ in your Symfony
project by configuring it through a ``ListenerManager`` and the DIC.
DoctrineExtensions's features
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **Tree** - this extension automates the tree handling process and adds some
tree specific functions on repository. (**closure**, **nestedset** or **materialized path**)
- **Translatable** - gives you a very handy solution for translating records into
different languages. Easy to setup, easier to use.
- **Sluggable** - *urlizes* your specified fields into single unique slug.
- **Timestampable** - updates date fields on create, update and even property
change.
- **Blameable** - updates string or association fields on create, update and
even property change with a user name resp. reference.
- **Loggable** - helps tracking changes and history of objects, also supports
version management.
- **Sortable** - makes any document or entity sortable
- **Translator** - explicit way to handle translations
- **Softdeleteable** - allows to implicitly remove records
- **Uploadable** - provides file upload handling in entity fields
- **Reference Integrity** - provides reference integrity for MongoDB, supports
``nullify`` and ``restrict``.
All these extensions can be nested together. And most already use only
annotations without interface requirement to not to aggregate the entity itself
and has implemented proper caching for metadata.
See the official `DoctrineExtensions documentation`_ for more details.
Installation
------------
Step 1: Download the Bundle
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Open a command console, enter your project directory and execute the
following command to download the latest stable version of this bundle:
.. code-block:: bash
$ composer require stof/doctrine-extensions-bundle
This command requires you to have Composer installed globally, as explained
in the `installation chapter`_ of the Composer documentation.
Step 2: Enable the Bundle
~~~~~~~~~~~~~~~~~~~~~~~~~
Then, enable the bundle by adding the following line in the ``app/AppKernel.php``
file of your project:
.. code-block:: php
// app/AppKernel.php
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
// ...
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
);
// ...
}
// ...
}
Step 3: Add the extensions to your mapping
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Some of the extensions uses their own entities to do their work. You need
to register their mapping in Doctrine when you want to use them.
.. code-block:: yaml
# app/config/config.yml
doctrine:
orm:
entity_managers:
default:
mappings:
gedmo_translatable:
type: annotation
prefix: Gedmo\Translatable\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity"
alias: GedmoTranslatable # (optional) it will default to the name set for the mapping
is_bundle: false
gedmo_translator:
type: annotation
prefix: Gedmo\Translator\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Entity"
alias: GedmoTranslator # (optional) it will default to the name set for the mapping
is_bundle: false
gedmo_loggable:
type: annotation
prefix: Gedmo\Loggable\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity"
alias: GedmoLoggable # (optional) it will default to the name set for the mappingmapping
is_bundle: false
gedmo_tree:
type: annotation
prefix: Gedmo\Tree\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity"
alias: GedmoTree # (optional) it will default to the name set for the mapping
is_bundle: false
.. note::
If you are using the short syntax for the ORM configuration, the ``mappings``
key is directly under ``orm:``
.. note::
If you are using several entity managers, take care to register the entities
for the right ones.
.. note::
The mapping for MongoDB is similar. The ODM documents are in the ``Document``
subnamespace of each extension instead of ``Entity``.
Enable the ``softdeleteable`` filter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you want to use the SoftDeleteable behavior, you have to enable the
Doctrine filter.
.. code-block:: yaml
# app/config/config.yml
doctrine:
orm:
entity_managers:
default:
filters:
softdeleteable:
class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
enabled: true
.. note::
If you are using the short syntax for the ORM configuration, the ``filters``
key is directly under ``orm:``
.. note::
If you are using several entity managers, take care to register the filter
for the right ones.
To disable the behavior, e.g. for admin users who may see deleted items,
disable the filter. Here is an example:
.. code-block:: php
$filters = $em->getFilters();
$filters->disable('softdeleteable');
Using ``Uploadable`` extension
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you want to use the Uploadable extension, first read the documentation in
DoctrineExtensions. Once everything is ready, use the form component as usual.
Then, after you verify the form is valid, do the following:
.. code-block:: php
$document = new Document();
$form = $this->createFormBuilder($document)
->add('name')
->add('myFile')
->getForm()
;
if ($this->getRequest()->getMethod() === 'POST') {
$form->bind($this->getRequest());
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($document);
$uploadableManager = $this->get('stof_doctrine_extensions.uploadable.manager');
// Here, "getMyFile" returns the "UploadedFile" instance that the form bound in your $myFile property
$uploadableManager->markEntityToUpload($document, $document->getMyFile());
$em->flush();
$this->redirect($this->generateUrl(...));
}
}
return array('form' => $form->createView());
And that's it. The Uploadable extension handles the rest of the stuff. Remember
to read its documentation!
Configure the bundle
--------------------
You have to activate the extensions for each entity manager for which you want
to enable the extensions. The id is the id of the DBAL connection when using the
ORM behaviors. It is the id of the document manager when using mongoDB.
This bundle needs a default locale used if the translation does not exists in
the asked language. If you don't provide it explicitly, it will default to
``en``.
.. configuration-block::
.. code-block:: yaml
# app/config/config.yml
stof_doctrine_extensions:
default_locale: en_US
# Only used if you activated the Uploadable extension
uploadable:
# Default file path: This is one of the three ways you can configure the path for the Uploadable extension
default_file_path: %kernel.root_dir%/../web/uploads
# Mime type guesser class: Optional. By default, we provide an adapter for the one present in the HttpFoundation component of Symfony
mime_type_guesser_class: Stof\DoctrineExtensionsBundle\Uploadable\MimeTypeGuesserAdapter
# Default file info class implementing FileInfoInterface: Optional. By default we provide a class which is prepared to receive an UploadedFile instance.
default_file_info_class: Stof\DoctrineExtensionsBundle\Uploadable\UploadedFileInfo
orm:
default: ~
mongodb:
default: ~
.. code-block:: xml
<!-- app/config/config.xml -->
<container xmlns:stof-doctrine-extensions="http://example.org/schema/dic/stof_doctrine_extensions">
<stof-doctrine-extensions:config default-locale="en_US">
<stof-doctrine-extensions:orm>
<stof-doctrine-extensions:entity-manager id="default" />
</stof-doctrine-extensions:orm>
<stof-doctrine-extensions:mongodb>
<stof-doctrine-extensions:document-manager id="default" />
</stof-doctrine-extensions:mongodb>
</stof-doctrine-extensions:config>
</container>
Activate the extensions you want
--------------------------------
By default the bundle does not attach any listener. For each of your entity
manager, declare the extensions you want to enable:
.. configuration-block::
.. code-block:: yaml
# app/config/config.yml
stof_doctrine_extensions:
default_locale: en_US
orm:
default:
tree: true
timestampable: false # not needed: listeners are not enabled by default
other:
timestampable: true
.. code-block:: xml
<!-- app/config/config.xml -->
<container xmlns:doctrine_extensions="http://example.org/schema/dic/stof_doctrine_extensions">
<stof-doctrine-extensions:config default-locale="en_US">
<stof-doctrine-extensions:orm>
<stof-doctrine-extensions:entity-manager
id="default"
tree="true"
timestampable="false"
/>
<stof-doctrine-extensions:entity-manager
id="other"
timestampable="true"
/>
</stof-doctrine-extensions:orm>
</stof-doctrine-extensions:config>
</container>
Same is available for MongoDB using ``document-manager`` in the XML files
instead of ``entity-manager``.
.. caution::
If you configure the listeners of an entity manager in several configuration
files, the last one will be used. So you have to list all the listeners you
want to detach.
Use the DoctrineExtensions library
----------------------------------
All explanations about this library are available on the official
`DoctrineExtensions documentation`_.
Advanced use
------------
Overriding the listeners
~~~~~~~~~~~~~~~~~~~~~~~~
You can change the listeners used by extending the Gedmo listeners (or the
listeners of the bundle for translations) and giving the class name in the
configuration.
.. configuration-block::
.. code-block:: yaml
# app/config/config.yml
stof_doctrine_extensions:
class:
tree: MyBundle\TreeListener
timestampable: MyBundle\TimestampableListener
blameable: ~
sluggable: ~
translatable: ~
loggable: ~
softdeleteable: ~
uploadable: ~
.. code-block:: xml
<!-- app/config/config.xml -->
<container xmlns:doctrine_extensions="http://example.org/schema/dic/stof_doctrine_extensions">
<stof-doctrine-extensions:config>
<stof-doctrine-extensions:class
tree="MyBundle\TreeListener"
timestampable="MyBundle\TimestampableListener"
/>
</stof-doctrine-extensions:config>
</container>
.. _`DoctrineExtensions`: https://github.com/Atlantic18/DoctrineExtensions
.. _`DoctrineExtensions documentation`: https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc/
.. _`installation chapter`: https://getcomposer.org/doc/00-intro.md

View file

@ -0,0 +1,28 @@
<?php
namespace Stof\DoctrineExtensionsBundle;
use Stof\DoctrineExtensionsBundle\DependencyInjection\Compiler\SecurityContextPass;
use Stof\DoctrineExtensionsBundle\DependencyInjection\Compiler\ValidateExtensionConfigurationPass;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Gedmo\Uploadable\Mapping\Validator;
class StofDoctrineExtensionsBundle extends Bundle
{
/**
* {@inheritdoc}
*/
public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new ValidateExtensionConfigurationPass());
$container->addCompilerPass(new SecurityContextPass());
}
public function boot()
{
if ($this->container->hasParameter('stof_doctrine_extensions.uploadable.validate_writable_directory')) {
Validator::$validateWritableDirectory = $this->container->getParameter('stof_doctrine_extensions.uploadable.validate_writable_directory');
}
}
}

View file

@ -0,0 +1,17 @@
Upgrading from 1.0 to 1.1
=========================
This file describes the needed changes when upgrading from 1.0 to 1.1
### Bumped the requirements
The bundle now requires Symfony 2.1 and the 2.3 version of the Gedmo extensions
(which is the master branch at the time of this writing)
### Removed the duplicated entities.
The bundle no longer duplicates the entities provided by the extensions
to make the maintenance easier. You need to configure the mapping explicitly
for the extensions as DoctrineBundle cannot guess it.
See the updated documentation about registering the mapping for the way to
register them.

View file

@ -0,0 +1,15 @@
<?php
namespace Stof\DoctrineExtensionsBundle\Uploadable;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
use Gedmo\Uploadable\MimeType\MimeTypeGuesserInterface;
class MimeTypeGuesserAdapter implements MimeTypeGuesserInterface
{
public function guess($filePath)
{
return MimeTypeGuesser::getInstance()->guess($filePath);
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace Stof\DoctrineExtensionsBundle\Uploadable;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Gedmo\Uploadable\UploadableListener;
class UploadableManager
{
/** @var \Gedmo\Uploadable\UploadableListener */
private $listener;
private $fileInfoClass;
public function __construct(UploadableListener $listener, $fileInfoClass)
{
$this->listener = $listener;
$this->fileInfoClass = $fileInfoClass;
}
/**
* This method marks an entity to be uploaded as soon as the "flush" method of your object manager is called.
* After calling this method, the file info you passed is set for this entity in the listener. This is all it takes
* to upload a file for an entity in the Uploadable extension.
*
* @param object $entity - The entity you are marking to "Upload" as soon as you call "flush".
* @param mixed $fileInfo - The file info object or array. In Symfony 2, this will be typically an UploadedFile instance.
*/
public function markEntityToUpload($entity, $fileInfo)
{
if (is_object($fileInfo) && $fileInfo instanceof UploadedFile) {
$fileInfoClass = $this->fileInfoClass;
$fileInfo = new $fileInfoClass($fileInfo);
}
$this->listener->addEntityFileInfo($entity, $fileInfo);
}
/**
* @return \Gedmo\Uploadable\UploadableListener
*/
public function getUploadableListener()
{
return $this->listener;
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace Stof\DoctrineExtensionsBundle\Uploadable;
use Gedmo\Uploadable\FileInfo\FileInfoInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class UploadedFileInfo implements FileInfoInterface
{
private $uploadedFile;
public function __construct(UploadedFile $uploadedFile)
{
$this->uploadedFile = $uploadedFile;
}
public function getTmpName()
{
return $this->uploadedFile->getPathname();
}
public function getName()
{
return $this->uploadedFile->getClientOriginalName();
}
public function getSize()
{
return $this->uploadedFile->getClientSize();
}
public function getType()
{
return $this->uploadedFile->getMimeType();
}
public function getError()
{
return $this->uploadedFile->getError();
}
/**
* {@inheritDoc}
*/
public function isUploadedFile()
{
return is_uploaded_file($this->uploadedFile->getPathname());
}
}

View file

@ -0,0 +1,31 @@
{
"name": "stof/doctrine-extensions-bundle",
"type": "symfony-bundle",
"description": "Integration of the gedmo/doctrine-extensions with Symfony2",
"keywords": ["tree", "behaviors", "doctrine2", "extensions", "gedmo", "sluggable","loggable", "translatable", "nestedset", "sortable", "timestampable"],
"homepage": "https://github.com/stof/StofDoctrineExtensionsBundle",
"license": "MIT",
"authors": [
{
"name": "Christophe Coevoet",
"email": "stof@notk.org"
}
],
"require": {
"php": ">=5.3.2",
"symfony/framework-bundle": "~2.1|~3.0",
"gedmo/doctrine-extensions": "^2.3.1"
},
"suggest": {
"doctrine/doctrine-bundle": "to use the ORM extensions",
"doctrine/mongodb-odm-bundle": "to use the MongoDB ODM extensions"
},
"autoload": {
"psr-4": { "Stof\\DoctrineExtensionsBundle\\": "" }
},
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
}
}