From e606d181a408d56e415ee477fcda2746d3823aa5 Mon Sep 17 00:00:00 2001 From: Johannes Wachter Date: Sun, 10 Jul 2016 20:42:15 +0200 Subject: [PATCH 1/3] init article types --- Admin/ArticleAdmin.php | 1 + Admin/ArticleJsConfig.php | 61 ++++++++++++++++ Controller/ArticleController.php | 6 +- Controller/TemplateController.php | 58 +++++++++++++++ Document/ArticleOngrDocument.php | 31 ++++++++ Document/Index/ArticleIndexer.php | 16 +++- Document/Serializer/TypeSubscriber.php | 73 +++++++++++++++++++ Resources/config/routing.xml | 10 +++ Resources/config/services.xml | 13 ++++ .../components/articles/edit/details/main.js | 2 +- .../dist/components/articles/edit/main.js | 2 +- .../dist/components/articles/list/main.js | 2 +- Resources/public/dist/main.js | 2 +- .../components/articles/edit/details/main.js | 8 +- .../js/components/articles/edit/main.js | 26 +++++-- .../js/components/articles/list/main.js | 60 +++++++++++++-- Resources/public/js/main.js | 69 +++++++++++++----- Resources/translations/sulu/backend.de.xlf | 4 + Resources/translations/sulu/backend.en.xlf | 4 + Resources/translations/sulu/backend.fr.xlf | 4 + .../Controller/ArticleControllerTest.php | 64 ++++++++++++++-- .../Controller/TemplateControllerTest.php | 42 +++++++++++ Tests/app/Resources/articles/default.xml | 2 + Tests/app/Resources/articles/simple.xml | 2 + Tests/app/config/routing.yml | 4 + Util/TypeTrait.php | 42 +++++++++++ 26 files changed, 560 insertions(+), 48 deletions(-) create mode 100644 Admin/ArticleJsConfig.php create mode 100644 Controller/TemplateController.php create mode 100644 Document/Serializer/TypeSubscriber.php create mode 100644 Resources/config/routing.xml create mode 100644 Tests/Functional/Controller/TemplateControllerTest.php create mode 100644 Util/TypeTrait.php diff --git a/Admin/ArticleAdmin.php b/Admin/ArticleAdmin.php index 2e0163c90..89ffb8d10 100644 --- a/Admin/ArticleAdmin.php +++ b/Admin/ArticleAdmin.php @@ -22,6 +22,7 @@ */ class ArticleAdmin extends Admin { + const STRUCTURE_TAG_TYPE = 'sulu.type'; const SECURITY_CONTEXT = 'sulu.modules.articles'; /** diff --git a/Admin/ArticleJsConfig.php b/Admin/ArticleJsConfig.php new file mode 100644 index 000000000..2801f31f0 --- /dev/null +++ b/Admin/ArticleJsConfig.php @@ -0,0 +1,61 @@ +structureManager = $structureManager; + } + + /** + * {@inheritdoc} + */ + public function getParameters() + { + $types = []; + foreach ($this->structureManager->getStructures('article') as $structure) { + $type = $this->getType($structure->getStructure()); + if (!array_key_exists($type, $types)) { + $types[$type] = $structure->getKey(); + } + } + + return $types; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'sulu_article.types'; + } +} diff --git a/Controller/ArticleController.php b/Controller/ArticleController.php index 5fe61e6b0..49dc765a7 100644 --- a/Controller/ArticleController.php +++ b/Controller/ArticleController.php @@ -15,7 +15,7 @@ use JMS\Serializer\SerializationContext; use ONGR\ElasticsearchBundle\Service\Manager; use ONGR\ElasticsearchDSL\Query\FuzzyQuery; -use ONGR\ElasticsearchDSL\Query\MatchAllQuery; +use ONGR\ElasticsearchDSL\Query\TermQuery; use ONGR\ElasticsearchDSL\Sort\FieldSort; use Sulu\Bundle\ArticleBundle\Document\ArticleOngrDocument; use Sulu\Bundle\ArticleBundle\Document\Form\ArticleDocumentType; @@ -88,10 +88,10 @@ public function cgetAction(Request $request) foreach ($restHelper->getSearchFields() as $searchField) { $search->addQuery(new FuzzyQuery($searchField, $searchPattern)); } - } else { - $search->addQuery(new MatchAllQuery()); } + $search->addQuery(new TermQuery('type', $request->get('type', 'default'))); + $count = $repository->count($search); if (null !== $restHelper->getSortColumn()) { diff --git a/Controller/TemplateController.php b/Controller/TemplateController.php new file mode 100644 index 000000000..8232ffc84 --- /dev/null +++ b/Controller/TemplateController.php @@ -0,0 +1,58 @@ +get('sulu.content.structure_manager'); + $type = $request->get('type', 'default'); + + $templates = []; + foreach ($structureProvider->getStructures('article') as $structure) { + if ($this->getType($structure->getStructure()) !== $type) { + continue; + } + + $templates[] = [ + 'internal' => $structure->getInternal(), + 'template' => $structure->getKey(), + 'title' => $structure->getLocalizedTitle($this->getUser()->getLocale()), + ]; + } + + return new JsonResponse( + [ + '_embedded' => $templates, + 'total' => count($templates), + ] + ); + } +} diff --git a/Document/ArticleOngrDocument.php b/Document/ArticleOngrDocument.php index 31710e0af..f408ddaa0 100644 --- a/Document/ArticleOngrDocument.php +++ b/Document/ArticleOngrDocument.php @@ -41,6 +41,13 @@ class ArticleOngrDocument */ protected $title; + /** + * @var string + * + * @Property(type="string") + */ + protected $type; + /** * @var string * @@ -149,6 +156,30 @@ public function setTitle($title) return $this; } + /** + * Returns type. + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Set type. + * + * @param string $type + * + * @return self + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + /** * Returns changer. * diff --git a/Document/Index/ArticleIndexer.php b/Document/Index/ArticleIndexer.php index 4bffee78a..0c41193b6 100644 --- a/Document/Index/ArticleIndexer.php +++ b/Document/Index/ArticleIndexer.php @@ -14,13 +14,22 @@ use ONGR\ElasticsearchBundle\Service\Manager; use Sulu\Bundle\ArticleBundle\Document\ArticleDocument; use Sulu\Bundle\ArticleBundle\Document\ArticleOngrDocument; +use Sulu\Bundle\ArticleBundle\Util\TypeTrait; use Sulu\Bundle\SecurityBundle\UserManager\UserManager; +use Sulu\Component\Content\Compat\StructureManagerInterface; /** * Provides methods to index articles. */ class ArticleIndexer implements IndexerInterface { + use TypeTrait; + + /** + * @var StructureManagerInterface + */ + private $structureManager; + /** * @var UserManager */ @@ -32,11 +41,13 @@ class ArticleIndexer implements IndexerInterface private $manager; /** + * @param StructureManagerInterface $structureManager * @param UserManager $userManager * @param Manager $manager */ - public function __construct(UserManager $userManager, Manager $manager) + public function __construct(StructureManagerInterface $structureManager, UserManager $userManager, Manager $manager) { + $this->structureManager = $structureManager; $this->userManager = $userManager; $this->manager = $manager; } @@ -59,11 +70,14 @@ public function index(ArticleDocument $document) $article = new ArticleOngrDocument($document->getUuid()); } + $structure = $this->structureManager->getStructure($document->getStructureType(), 'article'); + $article->setTitle($document->getTitle()); $article->setChanged($document->getChanged()); $article->setCreated($document->getCreated()); $article->setChanger($this->userManager->getFullNameByUserId($document->getChanger())); $article->setCreator($this->userManager->getFullNameByUserId($document->getCreator())); + $article->setType($this->getType($structure->getStructure())); $this->manager->persist($article); } diff --git a/Document/Serializer/TypeSubscriber.php b/Document/Serializer/TypeSubscriber.php new file mode 100644 index 000000000..a44600214 --- /dev/null +++ b/Document/Serializer/TypeSubscriber.php @@ -0,0 +1,73 @@ +structureManager = $structureManager; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return [ + [ + 'event' => Events::POST_SERIALIZE, + 'format' => 'json', + 'method' => 'onPostSerialize', + ], + ]; + } + + /** + * Append type to result. + * + * @param ObjectEvent $event + */ + public function onPostSerialize(ObjectEvent $event) + { + $article = $event->getObject(); + $visitor = $event->getVisitor(); + $context = $event->getContext(); + + if (!($article instanceof ArticleDocument)) { + return; + } + + $structure = $this->structureManager->getStructure($article->getStructureType(), 'article'); + $visitor->addData('type', $context->accept($this->getType($structure->getStructure()))); + } +} diff --git a/Resources/config/routing.xml b/Resources/config/routing.xml new file mode 100644 index 000000000..beec82f77 --- /dev/null +++ b/Resources/config/routing.xml @@ -0,0 +1,10 @@ + + + + + SuluArticleBundle:Template:get + + + diff --git a/Resources/config/services.xml b/Resources/config/services.xml index 3dca8eeb4..346e41e39 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -16,10 +16,17 @@ + + + + + + + @@ -47,5 +54,11 @@ + + + + + + diff --git a/Resources/public/dist/components/articles/edit/details/main.js b/Resources/public/dist/components/articles/edit/details/main.js index a453f78d5..2d8a907ce 100644 --- a/Resources/public/dist/components/articles/edit/details/main.js +++ b/Resources/public/dist/components/articles/edit/details/main.js @@ -1 +1 @@ -define(["underscore","jquery"],function(a,b){"use strict";return{layout:{extendExisting:!0,content:{width:"fixed",rightSpace:!1,leftSpace:!1}},initialize:function(){this.saved=!0,this.render(),this.bindCustomEvents(),this.listenForChange()},bindCustomEvents:function(){this.sandbox.on("sulu.tab.template-change",function(a){this.checkRenderTemplate(a.template)},this),this.sandbox.on("sulu.content.contents.default-template",function(a){this.template=a,this.sandbox.emit("sulu.header.toolbar.item.change","template",a)}.bind(this)),this.sandbox.on("sulu.tab.save",this.save.bind(this))},listenForChange:function(){this.sandbox.dom.on(this.$el,"keyup change",a.debounce(this.setDirty.bind(this),10),".trigger-save-button"),this.sandbox.on("sulu.content.changed",this.setDirty.bind(this))},setDirty:function(){this.saved=!1,this.sandbox.emit("sulu.tab.dirty")},save:function(){if(!this.sandbox.form.validate(this.formId))return this.sandbox.emit("sulu.tab.dirty",!0);var a=this.sandbox.form.getData(this.formId);a.template=this.template,b.ajax(this.options.url(),{method:this.data.id?"PUT":"POST",data:a}).then(function(a){this.data=a,this.sandbox.emit("sulu.tab.saved",a)}.bind(this))},render:function(){this.checkRenderTemplate(this.data.template||null)},checkRenderTemplate:function(a){return a&&this.template===a?this.sandbox.emit("sulu.header.toolbar.item.enable","template",!1):(this.sandbox.emit("sulu.header.toolbar.item.loading","template"),void(""===this.template||this.saved?this.loadFormTemplate(a):this.showRenderTemplateDialog(a)))},showRenderTemplateDialog:function(a){this.sandbox.emit("sulu.overlay.show-warning","sulu.overlay.be-careful","content.template.dialog.content",function(){this.sandbox.emit("sulu.header.toolbar.item.enable","template",!1),this.template&&this.sandbox.emit("sulu.header.toolbar.item.change","template",this.template,!1)}.bind(this),function(){this.loadFormTemplate(a)}.bind(this))},loadFormTemplate:function(a){if(a&&(this.template=a,this.setDirty()),this.formId="#content-form-container",this.$container=this.sandbox.dom.createElement('
'),this.html(this.$container),this.sandbox.form.getObject(this.formId)){var b=this.data;this.data=this.sandbox.form.getData(this.formId),b.id&&(this.data.id=b.id),this.data=this.sandbox.util.extend({},b,this.data)}require([this.getTemplateUrl(a)],function(a){this.renderFormTemplate(a)}.bind(this))},getTemplateUrl:function(a){var b="text!/admin/content/template/form";return b+=a?"/"+a+".html":".html",b+="?type=article&language="+this.options.locale,this.data.id&&(b+="&uuid="+this.data.id),b},renderFormTemplate:function(a){this.sandbox.dom.html(this.formId,this.sandbox.util.template(a,{translate:this.sandbox.translate,content:this.data,options:this.options})),this.createForm(this.data).then(function(){this.changeTemplateDropdownHandler()}.bind(this))},changeTemplateDropdownHandler:function(){this.template&&this.sandbox.emit("sulu.header.toolbar.item.change","template",this.template),this.sandbox.emit("sulu.header.toolbar.item.enable","template",!1)},createForm:function(a){var b=this.sandbox.form.create(this.formId),c=this.sandbox.data.deferred();return b.initialized.then(function(){this.sandbox.form.setData(this.formId,a).then(function(){this.sandbox.start(this.$el,{reset:!0}).then(function(){this.initSortableBlock(),this.bindFormEvents(),c.resolve()}.bind(this))}.bind(this))}.bind(this)),c.promise()},initSortableBlock:function(){var a,b=this.sandbox.dom.find(".sortable",this.$el);b&&b.length>0&&(this.sandbox.dom.sortable(b,"destroy"),a=this.sandbox.dom.sortable(b,{handle:".move",forcePlaceholderSize:!0}),this.sandbox.dom.unbind(a,"sortupdate"),a.bind("sortupdate",function(a){this.sandbox.emit("sulu.content.changed")}.bind(this)))},bindFormEvents:function(){this.sandbox.dom.on(this.formId,"form-remove",function(){this.initSortableBlock(),this.setDirty()}.bind(this)),this.sandbox.dom.on(this.formId,"form-add",function(a,b,c,d){this.createConfiguration(a.currentTarget);var e=this.sandbox.dom.children(this.$find('[data-mapper-property="'+b+'"]')),f=void 0!==d&&e.length>d?e[d]:this.sandbox.dom.last(e);this.sandbox.start(f),this.setDirty(),this.initSortableBlock()}.bind(this)),this.sandbox.dom.on(this.formId,"init-sortable",function(a){this.initSortableBlock()}.bind(this))},loadComponentData:function(){var a=b.Deferred();return a.resolve(this.options.data()),a}}}); \ No newline at end of file +define(["underscore","jquery","config"],function(a,b,c){"use strict";return{layout:{extendExisting:!0,content:{width:"fixed",rightSpace:!1,leftSpace:!1}},initialize:function(){this.saved=!0,this.render(),this.bindCustomEvents(),this.listenForChange()},bindCustomEvents:function(){this.sandbox.on("sulu.tab.template-change",function(a){this.checkRenderTemplate(a.template)},this),this.sandbox.on("sulu.content.contents.default-template",function(a){this.template=a,this.sandbox.emit("sulu.header.toolbar.item.change","template",a)}.bind(this)),this.sandbox.on("sulu.tab.save",this.save.bind(this))},listenForChange:function(){this.sandbox.dom.on(this.$el,"keyup change",a.debounce(this.setDirty.bind(this),10),".trigger-save-button"),this.sandbox.on("sulu.content.changed",this.setDirty.bind(this))},setDirty:function(){this.saved=!1,this.sandbox.emit("sulu.tab.dirty")},save:function(){if(!this.sandbox.form.validate(this.formId))return this.sandbox.emit("sulu.tab.dirty",!0);var a=this.sandbox.form.getData(this.formId);a.template=this.template,b.ajax(this.options.url(),{method:this.data.id?"PUT":"POST",data:a}).then(function(a){this.data=a,this.sandbox.emit("sulu.tab.saved",a)}.bind(this))},render:function(){this.checkRenderTemplate(this.data.template||null)},checkRenderTemplate:function(a){return a&&this.template===a?this.sandbox.emit("sulu.header.toolbar.item.enable","template",!1):(this.sandbox.emit("sulu.header.toolbar.item.loading","template"),void(""===this.template||this.saved?this.loadFormTemplate(a):this.showRenderTemplateDialog(a)))},showRenderTemplateDialog:function(a){this.sandbox.emit("sulu.overlay.show-warning","sulu.overlay.be-careful","content.template.dialog.content",function(){this.sandbox.emit("sulu.header.toolbar.item.enable","template",!1),this.template&&this.sandbox.emit("sulu.header.toolbar.item.change","template",this.template,!1)}.bind(this),function(){this.loadFormTemplate(a)}.bind(this))},loadFormTemplate:function(a){if(a)this.setDirty();else{var b=c.get("sulu_article.types");a=b[this.options.type||this.data.type]}if(this.template=a,this.formId="#content-form-container",this.$container=this.sandbox.dom.createElement('
'),this.html(this.$container),this.sandbox.form.getObject(this.formId)){var d=this.data;this.data=this.sandbox.form.getData(this.formId),d.id&&(this.data.id=d.id),this.data=this.sandbox.util.extend({},d,this.data)}require([this.getTemplateUrl(a)],function(a){this.renderFormTemplate(a)}.bind(this))},getTemplateUrl:function(a){var b="text!/admin/content/template/form";return b+=a?"/"+a+".html":".html",b+="?type=article&language="+this.options.locale,this.data.id&&(b+="&uuid="+this.data.id),b},renderFormTemplate:function(a){this.sandbox.dom.html(this.formId,this.sandbox.util.template(a,{translate:this.sandbox.translate,content:this.data,options:this.options})),this.createForm(this.data).then(function(){this.changeTemplateDropdownHandler()}.bind(this))},changeTemplateDropdownHandler:function(){this.template&&this.sandbox.emit("sulu.header.toolbar.item.change","template",this.template),this.sandbox.emit("sulu.header.toolbar.item.enable","template",!1)},createForm:function(a){var b=this.sandbox.form.create(this.formId),c=this.sandbox.data.deferred();return b.initialized.then(function(){this.sandbox.form.setData(this.formId,a).then(function(){this.sandbox.start(this.$el,{reset:!0}).then(function(){this.initSortableBlock(),this.bindFormEvents(),c.resolve()}.bind(this))}.bind(this))}.bind(this)),c.promise()},initSortableBlock:function(){var a,b=this.sandbox.dom.find(".sortable",this.$el);b&&b.length>0&&(this.sandbox.dom.sortable(b,"destroy"),a=this.sandbox.dom.sortable(b,{handle:".move",forcePlaceholderSize:!0}),this.sandbox.dom.unbind(a,"sortupdate"),a.bind("sortupdate",function(a){this.sandbox.emit("sulu.content.changed")}.bind(this)))},bindFormEvents:function(){this.sandbox.dom.on(this.formId,"form-remove",function(){this.initSortableBlock(),this.setDirty()}.bind(this)),this.sandbox.dom.on(this.formId,"form-add",function(a,b,c,d){this.createConfiguration(a.currentTarget);var e=this.sandbox.dom.children(this.$find('[data-mapper-property="'+b+'"]')),f=void 0!==d&&e.length>d?e[d]:this.sandbox.dom.last(e);this.sandbox.start(f),this.setDirty(),this.initSortableBlock()}.bind(this)),this.sandbox.dom.on(this.formId,"init-sortable",function(a){this.initSortableBlock()}.bind(this))},loadComponentData:function(){var a=b.Deferred();return a.resolve(this.options.data()),a}}}); \ No newline at end of file diff --git a/Resources/public/dist/components/articles/edit/main.js b/Resources/public/dist/components/articles/edit/main.js index 6a9d201eb..7866e5581 100644 --- a/Resources/public/dist/components/articles/edit/main.js +++ b/Resources/public/dist/components/articles/edit/main.js @@ -1 +1 @@ -define(["jquery","underscore"],function(a,b){"use strict";return{defaults:{options:{config:{}},templates:{url:"/admin/api/articles<% if (!!id) { %>/<%= id %><% } %>?locale=<%= locale %>"},translations:{headline:"sulu_article.edit.title"}},header:function(){return{tabs:{url:"/admin/content-navigations?alias=article",options:{data:function(){return this.sandbox.util.deepCopy(this.data)}.bind(this),url:function(){return this.templates.url({id:this.options.id,locale:this.options.locale})}.bind(this),config:this.options.config},componentOptions:{values:this.data}},toolbar:{buttons:{save:{parent:"saveWithOptions"},template:{options:{dropdownOptions:{url:"/admin/content/template?type=article",callback:function(a){this.template=a.template,this.sandbox.emit("sulu.tab.template-change",a)}.bind(this)}}}}}}},initialize:function(){this.saveState="disabled",this.bindCustomEvents()},bindCustomEvents:function(){this.sandbox.on("sulu.header.back",this.toList.bind(this)),this.sandbox.on("sulu.tab.dirty",this.enableSave.bind(this)),this.sandbox.on("sulu.toolbar.save",this.save.bind(this)),this.sandbox.on("sulu.tab.data-changed",this.setData.bind(this)),this.sandbox.on("sulu.header.language-changed",function(a){this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey,a.id),this.toEdit(a.id)}.bind(this))},toEdit:function(a){this.sandbox.emit("sulu.router.navigate","articles/"+(a||this.options.locale)+"/edit:"+this.options.id)},toList:function(){this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale)},save:function(a){this.loadingSave(),this.saveTab().then(function(b){this.afterSave(a,b)}.bind(this))},setData:function(a){this.data=a},saveTab:function(){var b=a.Deferred();return this.sandbox.once("sulu.tab.saved",function(a){this.setData(a),b.resolve(a)}.bind(this)),this.sandbox.emit("sulu.tab.save"),b},enableSave:function(a){(a||"loading"!==this.saveState)&&(this.saveState="enabled",this.sandbox.emit("sulu.header.toolbar.item.enable","save",!1))},disableSave:function(a){(a||"loading"!==this.saveState)&&(this.saveState="disabled",this.sandbox.emit("sulu.header.toolbar.item.disable","save",!0))},loadingSave:function(){this.saveState="loading",this.sandbox.emit("sulu.header.toolbar.item.loading","save")},afterSave:function(a,b){this.disableSave(!0),this.sandbox.emit("sulu.header.saved",b),"back"===a?this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale):"new"===a?this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale+"/add"):this.options.id||this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale+"/edit:"+b.id)},loadComponentData:function(){var c=a.Deferred();return this.options.id?(this.sandbox.util.load(b.template(this.defaults.templates.url,{id:this.options.id,locale:this.options.locale})).done(function(a){c.resolve(a)}),c):(c.resolve({}),c)}}}); \ No newline at end of file +define(["jquery","underscore"],function(a,b){"use strict";return{defaults:{options:{config:{}},templates:{url:"/admin/api/articles<% if (!!id) { %>/<%= id %><% } %>?locale=<%= locale %>"},translations:{headline:"sulu_article.edit.title"}},header:function(){return{tabs:{url:"/admin/content-navigations?alias=article",options:{data:function(){return this.sandbox.util.deepCopy(this.data)}.bind(this),url:function(){return this.templates.url({id:this.options.id,locale:this.options.locale})}.bind(this),config:this.options.config},componentOptions:{values:this.data}},toolbar:{buttons:{save:{parent:"saveWithOptions"},template:{options:{dropdownOptions:{url:"/admin/articles/templates?type="+(this.options.type||this.data.type),callback:function(a){this.template=a.template,this.sandbox.emit("sulu.tab.template-change",a)}.bind(this)}}}}}}},initialize:function(){this.saveState="disabled",this.bindCustomEvents()},bindCustomEvents:function(){this.sandbox.on("sulu.header.back",this.toList.bind(this)),this.sandbox.on("sulu.tab.dirty",this.enableSave.bind(this)),this.sandbox.on("sulu.toolbar.save",this.save.bind(this)),this.sandbox.on("sulu.tab.data-changed",this.setData.bind(this)),this.sandbox.on("sulu.header.language-changed",function(a){this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey,a.id),this.toEdit(a.id)}.bind(this))},toEdit:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(a||this.options.locale)+"/edit:"+(b||this.options.id),!0,!0)},toList:function(){1===this.options.config.types.length?this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale):this.sandbox.emit("sulu.router.navigate","articles:"+(this.options.type||this.data.type)+"/"+this.options.locale)},toAdd:function(){1===this.options.config.types.length?this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale+"/add",!0,!0):this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale+"/add:"+(this.options.type||this.data.type),!0,!0)},save:function(a){this.loadingSave(),this.saveTab().then(function(b){this.afterSave(a,b)}.bind(this))},setData:function(a){this.data=a},saveTab:function(){var b=a.Deferred();return this.sandbox.once("sulu.tab.saved",function(a){this.setData(a),b.resolve(a)}.bind(this)),this.sandbox.emit("sulu.tab.save"),b},enableSave:function(a){(a||"loading"!==this.saveState)&&(this.saveState="enabled",this.sandbox.emit("sulu.header.toolbar.item.enable","save",!1))},disableSave:function(a){(a||"loading"!==this.saveState)&&(this.saveState="disabled",this.sandbox.emit("sulu.header.toolbar.item.disable","save",!0))},loadingSave:function(){this.saveState="loading",this.sandbox.emit("sulu.header.toolbar.item.loading","save")},afterSave:function(a,b){this.disableSave(!0),this.sandbox.emit("sulu.header.saved",b),"back"===a?this.toList():"new"===a?this.toAdd():this.options.id||this.toEdit(this.options.locale,b.id)},loadComponentData:function(){var c=a.Deferred();return this.options.id?(this.sandbox.util.load(b.template(this.defaults.templates.url,{id:this.options.id,locale:this.options.locale})).done(function(a){c.resolve(a)}),c):(c.resolve({}),c)}}}); \ No newline at end of file diff --git a/Resources/public/dist/components/articles/list/main.js b/Resources/public/dist/components/articles/list/main.js index 004ee4471..ea4cd3d53 100644 --- a/Resources/public/dist/components/articles/list/main.js +++ b/Resources/public/dist/components/articles/list/main.js @@ -1 +1 @@ -define(function(){"use strict";var a={options:{config:{}},templates:{list:['
','
','
','
'].join("")},translations:{headline:"sulu_article.list.title"}};return{defaults:a,header:function(){return{title:this.translations.headline,underline:!1,noBack:!0,toolbar:{buttons:{add:{},deleteSelected:{}},languageChanger:{data:this.options.config.languageChanger,preSelected:this.options.locale}}}},layout:{content:{width:"max"}},initialize:function(){this.render(),this.bindCustomEvents()},render:function(){this.$el.html(this.templates.list()),this.sandbox.sulu.initListToolbarAndList.call(this,"article","/admin/api/articles/fields",{el:this.$find(".list-toolbar-container"),instanceName:"articles",template:this.sandbox.sulu.buttons.get({settings:{options:{dropdownItems:[{type:"columnOptions"}]}}})},{el:this.sandbox.dom.find(".datagrid-container"),url:"/admin/api/articles?locale="+this.options.locale,searchInstanceName:"articles",searchFields:["title"],resultKey:"articles",instanceName:"articles",actionCallback:function(a){this.toEdit(a)}.bind(this),viewOptions:{table:{actionIconColumn:"title"}}})},toEdit:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(b||this.options.locale)+"/edit:"+a)},toAdd:function(a){this.sandbox.emit("sulu.router.navigate","articles/"+(a||this.options.locale)+"/add")},toList:function(a){this.sandbox.emit("sulu.router.navigate","articles/"+(a||this.options.locale))},deleteItems:function(a){for(var b=0,c=a.length;b0?"enable":"disable";this.sandbox.emit("sulu.header.toolbar.item."+b,"deleteSelected",!1)}.bind(this)),this.sandbox.on("sulu.toolbar.delete",function(){this.sandbox.emit("husky.datagrid.news.items.get-selected",this.deleteItems.bind(this))}.bind(this)),this.sandbox.on("sulu.header.language-changed",function(a){this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey,a.id),this.toList(a.id)}.bind(this))}}}); \ No newline at end of file +define(["underscore"],function(a){"use strict";var b={options:{config:{}},templates:{list:['
','
','
','
','
'].join("")},translations:{headline:"sulu_article.list.title"}};return{defaults:b,header:function(){var b,c=this.options.config.types,d={icon:"plus-circle",title:"public.add-new"},e=!1;return 1===c.length?d.callback=function(){this.toAdd(c[0])}.bind(this):(d.dropdownItems=a.map(c,function(a){return{title:a,callback:function(){this.toAdd(a)}.bind(this)}}.bind(this)),b=a.map(c,function(a){return{id:a,name:a,key:a}}),e={componentOptions:{callback:this.typeChange.bind(this),preselector:"name",preselect:this.options.type},data:b}),{title:this.translations.headline,underline:!1,noBack:!0,tabs:e,toolbar:{buttons:{addArticle:{options:d},deleteSelected:{}},languageChanger:{data:this.options.config.languageChanger,preSelected:this.options.locale}}}},layout:{content:{width:"max"}},initialize:function(){this.render(),this.bindCustomEvents()},render:function(){this.$el.html(this.templates.list()),this.sandbox.sulu.initListToolbarAndList.call(this,"article","/admin/api/articles/fields",{el:this.$find(".list-toolbar-container"),instanceName:"articles",template:this.sandbox.sulu.buttons.get({settings:{options:{dropdownItems:[{type:"columnOptions"}]}}})},{el:this.sandbox.dom.find(".datagrid-container"),url:"/admin/api/articles?locale="+this.options.locale+"&type="+this.options.type,searchInstanceName:"articles",searchFields:["title"],resultKey:"articles",instanceName:"articles",actionCallback:function(a){this.toEdit(a)}.bind(this),viewOptions:{table:{actionIconColumn:"title"}}})},toEdit:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(b||this.options.locale)+"/edit:"+a)},toAdd:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(b||this.options.locale)+"/add"+(this.options.config.types.length>1?":"+a:""))},toList:function(a){this.sandbox.emit("sulu.router.navigate","articles/"+(a||this.options.locale))},deleteItems:function(a){for(var b=0,c=a.length;b0?"enable":"disable";this.sandbox.emit("sulu.header.toolbar.item."+b,"deleteSelected",!1)}.bind(this)),this.sandbox.on("sulu.toolbar.delete",function(){this.sandbox.emit("husky.datagrid.news.items.get-selected",this.deleteItems.bind(this))}.bind(this)),this.sandbox.on("sulu.header.language-changed",function(a){this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey,a.id),this.toList(a.id)}.bind(this))}}}); \ No newline at end of file diff --git a/Resources/public/dist/main.js b/Resources/public/dist/main.js index 394269c0c..af7438a6c 100644 --- a/Resources/public/dist/main.js +++ b/Resources/public/dist/main.js @@ -1 +1 @@ -require.config({paths:{suluarticle:"../../suluarticle/dist",suluarticlecss:"../../suluarticle/css"}}),define(["underscore","config"],function(a,b){"use strict";return{name:"Sulu Article Bundle",initialize:function(c){c.components.addSource("suluarticle","/bundles/suluarticle/dist/components");var d=Object.keys(b.get("sulu-content").locales),e={defaultLocale:d[0],locales:d,languageChanger:a.map(d,function(a){return{id:a,title:a}}),settingsKey:"articleLanguage"};b.set("sulu_article",e);var f=function(){return c.sandbox.sulu.getUserSetting(e.settingsKey)||e.defaultLocale};c.sandbox.mvc.routes.push({route:"articles",callback:function(){return c.sandbox.emit("sulu.router.navigate","articles/"+f())}}),c.sandbox.mvc.routes.push({route:"articles/:locale",callback:function(a){return'
"}}),c.sandbox.mvc.routes.push({route:"articles/:locale/add",callback:function(a){return'
"}}),c.sandbox.mvc.routes.push({route:"articles/:locale/edit::id",callback:function(a,b){return'
"}})}}}); \ No newline at end of file +require.config({paths:{suluarticle:"../../suluarticle/dist",suluarticlecss:"../../suluarticle/css"}}),define(["underscore","config"],function(a,b){"use strict";return{name:"Sulu Article Bundle",initialize:function(c){c.components.addSource("suluarticle","/bundles/suluarticle/dist/components");var d=Object.keys(b.get("sulu-content").locales),e={defaultLocale:d[0],locales:d,languageChanger:a.map(d,function(a){return{id:a,title:a}}),settingsKey:"articleLanguage",types:Object.keys(b.get("sulu_article.types"))};b.set("sulu_article",e);var f=function(){return c.sandbox.sulu.getUserSetting(e.settingsKey)||e.defaultLocale};1===e.types.length?(c.sandbox.mvc.routes.push({route:"articles",callback:function(){return c.sandbox.emit("sulu.router.navigate","articles/"+f())}}),c.sandbox.mvc.routes.push({route:"articles/:locale",callback:function(a){return'
"}}),c.sandbox.mvc.routes.push({route:"articles/:locale/add",callback:function(a,b){return'
"}})):(c.sandbox.mvc.routes.push({route:"articles",callback:function(){return c.sandbox.emit("sulu.router.navigate","articles:"+e.types[0]+"/"+f())}}),c.sandbox.mvc.routes.push({route:"articles::type",callback:function(a){return c.sandbox.emit("sulu.router.navigate","articles:"+a+"/"+f())}}),c.sandbox.mvc.routes.push({route:"articles::type/:locale",callback:function(a,b){return'
'}}),c.sandbox.mvc.routes.push({route:"articles/:locale/add::type",callback:function(a,b){return'
'}})),c.sandbox.mvc.routes.push({route:"articles/:locale/edit::id",callback:function(a,b){return'
"}})}}}); \ No newline at end of file diff --git a/Resources/public/js/components/articles/edit/details/main.js b/Resources/public/js/components/articles/edit/details/main.js index 90e6026ca..7c4527978 100644 --- a/Resources/public/js/components/articles/edit/details/main.js +++ b/Resources/public/js/components/articles/edit/details/main.js @@ -7,7 +7,7 @@ * with this source code in the file LICENSE. */ -define(['underscore', 'jquery'], function(_, $) { +define(['underscore', 'jquery', 'config'], function(_, $, Config) { 'use strict'; @@ -108,9 +108,13 @@ define(['underscore', 'jquery'], function(_, $) { loadFormTemplate: function(template) { if (!!template) { - this.template = template; this.setDirty(); + } else { + var types = Config.get('sulu_article.types'); + template = types[(this.options.type || this.data.type)]; } + + this.template = template; this.formId = '#content-form-container'; this.$container = this.sandbox.dom.createElement('
'); this.html(this.$container); diff --git a/Resources/public/js/components/articles/edit/main.js b/Resources/public/js/components/articles/edit/main.js index 68ba86c82..973ac5d92 100644 --- a/Resources/public/js/components/articles/edit/main.js +++ b/Resources/public/js/components/articles/edit/main.js @@ -53,7 +53,7 @@ define(['jquery', 'underscore'], function($, _) { template: { options: { dropdownOptions: { - url: '/admin/content/template?type=article', + url: '/admin/articles/templates?type=' + (this.options.type || this.data.type), callback: function(item) { this.template = item.template; this.sandbox.emit('sulu.tab.template-change', item); @@ -84,12 +84,24 @@ define(['jquery', 'underscore'], function($, _) { }.bind(this)); }, - toEdit: function(locale) { - this.sandbox.emit('sulu.router.navigate', 'articles/' + (locale || this.options.locale) + '/edit:' + this.options.id); + toEdit: function(locale, id) { + this.sandbox.emit('sulu.router.navigate', 'articles/' + (locale || this.options.locale) + '/edit:' + (id || this.options.id), true, true); }, toList: function() { - this.sandbox.emit('sulu.router.navigate', 'articles/' + this.options.locale); + if (this.options.config.types.length === 1) { + this.sandbox.emit('sulu.router.navigate', 'articles/' + this.options.locale); + } else { + this.sandbox.emit('sulu.router.navigate', 'articles:' + (this.options.type || this.data.type) + '/' + this.options.locale); + } + }, + + toAdd: function() { + if (this.options.config.types.length === 1) { + this.sandbox.emit('sulu.router.navigate', 'articles/' + this.options.locale + '/add', true, true); + } else { + this.sandbox.emit('sulu.router.navigate', 'articles/' + this.options.locale + '/add:' + (this.options.type || this.data.type), true, true); + } }, save: function(action) { @@ -146,11 +158,11 @@ define(['jquery', 'underscore'], function($, _) { this.sandbox.emit('sulu.header.saved', data); if (action === 'back') { - this.sandbox.emit('sulu.router.navigate', 'articles/' + this.options.locale); + this.toList(); } else if (action === 'new') { - this.sandbox.emit('sulu.router.navigate', 'articles/' + this.options.locale + '/add'); + this.toAdd(); } else if (!this.options.id) { - this.sandbox.emit('sulu.router.navigate', 'articles/' + this.options.locale + '/edit:' + data.id); + this.toEdit(this.options.locale, data.id); } }, diff --git a/Resources/public/js/components/articles/list/main.js b/Resources/public/js/components/articles/list/main.js index 2cc53ac78..36e555dba 100644 --- a/Resources/public/js/components/articles/list/main.js +++ b/Resources/public/js/components/articles/list/main.js @@ -7,7 +7,7 @@ * with this source code in the file LICENSE. */ -define(function() { +define(['underscore'], function(_) { 'use strict'; @@ -18,6 +18,7 @@ define(function() { templates: { list: [ + '
', '
', '
', '
', @@ -35,15 +36,57 @@ define(function() { defaults: defaults, header: function() { + var types = this.options.config.types, + button = { + icon: 'plus-circle', + title: 'public.add-new' + }, + tabs = false, + items; + + if (types.length === 1) { + button.callback = function() { + this.toAdd(types[0]); + }.bind(this); + } else { + button.dropdownItems = _.map(types, function(item) { + return { + title: item, + callback: function() { + this.toAdd(item); + }.bind(this) + }; + }.bind(this)); + + items = _.map(types, function(type) { + return { + id: type, + name: type, + key: type + }; + }); + + tabs = { + componentOptions: { + callback: this.typeChange.bind(this), + preselector: 'name', + preselect: this.options.type + }, + data: items + }; + } + return { title: this.translations.headline, underline: false, noBack: true, + tabs: tabs, + toolbar: { buttons: { - add: {}, + addArticle: {options: button}, deleteSelected: {} }, @@ -90,7 +133,7 @@ define(function() { }, { el: this.sandbox.dom.find('.datagrid-container'), - url: '/admin/api/articles?locale=' + this.options.locale, + url: '/admin/api/articles?locale=' + this.options.locale + '&type=' + this.options.type, searchInstanceName: 'articles', searchFields: ['title'], resultKey: 'articles', @@ -111,8 +154,8 @@ define(function() { this.sandbox.emit('sulu.router.navigate', 'articles/' + (locale || this.options.locale) + '/edit:' + id); }, - toAdd: function(locale) { - this.sandbox.emit('sulu.router.navigate', 'articles/' + (locale || this.options.locale) + '/add'); + toAdd: function(type, locale) { + this.sandbox.emit('sulu.router.navigate', 'articles/' + (locale || this.options.locale) + '/add' + (this.options.config.types.length > 1 ? (':' + type) : '')); }, toList: function(locale) { @@ -131,9 +174,12 @@ define(function() { }.bind(this)); }, - bindCustomEvents: function() { - this.sandbox.on('sulu.toolbar.add', this.toAdd.bind(this)); + typeChange: function(item) { + this.sandbox.emit('husky.datagrid.articles.url.update', {type: item.id}); + this.sandbox.emit('sulu.router.navigate', 'articles:' + item.id + '/' + this.options.locale, false, false); + }, + bindCustomEvents: function() { this.sandbox.on('husky.datagrid.news.number.selections', function(number) { var postfix = number > 0 ? 'enable' : 'disable'; this.sandbox.emit('sulu.header.toolbar.item.' + postfix, 'deleteSelected', false); diff --git a/Resources/public/js/main.js b/Resources/public/js/main.js index e9c98420f..f8f7369d7 100644 --- a/Resources/public/js/main.js +++ b/Resources/public/js/main.js @@ -33,7 +33,8 @@ define(['underscore', 'config'], function(_, Config) { languageChanger: _.map(locales, function(locale) { return {id: locale, title: locale}; }), - settingsKey: 'articleLanguage' + settingsKey: 'articleLanguage', + types: Object.keys(Config.get('sulu_article.types')) }; Config.set('sulu_article', config); @@ -46,26 +47,56 @@ define(['underscore', 'config'], function(_, Config) { return app.sandbox.sulu.getUserSetting(config.settingsKey) || config.defaultLocale; }; - app.sandbox.mvc.routes.push({ - route: 'articles', - callback: function() { - return app.sandbox.emit('sulu.router.navigate', 'articles/' + getLocale()); - } - }); + if (config.types.length === 1) { + app.sandbox.mvc.routes.push({ + route: 'articles', + callback: function() { + return app.sandbox.emit('sulu.router.navigate', 'articles/' + getLocale()); + } + }); - app.sandbox.mvc.routes.push({ - route: 'articles/:locale', - callback: function(locale) { - return '
'; - } - }); + app.sandbox.mvc.routes.push({ + route: 'articles/:locale', + callback: function(locale) { + return '
'; + } + }); - app.sandbox.mvc.routes.push({ - route: 'articles/:locale/add', - callback: function(locale) { - return '
'; - } - }); + app.sandbox.mvc.routes.push({ + route: 'articles/:locale/add', + callback: function(locale, type) { + return '
'; + } + }); + } else { + app.sandbox.mvc.routes.push({ + route: 'articles', + callback: function() { + return app.sandbox.emit('sulu.router.navigate', 'articles:' + config.types[0] + '/' + getLocale()); + } + }); + + app.sandbox.mvc.routes.push({ + route: 'articles::type', + callback: function(type) { + return app.sandbox.emit('sulu.router.navigate', 'articles:' + type + '/' + getLocale()); + } + }); + + app.sandbox.mvc.routes.push({ + route: 'articles::type/:locale', + callback: function(type, locale) { + return '
'; + } + }); + + app.sandbox.mvc.routes.push({ + route: 'articles/:locale/add::type', + callback: function(locale, type) { + return '
'; + } + }); + } app.sandbox.mvc.routes.push({ route: 'articles/:locale/edit::id', diff --git a/Resources/translations/sulu/backend.de.xlf b/Resources/translations/sulu/backend.de.xlf index 9460105b2..1f9fdf0d0 100644 --- a/Resources/translations/sulu/backend.de.xlf +++ b/Resources/translations/sulu/backend.de.xlf @@ -18,6 +18,10 @@ sulu_article.list.changer Bearbeitet von + + sulu_article.list.type + Typ + diff --git a/Resources/translations/sulu/backend.en.xlf b/Resources/translations/sulu/backend.en.xlf index 4e8fffe8a..bba04b25b 100644 --- a/Resources/translations/sulu/backend.en.xlf +++ b/Resources/translations/sulu/backend.en.xlf @@ -18,6 +18,10 @@ sulu_article.list.changer Changed by + + sulu_article.list.type + Type + diff --git a/Resources/translations/sulu/backend.fr.xlf b/Resources/translations/sulu/backend.fr.xlf index 9d627d4e2..dfbe9e314 100644 --- a/Resources/translations/sulu/backend.fr.xlf +++ b/Resources/translations/sulu/backend.fr.xlf @@ -18,6 +18,10 @@ sulu_article.list.changer Modifié by + + sulu_article.list.type + Type + diff --git a/Tests/Functional/Controller/ArticleControllerTest.php b/Tests/Functional/Controller/ArticleControllerTest.php index 42abb78e0..4d4940ac5 100644 --- a/Tests/Functional/Controller/ArticleControllerTest.php +++ b/Tests/Functional/Controller/ArticleControllerTest.php @@ -16,6 +16,8 @@ class ArticleControllerTest extends SuluTestCase { + private static $typeMap = ['default' => 'blog', 'simple' => 'video']; + /** * {@inheritdoc} */ @@ -26,15 +28,17 @@ public function setUp() $this->initPhpcr(); } - public function testPost($title = 'Test-Article') + public function testPost($title = 'Test-Article', $template = 'default') { $client = $this->createAuthenticatedClient(); - $client->request('POST', '/api/articles?locale=de', ['title' => $title, 'template' => 'default']); + $client->request('POST', '/api/articles?locale=de', ['title' => $title, 'template' => $template]); $this->assertHttpStatusCode(200, $client->getResponse()); $response = json_decode($client->getResponse()->getContent(), true); $this->assertEquals($title, $response['title']); + $this->assertEquals(self::$typeMap[$template], $response['type']); + $this->assertEquals($template, $response['template']); return $response; } @@ -86,6 +90,8 @@ public function testPutDifferentTemplate($title = 'Sulu', $description = 'Sulu i $response = json_decode($client->getResponse()->getContent(), true); $this->assertNotEquals($article['title'], $response['title']); $this->assertEquals($title, $response['title']); + $this->assertEquals('simple', $response['template']); + $this->assertEquals(self::$typeMap['simple'], $response['type']); $this->assertEquals($description, $response['description']); } @@ -117,7 +123,7 @@ public function testCGet() $this->flush(); $client = $this->createAuthenticatedClient(); - $client->request('GET', '/api/articles?locale=de'); + $client->request('GET', '/api/articles?locale=de&type=blog'); $this->assertHttpStatusCode(200, $client->getResponse()); @@ -147,7 +153,7 @@ public function testCGetSearch() $this->flush(); $client = $this->createAuthenticatedClient(); - $client->request('GET', '/api/articles?locale=de&searchFields=title&search=awesome'); + $client->request('GET', '/api/articles?locale=de&searchFields=title&search=awesome&type=blog'); $this->assertHttpStatusCode(200, $client->getResponse()); @@ -169,7 +175,7 @@ public function testCGetSort() $this->flush(); $client = $this->createAuthenticatedClient(); - $client->request('GET', '/api/articles?locale=de&sortBy=title&sortOrder=desc'); + $client->request('GET', '/api/articles?locale=de&sortBy=title&sortOrder=desc&type=blog'); $this->assertHttpStatusCode(200, $client->getResponse()); @@ -183,6 +189,54 @@ public function testCGetSort() $this->assertEquals($article1['title'], $response['_embedded']['articles'][1]['title']); } + public function testCGetTypes() + { + $this->purgeIndex(); + + $article1 = $this->testPost('Sulu'); + $this->flush(); + $article2 = $this->testPost('Sulu is awesome', 'simple'); + $this->flush(); + + $client = $this->createAuthenticatedClient(); + $client->request('GET', '/api/articles?locale=de&type=blog'); + + $this->assertHttpStatusCode(200, $client->getResponse()); + + $response = json_decode($client->getResponse()->getContent(), true); + + $this->assertEquals(1, $response['total']); + $this->assertCount(1, $response['_embedded']['articles']); + + $items = array_map( + function ($item) { + return [$item['id'], $item['title']]; + }, + $response['_embedded']['articles'] + ); + + $this->assertContains([$article1['id'], $article1['title']], $items); + + $client = $this->createAuthenticatedClient(); + $client->request('GET', '/api/articles?locale=de&type=video'); + + $this->assertHttpStatusCode(200, $client->getResponse()); + + $response = json_decode($client->getResponse()->getContent(), true); + + $this->assertEquals(1, $response['total']); + $this->assertCount(1, $response['_embedded']['articles']); + + $items = array_map( + function ($item) { + return [$item['id'], $item['title']]; + }, + $response['_embedded']['articles'] + ); + + $this->assertContains([$article2['id'], $article2['title']], $items); + } + private function purgeIndex() { /** @var IndexerInterface $indexer */ diff --git a/Tests/Functional/Controller/TemplateControllerTest.php b/Tests/Functional/Controller/TemplateControllerTest.php new file mode 100644 index 000000000..d0de94521 --- /dev/null +++ b/Tests/Functional/Controller/TemplateControllerTest.php @@ -0,0 +1,42 @@ +createAuthenticatedClient(); + $client->request('GET', '/articles/templates?type=blog'); + + $this->assertHttpStatusCode(200, $client->getResponse()); + + $response = json_decode($client->getResponse()->getContent(), true); + $this->assertEquals(1, $response['total']); + $this->assertCount(1, $response['_embedded']); + $this->assertEquals('default', $response['_embedded'][0]['template']); + $this->assertEquals('Default', $response['_embedded'][0]['title']); + + $client = $this->createAuthenticatedClient(); + $client->request('GET', '/articles/templates?type=video'); + + $this->assertHttpStatusCode(200, $client->getResponse()); + + $response = json_decode($client->getResponse()->getContent(), true); + $this->assertEquals(1, $response['total']); + $this->assertCount(1, $response['_embedded']); + $this->assertEquals('simple', $response['_embedded'][0]['template']); + $this->assertEquals('Simple', $response['_embedded'][0]['title']); + } +} diff --git a/Tests/app/Resources/articles/default.xml b/Tests/app/Resources/articles/default.xml index 6ffe911a0..4890cab45 100644 --- a/Tests/app/Resources/articles/default.xml +++ b/Tests/app/Resources/articles/default.xml @@ -9,6 +9,8 @@ SuluWebsiteBundle:Default:index 2400 + + diff --git a/Tests/app/Resources/articles/simple.xml b/Tests/app/Resources/articles/simple.xml index c339691d8..3f66a2fca 100644 --- a/Tests/app/Resources/articles/simple.xml +++ b/Tests/app/Resources/articles/simple.xml @@ -9,6 +9,8 @@ SuluWebsiteBundle:Default:index 2400 + + diff --git a/Tests/app/config/routing.yml b/Tests/app/config/routing.yml index 4221a0001..c4a4ed142 100644 --- a/Tests/app/config/routing.yml +++ b/Tests/app/config/routing.yml @@ -2,3 +2,7 @@ sulu_content_api: type: rest resource: "@SuluArticleBundle/Resources/config/routing_api.xml" prefix: /api + +sulu_content: + resource: "@SuluArticleBundle/Resources/config/routing.xml" + prefix: /articles diff --git a/Util/TypeTrait.php b/Util/TypeTrait.php new file mode 100644 index 000000000..b5be9fa7a --- /dev/null +++ b/Util/TypeTrait.php @@ -0,0 +1,42 @@ +hasTag(ArticleAdmin::STRUCTURE_TAG_TYPE)) { + return 'default'; + } + + $tag = $metadata->getTag(ArticleAdmin::STRUCTURE_TAG_TYPE); + if (!array_key_exists('type', $tag['attributes'])) { + return 'default'; + } + + return $tag['attributes']['type']; + } +} From 9d5ce92afe6c343263346a5e9ccdd63e2cb03fb0 Mon Sep 17 00:00:00 2001 From: Johannes Wachter Date: Mon, 11 Jul 2016 10:56:48 +0200 Subject: [PATCH 2/3] added route generation --- Document/ArticleDocument.php | 76 +++++++++++++- Document/Subscriber/ArticleSubscriber.php | 99 +++++++++++++++++-- .../serializer/Document.ArticleDocument.xml | 1 + Resources/config/services.xml | 10 ++ Routing/ArticleRouteDefaultProvider.php | 74 ++++++++++++++ Tests/app/config/config.yml | 5 + 6 files changed, 258 insertions(+), 7 deletions(-) create mode 100644 Routing/ArticleRouteDefaultProvider.php diff --git a/Document/ArticleDocument.php b/Document/ArticleDocument.php index b841c3fa6..bb10ea8e1 100644 --- a/Document/ArticleDocument.php +++ b/Document/ArticleDocument.php @@ -12,6 +12,8 @@ namespace Sulu\Bundle\ArticleBundle\Document; use Sulu\Bundle\ArticleBundle\Document\Behavior\DateShardingBehavior; +use Sulu\Bundle\RouteBundle\Model\RoutableInterface; +use Sulu\Bundle\RouteBundle\Model\RouteInterface; use Sulu\Component\Content\Document\Behavior\LocalizedAuditableBehavior; use Sulu\Component\Content\Document\Behavior\LocalizedStructureBehavior; use Sulu\Component\Content\Document\Behavior\StructureBehavior; @@ -35,7 +37,8 @@ class ArticleDocument implements StructureBehavior, LocalizedStructureBehavior, LocalizedAuditableBehavior, - DateShardingBehavior + DateShardingBehavior, + RoutableInterface { /** * @var string @@ -62,6 +65,16 @@ class ArticleDocument implements */ private $title; + /** + * @var RouteInterface + */ + private $route; + + /** + * @var string + */ + private $routePath; + /** * @var string */ @@ -115,6 +128,16 @@ public function getUuid() return $this->uuid; } + /** + * Set uuid. + * + * @param string $uuid + */ + public function setUuid($uuid) + { + $this->uuid = $uuid; + } + /** * {@inheritdoc} */ @@ -163,6 +186,47 @@ public function setTitle($title) $this->title = $title; } + /** + * Returns route. + * + * @return RouteInterface + */ + public function getRoute() + { + return $this->route; + } + + /** + * Set route. + * + * @param RouteInterface $route + */ + public function setRoute(RouteInterface $route) + { + $this->route = $route; + $this->routePath = $route->getPath(); + } + + /** + * Returns route-path. + * + * @return string + */ + public function getRoutePath() + { + return $this->routePath; + } + + /** + * Set route-path. + * + * @param string $routePath + */ + public function setRoutePath($routePath) + { + $this->routePath = $routePath; + } + /** * {@inheritdoc} */ @@ -250,4 +314,14 @@ public function getChanged() { return $this->changed; } + + /** + * Returns identifier. + * + * @return mixed + */ + public function getId() + { + return $this->getUuid(); + } } diff --git a/Document/Subscriber/ArticleSubscriber.php b/Document/Subscriber/ArticleSubscriber.php index abdfc06b9..f6443ace4 100644 --- a/Document/Subscriber/ArticleSubscriber.php +++ b/Document/Subscriber/ArticleSubscriber.php @@ -11,15 +11,20 @@ namespace Sulu\Bundle\ArticleBundle\Document\Subscriber; +use Doctrine\ORM\EntityManagerInterface; use Sulu\Bundle\ArticleBundle\Document\ArticleDocument; use Sulu\Bundle\ArticleBundle\Document\Index\IndexerInterface; +use Sulu\Bundle\RouteBundle\Entity\RouteRepositoryInterface; +use Sulu\Bundle\RouteBundle\Manager\RouteManagerInterface; +use Sulu\Component\DocumentManager\Event\HydrateEvent; +use Sulu\Component\DocumentManager\Event\MetadataLoadEvent; use Sulu\Component\DocumentManager\Event\PersistEvent; use Sulu\Component\DocumentManager\Event\RemoveEvent; use Sulu\Component\DocumentManager\Events; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** - * Indexes article on persist and removes it from index on delete. + * Indexes article and generate route on persist and removes it from index and routing on delete. */ class ArticleSubscriber implements EventSubscriberInterface { @@ -28,12 +33,37 @@ class ArticleSubscriber implements EventSubscriberInterface */ private $indexer; + /** + * @var RouteManagerInterface + */ + private $routeManager; + + /** + * @var RouteRepositoryInterface + */ + private $routeRepository; + + /** + * @var EntityManagerInterface + */ + private $entityManager; + /** * @param IndexerInterface $indexer + * @param RouteManagerInterface $routeManager + * @param RouteRepositoryInterface $routeRepository + * @param EntityManagerInterface $entityManager */ - public function __construct(IndexerInterface $indexer) - { + public function __construct( + IndexerInterface $indexer, + RouteManagerInterface $routeManager, + RouteRepositoryInterface $routeRepository, + EntityManagerInterface $entityManager + ) { $this->indexer = $indexer; + $this->routeManager = $routeManager; + $this->routeRepository = $routeRepository; + $this->entityManager = $entityManager; } /** @@ -42,17 +72,53 @@ public function __construct(IndexerInterface $indexer) public static function getSubscribedEvents() { return [ - Events::PERSIST => [['handlePersist', -500]], + Events::HYDRATE => [['handleHydrate', -500]], + Events::PERSIST => [['handleRoute', 0], ['handleIndex', -500]], Events::REMOVE => [['handleRemove', -500]], + Events::METADATA_LOAD => 'handleMetadataLoad', ]; } /** - * Indexes article-document. + * Load route for article-document. + * + * @param HydrateEvent $event + */ + public function handleHydrate(HydrateEvent $event) + { + $document = $event->getDocument(); + if (!$document instanceof ArticleDocument || null === $document->getRoutePath()) { + return; + } + + $document->setRoute($this->routeRepository->findByPath($document->getRoutePath(), $event->getLocale())); + } + + /** + * Generate route for article-document. * * @param PersistEvent $event */ - public function handlePersist(PersistEvent $event) + public function handleRoute(PersistEvent $event) + { + $document = $event->getDocument(); + if (!$document instanceof ArticleDocument || null !== $document->getRoutePath()) { + return; + } + + $document->setUuid($event->getNode()->getIdentifier()); + + $route = $this->routeManager->create($document); + $this->entityManager->persist($route); + $this->entityManager->flush(); + } + + /** + * Indexes for article-document. + * + * @param PersistEvent $event + */ + public function handleIndex(PersistEvent $event) { $document = $event->getDocument(); if (!$document instanceof ArticleDocument) { @@ -78,4 +144,25 @@ public function handleRemove(RemoveEvent $event) $this->indexer->remove($document); $this->indexer->flush(); } + + /** + * Add route to metadata. + * + * @param MetadataLoadEvent $event + */ + public function handleMetadataLoad(MetadataLoadEvent $event) + { + if ($event->getMetadata()->getClass() !== ArticleDocument::class) { + return; + } + + $metadata = $event->getMetadata(); + $metadata->addFieldMapping( + 'routePath', + [ + 'encoding' => 'system_localized', + 'property' => 'routePath', + ] + ); + } } diff --git a/Resources/config/serializer/Document.ArticleDocument.xml b/Resources/config/serializer/Document.ArticleDocument.xml index 15bf3317f..7c9cf9acc 100644 --- a/Resources/config/serializer/Document.ArticleDocument.xml +++ b/Resources/config/serializer/Document.ArticleDocument.xml @@ -12,6 +12,7 @@ + diff --git a/Resources/config/services.xml b/Resources/config/services.xml index 346e41e39..007464e8b 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -42,6 +42,9 @@ + + + @@ -60,5 +63,12 @@ + + + + + + diff --git a/Routing/ArticleRouteDefaultProvider.php b/Routing/ArticleRouteDefaultProvider.php new file mode 100644 index 000000000..961888c66 --- /dev/null +++ b/Routing/ArticleRouteDefaultProvider.php @@ -0,0 +1,74 @@ +documentManager = $documentManager; + $this->structureMetadataFactory = $structureMetadataFactory; + } + + /** + * {@inheritdoc} + * + * @param ArticleDocument $object + */ + public function getByEntity($entityClass, $id, $locale, $object = null) + { + if (!$object) { + $object = $this->documentManager->find($id, $locale); + } + + $metadata = $this->structureMetadataFactory->getStructureMetadata('article', $object->getStructureType()); + + return [ + 'object' => $object, + 'view' => $metadata->view, + '_cacheLifetime' => $metadata->cacheLifetime, + '_controller' => $metadata->controller, + ]; + } + + /** + * {@inheritdoc} + */ + public function supports($entityClass) + { + return $entityClass === ArticleDocument::class; + } +} diff --git a/Tests/app/config/config.yml b/Tests/app/config/config.yml index 869d62880..464dde015 100644 --- a/Tests/app/config/config.yml +++ b/Tests/app/config/config.yml @@ -17,3 +17,8 @@ ongr_elasticsearch: connection: default mappings: - SuluArticleBundle +# Sulu Routing +sulu_route: + mappings: + Sulu\Bundle\ArticleBundle\Document\ArticleDocument: + route_schema: /articles/{object.getTitle()} From 2af6002c82554ee94790b6b83f5417b31b94a839 Mon Sep 17 00:00:00 2001 From: Johannes Wachter Date: Mon, 11 Jul 2016 12:25:05 +0200 Subject: [PATCH 3/3] fixed #3 added delete button on list and edit-form --- Controller/ArticleController.php | 38 ++++++++++++++++ .../dist/components/articles/edit/main.js | 2 +- .../dist/components/articles/list/main.js | 2 +- .../js/components/articles/edit/main.js | 45 ++++++++++++------- .../js/components/articles/list/main.js | 16 +++---- .../Controller/ArticleControllerTest.php | 42 +++++++++++++++++ 6 files changed, 117 insertions(+), 28 deletions(-) diff --git a/Controller/ArticleController.php b/Controller/ArticleController.php index 49dc765a7..47cbc6a73 100644 --- a/Controller/ArticleController.php +++ b/Controller/ArticleController.php @@ -205,6 +205,44 @@ public function putAction(Request $request, $uuid) ); } + /** + * Deletes multiple documents. + * + * @param Request $request + * + * @return Response + */ + public function cdeleteAction(Request $request) + { + $ids = array_filter(explode(',', $request->get('ids', ''))); + + $documentManager = $this->getDocumentManager(); + foreach ($ids as $id) { + $document = $documentManager->find($id); + $documentManager->remove($document); + } + $documentManager->flush(); + + return $this->handleView($this->view(null)); + } + + /** + * Deletes multiple documents. + * + * @param string $id + * + * @return Response + */ + public function deleteAction($id) + { + $documentManager = $this->getDocumentManager(); + $document = $documentManager->find($id); + $documentManager->remove($document); + $documentManager->flush(); + + return $this->handleView($this->view(null)); + } + /** * Persists the document using the given information. * diff --git a/Resources/public/dist/components/articles/edit/main.js b/Resources/public/dist/components/articles/edit/main.js index 7866e5581..93f67cce3 100644 --- a/Resources/public/dist/components/articles/edit/main.js +++ b/Resources/public/dist/components/articles/edit/main.js @@ -1 +1 @@ -define(["jquery","underscore"],function(a,b){"use strict";return{defaults:{options:{config:{}},templates:{url:"/admin/api/articles<% if (!!id) { %>/<%= id %><% } %>?locale=<%= locale %>"},translations:{headline:"sulu_article.edit.title"}},header:function(){return{tabs:{url:"/admin/content-navigations?alias=article",options:{data:function(){return this.sandbox.util.deepCopy(this.data)}.bind(this),url:function(){return this.templates.url({id:this.options.id,locale:this.options.locale})}.bind(this),config:this.options.config},componentOptions:{values:this.data}},toolbar:{buttons:{save:{parent:"saveWithOptions"},template:{options:{dropdownOptions:{url:"/admin/articles/templates?type="+(this.options.type||this.data.type),callback:function(a){this.template=a.template,this.sandbox.emit("sulu.tab.template-change",a)}.bind(this)}}}}}}},initialize:function(){this.saveState="disabled",this.bindCustomEvents()},bindCustomEvents:function(){this.sandbox.on("sulu.header.back",this.toList.bind(this)),this.sandbox.on("sulu.tab.dirty",this.enableSave.bind(this)),this.sandbox.on("sulu.toolbar.save",this.save.bind(this)),this.sandbox.on("sulu.tab.data-changed",this.setData.bind(this)),this.sandbox.on("sulu.header.language-changed",function(a){this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey,a.id),this.toEdit(a.id)}.bind(this))},toEdit:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(a||this.options.locale)+"/edit:"+(b||this.options.id),!0,!0)},toList:function(){1===this.options.config.types.length?this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale):this.sandbox.emit("sulu.router.navigate","articles:"+(this.options.type||this.data.type)+"/"+this.options.locale)},toAdd:function(){1===this.options.config.types.length?this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale+"/add",!0,!0):this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale+"/add:"+(this.options.type||this.data.type),!0,!0)},save:function(a){this.loadingSave(),this.saveTab().then(function(b){this.afterSave(a,b)}.bind(this))},setData:function(a){this.data=a},saveTab:function(){var b=a.Deferred();return this.sandbox.once("sulu.tab.saved",function(a){this.setData(a),b.resolve(a)}.bind(this)),this.sandbox.emit("sulu.tab.save"),b},enableSave:function(a){(a||"loading"!==this.saveState)&&(this.saveState="enabled",this.sandbox.emit("sulu.header.toolbar.item.enable","save",!1))},disableSave:function(a){(a||"loading"!==this.saveState)&&(this.saveState="disabled",this.sandbox.emit("sulu.header.toolbar.item.disable","save",!0))},loadingSave:function(){this.saveState="loading",this.sandbox.emit("sulu.header.toolbar.item.loading","save")},afterSave:function(a,b){this.disableSave(!0),this.sandbox.emit("sulu.header.saved",b),"back"===a?this.toList():"new"===a?this.toAdd():this.options.id||this.toEdit(this.options.locale,b.id)},loadComponentData:function(){var c=a.Deferred();return this.options.id?(this.sandbox.util.load(b.template(this.defaults.templates.url,{id:this.options.id,locale:this.options.locale})).done(function(a){c.resolve(a)}),c):(c.resolve({}),c)}}}); \ No newline at end of file +define(["jquery","underscore"],function(a,b){"use strict";return{defaults:{options:{config:{}},templates:{url:"/admin/api/articles<% if (!!id) { %>/<%= id %><% } %>?locale=<%= locale %>"},translations:{headline:"sulu_article.edit.title"}},header:function(){var a={save:{parent:"saveWithOptions"},template:{options:{dropdownOptions:{url:"/admin/articles/templates?type="+(this.options.type||this.data.type),callback:function(a){this.template=a.template,this.sandbox.emit("sulu.tab.template-change",a)}.bind(this)}}}};return this.options.id&&(a["delete"]={}),{tabs:{url:"/admin/content-navigations?alias=article",options:{data:function(){return this.sandbox.util.deepCopy(this.data)}.bind(this),url:function(){return this.templates.url({id:this.options.id,locale:this.options.locale})}.bind(this),config:this.options.config},componentOptions:{values:this.data}},toolbar:{buttons:a}}},initialize:function(){this.saveState="disabled",this.bindCustomEvents()},bindCustomEvents:function(){this.sandbox.on("sulu.header.back",this.toList.bind(this)),this.sandbox.on("sulu.tab.dirty",this.enableSave.bind(this)),this.sandbox.on("sulu.toolbar.save",this.save.bind(this)),this.sandbox.on("sulu.toolbar.delete",this.deleteItem.bind(this)),this.sandbox.on("sulu.tab.data-changed",this.setData.bind(this)),this.sandbox.on("sulu.header.language-changed",function(a){this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey,a.id),this.toEdit(a.id)}.bind(this))},deleteItem:function(){this.sandbox.util.save("/admin/api/articles/"+this.options.id,"DELETE").then(function(){this.toList()}.bind(this))},toEdit:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(a||this.options.locale)+"/edit:"+(b||this.options.id),!0,!0)},toList:function(){1===this.options.config.types.length?this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale):this.sandbox.emit("sulu.router.navigate","articles:"+(this.options.type||this.data.type)+"/"+this.options.locale)},toAdd:function(){1===this.options.config.types.length?this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale+"/add",!0,!0):this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale+"/add:"+(this.options.type||this.data.type),!0,!0)},save:function(a){this.loadingSave(),this.saveTab().then(function(b){this.afterSave(a,b)}.bind(this))},setData:function(a){this.data=a},saveTab:function(){var b=a.Deferred();return this.sandbox.once("sulu.tab.saved",function(a){this.setData(a),b.resolve(a)}.bind(this)),this.sandbox.emit("sulu.tab.save"),b},enableSave:function(a){(a||"loading"!==this.saveState)&&(this.saveState="enabled",this.sandbox.emit("sulu.header.toolbar.item.enable","save",!1))},disableSave:function(a){(a||"loading"!==this.saveState)&&(this.saveState="disabled",this.sandbox.emit("sulu.header.toolbar.item.disable","save",!0))},loadingSave:function(){this.saveState="loading",this.sandbox.emit("sulu.header.toolbar.item.loading","save")},afterSave:function(a,b){this.disableSave(!0),this.sandbox.emit("sulu.header.saved",b),"back"===a?this.toList():"new"===a?this.toAdd():this.options.id||this.toEdit(this.options.locale,b.id)},loadComponentData:function(){var c=a.Deferred();return this.options.id?(this.sandbox.util.load(b.template(this.defaults.templates.url,{id:this.options.id,locale:this.options.locale})).done(function(a){c.resolve(a)}),c):(c.resolve({}),c)}}}); \ No newline at end of file diff --git a/Resources/public/dist/components/articles/list/main.js b/Resources/public/dist/components/articles/list/main.js index ea4cd3d53..e149fa7e2 100644 --- a/Resources/public/dist/components/articles/list/main.js +++ b/Resources/public/dist/components/articles/list/main.js @@ -1 +1 @@ -define(["underscore"],function(a){"use strict";var b={options:{config:{}},templates:{list:['
','
','
','
','
'].join("")},translations:{headline:"sulu_article.list.title"}};return{defaults:b,header:function(){var b,c=this.options.config.types,d={icon:"plus-circle",title:"public.add-new"},e=!1;return 1===c.length?d.callback=function(){this.toAdd(c[0])}.bind(this):(d.dropdownItems=a.map(c,function(a){return{title:a,callback:function(){this.toAdd(a)}.bind(this)}}.bind(this)),b=a.map(c,function(a){return{id:a,name:a,key:a}}),e={componentOptions:{callback:this.typeChange.bind(this),preselector:"name",preselect:this.options.type},data:b}),{title:this.translations.headline,underline:!1,noBack:!0,tabs:e,toolbar:{buttons:{addArticle:{options:d},deleteSelected:{}},languageChanger:{data:this.options.config.languageChanger,preSelected:this.options.locale}}}},layout:{content:{width:"max"}},initialize:function(){this.render(),this.bindCustomEvents()},render:function(){this.$el.html(this.templates.list()),this.sandbox.sulu.initListToolbarAndList.call(this,"article","/admin/api/articles/fields",{el:this.$find(".list-toolbar-container"),instanceName:"articles",template:this.sandbox.sulu.buttons.get({settings:{options:{dropdownItems:[{type:"columnOptions"}]}}})},{el:this.sandbox.dom.find(".datagrid-container"),url:"/admin/api/articles?locale="+this.options.locale+"&type="+this.options.type,searchInstanceName:"articles",searchFields:["title"],resultKey:"articles",instanceName:"articles",actionCallback:function(a){this.toEdit(a)}.bind(this),viewOptions:{table:{actionIconColumn:"title"}}})},toEdit:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(b||this.options.locale)+"/edit:"+a)},toAdd:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(b||this.options.locale)+"/add"+(this.options.config.types.length>1?":"+a:""))},toList:function(a){this.sandbox.emit("sulu.router.navigate","articles/"+(a||this.options.locale))},deleteItems:function(a){for(var b=0,c=a.length;b0?"enable":"disable";this.sandbox.emit("sulu.header.toolbar.item."+b,"deleteSelected",!1)}.bind(this)),this.sandbox.on("sulu.toolbar.delete",function(){this.sandbox.emit("husky.datagrid.news.items.get-selected",this.deleteItems.bind(this))}.bind(this)),this.sandbox.on("sulu.header.language-changed",function(a){this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey,a.id),this.toList(a.id)}.bind(this))}}}); \ No newline at end of file +define(["underscore"],function(a){"use strict";var b={options:{config:{}},templates:{list:['
','
','
','
','
'].join("")},translations:{headline:"sulu_article.list.title"}};return{defaults:b,header:function(){var b,c=this.options.config.types,d={icon:"plus-circle",title:"public.add-new"},e=!1;return 1===c.length?d.callback=function(){this.toAdd(c[0])}.bind(this):(d.dropdownItems=a.map(c,function(a){return{title:a,callback:function(){this.toAdd(a)}.bind(this)}}.bind(this)),b=a.map(c,function(a){return{id:a,name:a,key:a}}),e={componentOptions:{callback:this.typeChange.bind(this),preselector:"name",preselect:this.options.type},data:b}),{title:this.translations.headline,underline:!1,noBack:!0,tabs:e,toolbar:{buttons:{addArticle:{options:d},deleteSelected:{}},languageChanger:{data:this.options.config.languageChanger,preSelected:this.options.locale}}}},layout:{content:{width:"max"}},initialize:function(){this.render(),this.bindCustomEvents()},render:function(){this.$el.html(this.templates.list()),this.sandbox.sulu.initListToolbarAndList.call(this,"article","/admin/api/articles/fields",{el:this.$find(".list-toolbar-container"),instanceName:"articles",template:this.sandbox.sulu.buttons.get({settings:{options:{dropdownItems:[{type:"columnOptions"}]}}})},{el:this.sandbox.dom.find(".datagrid-container"),url:"/admin/api/articles?locale="+this.options.locale+"&type="+this.options.type,searchInstanceName:"articles",searchFields:["title"],resultKey:"articles",instanceName:"articles",actionCallback:function(a){this.toEdit(a)}.bind(this),viewOptions:{table:{actionIconColumn:"title"}}})},toEdit:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(b||this.options.locale)+"/edit:"+a)},toAdd:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(b||this.options.locale)+"/add"+(this.options.config.types.length>1?":"+a:""))},toList:function(a){this.sandbox.emit("sulu.router.navigate","articles/"+(a||this.options.locale))},deleteItems:function(b){this.sandbox.util.save("/admin/api/articles?ids="+b.join(","),"DELETE").then(function(){a.each(b,function(a){this.sandbox.emit("husky.datagrid.articles.record.remove",a)}.bind(this))}.bind(this))},typeChange:function(a){this.sandbox.emit("husky.datagrid.articles.url.update",{type:a.id}),this.sandbox.emit("sulu.router.navigate","articles:"+a.id+"/"+this.options.locale,!1,!1)},bindCustomEvents:function(){this.sandbox.on("husky.datagrid.articles.number.selections",function(a){var b=a>0?"enable":"disable";this.sandbox.emit("sulu.header.toolbar.item."+b,"deleteSelected",!1)}.bind(this)),this.sandbox.on("sulu.toolbar.delete",function(){this.sandbox.emit("husky.datagrid.articles.items.get-selected",this.deleteItems.bind(this))}.bind(this)),this.sandbox.on("sulu.header.language-changed",function(a){this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey,a.id),this.toList(a.id)}.bind(this))}}}); \ No newline at end of file diff --git a/Resources/public/js/components/articles/edit/main.js b/Resources/public/js/components/articles/edit/main.js index 973ac5d92..45d16fc04 100644 --- a/Resources/public/js/components/articles/edit/main.js +++ b/Resources/public/js/components/articles/edit/main.js @@ -28,6 +28,27 @@ define(['jquery', 'underscore'], function($, _) { }, header: function() { + var buttons = { + save: { + parent: 'saveWithOptions' + }, + template: { + options: { + dropdownOptions: { + url: '/admin/articles/templates?type=' + (this.options.type || this.data.type), + callback: function(item) { + this.template = item.template; + this.sandbox.emit('sulu.tab.template-change', item); + }.bind(this) + } + } + } + }; + + if (!!this.options.id) { + buttons.delete = {}; + } + return { tabs: { url: '/admin/content-navigations?alias=article', @@ -46,22 +67,7 @@ define(['jquery', 'underscore'], function($, _) { }, toolbar: { - buttons: { - save: { - parent: 'saveWithOptions' - }, - template: { - options: { - dropdownOptions: { - url: '/admin/articles/templates?type=' + (this.options.type || this.data.type), - callback: function(item) { - this.template = item.template; - this.sandbox.emit('sulu.tab.template-change', item); - }.bind(this) - } - } - } - } + buttons: buttons } }; }, @@ -76,6 +82,7 @@ define(['jquery', 'underscore'], function($, _) { this.sandbox.on('sulu.header.back', this.toList.bind(this)); this.sandbox.on('sulu.tab.dirty', this.enableSave.bind(this)); this.sandbox.on('sulu.toolbar.save', this.save.bind(this)); + this.sandbox.on('sulu.toolbar.delete', this.deleteItem.bind(this)); this.sandbox.on('sulu.tab.data-changed', this.setData.bind(this)); this.sandbox.on('sulu.header.language-changed', function(item) { @@ -84,6 +91,12 @@ define(['jquery', 'underscore'], function($, _) { }.bind(this)); }, + deleteItem: function() { + this.sandbox.util.save('/admin/api/articles/' + this.options.id, 'DELETE').then(function() { + this.toList(); + }.bind(this)); + }, + toEdit: function(locale, id) { this.sandbox.emit('sulu.router.navigate', 'articles/' + (locale || this.options.locale) + '/edit:' + (id || this.options.id), true, true); }, diff --git a/Resources/public/js/components/articles/list/main.js b/Resources/public/js/components/articles/list/main.js index 36e555dba..5901a4e7c 100644 --- a/Resources/public/js/components/articles/list/main.js +++ b/Resources/public/js/components/articles/list/main.js @@ -163,14 +163,10 @@ define(['underscore'], function(_) { }, deleteItems: function(ids) { - for (var i = 0, length = ids.length; i < length; i++) { - this.deleteItem(ids[i]); - } - }, - - deleteItem: function(id) { - this.sandbox.util.save('/admin/api/articles/' + id, 'DELETE').then(function() { - this.sandbox.emit('husky.datagrid.news.record.remove', id); + this.sandbox.util.save('/admin/api/articles?ids=' + ids.join(','), 'DELETE').then(function() { + _.each(ids, function(id) { + this.sandbox.emit('husky.datagrid.articles.record.remove', id); + }.bind(this)); }.bind(this)); }, @@ -180,13 +176,13 @@ define(['underscore'], function(_) { }, bindCustomEvents: function() { - this.sandbox.on('husky.datagrid.news.number.selections', function(number) { + this.sandbox.on('husky.datagrid.articles.number.selections', function(number) { var postfix = number > 0 ? 'enable' : 'disable'; this.sandbox.emit('sulu.header.toolbar.item.' + postfix, 'deleteSelected', false); }.bind(this)); this.sandbox.on('sulu.toolbar.delete', function() { - this.sandbox.emit('husky.datagrid.news.items.get-selected', this.deleteItems.bind(this)); + this.sandbox.emit('husky.datagrid.articles.items.get-selected', this.deleteItems.bind(this)); }.bind(this)); this.sandbox.on('sulu.header.language-changed', function(item) { diff --git a/Tests/Functional/Controller/ArticleControllerTest.php b/Tests/Functional/Controller/ArticleControllerTest.php index 4d4940ac5..aebe4f947 100644 --- a/Tests/Functional/Controller/ArticleControllerTest.php +++ b/Tests/Functional/Controller/ArticleControllerTest.php @@ -237,6 +237,48 @@ function ($item) { $this->assertContains([$article2['id'], $article2['title']], $items); } + public function testDelete() + { + $this->purgeIndex(); + + $article = $this->testPost('Sulu'); + $this->flush(); + + $client = $this->createAuthenticatedClient(); + $client->request('DELETE', '/api/articles/' . $article['id']); + $this->flush(); + + $this->assertHttpStatusCode(204, $client->getResponse()); + + $client->request('GET', '/api/articles?locale=de&type=blog'); + + $response = json_decode($client->getResponse()->getContent(), true); + $this->assertEquals(0, $response['total']); + $this->assertCount(0, $response['_embedded']['articles']); + } + + public function testCDelete() + { + $this->purgeIndex(); + + $article1 = $this->testPost('Sulu'); + $this->flush(); + $article2 = $this->testPost('Sulu is awesome', 'simple'); + $this->flush(); + + $client = $this->createAuthenticatedClient(); + $client->request('DELETE', '/api/articles?ids=' . implode(',', [$article1['id'], $article2['id']])); + $this->flush(); + + $this->assertHttpStatusCode(204, $client->getResponse()); + + $client->request('GET', '/api/articles?locale=de&type=blog'); + + $response = json_decode($client->getResponse()->getContent(), true); + $this->assertEquals(0, $response['total']); + $this->assertCount(0, $response['_embedded']['articles']); + } + private function purgeIndex() { /** @var IndexerInterface $indexer */