diff --git a/Controller/JsloaderController.php b/Controller/JsloaderController.php index eed3377..38786e5 100644 --- a/Controller/JsloaderController.php +++ b/Controller/JsloaderController.php @@ -6,6 +6,7 @@ FOS\RestBundle\View\View; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\SecurityContextInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * This controller includes the correct twig file to bootstrap the javascript @@ -43,6 +44,26 @@ class JsloaderController */ private $plainTextTypes; + /** + * @var array + */ + private $createRoutesForTypes; + + /** + * @var array locales of the application + */ + private $locales; + + /** + * @var string content prefix in the repository path, like /cms/content + */ + private $contentPrefix; + + /** + * @var string routes prefix in the repository path, like /cms/routes + */ + private $routesPrefix; + /** * Create the Controller @@ -52,32 +73,48 @@ class JsloaderController * @param string $imageClass used to determine whether image upload should be activated * @param Boolean $fixedToolbar whether the hallo toolbar is fixed or floating * @param array $plainTextTypes RDFa types to edit in raw text only - * @param string $requiredRole + * @param array $createRoutesForTypes types for which it is needed to create a route + * @param string $requiredRole the role name for the security check * @param SecurityContextInterface $securityContext + * @param array $locales the locales of the application + * @param string $contentPrefix content prefix in the repository path + * @param string $routesPrefix routes prefix in the repository path */ public function __construct( ViewHandlerInterface $viewHandler, $stanbolUrl, $imageClass, $fixedToolbar = true, - $plainTextTypes = array(), + array $plainTextTypes = array(), + array $createRoutesForTypes = array(), $requiredRole = "IS_AUTHENTICATED_ANONYMOUSLY", - SecurityContextInterface $securityContext = null + SecurityContextInterface $securityContext = null, + array $locales, + $contentPrefix, + $routesPrefix ) { $this->viewHandler = $viewHandler; $this->stanbolUrl = $stanbolUrl; $this->imageClass = $imageClass; $this->fixedToolbar = $fixedToolbar; $this->plainTextTypes = $plainTextTypes; - + $this->createRoutesForTypes = $createRoutesForTypes; $this->requiredRole = $requiredRole; $this->securityContext = $securityContext; + $this->locales = $locales; + $this->contentPrefix = $contentPrefix; + $this->routesPrefix = $routesPrefix; } /** * Render js inclusion for create.js and dependencies and bootstrap code. * * The hallo editor is bundled with create.js and available automatically. +<<<<<<< HEAD + * To use aloha, you need to download the zip, as explained in step 8 of + * the README. +======= +>>>>>>> master * * When using hallo, the controller can include the compiled js files from * hallo's examples folder or use the assetic coffee filter. @@ -101,11 +138,17 @@ public function includeJSFilesAction($editor = 'hallo') $view->setTemplate(sprintf('SymfonyCmfCreateBundle::includejsfiles-%s.html.twig', $editor)); + + $view->setData(array( 'cmfCreateStanbolUrl' => $this->stanbolUrl, 'cmfCreateImageUploadEnabled' => (boolean) $this->imageClass, 'cmfCreateHalloFixedToolbar' => (boolean) $this->fixedToolbar, - 'cmfCreateHalloPlainTextTypes' => json_encode($this->plainTextTypes) + 'cmfCreateHalloPlainTextTypes' => json_encode($this->plainTextTypes), + 'cmfCreateCreateRoutesTypes' => json_encode($this->createRoutesForTypes), + 'cmfCreateLocales' => json_encode($this->locales), + 'cmfCreateContentPrefix' => $this->contentPrefix, + 'cmfCreateRoutesPrefix' => $this->routesPrefix ) ); diff --git a/Controller/RestController.php b/Controller/RestController.php index bb52f76..2114ac8 100755 --- a/Controller/RestController.php +++ b/Controller/RestController.php @@ -115,16 +115,22 @@ public function postDocumentAction(Request $request) $this->performSecurityChecks(); $rdfType = trim($request->request->get('@type'), '<>'); + $type = $this->typeFactory->getTypeByRdf($rdfType); - $result = $this->restHandler->run($request->request->all(), $type, null, RestService::HTTP_POST); + $result = null; + try { + $result = $this->restHandler->run($request->request->all(), $type, null, RestService::HTTP_POST); + } catch (\Exception $e) { + return Response::create("The document '$rdfType' could not be created: " . $e->getMessage(), 500); + } if (!is_null($result)) { $view = View::create($result)->setFormat('json'); return $this->viewHandler->handle($view, $request); } - return Response::create('The document could not be created', 500); + return Response::create("The document '$rdfType' could not be created", 500); } /** diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 02ef8e5..8772cc5 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -27,6 +27,10 @@ public function getConfigTreeBuilder() ->useAttributeAsKey('name') ->prototype('scalar')->end() ->end() + ->arrayNode('rdfmapper') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() ->scalarNode('role')->defaultValue('IS_AUTHENTICATED_ANONYMOUSLY')->end() ->arrayNode('image') ->canBeUnset() @@ -43,6 +47,10 @@ public function getConfigTreeBuilder() ->useAttributeAsKey('name') ->prototype('scalar')->end() ->end() + ->arrayNode('create_routes_types') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() ->arrayNode('rdf_config_dirs') ->useAttributeAsKey('dir') ->prototype('scalar')->end() diff --git a/DependencyInjection/SymfonyCmfCreateExtension.php b/DependencyInjection/SymfonyCmfCreateExtension.php index c1c3da1..88c21db 100644 --- a/DependencyInjection/SymfonyCmfCreateExtension.php +++ b/DependencyInjection/SymfonyCmfCreateExtension.php @@ -41,6 +41,8 @@ public function load(array $configs, ContainerBuilder $container) $container->setParameter($this->getAlias().'.map', $config['map']); + $container->setParameter($this->getAlias().'.rdfmapper', $config['rdfmapper']); + $container->setParameter($this->getAlias().'.stanbol_url', $config['stanbol_url']); $container->setParameter($this->getAlias().'.role', $config['role']); @@ -52,6 +54,8 @@ public function load(array $configs, ContainerBuilder $container) } $container->setParameter($this->getAlias().'.plain_text_types', $config['plain_text_types']); + $container->setParameter($this->getAlias().'.create_routes_types', $config['create_routes_types']); + if ($config['auto_mapping']) { foreach ($container->getParameter('kernel.bundles') as $class) { $bundle = new \ReflectionClass($class); diff --git a/Mapper/RouteDoctrinePhpcrOdmMapper.php b/Mapper/RouteDoctrinePhpcrOdmMapper.php new file mode 100644 index 0000000..5dca5d6 --- /dev/null +++ b/Mapper/RouteDoctrinePhpcrOdmMapper.php @@ -0,0 +1,33 @@ +getIdentifier() === 'locale') { + $object->setDefault('_locale', $value); + $object->setRequirement('_locale', $value); + return $object; + } + return parent::setPropertyValue($object, $property, $value); + } + + public function getPropertyValue($object, PropertyInterface $property) + { + if ($object instanceof Route && $property->getIdentifier() === 'locale') { + return $object->getDefault('_locale'); + } + return parent::getPropertyValue($object, $property); + } +} diff --git a/Metadata/ContainerRdfTypeFactory.php b/Metadata/ContainerRdfTypeFactory.php new file mode 100644 index 0000000..d7ca433 --- /dev/null +++ b/Metadata/ContainerRdfTypeFactory.php @@ -0,0 +1,64 @@ +mapperServices = $mapperServices; + parent::__construct($defaultMapper, $driver); + } + + /** + * Get the mapper for type $name in the $symfony container, or the defaultMapper if there is no specific one + * + * @param string $name the type name for which to get the mapper + * + * @return RdfMapperInterface + */ + protected function getMapper($name) + { + if (isset($this->mapperServices[$name])) { + return $this->container->get($this->mapperServices[$name]); + } + + return parent::getMapper($name); + } + + /** + * @see ContainerAwareInterface::setContainer() + */ + public function setContainer(ContainerInterface $container = null) + { + $this->container = $container; + } +} diff --git a/Resources/config/phpcr_odm.xml b/Resources/config/phpcr_odm.xml index ef12ed4..cf5bc58 100755 --- a/Resources/config/phpcr_odm.xml +++ b/Resources/config/phpcr_odm.xml @@ -6,6 +6,7 @@ Midgard\CreatePHP\Mapper\DoctrinePhpcrOdmMapper + Symfony\Cmf\Bundle\CreateBundle\Mapper\RouteDoctrinePhpcrOdmMapper @@ -16,5 +17,11 @@ null + + %symfony_cmf_create.map% + + null + + diff --git a/Resources/config/services.xml b/Resources/config/services.xml index 5fecac1..fe06428 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -8,6 +8,7 @@ Symfony\Cmf\Bundle\CreateBundle\Controller\JsloaderController Symfony\Cmf\Bundle\CreateBundle\Controller\RestController Midgard\CreatePHP\Metadata\RdfTypeFactory + Symfony\Cmf\Bundle\CreateBundle\Metadata\ContainerRdfTypeFactory Midgard\CreatePHP\RestService @@ -18,21 +19,25 @@ %symfony_cmf_create.image.model_class% %symfony_cmf_create.fixed_toolbar% %symfony_cmf_create.plain_text_types% + %symfony_cmf_create.create_routes_types% %symfony_cmf_create.role% + %locales% + %symfony_cmf_content.content_basepath% + %symfony_cmf_routing_extra.routing_repositoryroot% - + %symfony_cmf_create.role% - + @@ -40,10 +45,13 @@ %symfony_cmf_create.rdf_config_dirs% - + - %symfony_cmf_create.map% + %symfony_cmf_create.rdfmapper% + + + diff --git a/Resources/public/js/create-routes-handler.js b/Resources/public/js/create-routes-handler.js new file mode 100644 index 0000000..1ff24d5 --- /dev/null +++ b/Resources/public/js/create-routes-handler.js @@ -0,0 +1,88 @@ +jQuery(document).ready(function() { + + (function(){ + + var createRouteForTypes = []; //types currently needing a route creation + + //remove the enclosing <> + function trimAttribute(value) { + if (value instanceof Array) { + //Create.js sometimes adds the owl#Thing type without reasons + value = value[value.length - 1]; + } + return value.substring(1, value.length - 1); + } + + //an entity has been saved and the response of the backend received + $('body').bind('midgardstoragesavedentity', function (event, options) { + + var createdType = trimAttribute(options.entity.attributes["@type"]); + + if ($.inArray(createdType, createRouteForTypes) == -1) { + return; + } + //reset the types for which route creation is currently needed + createRouteForTypes.splice($.inArray(createdType, createRouteForTypes),1); + + var vie = options.entity.vie; + + /** + * Common request content + */ + var trimmedSubject = options.entity.id.substr(1, options.entity.id.length - 2); + var lastSlashPos = trimmedSubject.lastIndexOf("/") + 1; + var contentName = trimmedSubject.substr(lastSlashPos, trimmedSubject.length - lastSlashPos); + var partOf = options.entity.attributes[""].models[0]["@subject"]; + var trimmedPartOf = partOf.substr(1, partOf.length - 2); // "/cms/content/news" + var lastSlashPos = trimmedPartOf.lastIndexOf("/") + 1; + var parentName = trimmedPartOf.substr(lastSlashPos, trimmedPartOf.length - lastSlashPos); + + /** + * Request types + */ + var parentType = "<" + cmfCreateRouteRdfType + "/Parent" + ">"; + var nameType = "<" + cmfCreateRouteRdfType + "/Name" + ">"; + var routeContentType = "<" + cmfCreateRouteRdfType + "/RouteContent" + ">"; + var localeType = "<" + cmfCreateRouteRdfType + "/Locale" + ">"; + var partOfType = ""; + + for(var i in cmfCreateLocales) { + var parentPath = cmfCreateRoutesPrefix + "/" + cmfCreateLocales[i] + "/" + parentName; + + var routeRequest = {}; + routeRequest["@type"] = "<" + cmfCreateRouteRdfType + ">"; + routeRequest[nameType] = contentName; + routeRequest[routeContentType] = trimmedSubject; + routeRequest[partOfType] = [parentPath]; + routeRequest[localeType] = cmfCreateLocales[i]; + routeRequest[parentType] = parentPath; + + var routeEntity = new vie.Entity(); + routeEntity.set(routeRequest); + vie.entities.add(routeEntity); + jQuery('body').midgardStorage('saveRemote', routeEntity, { + success: function (m, err) { + jQuery('body').midgardNotifications('create', { + body: 'Route ' + m.attributes[routeContentType] + ' created successfully' + }); + }, + error: function (m, err) { + jQuery('body').midgardNotifications('create', { + body: 'Error during creation of route ' + m.attributes[partOfType] + '/' + m.attributes[nameType] + '. ' + err.responseText, + timeout: 0 + }); + } + }); + } + }); + + //an entity will be saved and sent to the backend + $('body').bind('midgardstoragesaveentity', function (event, options) { + var type = trimAttribute(options.entity.attributes['@type']); + if (options.entity.isNew() && + $.inArray(type, cmfCreateCreateRoutesTypes) != -1) { + createRouteForTypes.push(type); + } + }); + })() +}); diff --git a/Resources/public/vendor/create b/Resources/public/vendor/create index 271e011..0011a6f 160000 --- a/Resources/public/vendor/create +++ b/Resources/public/vendor/create @@ -1 +1 @@ -Subproject commit 271e0114a039ab256ffcceacdf7f361803995e05 +Subproject commit 0011a6faecbb9421d3bf19e7c85d147f8698da47 diff --git a/Resources/rdf-mappings/Symfony.Cmf.Bundle.RoutingExtraBundle.Document.Route.xml b/Resources/rdf-mappings/Symfony.Cmf.Bundle.RoutingExtraBundle.Document.Route.xml new file mode 100644 index 0000000..26fb36e --- /dev/null +++ b/Resources/rdf-mappings/Symfony.Cmf.Bundle.RoutingExtraBundle.Document.Route.xml @@ -0,0 +1,20 @@ + + dcterms:partOf + + + + + + + + + + + diff --git a/Resources/views/includejsfiles-create.html.twig b/Resources/views/includejsfiles-create.html.twig index 872670b..01fc0a0 100644 --- a/Resources/views/includejsfiles-create.html.twig +++ b/Resources/views/includejsfiles-create.html.twig @@ -20,6 +20,12 @@ var cmfCreateHalloParentElement = 'body'; {% endif %} var cmfCreateHalloPlainTextTypes = {{ cmfCreateHalloPlainTextTypes|raw }}; + var cmfCreateCreateRoutesTypes = {{ cmfCreateCreateRoutesTypes|raw }}; + var cmfCreateLocales = {{ cmfCreateLocales|raw }}; + var cmfCreateContentPrefix = '{{ cmfCreateContentPrefix }}'; + var cmfCreateRoutesPrefix = '{{ cmfCreateRoutesPrefix }}'; + var cmfCreateRouteRdfType = "http://cmf.symfony.com/CmfRoute"; + {% javascripts output="js/create.js" @@ -33,6 +39,7 @@ '@SymfonyCmfCreateBundle/Resources/public/vendor/create/deps/jquery.tagsinput.min.js' '@SymfonyCmfCreateBundle/Resources/public/vendor/create/deps/annotate-min.js' '@SymfonyCmfCreateBundle/Resources/public/vendor/create/examples/create-min.js' + '@SymfonyCmfCreateBundle/Resources/public/js/create-routes-handler.js' %} {% endjavascripts %} @@ -61,4 +68,4 @@ and set up assetic to handle coffee script files '@SymfonyCmfCreateBundle/Resources/public/js/init-vie-hallo.js' '@SymfonyCmfCreateBundle/Resources/public/vendor/hallo/hallo.coffee' '@SymfonyCmfCreateBundle/Resources/public/vendor/hallo/plugins/*.coffee' -#} \ No newline at end of file +#}