fix folder name

This commit is contained in:
2021-03-18 13:37:13 +01:00
parent a2f6773f5a
commit eaa0ad925f
1578 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,259 @@
<?php
/*
* Copyright (C) 2014-2018 Julien Fastré <julien.fastre@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface;
use Chill\MainBundle\DependencyInjection\Configuration;
use Chill\MainBundle\Doctrine\DQL\GetJsonFieldByKey;
use Chill\MainBundle\Doctrine\DQL\Unaccent;
use Chill\MainBundle\Doctrine\DQL\JsonAggregate;
use Chill\MainBundle\Doctrine\DQL\JsonbExistsInArray;
use Chill\MainBundle\Doctrine\DQL\Similarity;
use Chill\MainBundle\Doctrine\DQL\OverlapsI;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Chill\MainBundle\Doctrine\DQL\Replace;
/**
* Class ChillMainExtension
* This class load config for chillMainExtension.
*
* @package Chill\MainBundle\DependencyInjection
*/
class ChillMainExtension extends Extension implements PrependExtensionInterface,
Widget\HasWidgetFactoriesExtensionInterface
{
/**
* widget factory
*
* @var WidgetFactoryInterface[]
*/
protected $widgetFactories = array();
/**
* @param WidgetFactoryInterface $factory
*/
public function addWidgetFactory(WidgetFactoryInterface $factory)
{
$this->widgetFactories[] = $factory;
}
/**
* @return WidgetFactoryInterface[]
*/
public function getWidgetFactories()
{
return $this->widgetFactories;
}
/**
* {@inheritDoc}
* @param array $configs
* @param ContainerBuilder $container
* @throws \Exception
*/
public function load(array $configs, ContainerBuilder $container)
{
// configuration for main bundle
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
$container->setParameter('chill_main.installation_name',
$config['installation_name']);
$container->setParameter('chill_main.available_languages',
$config['available_languages']);
$container->setParameter('chill_main.routing.resources',
$config['routing']['resources']);
$container->setParameter('chill_main.pagination.item_per_page',
$config['pagination']['item_per_page']);
$container->setParameter('chill_main.notifications',
$config['notifications']);
$container->setParameter('chill_main.redis',
$config['redis']);
$container->setParameter('chill_main.phone_helper',
$config['phone_helper'] ?? []);
// add the key 'widget' without the key 'enable'
$container->setParameter('chill_main.widgets',
isset($config['widgets']['homepage']) ?
array('homepage' => $config['widgets']['homepage']):
array()
);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
$loader->load('services.yaml');
$loader->load('services/doctrine.yaml');
$loader->load('services/logger.yaml');
$loader->load('services/repositories.yaml');
$loader->load('services/pagination.yaml');
$loader->load('services/export.yaml');
$loader->load('services/form.yaml');
$loader->load('services/validator.yaml');
$loader->load('services/widget.yaml');
$loader->load('services/controller.yaml');
$loader->load('services/routing.yaml');
$loader->load('services/fixtures.yaml');
$loader->load('services/menu.yaml');
$loader->load('services/security.yaml');
$loader->load('services/notification.yaml');
$loader->load('services/redis.yaml');
$loader->load('services/command.yaml');
$loader->load('services/phonenumber.yaml');
$loader->load('services/cache.yaml');
$loader->load('services/templating.yaml');
$loader->load('services/timeline.yaml');
$loader->load('services/search.yaml');
$this->configureCruds($container, $config['cruds'], $loader);
}
/**
* @param array $config
* @param ContainerBuilder $container
* @return \Chill\MainBundle\DependencyInjection\Configuration|null|object|\Symfony\Component\Config\Definition\ConfigurationInterface
*/
public function getConfiguration(array $config, ContainerBuilder $container)
{
return new Configuration($this->widgetFactories, $container);
}
/**
* @param ContainerBuilder $container
*/
public function prepend(ContainerBuilder $container)
{
//add installation_name and date_format to globals
$chillMainConfig = $container->getExtensionConfig($this->getAlias());
$config = $this->processConfiguration($this
->getConfiguration($chillMainConfig, $container), $chillMainConfig);
$twigConfig = array(
'globals' => array(
'installation' => array(
'name' => $config['installation_name']),
'available_languages' => $config['available_languages']
),
'form_themes' => array('@ChillMain/Form/fields.html.twig')
);
$container->prependExtensionConfig('twig', $twigConfig);
//add DQL function to ORM (default entity_manager)
$container->prependExtensionConfig('doctrine', array(
'orm' => array(
'dql' => array(
'string_functions' => array(
'unaccent' => Unaccent::class,
'GET_JSON_FIELD_BY_KEY' => GetJsonFieldByKey::class,
'AGGREGATE' => JsonAggregate::class,
'REPLACE' => Replace::class,
),
'numeric_functions' => [
'JSONB_EXISTS_IN_ARRAY' => JsonbExistsInArray::class,
'SIMILARITY' => Similarity::class,
'OVERLAPSI' => OverlapsI::class
]
)
)
));
//add dbal types (default entity_manager)
$container->prependExtensionConfig('doctrine', array(
'dbal' => [
'types' => [
'dateinterval' => \Chill\MainBundle\Doctrine\Type\NativeDateIntervalType::class
]
]
));
//add current route to chill main
$container->prependExtensionConfig('chill_main', array(
'routing' => array(
'resources' => array(
'@ChillMainBundle/config/routes.yaml'
)
)
));
//add a channel to log app events
$container->prependExtensionConfig('monolog', array(
'channels' => array('chill')
));
}
/**
* @param ContainerBuilder $container
* @param array $config the config under 'cruds' key
* @return null
*/
protected function configureCruds(ContainerBuilder $container, $config, Loader\YamlFileLoader $loader)
{
if (count($config) === 0) {
return;
}
$loader->load('services/crud.yaml');
$container->setParameter('chill_main_crud_route_loader_config', $config);
$definition = new Definition();
$definition
->setClass(\Chill\MainBundle\CRUD\Routing\CRUDRoutesLoader::class)
->addArgument('%chill_main_crud_route_loader_config%')
;
$container->setDefinition('chill_main_crud_route_loader', $definition);
$alreadyExistingNames = [];
foreach ($config as $crudEntry) {
$controller = $crudEntry['controller'];
$controllerServiceName = 'cscrud_'.$crudEntry['name'].'_controller';
$name = $crudEntry['name'];
// check for existing crud names
if (\in_array($name, $alreadyExistingNames)) {
throw new LogicException(sprintf("the name %s is defined twice in CRUD", $name));
}
if (!$container->has($controllerServiceName)) {
$controllerDefinition = new Definition($controller);
$controllerDefinition->addTag('controller.service_arguments');
$controllerDefinition->setAutoconfigured(true);
$controllerDefinition->setClass($crudEntry['controller']);
$container->setDefinition($controllerServiceName, $controllerDefinition);
}
$container->setParameter('chill_main_crud_config_'.$name, $crudEntry);
$container->getDefinition($controllerServiceName)
->addMethodCall('setCrudConfig', ['%chill_main_crud_config_'.$name.'%']);
}
}
}

View File

@@ -0,0 +1,39 @@
<?php
/*
*/
namespace Chill\MainBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Chill\MainBundle\Form\PermissionsGroupType;
use Symfony\Component\DependencyInjection\Reference;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ACLFlagsCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$permissionGroupType = $container->getDefinition(PermissionsGroupType::class);
foreach($container->findTaggedServiceIds('chill_main.flags') as $id => $tags) {
$reference = new Reference($id);
foreach ($tags as $tag) {
switch($tag['scope']) {
case PermissionsGroupType::FLAG_SCOPE:
$permissionGroupType->addMethodCall('addFlagProvider', [ $reference ]);
break;
default:
throw new \LogicalException(sprintf(
"This tag 'scope' is not implemented: %s, on service with id %s", $tag['scope'], $id)
);
}
}
}
}
}

View File

@@ -0,0 +1,210 @@
<?php
/*
* Copyright (C) 2015 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Definition;
/**
* Compiles the services tagged with :
*
* - chill.export
* - chill.export_formatter
* - chill.export_aggregator
* - chill.export_filter
* - chill.export_elements_provider
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ExportsCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->has('Chill\MainBundle\Export\ExportManager')) {
throw new \LogicException('service Chill\MainBundle\Export\ExportManager '
. 'is not defined. It is required by ExportsCompilerPass');
}
$chillManagerDefinition = $container->findDefinition(
'Chill\MainBundle\Export\ExportManager'
);
$this->compileExports($chillManagerDefinition, $container);
$this->compileFilters($chillManagerDefinition, $container);
$this->compileAggregators($chillManagerDefinition, $container);
$this->compileFormatters($chillManagerDefinition, $container);
$this->compileExportElementsProvider($chillManagerDefinition, $container);
}
private function compileExports(Definition $chillManagerDefinition,
ContainerBuilder $container)
{
$taggedServices = $container->findTaggedServiceIds(
'chill.export'
);
$knownAliases = array();
foreach ($taggedServices as $id => $tagAttributes) {
foreach ($tagAttributes as $attributes) {
if (!isset($attributes["alias"])) {
throw new \LogicException("the 'alias' attribute is missing in your ".
"service '$id' definition");
}
if (array_search($attributes["alias"], $knownAliases)) {
throw new \LogicException("There is already a chill.export service with alias "
.$attributes["alias"].". Choose another alias.");
}
$knownAliases[] = $attributes["alias"];
$chillManagerDefinition->addMethodCall(
'addExport',
array(new Reference($id), $attributes["alias"])
);
}
}
}
private function compileFilters(Definition $chillManagerDefinition,
ContainerBuilder $container)
{
$taggedServices = $container->findTaggedServiceIds(
'chill.export_filter'
);
$knownAliases = array();
foreach ($taggedServices as $id => $tagAttributes) {
foreach ($tagAttributes as $attributes) {
if (!isset($attributes["alias"])) {
throw new \LogicException("the 'alias' attribute is missing in your ".
"service '$id' definition");
}
if (array_search($attributes["alias"], $knownAliases)) {
throw new \LogicException("There is already a chill.export_filter service with alias "
.$attributes["alias"].". Choose another alias.");
}
$knownAliases[] = $attributes["alias"];
$chillManagerDefinition->addMethodCall(
'addFilter',
array(new Reference($id), $attributes["alias"])
);
}
}
}
private function compileAggregators(Definition $chillManagerDefinition,
ContainerBuilder $container)
{
$taggedServices = $container->findTaggedServiceIds(
'chill.export_aggregator'
);
$knownAliases = array();
foreach ($taggedServices as $id => $tagAttributes) {
foreach ($tagAttributes as $attributes) {
if (!isset($attributes["alias"])) {
throw new \LogicException("the 'alias' attribute is missing in your ".
"service '$id' definition");
}
if (array_search($attributes["alias"], $knownAliases)) {
throw new \LogicException("There is already a chill.export_aggregator service with alias "
.$attributes["alias"].". Choose another alias.");
}
$knownAliases[] = $attributes["alias"];
$chillManagerDefinition->addMethodCall(
'addAggregator',
array(new Reference($id), $attributes["alias"])
);
}
}
}
private function compileFormatters(Definition $chillManagerDefinition,
ContainerBuilder $container)
{
$taggedServices = $container->findTaggedServiceIds(
'chill.export_formatter'
);
$knownAliases = array();
foreach ($taggedServices as $id => $tagAttributes) {
foreach ($tagAttributes as $attributes) {
if (!isset($attributes["alias"])) {
throw new \LogicException("the 'alias' attribute is missing in your ".
"service '$id' definition");
}
if (array_search($attributes["alias"], $knownAliases)) {
throw new \LogicException("There is already a chill.export_formatter service with alias "
.$attributes["alias"].". Choose another alias.");
}
$knownAliases[] = $attributes["alias"];
$chillManagerDefinition->addMethodCall(
'addFormatter',
array(new Reference($id), $attributes["alias"])
);
}
}
}
private function compileExportElementsProvider(Definition $chillManagerDefinition,
ContainerBuilder $container)
{
$taggedServices = $container->findTaggedServiceIds(
'chill.export_elements_provider'
);
$knownAliases = array();
foreach ($taggedServices as $id => $tagAttributes) {
foreach ($tagAttributes as $attributes) {
if (!isset($attributes["prefix"])) {
throw new \LogicException("the 'prefix' attribute is missing in your ".
"service '$id' definition");
}
if (array_search($attributes["prefix"], $knownAliases)) {
throw new \LogicException("There is already a chill.export_elements_provider service with prefix "
.$attributes["prefix"].". Choose another prefix.");
}
$knownAliases[] = $attributes["prefix"];
$chillManagerDefinition->addMethodCall(
'addExportElementsProvider',
array(new Reference($id), $attributes["prefix"])
);
}
}
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* Copyright (C) 2019 Champs-Libres <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
use Chill\MainBundle\Form\Type\Export\PickCenterType;
/**
*
*
*
*/
class GroupingCenterCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (FALSE === $container->hasDefinition('chill.main.form.pick_centers_type')) {
throw new \LogicException("The service chill.main.form.pick_centers_type does "
. "not exists in container");
}
$pickCenterType = $container->getDefinition('chill.main.form.pick_centers_type');
foreach ($container->findTaggedServiceIds('chill.grouping_center') as $serviceId => $tagged) {
$pickCenterType->addMethodCall('addGroupingCenter',
[ new Reference($serviceId) ]);
}
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
use Chill\MainBundle\Routing\MenuComposer;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class MenuCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('chill.main.menu_composer')) {
throw new \LogicException(sprintf("The service %s does not exists in "
. "container.", MenuComposer::class));
}
$menuComposerDefinition = $container->getDefinition('chill.main.menu_composer');
$services = [];
foreach ($container->findTaggedServiceIds('chill.menu_builder') as $id => $tags) {
$services[] = [
'id' => $id,
'priority' => $tags[0]['priority'] ?? 100,
];
}
usort($services, function ($a, $b) {
if ($a['priority'] == $b['priority']) {
return 0;
}
return ($a['priority'] < $b['priority']) ? -1 : 1;
});
foreach ($services as $service) {
$class = $container->getDefinition($service['id'])->getClass();
foreach ($class::getMenuIds() as $menuId) {
$menuComposerDefinition
->addMethodCall('addLocalMenuBuilder', [new Reference($service['id']), $menuId]);
}
}
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Chill\MainBundle\Templating\UI\CountNotificationUser;
use Symfony\Component\DependencyInjection\Reference;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class NotificationCounterCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition(CountNotificationUser::class)) {
throw new \LogicException("The service ".CountNotificationUser::class." "
. "should be defined");
}
$notificationCounterDefinition = $container->getDefinition(CountNotificationUser::class);
foreach ($container->findTaggedServiceIds('chill.count_notification.user') as $id => $tags) {
$notificationCounterDefinition
->addMethodCall('addNotificationCounter', [new Reference($id)]);
}
}
}

View File

@@ -0,0 +1,73 @@
<?php
/*
* Chill is a software for social workers
*
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
class SearchableServicesCompilerPass implements CompilerPassInterface
{
/*
* (non-PHPdoc)
* @see \Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface::process()
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('chill_main.search_provider')) {
throw new \LogicException('service chill_main.search_provider '
. 'is not defined.');
}
$definition = $container->getDefinition(
'chill_main.search_provider'
);
$taggedServices = $container->findTaggedServiceIds(
'chill.search'
);
$knownAliases = array();
foreach ($taggedServices as $id => $tagAttributes) {
foreach ($tagAttributes as $attributes) {
if (!isset($attributes["alias"])) {
throw new \LogicException("the 'name' attribute is missing in your ".
"service '$id' definition");
}
if (array_search($attributes["alias"], $knownAliases)) {
throw new \LogicException("There is already a chill.search service with alias "
.$attributes["alias"].". Choose another alias.");
}
$knownAliases[] = $attributes["alias"];
$definition->addMethodCall(
'addSearchService',
array(new Reference($id), $attributes["alias"])
);
}
}
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* Copyright (C) 2015 Champs-Libres Coopérative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* Add services taggued with `name: chill.timeline` to
* timeline_builder service definition
*
*/
class TimelineCompilerClass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('chill_main.timeline_builder')) {
throw new \LogicException('service chill_main.timeline_builder '
. 'is not defined.');
}
$definition = $container->getDefinition(
'chill_main.timeline_builder'
);
$taggedServices = $container->findTaggedServiceIds(
'chill.timeline'
);
foreach ($taggedServices as $id => $tagAttributes) {
foreach ($tagAttributes as $attributes) {
if (!isset($attributes["context"])) {
throw new \LogicException("the 'context' attribute is missing in your ".
"service '$id' definition");
}
$definition->addMethodCall(
'addProvider',
array($attributes["context"], $id, new Reference($id))
);
}
}
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* Copyright (C) 2016 Julien Fastré <julien.fastre@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Chill\MainBundle\DependencyInjection\Widget\AbstractWidgetsCompilerPass;
/**
* Compile the service definition to register widgets.
*
*/
class WidgetsCompilerPass extends AbstractWidgetsCompilerPass {
public function process(ContainerBuilder $container)
{
$this->doProcess($container, 'chill_main', 'chill_main.widgets');
}
}

View File

@@ -0,0 +1,61 @@
<?php
/*
* Chill is a software for social workers
* Copyright (C) 2015 Champs-Libres Coopérative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Description of ConfigConsistencyCompilerPass
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class ConfigConsistencyCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$availableLanguages = $container
->getParameter('chill_main.available_languages');
$methodCallsTranslator = $container
->findDefinition('translator.default')
->getMethodCalls();
$fallbackLocales = array();
foreach($methodCallsTranslator as $call) {
if ($call[0] === 'setFallbackLocales') {
$fallbackLocales = array_merge($fallbackLocales,
$call[1][0]);
}
}
if (count($fallbackLocales) === 0) {
throw new \LogicException('the fallback locale are not defined. '
. 'The framework config should not allow this.');
}
$diff = array_diff($fallbackLocales, $availableLanguages);
if (count($diff) > 0) {
throw new \RuntimeException(sprintf('The chill_main.available_languages'
. ' parameter does not contains fallback locales. The languages %s'
. ' are missing.', implode(', ', $diff)));
}
}
}

View File

@@ -0,0 +1,178 @@
<?php
namespace Chill\MainBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Chill\MainBundle\DependencyInjection\Widget\AddWidgetConfigurationTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Configure the main bundle
*/
class Configuration implements ConfigurationInterface
{
use AddWidgetConfigurationTrait;
/**
*
* @var ContainerBuilder
*/
private $containerBuilder;
public function __construct(array $widgetFactories = array(),
ContainerBuilder $containerBuilder)
{
$this->setWidgetFactories($widgetFactories);
$this->containerBuilder = $containerBuilder;
}
/**
* {@inheritDoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('chill_main');
$rootNode = $treeBuilder->getRootNode('chill_main');
$rootNode
->children()
->scalarNode('installation_name')
->cannotBeEmpty()
->defaultValue('Chill')
->end() // end of scalar 'installation_name'
->arrayNode('available_languages')
->defaultValue(array('fr'))
->prototype('scalar')->end()
->end() // end of array 'available_languages'
->arrayNode('routing')
->children()
->arrayNode('resources')
->prototype('scalar')->end()
->end() // end of array 'resources'
->end() // end of children
->end() // end of array node 'routing'
->arrayNode('pagination')
->canBeDisabled()
->children()
->integerNode('item_per_page')
->info('The number of item to show in the page result, by default')
->min(1)
->defaultValue(50)
->end() // end of integer 'item_per_page'
->end() // end of children
->end() // end of pagination
->arrayNode('notifications')
->children()
->scalarNode('from_email')
->cannotBeEmpty()
->end()
->scalarNode('from_name')
->cannotBeEmpty()
->end()
->enumNode('scheme')
->cannotBeEmpty()
->values(['http', 'https'])
->defaultValue('https')
->end()
->scalarNode('host')
->cannotBeEmpty()
->end()
->end()
->end() // end of notifications
->arrayNode('phone_helper')
->canBeUnset()
->children()
->scalarNode('twilio_sid')
->defaultNull()
->end()
->scalarNode('twilio_secret')
->defaultNull()
->end()
->end()
->end()
->arrayNode('redis')
->children()
->scalarNode('host')
->cannotBeEmpty()
->end()
->scalarNode('port')
->defaultValue(6379)
->end()
->scalarNode('timeout')
->defaultValue(1)
->end()
->end()
->end()
->arrayNode('widgets')
->canBeEnabled()
->canBeUnset()
->children()
->append($this->addWidgetsConfiguration('homepage', $this->containerBuilder))
->end() // end of widgets/children
->end() // end of widgets
->arrayNode('cruds')
->defaultValue([])
->arrayPrototype()
->children()
->scalarNode('class')->cannotBeEmpty()->isRequired()->end()
->scalarNode('controller')
->cannotBeEmpty()
->defaultValue(\Chill\MainBundle\CRUD\Controller\CRUDController::class)
->end()
->scalarNode('name')->cannotBeEmpty()->isRequired()->end()
->scalarNode('base_path')->cannotBeEmpty()->isRequired()->end()
->scalarNode('base_role')->defaultNull()->end()
->scalarNode('form_class')->defaultNull()->end()
->arrayNode('actions')
->defaultValue([
'edit' => [],
'new' => []
])
->useAttributeAsKey('name')
->arrayPrototype()
->children()
->scalarNode('controller_action')
->defaultNull()
->info('the method name to call in the route. Will be set to the action name if left empty.')
->example("'action'")
->end()
->scalarNode('path')
->defaultNull()
->info('the path that will be **appended** after the base path. Do not forget to add '
. 'arguments for the method. Will be set to the action name, including an `{id}` '
. 'parameter if left empty.')
->example('/{id}/my-action')
->end()
->arrayNode('requirements')
->ignoreExtraKeys(false)
->info('the requirements for the route. Will be set to `[ \'id\' => \'\d+\' ]` if left empty.')
->end()
->scalarNode('role')
->defaultNull()
->info('the role that will be required for this action. Override option `base_role`')
->end()
->scalarNode('template')
->defaultNull()
->info('the template to render the view')
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end() // end of root/children
->end() // end of root
;
return $treeBuilder;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Chill\MainBundle\DependencyInjection;
use Exception;
/**
* Description of MissingBundleException
*
* @author julien
*/
class MissingBundleException extends Exception {
public function __construct($missingBundleName) {
$message = "The bundle $missingBundleName is missing.";
parent::__construct($message, 500);
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* Copyright (C) 2015 Julien Fastré <julien.fastre@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
/**
*
*
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*/
class RoleProvidersCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('chill.main.role_provider')) {
throw new \LogicException('service chill.main.role_provider '
. 'is not defined. It is required by RoleProviderCompilerPass');
}
$definition = $container->getDefinition(
'chill.main.role_provider'
);
$taggedServices = $container->findTaggedServiceIds(
'chill.role'
);
foreach ($taggedServices as $id => $tagAttributes) {
$definition->addMethodCall(
'addProvider',
array(new Reference($id))
);
}
}
}

View File

@@ -0,0 +1,388 @@
<?php
/*
* Copyright (C) 2016 Julien Fastré <julien.fastre@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection\Widget;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Doctrine\Common\Proxy\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Definition;
use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface;
use Chill\MainBundle\DependencyInjection\Widget\HasWidgetFactoriesExtensionInterface;
/**
* Compile the configurations and inject required service into container.
*
* The widgets are services tagged with :
*
* ```
* { name: chill_widget, alias: my_alias, place: my_place }
* ```
*
* Or, if the tag does not exist or if you need to add some config to your
* service depending on the config, you should use a `WidgetFactory` (see
* `WidgetFactoryInterface`.
*
* To reuse this compiler pass, simple execute the doProcess metho in your
* compiler. Example :
*
* ```
* namespace Chill\MainBundle\DependencyInjection\CompilerPass;
*
* use Symfony\Component\DependencyInjection\ContainerBuilder;
* use Chill\MainBundle\DependencyInjection\Widget\AbstractWidgetsCompilerPass;
* class WidgetsCompilerPass extends AbstractWidgetsCompilerPass {
*
* public function process(ContainerBuilder $container)
* {
* $this->doProcess($container, 'chill_main', 'chill_main.widgets');
* }
* }
* ```
*
*
*/
abstract class AbstractWidgetsCompilerPass implements CompilerPassInterface
{
private $widgetServices = array();
/**
*
* @var WidgetFactoryInterface[]
*/
private $widgetFactories;
/**
* The service which will manage the widgets
*
* @var string
*/
const WIDGET_MANAGER = 'chill.main.twig.widget';
/**
* the method wich register the widget into give service.
*/
const WIDGET_MANAGER_METHOD_REGISTER = 'addWidget';
/**
* the value of the `name` key in service definitions's tag
*
* @var string
*/
const WIDGET_SERVICE_TAG_NAME = 'chill_widget';
/**
* the key used to collect the alias in the service definition's tag.
* the alias must be
* injected into the configuration under 'alias' key.
*
* @var string
*/
const WIDGET_SERVICE_TAG_ALIAS = 'alias';
/**
* the key used to collect the authorized place in the service definition's tag
*
* @var string
*/
const WIDGET_SERVICE_TAG_PLACES = 'place';
/**
* the key to use to order widget for a given place
*/
const WIDGET_CONFIG_ORDER = 'order';
/**
* the key to use to identify widget for a given place
*/
const WIDGET_CONFIG_ALIAS = 'widget_alias';
/**
* process the configuration and the container to add the widget available
*
* @param ContainerBuilder $container
* @param string $extension the extension of your bundle
* @param string $containerWidgetConfigParameterName the key under which we can use the widget configuration
* @throws \LogicException
* @throws \UnexpectedValueException if the given extension does not implement HasWidgetExtensionInterface
* @throws \InvalidConfigurationException if there are errors in the config
*/
public function doProcess(ContainerBuilder $container, $extension,
$containerWidgetConfigParameterName)
{
if (!$container->hasDefinition(self::WIDGET_MANAGER)) {
throw new \LogicException("the service ".self::WIDGET_MANAGER." should".
" be present. It is required by ".self::class);
}
$managerDefinition = $container->getDefinition(self::WIDGET_MANAGER);
// collect the widget factories
/* @var $extensionClass HasWidgetFactoriesExtensionInterface */
$extensionClass = $container->getExtension($extension);
// throw an error if extension does not implement HasWidgetFactoriesExtensionInterface
if (!$extensionClass instanceof HasWidgetFactoriesExtensionInterface) {
throw new \UnexpectedValueException(sprintf("The extension for %s "
. "do not implements %s. You should implement %s on %s",
$extension,
HasWidgetFactoriesExtensionInterface::class,
HasWidgetFactoriesExtensionInterface::class,
get_class($extensionClass)));
}
$this->widgetFactories = $extensionClass->getWidgetFactories();
// collect the availabled tagged services
$this->collectTaggedServices($container);
// collect the widgets and their config :
$widgetParameters = $container->getParameter($containerWidgetConfigParameterName);
// and add them to the delegated_block
foreach($widgetParameters as $place => $widgets) {
foreach ($widgets as $param) {
$alias = $param[self::WIDGET_CONFIG_ALIAS];
// check that the service exists
if (!array_key_exists($alias, $this->widgetServices)) {
throw new InvalidConfigurationException(sprintf("The alias %s".
" is not defined.", $alias));
}
// check that the widget is allowed at this place
if (!$this->isPlaceAllowedForWidget($place, $alias, $container)) {
throw new \InvalidConfigurationException(sprintf(
"The widget with alias %s is not allowed at place %s",
$alias,
$place
));
}
// get the order, eventually corrected
$order = $this->cacheAndGetOrdering($place, $param[self::WIDGET_CONFIG_ORDER]);
// register the widget with config to the service, using the method
// `addWidget`
if ($this->widgetServices[$alias] instanceof WidgetFactoryInterface) {
/* @var $factory WidgetFactoryInterface */
$factory = $this->widgetServices[$alias];
// get the config (under the key which equals to widget_alias
$config = isset($param[$factory->getWidgetAlias()]) ?
$param[$factory->getWidgetAlias()] : array();
// register the service into the container
$serviceId =$this->registerServiceIntoContainer($container,
$factory, $place, $order, $config);
$managerDefinition->addMethodCall(self::WIDGET_MANAGER_METHOD_REGISTER,
array(
$place,
$order,
new Reference($serviceId),
$config
));
} else {
$managerDefinition->addMethodCall(self::WIDGET_MANAGER_METHOD_REGISTER,
array(
$place,
$order,
new Reference($this->widgetServices[$alias]),
array() // the config is alway an empty array
));
}
}
}
}
/**
* register the service into container.
*
* @param ContainerBuilder $container
* @param WidgetFactoryInterface $factory
* @param string $place
* @param float $order
* @param array $config
* @return string the id of the new service
*/
protected function registerServiceIntoContainer(
ContainerBuilder $container,
WidgetFactoryInterface $factory,
$place,
$order,
array $config
) {
$serviceId = $factory->getServiceId($container, $place, $order, $config);
$definition = $factory->createDefinition($container, $place,
$order, $config);
$container->setDefinition($serviceId, $definition);
return $serviceId;
}
/**
* cache of ordering by place.
*
* @internal used by function cacheAndGetOrdering
* @var array
*/
private $cacheOrdering = array();
/**
* check if the ordering has already be used for the given $place and,
* if yes, correct the ordering by incrementation of 1 until the ordering
* has not be used.
*
* recursive method.
*
* @param string $place
* @param float $ordering
* @return float
*/
private function cacheAndGetOrdering($place, $ordering) {
// create a key in the cache array if not exists
if (!array_key_exists($place, $this->cacheOrdering)) {
$this->cacheOrdering[$place] = array();
}
// check if the order exists
if (array_search($ordering, $this->cacheOrdering[$place])) {
// if the order exists, increment of 1 and try again
return $this->cacheAndGetOrdering($place, $ordering + 1);
} else {
// cache the ordering
$this->cacheOrdering[$place][] = $ordering;
return $ordering;
}
}
/**
* get the places where the service is allowed
*
* @param Definition $definition
* @return unknown
*/
private function isPlaceAllowedForWidget($place, $widgetAlias, ContainerBuilder $container)
{
if ($this->widgetServices[$widgetAlias] instanceof WidgetFactoryInterface) {
if (in_array($place, $this->widgetServices[$widgetAlias]
->getAllowedPlaces())) {
return true;
}
} else {
$definition = $container->findDefinition($this->widgetServices[$widgetAlias]);
foreach($definition->getTag(self::WIDGET_SERVICE_TAG_NAME) as $attrs) {
$placeValue = $attrs[self::WIDGET_SERVICE_TAG_PLACES];
if ($placeValue === $place) {
return true;
}
}
}
return false;
}
/**
* This method collect all service tagged with `self::WIDGET_SERVICE_TAG`, and
* add also the widget defined by factories
*
* This method also check that the service is correctly tagged with `alias` and
* `places`, or the factory give a correct alias and more than one place.
*
* @param ContainerBuilder $container
* @throws InvalidConfigurationException
* @throws InvalidArgumentException
*/
protected function collectTaggedServices(ContainerBuilder $container)
{
// first, check the service tagged in service definition
foreach ($container->findTaggedServiceIds(self::WIDGET_SERVICE_TAG_NAME) as $id => $attrs) {
foreach ($attrs as $attr) {
// check the alias is set
if (!isset($attr[self::WIDGET_SERVICE_TAG_ALIAS])) {
throw new InvalidConfigurationException("you should add an ".self::WIDGET_SERVICE_TAG_ALIAS.
" key on the service ".$id);
}
// check the place is set
if (!isset($attr[self::WIDGET_SERVICE_TAG_PLACES])) {
throw new InvalidConfigurationException(sprintf(
"You should add a %s key on the service %s",
self::WIDGET_SERVICE_TAG_PLACES,
$id
));
}
// check the alias does not exists yet
if (array_key_exists($attr[self::WIDGET_SERVICE_TAG_ALIAS], $this->widgetServices)) {
throw new InvalidArgumentException("a service has already be defined with the ".
self::WIDGET_SERVICE_TAG_ALIAS." ".$attr[self::WIDGET_SERVICE_TAG_ALIAS]);
}
// register the service as available
$this->widgetServices[$attr[self::WIDGET_SERVICE_TAG_ALIAS]] = $id;
}
}
// add the services defined by factories
foreach($this->widgetFactories as $factory) {
/* @var $factory WidgetFactoryInterface */
$alias = $factory->getWidgetAlias();
// check the alias is not empty
if (empty($alias)) {
throw new \LogicException(sprintf(
"the widget factory %s returns an empty alias",
get_class($factory)));
}
// check the places are not empty
if (!is_array($factory->getAllowedPlaces())) {
throw new \UnexpectedValueException("the method 'getAllowedPlaces' "
. "should return a non-empty array. Unexpected value on ".
get_class($factory));
}
if (count($factory->getAllowedPlaces()) == 0) {
throw new \LengthException("The method 'getAllowedPlaces' should "
. "return a non-empty array, but returned 0 elements on ".
get_class($factory).'::getAllowedPlaces()');
}
// check the alias does not exists yet
if (array_key_exists($alias, $this->widgetServices)) {
throw new InvalidArgumentException("a service has already be defined with the ".
self::WIDGET_SERVICE_TAG_ALIAS." ".$alias);
}
// register the factory as available
$this->widgetServices[$factory->getWidgetAlias()] = $factory;
}
}
}

View File

@@ -0,0 +1,252 @@
<?php
/*
* Copyright (C) 2016 Julien Fastré <julien.fastre@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection\Widget;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Chill\MainBundle\DependencyInjection\Widget\AbstractWidgetsCompilerPass as WidgetsCompilerPass;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
/**
* This trait allow to add automatic configuration for widget inside your config.
*
* Usage
* ======
*
* 1. Register widget factories
* ----------------------------
*
* Add widget factories, using `setWidgetFactories`
*
* Example :
*
* ```
* use Symfony\Component\DependencyInjection\ContainerBuilder;
* use Chill\MainBundle\DependencyInjection\Widget\AddWidgetConfigurationTrait;
*
* class MyConfig
* {
*
* use addWidgetConfigurationTrait;
*
* public function __construct(array $widgetFactories = array(), ContainerBuilder $container)
* {
* $this->setWidgetFactories($widgetFactories);
* // will be used on next step
* $this->container = $container;
* }
*
* }
* ```
*
*
*
* 2. add widget config to your config
* -----------------------------------
*
* ```
* use Symfony\Component\DependencyInjection\ContainerBuilder;
* use Chill\MainBundle\DependencyInjection\Widget\AddWidgetConfigurationTrait;
* use Symfony\Component\Config\Definition\Builder\TreeBuilder;
*
* class MyConfig
* {
*
* use addWidgetConfigurationTrait;
*
* private $container;
*
* public function __construct(array $widgetFactories = array(), ContainerBuilder $container)
* {
* $this->setWidgetFactories($widgetFactories);
* $this->container;
* }
*
* public function getConfigTreeBuilder()
* {
* $treeBuilder = new TreeBuilder();
* $root = $treeBuilder->root('my_root');
*
* $root->children()
* ->arrayNode('widgets')
* ->canBeDisabled()
* ->children()
* ->append($this->addWidgetsConfiguration('homepage', $this->container))
* ->end()
* ->end()
* ;
*
* return $treeBuilder;
* }
*
* }
* ```
*
* the above code will add to the config :
*
* ```
* widgets:
* enabled: true
*
* # register widgets on place "homepage"
* homepage:
* order: ~ # Required
* widget_alias: ~ # One of "person_list"; "add_person", Required
* person_list:
* # options for the person_list
* ```
*
*
*/
trait AddWidgetConfigurationTrait
{
/**
* @param WidgetFactoryInterface[]
*/
private $widgetFactories;
/**
*
* @param WidgetFactoryInterface[] $widgetFactories
*/
public function setWidgetFactories(array $widgetFactories)
{
$this->widgetFactories = $widgetFactories;
}
/**
* @return WidgetFactoryInterface[]
*/
public function getWidgetFactories()
{
return $this->widgetFactories;
}
/**
* add configuration nodes for the widget at the given place.
*
* @param type $place
* @param ContainerBuilder $containerBuilder
* @return type
*/
protected function addWidgetsConfiguration($place, ContainerBuilder $containerBuilder)
{
$treeBuilder = new TreeBuilder($place);
$root = $treeBuilder->getRootNode($place)
->canBeUnset()
->info('register widgets on place "'.$place.'"');
// if no childen, return the root
if (count(iterator_to_array($this->filterWidgetByPlace($place))) === 0) {
return $root;
}
$prototypeChildren = $root->prototype('array')->children();
$prototypeChildren
->floatNode(WidgetsCompilerPass::WIDGET_CONFIG_ORDER)
->isRequired()
->info("the ordering of the widget. May be a number with decimal")
->example("10.58")
->end()
->scalarNode(WidgetsCompilerPass::WIDGET_CONFIG_ALIAS)
// this node is scalar: when the configuration is build, the
// tagged services are not available. But when the config reference
// is build, the services are avaialble => we add the possible aliases
// in the info.
->info("the widget alias (see your installed bundles config). "
. "Possible values are (maybe incomplete) : ".
\implode(", ", $this->getWidgetAliasesbyPlace($place, $containerBuilder)))
->isRequired()
->end()
;
// adding the possible config on each widget under the widget_alias
foreach ($this->filterWidgetByPlace($place) as $factory) {
$builder = new TreeBuilder($factory->getWidgetAlias());
$widgetOptionsRoot = $builder->getRootNode($factory->getWidgetAlias());
$widgetOptionsRoot->canBeUnset()
->info(sprintf('the configuration for the widget "%s" (only required if this widget is set in widget_alias)',
$factory->getWidgetAlias()));
$factory->configureOptions($place, $widgetOptionsRoot->children());
$prototypeChildren->append($widgetOptionsRoot);
}
return $root;
}
/**
* get all widget factories for the given place.
*
* @param string $place
* @return \Generator a generator containing a widget factory
*/
protected function filterWidgetByPlace($place)
{
foreach($this->widgetFactories as $factory) {
if (in_array($place, $factory->getAllowedPlaces())) {
yield $factory;
}
}
}
/**
* get the all possible aliases for the given place. This method
* search within service tags and widget factories
*
* **Note** that services are not available when the config is build: the whole
* aliases will be checked in compiler pass, or when the command
* `config:dump-reference` is runned.
*
* @param type $place
* @param ContainerBuilder $containerBuilder
* @return type
* @throws InvalidConfigurationException if a service's tag does not have the "alias" key
*/
protected function getWidgetAliasesbyPlace($place, ContainerBuilder $containerBuilder)
{
$result = array();
foreach ($this->filterWidgetByPlace($place) as $factory) {
$result[] = $factory->getWidgetAlias();
}
// append the aliases added without factory
foreach ($containerBuilder
->findTaggedServiceIds(WidgetsCompilerPass::WIDGET_SERVICE_TAG_NAME)
as $serviceId => $tags) {
foreach ($tags as $tag) {
// throw an error if no alias in definition
if (!array_key_exists(WidgetsCompilerPass::WIDGET_SERVICE_TAG_ALIAS, $tag)) {
throw new InvalidConfigurationException(sprintf(
"The service with id %s does not have any %d key",
$serviceId,
WidgetsCompilerPass::WIDGET_SERVICE_TAG_ALIAS
));
}
// add the key to the possible results
$result[] = $tag[WidgetsCompilerPass::WIDGET_SERVICE_TAG_ALIAS];
}
}
return $result;
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* Copyright (C) 2016 Julien Fastré <julien.fastre@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection\Widget\Factory;
use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Allow to easily create WidgetFactory.
*
* Extending this factory, the widget will be created using the already defined
* service created "as other services" in your configuration (the most frequent
* way is using `services.yml` file.
*
* If you need to create different service based upon place, position or
* definition, you should implements directly
* `Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface`
*
*
*/
abstract class AbstractWidgetFactory implements WidgetFactoryInterface
{
/**
*
* {@inheritdoc}
*
* Will create the definition by returning the definition from the `services.yml`
* file (or `services.xml` or `what-you-want.yml`).
*
* @see \Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface::createDefinition()
*/
public function createDefinition(ContainerBuilder $containerBuilder, $place, $order, array $config)
{
return $containerBuilder->getDefinition($this
->getServiceId($containerBuilder, $place, $order, $config)
);
}
}

View File

@@ -0,0 +1,103 @@
<?php
/*
* Copyright (C) 2016 Julien Fastré <julien.fastre@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection\Widget\Factory;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Factory for creating configuration of widgets.
*
* When you need a widget with some configuration, you should implements this
* interface on a factory. The factory will add configuration to the bundle
* giving the places for your widget.
*
* Using this interface, **you do not need** to define the service in your
* container configuration (`services.yml` files).
*
* Once the class is created, you should inject the factory inside the container
* at compile time, in your `Bundle` class :
*
*
* ```
* namespace Chill\PersonBundle;
*
* use Symfony\Component\HttpKernel\Bundle\Bundle;
* use Symfony\Component\DependencyInjection\ContainerBuilder;
* use Chill\PersonBundle\Widget\PersonListWidgetFactory;
*
* class ChillPersonBundle extends Bundle
* {
* public function build(ContainerBuilder $container)
* {
* parent::build($container);
* // register a widget factory into chill_main :
* $container->getExtension('chill_main')
* ->addWidgetFactory(new PersonListWidgetFactory());
* }
* }
* ```
*
*
*
*/
interface WidgetFactoryInterface
{
/**
* configure options for the widget. Those options will be added in
* configuration in the bundle where the widget will be used.
*
* @param type $place
* @param NodeBuilder $node
*/
public function configureOptions($place, NodeBuilder $node);
/**
* get the widget alias. This alias will be used in configuration (`config.yml`)
*/
public function getWidgetAlias();
/**
* Create a definition for the service which will render the widget.
*
* (Note: you can define the service by yourself, as other services,
* using the `AbstractWidgetFactory`)
*
* @param ContainerBuilder $containerBuilder
* @param type $place
* @param type $order
* @param array $config
*/
public function createDefinition(ContainerBuilder $containerBuilder, $place, $order, array $config);
/**
* return the service id to build the widget.
*
* @param ContainerBuilder $containerBuilder
* @param string $place
* @param float $order
* @param array $config
*
* @return string the service definition
*
*/
public function getServiceId(ContainerBuilder $containerBuilder, $place, $order, array $config);
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* Copyright (C) 2016 Julien Fastré <julien.fastre@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\DependencyInjection\Widget;
use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface;
/**
* Register widget factories to an extension.
*
*/
interface HasWidgetFactoriesExtensionInterface {
/**
* register a widget factory
*
* @param \Chill\MainBundle\DependencyInjection\Widget\WidgetFactoryInterface $factory
*/
public function addWidgetFactory(WidgetFactoryInterface $factory);
/**
* get all the widget factories registered for this extension
*
* @return WidgetFactoryInterface[]
*/
public function getWidgetFactories();
}