diff --git a/Controller/ArticleController.php b/Controller/ArticleController.php index c7ff777d1..22b799519 100644 --- a/Controller/ArticleController.php +++ b/Controller/ArticleController.php @@ -375,6 +375,13 @@ public function postTriggerAction($uuid, Request $request) $data = $this->getDocumentManager()->find($copiedPath, $locale); break; + case 'order': + $this->orderPages($this->getRequestParameter($request, 'pages', true), $locale); + $this->getDocumentManager()->flush(); + $this->getDocumentManager()->clear(); + + $data = $this->getDocumentManager()->find($uuid, $locale); + break; default: throw new RestException('Unrecognized action: ' . $action); } @@ -393,6 +400,22 @@ public function postTriggerAction($uuid, Request $request) return $this->handleView($view); } + /** + * Ordering given pages. + * + * @param array $pages + * @param string $locale + */ + private function orderPages(array $pages, $locale) + { + $documentManager = $this->getDocumentManager(); + + for ($i = 0; $i < count($pages); ++$i) { + $document = $documentManager->find($pages[$i], $locale); + $documentManager->reorder($document, null); + } + } + /** * {@inheritdoc} */ diff --git a/Document/Serializer/ArticleSubscriber.php b/Document/Serializer/ArticleSubscriber.php index 859c3baac..dec1604d3 100644 --- a/Document/Serializer/ArticleSubscriber.php +++ b/Document/Serializer/ArticleSubscriber.php @@ -15,14 +15,20 @@ use JMS\Serializer\EventDispatcher\EventSubscriberInterface; use JMS\Serializer\EventDispatcher\ObjectEvent; use Sulu\Bundle\ArticleBundle\Document\ArticleDocument; +use Sulu\Bundle\ArticleBundle\Document\ArticleInterface; use Sulu\Bundle\ArticleBundle\Metadata\ArticleTypeTrait; use Sulu\Component\Content\Compat\StructureManagerInterface; +use Sulu\Component\Content\Metadata\Factory\StructureMetadataFactoryInterface; +use Sulu\Component\Content\Metadata\PropertyMetadata; /** * Extends serialization for articles. */ class ArticleSubscriber implements EventSubscriberInterface { + const PAGE_TITLE_TAG_NAME = 'sulu_article.page_title'; + const PAGE_TITLE_PROPERTY_NAME = 'pageTitle'; + use ArticleTypeTrait; /** @@ -30,12 +36,21 @@ class ArticleSubscriber implements EventSubscriberInterface */ private $structureManager; + /** + * @var StructureMetadataFactoryInterface + */ + private $structureMetadataFactory; + /** * @param StructureManagerInterface $structureManager + * @param StructureMetadataFactoryInterface $structureMetadataFactory */ - public function __construct(StructureManagerInterface $structureManager) - { + public function __construct( + StructureManagerInterface $structureManager, + StructureMetadataFactoryInterface $structureMetadataFactory + ) { $this->structureManager = $structureManager; + $this->structureMetadataFactory = $structureMetadataFactory; } /** @@ -49,6 +64,11 @@ public static function getSubscribedEvents() 'format' => 'json', 'method' => 'addTypeOnPostSerialize', ], + [ + 'event' => Events::POST_SERIALIZE, + 'format' => 'json', + 'method' => 'addPageTitlePropertyNameOnPostSerialize', + ], ]; } @@ -70,4 +90,50 @@ public function addTypeOnPostSerialize(ObjectEvent $event) $structure = $this->structureManager->getStructure($article->getStructureType(), 'article'); $visitor->addData('articleType', $context->accept($this->getType($structure->getStructure()))); } + + /** + * Append page-title-property to result. + * + * @param ObjectEvent $event + */ + public function addPageTitlePropertyNameOnPostSerialize(ObjectEvent $event) + { + $article = $event->getObject(); + $visitor = $event->getVisitor(); + $context = $event->getContext(); + + if (!$article instanceof ArticleInterface) { + return; + } + + $property = $this->getPageTitleProperty($article); + if ($property) { + $visitor->addData('_pageTitlePropertyName', $context->accept($property->getName())); + } + } + + /** + * Find page-title property. + * + * @param ArticleInterface $document + * + * @return PropertyMetadata + */ + private function getPageTitleProperty(ArticleInterface $document) + { + $metadata = $this->structureMetadataFactory->getStructureMetadata( + 'article', + $document->getStructureType() + ); + + if ($metadata->hasPropertyWithTagName(self::PAGE_TITLE_TAG_NAME)) { + return $metadata->getPropertyByTagName(self::PAGE_TITLE_TAG_NAME); + } + + if ($metadata->hasProperty(self::PAGE_TITLE_PROPERTY_NAME)) { + return $metadata->getProperty(self::PAGE_TITLE_PROPERTY_NAME); + } + + return null; + } } diff --git a/Document/Serializer/ArticleWebsiteSubscriber.php b/Document/Serializer/ArticleWebsiteSubscriber.php index 41864cb46..b0df9c39e 100644 --- a/Document/Serializer/ArticleWebsiteSubscriber.php +++ b/Document/Serializer/ArticleWebsiteSubscriber.php @@ -23,9 +23,10 @@ use Sulu\Component\Content\Compat\StructureInterface; use Sulu\Component\Content\Compat\StructureManagerInterface; use Sulu\Component\Content\ContentTypeManagerInterface; +use Sulu\Component\Util\SortUtils; /** - * Extends serializer with addtional functionallity to prepare article(-page) data. + * Extends serializer with additional functionality to prepare article(-page) data. */ class ArticleWebsiteSubscriber implements EventSubscriberInterface { @@ -155,6 +156,8 @@ public function resolveContentForArticleOnPostSerialize(ObjectEvent $event) if (null !== $children && $context->attributes->containsKey('pageNumber')) { $pages = array_values(is_array($children) ? $children : iterator_to_array($children)); + $pages = SortUtils::multisort($pages, 'pageNumber'); + $pageNumber = $context->attributes->get('pageNumber')->get(); if ($pageNumber !== 1) { $article = $pages[$pageNumber - 2]; diff --git a/Document/Subscriber/ArticleSubscriber.php b/Document/Subscriber/ArticleSubscriber.php index c453afdbc..24760bc88 100644 --- a/Document/Subscriber/ArticleSubscriber.php +++ b/Document/Subscriber/ArticleSubscriber.php @@ -19,6 +19,7 @@ use Sulu\Bundle\DocumentManagerBundle\Bridge\DocumentInspector; use Sulu\Bundle\DocumentManagerBundle\Bridge\PropertyEncoder; use Sulu\Component\Content\Document\LocalizationState; +use Sulu\Component\Content\Document\WorkflowStage; use Sulu\Component\DocumentManager\DocumentManagerInterface; use Sulu\Component\DocumentManager\Event\AbstractMappingEvent; use Sulu\Component\DocumentManager\Event\CopyEvent; @@ -29,8 +30,10 @@ use Sulu\Component\DocumentManager\Event\PublishEvent; use Sulu\Component\DocumentManager\Event\RemoveDraftEvent; use Sulu\Component\DocumentManager\Event\RemoveEvent; +use Sulu\Component\DocumentManager\Event\ReorderEvent; use Sulu\Component\DocumentManager\Event\UnpublishEvent; use Sulu\Component\DocumentManager\Events; +use Sulu\Component\Util\SortUtils; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -123,6 +126,7 @@ public static function getSubscribedEvents() ['publishChildren', 0], ['persistPageData', -2000], ], + Events::REORDER => [['persistPageDataOnReorder', -2000]], Events::UNPUBLISH => 'handleUnpublish', Events::REMOVE_DRAFT => [['handleScheduleIndex', -1024], ['removeDraftChildren', 0]], Events::FLUSH => [['handleFlush', -2048], ['handleFlushLive', -2048]], @@ -236,6 +240,32 @@ public function publishChildren(PublishEvent $event) } } + /** + * Persist page-data for reordering children. + * + * @param ReorderEvent $event + */ + public function persistPageDataOnReorder(ReorderEvent $event) + { + $document = $event->getDocument(); + if (!$document instanceof ArticlePageDocument) { + return; + } + + $document = $document->getParent(); + $node = $this->documentInspector->getNode($document); + + $this->setPageData($document, $node, $document->getLocale()); + + $document->setWorkflowStage(WorkflowStage::TEST); + $this->documentManager->persist($document, $this->documentInspector->getLocale($document)); + + $this->documents[$document->getUuid()] = [ + 'uuid' => $document->getUuid(), + 'locale' => $document->getLocale(), + ]; + } + /** * Persist page-data. * @@ -248,6 +278,18 @@ public function persistPageData($event) return; } + $this->setPageData($document, $event->getNode(), $event->getLocale()); + } + + /** + * Set page-data for given document on given node. + * + * @param ArticleDocument $document + * @param NodeInterface $node + * @param string $locale + */ + private function setPageData(ArticleDocument $document, NodeInterface $node, $locale) + { $pages = [ [ 'uuid' => $document->getUuid(), @@ -270,9 +312,11 @@ public function persistPageData($event) } } + $pages = SortUtils::multisort($pages, '[pageNumber]'); + $document->setPages($pages); - $event->getNode()->setProperty( - $this->propertyEncoder->localizedSystemName(self::PAGES_PROPERTY, $event->getLocale()), + $node->setProperty( + $this->propertyEncoder->localizedSystemName(self::PAGES_PROPERTY, $locale), json_encode($pages) ); } diff --git a/Document/Subscriber/PageSubscriber.php b/Document/Subscriber/PageSubscriber.php index 6253ca2de..f2ecee942 100644 --- a/Document/Subscriber/PageSubscriber.php +++ b/Document/Subscriber/PageSubscriber.php @@ -14,10 +14,12 @@ use Sulu\Bundle\ArticleBundle\Document\Behavior\PageBehavior; use Sulu\Component\DocumentManager\Behavior\Mapping\ChildrenBehavior; use Sulu\Component\DocumentManager\DocumentInspector; +use Sulu\Component\DocumentManager\DocumentManagerInterface; use Sulu\Component\DocumentManager\Event\HydrateEvent; use Sulu\Component\DocumentManager\Event\PersistEvent; use Sulu\Component\DocumentManager\Event\PublishEvent; use Sulu\Component\DocumentManager\Event\RemoveEvent; +use Sulu\Component\DocumentManager\Event\ReorderEvent; use Sulu\Component\DocumentManager\Event\RestoreEvent; use Sulu\Component\DocumentManager\Events; use Sulu\Component\DocumentManager\PropertyEncoder; @@ -39,15 +41,24 @@ class PageSubscriber implements EventSubscriberInterface * @var PropertyEncoder */ private $propertyEncoder; + /** + * @var DocumentManagerInterface + */ + private $documentManager; /** * @param DocumentInspector $documentInspector * @param PropertyEncoder $propertyEncoder + * @param DocumentManagerInterface $documentManager */ - public function __construct(DocumentInspector $documentInspector, PropertyEncoder $propertyEncoder) - { + public function __construct( + DocumentInspector $documentInspector, + PropertyEncoder $propertyEncoder, + DocumentManagerInterface $documentManager + ) { $this->documentInspector = $documentInspector; $this->propertyEncoder = $propertyEncoder; + $this->documentManager = $documentManager; } /** @@ -60,6 +71,7 @@ public static function getSubscribedEvents() Events::PERSIST => [['handlePersist', -1024]], Events::REMOVE => [['handleRemove', 5]], Events::PUBLISH => [['handlePublishPageNumber', -1024]], + Events::REORDER => [['handleReorder', 0]], Events::RESTORE => [['handleRestore', -1024]], ]; } @@ -115,6 +127,33 @@ public function handlePersist(PersistEvent $event) $document->setPageNumber($page); } + /** + * Adjust the page-numbers of siblings when reordering a page. + * + * @param ReorderEvent $event + */ + public function handleReorder(ReorderEvent $event) + { + $document = $event->getDocument(); + if (!$document instanceof PageBehavior) { + return; + } + + $propertyName = $this->propertyEncoder->systemName(static::FIELD); + $parentNode = $this->documentInspector->getNode($document->getParent()); + + $page = 1; + foreach ($parentNode->getNodes() as $childNode) { + $child = $this->documentManager->find($childNode->getIdentifier(), $event->getLocale()); + if (!$child instanceof PageBehavior) { + continue; + } + + $childNode->setProperty($propertyName, ++$page); + $child->setPageNumber($page); + } + } + /** * Copy page-number to live workspace. * diff --git a/Document/Subscriber/RoutableSubscriber.php b/Document/Subscriber/RoutableSubscriber.php index cc479d11c..e2719070f 100644 --- a/Document/Subscriber/RoutableSubscriber.php +++ b/Document/Subscriber/RoutableSubscriber.php @@ -27,11 +27,13 @@ use Sulu\Component\Content\Exception\ResourceLocatorAlreadyExistsException; use Sulu\Component\Content\Metadata\Factory\StructureMetadataFactoryInterface; use Sulu\Component\DocumentManager\Behavior\Mapping\ChildrenBehavior; +use Sulu\Component\DocumentManager\Behavior\Mapping\ParentBehavior; use Sulu\Component\DocumentManager\DocumentManagerInterface; use Sulu\Component\DocumentManager\Event\AbstractMappingEvent; use Sulu\Component\DocumentManager\Event\CopyEvent; use Sulu\Component\DocumentManager\Event\PublishEvent; use Sulu\Component\DocumentManager\Event\RemoveEvent; +use Sulu\Component\DocumentManager\Event\ReorderEvent; use Sulu\Component\DocumentManager\Events; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -138,6 +140,7 @@ public static function getSubscribedEvents() ['handleRemove', 1024], ], Events::PUBLISH => ['handlePublish', -2000], + Events::REORDER => ['handleReorder', -1000], Events::COPY => ['handleCopy', -2000], ]; } @@ -187,6 +190,35 @@ public function handlePersist(AbstractMappingEvent $event) $event->getNode()->setProperty($propertyName, $route->getPath()); } + /** + * Regenerate routes for siblings on reorder. + * + * @param ReorderEvent $event + */ + public function handleReorder(ReorderEvent $event) + { + $document = $event->getDocument(); + if (!$document instanceof RoutablePageBehavior || !$document instanceof ParentBehavior) { + return; + } + + $parentDocument = $document->getParent(); + if (!$parentDocument instanceof ChildrenBehavior) { + return; + } + + $locale = $this->documentInspector->getLocale($parentDocument); + $propertyName = $this->getRoutePathPropertyName($parentDocument->getStructureType(), $locale); + foreach ($parentDocument->getChildren() as $childDocument) { + $node = $this->documentInspector->getNode($childDocument); + + $route = $this->chainRouteGenerator->generate($childDocument); + $childDocument->setRoutePath($route->getPath()); + + $node->setProperty($propertyName, $route->getPath()); + } + } + /** * Handle publish event and generate route and the child-routes. * @@ -244,8 +276,12 @@ public function handlePublish(PublishEvent $event) */ private function createOrUpdatePageRoute(RoutablePageBehavior $document, $locale) { - $route = $document->getRoute(); + $route = $this->reallocateExistingRoute($document, $locale); + if ($route) { + return $route; + } + $route = $document->getRoute(); if (!$route) { $route = $this->routeRepository->findByEntity($document->getClass(), $document->getUuid(), $locale); } @@ -253,12 +289,34 @@ private function createOrUpdatePageRoute(RoutablePageBehavior $document, $locale if ($route) { $document->setRoute($route); - return $this->routeManager->update($document); + return $this->routeManager->update($document, null, false); } return $this->routeManager->create($document); } + /** + * Reallocates existing route to given document. + * + * @param RoutablePageBehavior $document + * @param string $locale + * + * @return RouteInterface + */ + private function reallocateExistingRoute(RoutablePageBehavior $document, $locale) + { + $route = $this->routeRepository->findByPath($document->getRoutePath(), $locale); + if (!$route) { + return; + } + + $route->setEntityClass(get_class($document)); + $route->setEntityId($document->getId()); + $route->setHistory(false); + + return $route; + } + /** * Create or update for given document. * diff --git a/Resources/config/services.xml b/Resources/config/services.xml index b9b857dca..f45e89ba9 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -125,7 +125,7 @@ class="Sulu\Bundle\ArticleBundle\Document\Subscriber\PageSubscriber"> - + @@ -153,6 +153,7 @@ + diff --git a/Resources/public/dist/components/articles/edit/main.js b/Resources/public/dist/components/articles/edit/main.js index 7c1f5a18e..0f90d7f82 100644 --- a/Resources/public/dist/components/articles/edit/main.js +++ b/Resources/public/dist/components/articles/edit/main.js @@ -1 +1 @@ -define(["jquery","underscore","config","services/husky/util","services/suluarticle/article-manager","services/suluarticle/article-router","sulusecurity/services/user-manager","sulusecurity/services/security-checker","sulucontent/components/copy-locale-overlay/main","sulucontent/components/open-ghost-overlay/main","services/sulucontent/smart-content-manager","./adapter/article","./adapter/article-page"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){"use strict";var n={headerRightSelector:".right-container"},o={resourceLocatorAlreadyExists:1103};return{defaults:{options:{page:1,config:{}},templates:{url:"/admin/api/articles<% if (!!id) { %>/<%= id %><% } %>?locale=<%= locale %>",pageSwitcher:['
',' <%= label %>',' ',"
"].join("")},translations:{headline:"sulu_article.edit.title",draftLabel:"sulu-document-manager.draft-label",removeDraft:"sulu-content.delete-draft",unpublish:"sulu-document-manager.unpublish",unpublishConfirmTextNoDraft:"sulu-content.unpublish-confirm-text-no-draft",unpublishConfirmTextWithDraft:"sulu-content.unpublish-confirm-text-with-draft",unpublishConfirmTitle:"sulu-content.unpublish-confirm-title",deleteDraftConfirmTitle:"sulu-content.delete-draft-confirm-title",deleteDraftConfirmText:"sulu-content.delete-draft-confirm-text",copy:"sulu_article.edit.copy",deletePage:"sulu_article.edit.delete_page",pageOf:"sulu_article.edit.page-of",newPage:"sulu_article.edit.new-page",openGhostOverlay:{info:"sulu_article.settings.open-ghost-overlay.info","new":"sulu_article.settings.open-ghost-overlay.new",copy:"sulu_article.settings.open-ghost-overlay.copy",ok:"sulu_article.settings.open-ghost-overlay.ok"},copyLocaleOverlay:{info:"sulu_article.settings.copy-locale-overlay.info"}}},layout:function(){return{navigation:{collapsed:!0},content:{shrinkable:!!this.options.id},sidebar:!!this.options.id&&"max"}},header:function(){var a={},d={},e={};return h.hasPermission(this.data,"edit")&&(e.saveDraft={},h.hasPermission(this.data,"live")&&(e.savePublish={},e.publish={}),c.has("sulu_automation.enabled")&&(e.automationInfo={options:{entityId:this.options.id,entityClass:"Sulu\\Bundle\\ArticleBundle\\Document\\ArticleDocument",handlerClass:["Sulu\\Bundle\\ContentBundle\\Automation\\DocumentPublishHandler","Sulu\\Bundle\\ContentBundle\\Automation\\DocumentUnpublishHandler"]}}),a.save={parent:"saveWithDraft",options:{callback:function(){this.sandbox.emit("sulu.toolbar.save","publish")}.bind(this),dropdownItems:e}},a.template={options:{dropdownOptions:{url:"/admin/articles/templates?type="+(this.options.type||this.data.articleType),callback:function(a){this.template=a.template,this.sandbox.emit("sulu.tab.template-change",a),this.setHeaderBar()}.bind(this)}}}),h.hasPermission(this.data,"live")&&(d.unpublish={options:{title:this.translations.unpublish,disabled:!this.data.published,callback:this.unpublish.bind(this)}},d.divider={options:{divider:!0}}),h.hasPermission(this.data,"delete")&&(d["delete"]={options:{disabled:!this.options.id,callback:this.deleteArticle.bind(this)}},d.deletePage={options:{title:this.translations.deletePage,disabled:!this.options.page||1===this.options.page,callback:this.deleteArticlePage.bind(this)}}),d.copyLocale={options:{title:this.sandbox.translate("toolbar.copy-locale"),callback:function(){i.startCopyLocalesOverlay.call(this,this.translations.copyLocaleOverlay).then(function(a){return b.contains(a,this.options.locale)?void this.toEdit(this.options.locale):(this.data.concreteLanguages=b.uniq(this.data.concreteLanguages.concat(a)),void this.sandbox.emit("sulu.labels.success.show","labels.success.copy-locale-desc","labels.success"))}.bind(this))}.bind(this)}},h.hasPermission(this.data,"edit")&&(d.copy={options:{title:this.translations.copy,callback:this.copy.bind(this)}}),this.sandbox.util.isEmpty(d)||(a.edit={options:{dropdownItems:d}}),a.statePublished={},a.stateTest={},{tabs:{url:"/admin/content-navigations?alias=article&id="+this.options.id+"&locale="+this.options.locale+(this.options.page?"&page="+this.options.page:""),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,preview:this.preview,adapter:this.getAdapter(),page:this.options.page,id:this.options.id},componentOptions:{values:b.defaults(this.data,{type:null})}},toolbar:{buttons:a,languageChanger:{data:this.options.config.languageChanger,preSelected:this.options.locale}}}},initialize:function(){this.$el.addClass("article-form"),k.initialize(),this.startPageSwitcher(),this.bindCustomEvents(),this.showDraftLabel(),this.setHeaderBar(!0),this.loadLocalizations(),this.options.language=this.options.locale},bindCustomEvents:function(){this.sandbox.on("sulu.header.back",this.toList.bind(this)),this.sandbox.on("sulu.tab.dirty",this.setHeaderBar.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.article.error",this.handleError.bind(this)),this.sandbox.on("husky.tabs.header.item.select",this.tabChanged.bind(this)),this.sandbox.on("sulu.header.language-changed",this.languageChanged.bind(this))},languageChanged:function(a){if(a.id!==this.options.locale){this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey,a.id);var c=this.getAdapter().prepareData(this.data,this);-1===b(c.concreteLanguages).indexOf(a.id)?j.openGhost.call(this,c,this.translations.openGhostOverlay).then(function(b,d){b?i.copyLocale.call(this,c.id,d,[a.id],function(){this.toEdit(a.id)}.bind(this)):this.toEdit(a.id)}.bind(this)).fail(function(){this.sandbox.emit("sulu.header.change-language",this.options.language)}.bind(this)):this.toEdit(a.id)}},tabChanged:function(a){this.options.content=a.id},handleError:function(a,b,c,d){switch(b){case o.resourceLocatorAlreadyExists:this.sandbox.emit("sulu.labels.error.show","labels.error.content-save-resource-locator","labels.error"),this.sandbox.emit("sulu.header.toolbar.item.enable","save");break;default:this.sandbox.emit("sulu.labels.error.show","labels.error.content-save-desc","labels.error"),this.sandbox.emit("sulu.header.toolbar.item.enable","save")}},deleteArticle:function(){this.sandbox.sulu.showDeleteDialog(function(a){a&&e.remove(this.options.id,this.options.locale).then(function(){this.toList()}.bind(this))}.bind(this))},deleteArticlePage:function(){this.sandbox.sulu.showDeleteDialog(function(a){if(a){var b=this.getAdapter().prepareData(this.data,this);e.removePage(this.options.id,b.id,this.options.locale).then(function(){f.toEditForce(this.options.id,this.options.locale)}.bind(this))}}.bind(this))},toEdit:function(a,b){return this.options.page&&1!==this.options.page?f.toPageEdit(b||this.options.id,this.options.page,a||this.options.locale):void f.toEdit(b||this.options.id,a||this.options.locale,this.options.content)},toList:function(a){f.toList(a||this.options.locale,this.options.type||this.data.articleType)},toAdd:function(a){f.toAdd(a||this.options.locale,this.options.type||this.data.articleType)},save:function(a){this.loadingSave(),this.saveTab(a).then(function(b){this.saved(b.id,b,a)}.bind(this))},setData:function(a){this.data=a},saveTab:function(c){var d=a.Deferred();return this.sandbox.emit("sulu.header.toolbar.item.loading","save"),this.sandbox.once("sulu.tab.saved",function(a,c){d.resolve(b.defaults(c,{type:null}))}.bind(this)),this.sandbox.emit("sulu.tab.save",c),d},setHeaderBar:function(a){var b=!a,c=!a,d=!!a&&!this.data.publishedState;this.setSaveToolbarItems.call(this,"saveDraft",b),this.setSaveToolbarItems.call(this,"savePublish",c),this.setSaveToolbarItems.call(this,"publish",d),this.setSaveToolbarItems.call(this,"unpublish",!!this.data.published),b||c||d?this.sandbox.emit("sulu.header.toolbar.item.enable","save",!1):this.sandbox.emit("sulu.header.toolbar.item.disable","save",!1),this.showState(!!this.data.published)},setSaveToolbarItems:function(a,b){this.sandbox.emit("sulu.header.toolbar.item."+(b?"enable":"disable"),a,!1)},loadingSave:function(){this.sandbox.emit("sulu.header.toolbar.item.loading","save")},afterSaveAction:function(a){"back"===a?this.toList():"new"===a?this.toAdd():this.options.id?this.options.page||f.toPageEdit(this.data.id,this.data._embedded.pages.length+1,this.options.locale):this.toEdit(this.options.locale,this.data.id)},showDraftLabel:function(){this.sandbox.emit("sulu.header.tabs.label.hide"),this.hasDraft(this.data)||g.find(this.data.changer).then(function(a){this.sandbox.emit("sulu.header.tabs.label.show",this.sandbox.util.sprintf(this.translations.draftLabel,{changed:this.sandbox.date.format(this.data.changed,!0),user:a.username}),[{id:"delete-draft",title:this.translations.removeDraft,skin:"critical",onClick:this.deleteDraft.bind(this)}])}.bind(this))},deleteDraft:function(){this.sandbox.sulu.showDeleteDialog(function(a){a&&(this.sandbox.emit("husky.label.header.loading"),e.removeDraft(this.data.id,this.options.locale).always(function(){this.sandbox.emit("sulu.header.toolbar.item.enable","edit")}.bind(this)).then(function(a){f.toEdit(this.options.id,this.options.locale),this.saved(a.id,a)}.bind(this)).fail(function(){this.sandbox.emit("husky.label.header.reset"),this.sandbox.emit("sulu.labels.error.show","labels.error.remove-draft-desc","labels.error")}.bind(this)))}.bind(this),this.translations.deleteDraftConfirmTitle,this.translations.deleteDraftConfirmText)},hasDraft:function(a){return!a.id||!!a.publishedState||!a.published},getUrl:function(a){var c=b.template(this.defaults.templates.url,{id:this.options.id,locale:this.options.locale});return a&&(c+="&action="+a),c},loadComponentData:function(){if(!this.options.id)return{_embedded:{pages:[]}};var b=a.Deferred();return this.sandbox.util.load(this.getUrl()).done(function(a){this.preview=this.getAdapter().startPreview(this,a),b.resolve(a)}.bind(this)),b},getAdapter:function(){return this.adapter?this.adapter:this.adapter=1===this.options.page?l:m},destroy:function(){this.preview&&this.getAdapter().destroyPreview(this.preview),this.$dropdownElement&&this.sandbox.stop(this.$dropdownElement)},showState:function(a){a&&!this.data.type?(this.sandbox.emit("sulu.header.toolbar.item.hide","stateTest"),this.sandbox.emit("sulu.header.toolbar.item.show","statePublished")):(this.sandbox.emit("sulu.header.toolbar.item.hide","statePublished"),this.sandbox.emit("sulu.header.toolbar.item.show","stateTest"))},unpublish:function(){this.sandbox.sulu.showConfirmationDialog({callback:function(a){a&&(this.sandbox.emit("sulu.header.toolbar.item.loading","edit"),e.unpublish(this.data.id,this.options.locale).always(function(){this.sandbox.emit("sulu.header.toolbar.item.enable","edit")}.bind(this)).then(function(a){this.sandbox.emit("sulu.labels.success.show","labels.success.content-unpublish-desc","labels.success"),this.saved(a.id,a)}.bind(this)).fail(function(){this.sandbox.emit("sulu.labels.error.show","labels.error.content-unpublish-desc","labels.error")}.bind(this)))}.bind(this),title:this.translations.unpublishConfirmTitle,description:this.hasDraft(this.data)?this.translations.unpublishConfirmTextNoDraft:this.translations.unpublishConfirmTextWithDraft})},copy:function(){e.copy(this.data.id,this.options.locale).done(function(a){f.toEdit(a.id,this.options.locale)}.bind(this))},saved:function(a,b,c){this.setData(b),this.options.id?(this.setHeaderBar(!0),this.showDraftLabel(),this.sandbox.emit("sulu.header.saved",b),this.sandbox.emit("sulu.labels.success.show","labels.success.content-save-desc","labels.success")):this.sandbox.sulu.viewStates.justSaved=!0,this.afterSaveAction(c)},loadLocalizations:function(){this.sandbox.util.load("/admin/api/localizations").then(function(a){this.localizations=a._embedded.localizations.map(function(a){return{id:a.localization,title:a.localization}})}.bind(this))},getCopyLocaleUrl:function(a,b,c){return e.getCopyLocaleUrl(a,b,c)},startPageSwitcher:function(){var b=this.options.page,c=(this.data._embedded.pages||[]).length+1,e=[];b||(b=++c);for(var g=1;g<=c;g++)e.push({id:g,title:d.sprintf(this.translations.pageOf,g,c)});this.options.id&&(e=e.concat([{divider:!0},{id:"add",title:this.translations.newPage}])),this.$dropdownElement=a(this.templates.pageSwitcher({label:d.sprintf(this.translations.pageOf,b,c)}));var h=a(n.headerRightSelector);h.prepend(this.$dropdownElement),h.addClass("wide"),this.sandbox.start([{name:"dropdown@husky",options:{el:this.$dropdownElement,instanceName:"header-pages",alignment:"right",valueName:"title",data:e,clickCallback:function(a){return"add"===a.id?f.toPageAdd(this.options.id,this.options.locale):1===a.id?f.toEdit(this.options.id,this.options.locale):f.toPageEdit(this.options.id,a.id,this.options.locale)}.bind(this)}}])}}}); \ No newline at end of file +define(["jquery","underscore","config","services/husky/util","services/suluarticle/article-manager","services/suluarticle/article-router","sulusecurity/services/user-manager","sulusecurity/services/security-checker","sulucontent/components/copy-locale-overlay/main","sulucontent/components/open-ghost-overlay/main","services/sulucontent/smart-content-manager","./adapter/article","./adapter/article-page"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){"use strict";var n={headerRightSelector:".right-container"},o={resourceLocatorAlreadyExists:1103};return{defaults:{options:{page:1,config:{}},templates:{url:"/admin/api/articles<% if (!!id) { %>/<%= id %><% } %>?locale=<%= locale %>",pageSwitcher:['
',' <%= label %>',' ',"
"].join("")},translations:{headline:"sulu_article.edit.title",draftLabel:"sulu-document-manager.draft-label",removeDraft:"sulu-content.delete-draft",unpublish:"sulu-document-manager.unpublish",unpublishConfirmTextNoDraft:"sulu-content.unpublish-confirm-text-no-draft",unpublishConfirmTextWithDraft:"sulu-content.unpublish-confirm-text-with-draft",unpublishConfirmTitle:"sulu-content.unpublish-confirm-title",deleteDraftConfirmTitle:"sulu-content.delete-draft-confirm-title",deleteDraftConfirmText:"sulu-content.delete-draft-confirm-text",copy:"sulu_article.edit.copy",deletePage:"sulu_article.edit.delete_page",pageOf:"sulu_article.edit.page-of",newPage:"sulu_article.edit.new-page",orderPage:"sulu_article.edit.order-page",page:"sulu_article.edit.page",openGhostOverlay:{info:"sulu_article.settings.open-ghost-overlay.info","new":"sulu_article.settings.open-ghost-overlay.new",copy:"sulu_article.settings.open-ghost-overlay.copy",ok:"sulu_article.settings.open-ghost-overlay.ok"},copyLocaleOverlay:{info:"sulu_article.settings.copy-locale-overlay.info"}}},layout:function(){return{navigation:{collapsed:!0},content:{shrinkable:!!this.options.id},sidebar:!!this.options.id&&"max"}},header:function(){var a={},d={},e={};return h.hasPermission(this.data,"edit")&&(e.saveDraft={},h.hasPermission(this.data,"live")&&(e.savePublish={},e.publish={}),c.has("sulu_automation.enabled")&&(e.automationInfo={options:{entityId:this.options.id,entityClass:"Sulu\\Bundle\\ArticleBundle\\Document\\ArticleDocument",handlerClass:["Sulu\\Bundle\\ContentBundle\\Automation\\DocumentPublishHandler","Sulu\\Bundle\\ContentBundle\\Automation\\DocumentUnpublishHandler"]}}),a.save={parent:"saveWithDraft",options:{callback:function(){this.sandbox.emit("sulu.toolbar.save","publish")}.bind(this),dropdownItems:e}},a.template={options:{dropdownOptions:{url:"/admin/articles/templates?type="+(this.options.type||this.data.articleType),callback:function(a){this.template=a.template,this.sandbox.emit("sulu.tab.template-change",a),this.setHeaderBar()}.bind(this)}}}),h.hasPermission(this.data,"live")&&(d.unpublish={options:{title:this.translations.unpublish,disabled:!this.data.published,callback:this.unpublish.bind(this)}},d.divider={options:{divider:!0}}),h.hasPermission(this.data,"delete")&&(d["delete"]={options:{disabled:!this.options.id,callback:this.deleteArticle.bind(this)}},d.deletePage={options:{title:this.translations.deletePage,disabled:!this.options.page||1===this.options.page,callback:this.deleteArticlePage.bind(this)}}),d.copyLocale={options:{title:this.sandbox.translate("toolbar.copy-locale"),callback:function(){i.startCopyLocalesOverlay.call(this,this.translations.copyLocaleOverlay).then(function(a){return b.contains(a,this.options.locale)?void this.toEdit(this.options.locale):(this.data.concreteLanguages=b.uniq(this.data.concreteLanguages.concat(a)),void this.sandbox.emit("sulu.labels.success.show","labels.success.copy-locale-desc","labels.success"))}.bind(this))}.bind(this)}},h.hasPermission(this.data,"edit")&&(d.copy={options:{title:this.translations.copy,callback:this.copy.bind(this)}}),this.sandbox.util.isEmpty(d)||(a.edit={options:{dropdownItems:d}}),a.statePublished={},a.stateTest={},{tabs:{url:"/admin/content-navigations?alias=article&id="+this.options.id+"&locale="+this.options.locale+(this.options.page?"&page="+this.options.page:""),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,preview:this.preview,adapter:this.getAdapter(),page:this.options.page,id:this.options.id},componentOptions:{values:b.defaults(this.data,{type:null})}},toolbar:{buttons:a,languageChanger:{data:this.options.config.languageChanger,preSelected:this.options.locale}}}},initialize:function(){this.$el.addClass("article-form"),k.initialize(),this.startPageSwitcher(),this.bindCustomEvents(),this.showDraftLabel(),this.setHeaderBar(!0),this.loadLocalizations(),this.options.language=this.options.locale},bindCustomEvents:function(){this.sandbox.on("sulu.header.back",this.toList.bind(this)),this.sandbox.on("sulu.tab.dirty",this.setHeaderBar.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.article.error",this.handleError.bind(this)),this.sandbox.on("husky.tabs.header.item.select",this.tabChanged.bind(this)),this.sandbox.on("sulu.header.language-changed",this.languageChanged.bind(this))},languageChanged:function(a){if(a.id!==this.options.locale){this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey,a.id);var c=this.getAdapter().prepareData(this.data,this);-1===b(c.concreteLanguages).indexOf(a.id)?j.openGhost.call(this,c,this.translations.openGhostOverlay).then(function(b,d){b?i.copyLocale.call(this,c.id,d,[a.id],function(){this.toEdit(a.id)}.bind(this)):this.toEdit(a.id)}.bind(this)).fail(function(){this.sandbox.emit("sulu.header.change-language",this.options.language)}.bind(this)):this.toEdit(a.id)}},tabChanged:function(a){this.options.content=a.id},handleError:function(a,b,c,d){switch(b){case o.resourceLocatorAlreadyExists:this.sandbox.emit("sulu.labels.error.show","labels.error.content-save-resource-locator","labels.error"),this.sandbox.emit("sulu.header.toolbar.item.enable","save");break;default:this.sandbox.emit("sulu.labels.error.show","labels.error.content-save-desc","labels.error"),this.sandbox.emit("sulu.header.toolbar.item.enable","save")}},deleteArticle:function(){this.sandbox.sulu.showDeleteDialog(function(a){a&&e.remove(this.options.id,this.options.locale).then(function(){this.toList()}.bind(this))}.bind(this))},deleteArticlePage:function(){this.sandbox.sulu.showDeleteDialog(function(a){if(a){var b=this.getAdapter().prepareData(this.data,this);e.removePage(this.options.id,b.id,this.options.locale).then(function(){f.toEditForce(this.options.id,this.options.locale)}.bind(this))}}.bind(this))},toEdit:function(a,b){return this.options.page&&1!==this.options.page?f.toPageEdit(b||this.options.id,this.options.page,a||this.options.locale):void f.toEdit(b||this.options.id,a||this.options.locale,this.options.content)},toList:function(a){f.toList(a||this.options.locale,this.options.type||this.data.articleType)},toAdd:function(a){f.toAdd(a||this.options.locale,this.options.type||this.data.articleType)},save:function(a){this.loadingSave(),this.saveTab(a).then(function(b){this.saved(b.id,b,a)}.bind(this))},setData:function(a){this.data=a},saveTab:function(c){var d=a.Deferred();return this.sandbox.emit("sulu.header.toolbar.item.loading","save"),this.sandbox.once("sulu.tab.saved",function(a,c){d.resolve(b.defaults(c,{type:null}))}.bind(this)),this.sandbox.emit("sulu.tab.save",c),d},setHeaderBar:function(a){var b=!a,c=!a,d=!!a&&!this.data.publishedState;this.setSaveToolbarItems.call(this,"saveDraft",b),this.setSaveToolbarItems.call(this,"savePublish",c),this.setSaveToolbarItems.call(this,"publish",d),this.setSaveToolbarItems.call(this,"unpublish",!!this.data.published),b||c||d?this.sandbox.emit("sulu.header.toolbar.item.enable","save",!1):this.sandbox.emit("sulu.header.toolbar.item.disable","save",!1),this.showState(!!this.data.published)},setSaveToolbarItems:function(a,b){this.sandbox.emit("sulu.header.toolbar.item."+(b?"enable":"disable"),a,!1)},loadingSave:function(){this.sandbox.emit("sulu.header.toolbar.item.loading","save")},afterSaveAction:function(a){"back"===a?this.toList():"new"===a?this.toAdd():this.options.id?this.options.page||f.toPageEdit(this.data.id,this.data._embedded.pages.length+1,this.options.locale):this.toEdit(this.options.locale,this.data.id)},showDraftLabel:function(){this.sandbox.emit("sulu.header.tabs.label.hide"),this.hasDraft(this.data)||g.find(this.data.changer).then(function(a){this.sandbox.emit("sulu.header.tabs.label.show",this.sandbox.util.sprintf(this.translations.draftLabel,{changed:this.sandbox.date.format(this.data.changed,!0),user:a.username}),[{id:"delete-draft",title:this.translations.removeDraft,skin:"critical",onClick:this.deleteDraft.bind(this)}])}.bind(this))},deleteDraft:function(){this.sandbox.sulu.showDeleteDialog(function(a){a&&(this.sandbox.emit("husky.label.header.loading"),e.removeDraft(this.data.id,this.options.locale).always(function(){this.sandbox.emit("sulu.header.toolbar.item.enable","edit")}.bind(this)).then(function(a){f.toEdit(this.options.id,this.options.locale),this.saved(a.id,a)}.bind(this)).fail(function(){this.sandbox.emit("husky.label.header.reset"),this.sandbox.emit("sulu.labels.error.show","labels.error.remove-draft-desc","labels.error")}.bind(this)))}.bind(this),this.translations.deleteDraftConfirmTitle,this.translations.deleteDraftConfirmText)},hasDraft:function(a){return!a.id||!!a.publishedState||!a.published},getUrl:function(a){var c=b.template(this.defaults.templates.url,{id:this.options.id,locale:this.options.locale});return a&&(c+="&action="+a),c},loadComponentData:function(){if(!this.options.id)return{_embedded:{pages:[]}};var b=a.Deferred();return this.sandbox.util.load(this.getUrl()).done(function(a){this.preview=this.getAdapter().startPreview(this,a),b.resolve(a)}.bind(this)),b},getAdapter:function(){return this.adapter?this.adapter:this.adapter=1===this.options.page?l:m},destroy:function(){this.preview&&this.getAdapter().destroyPreview(this.preview),this.$dropdownElement&&this.sandbox.stop(this.$dropdownElement)},showState:function(a){a&&!this.data.type?(this.sandbox.emit("sulu.header.toolbar.item.hide","stateTest"),this.sandbox.emit("sulu.header.toolbar.item.show","statePublished")):(this.sandbox.emit("sulu.header.toolbar.item.hide","statePublished"),this.sandbox.emit("sulu.header.toolbar.item.show","stateTest"))},unpublish:function(){this.sandbox.sulu.showConfirmationDialog({callback:function(a){a&&(this.sandbox.emit("sulu.header.toolbar.item.loading","edit"),e.unpublish(this.data.id,this.options.locale).always(function(){this.sandbox.emit("sulu.header.toolbar.item.enable","edit")}.bind(this)).then(function(a){this.sandbox.emit("sulu.labels.success.show","labels.success.content-unpublish-desc","labels.success"),this.saved(a.id,a)}.bind(this)).fail(function(){this.sandbox.emit("sulu.labels.error.show","labels.error.content-unpublish-desc","labels.error")}.bind(this)))}.bind(this),title:this.translations.unpublishConfirmTitle,description:this.hasDraft(this.data)?this.translations.unpublishConfirmTextNoDraft:this.translations.unpublishConfirmTextWithDraft})},copy:function(){e.copy(this.data.id,this.options.locale).done(function(a){f.toEdit(a.id,this.options.locale)}.bind(this))},saved:function(a,b,c){this.setData(b),this.options.id?(this.setHeaderBar(!0),this.showDraftLabel(),this.sandbox.emit("sulu.header.saved",b),this.sandbox.emit("sulu.labels.success.show","labels.success.content-save-desc","labels.success")):this.sandbox.sulu.viewStates.justSaved=!0,this.afterSaveAction(c)},loadLocalizations:function(){this.sandbox.util.load("/admin/api/localizations").then(function(a){this.localizations=a._embedded.localizations.map(function(a){return{id:a.localization,title:a.localization}})}.bind(this))},getCopyLocaleUrl:function(a,b,c){return e.getCopyLocaleUrl(a,b,c)},startPageSwitcher:function(){var b=this.options.page,c=this.data._embedded.pages||[],e=c.length+1,g=[];b||(b=++e);for(var h=1;h<=e;h++)g.push({id:h,title:d.sprintf(this.translations.pageOf,h,e)});this.options.id&&(g=g.concat([{divider:!0},{id:"add",title:this.translations.newPage}])),c.length>1&&g.push({id:"order",title:this.translations.orderPage}),this.$dropdownElement=a(this.templates.pageSwitcher({label:d.sprintf(this.translations.pageOf,b,e)}));var i=a(n.headerRightSelector);i.prepend(this.$dropdownElement),i.addClass("wide"),this.sandbox.start([{name:"dropdown@husky",options:{el:this.$dropdownElement,instanceName:"header-pages",alignment:"right",valueName:"title",data:g,clickCallback:function(a){return"add"===a.id?f.toPageAdd(this.options.id,this.options.locale):"order"===a.id?this.orderPages():1===a.id?f.toEdit(this.options.id,this.options.locale):f.toPageEdit(this.options.id,a.id,this.options.locale)}.bind(this)}}])},orderPages:function(){var b=a("
"),c=this.data._embedded.pages||[],d=this.data._pageTitlePropertyName,g=[];this.$el.append(b);for(var h=0,i=c.length;h"),this.$componentContainer=a("
"),this.$el.append(this.$container),this.sandbox.start([{name:"overlay@husky",options:{el:this.$container,instanceName:"page-order",openOnStart:!0,removeOnClose:!0,skin:"medium",slides:[{title:this.translations.orderPage,data:this.$componentContainer,okCallback:function(){return this.$el.focus(),this.sandbox.emit("husky.overlay.page-order.show-loader"),this.options.saveCallback(this.pages).done(function(){this.sandbox.emit("husky.overlay.page-order.close")}.bind(this)).fail(function(){this.sandbox.emit("husky.overlay.page-order.hide-loader")}.bind(this)),!1}.bind(this)}]}}]),this.sandbox.once("husky.overlay.page-order.opened",function(){this.sandbox.start([{name:"articles/edit/page-order/page-grid@suluarticle",options:{el:this.$componentContainer,pages:this.options.pages,updateCallback:function(a){this.pages=a}.bind(this)}}])}.bind(this))}}}); \ No newline at end of file diff --git a/Resources/public/dist/components/articles/edit/page-order/page-grid/grid.html b/Resources/public/dist/components/articles/edit/page-order/page-grid/grid.html new file mode 100644 index 000000000..790d4a7d9 --- /dev/null +++ b/Resources/public/dist/components/articles/edit/page-order/page-grid/grid.html @@ -0,0 +1,16 @@ + + + + + + + + + <% _.each(pages, function(page, index) { %> + + + + + <% }) %> + +
<%= translations.order %><%= translations.title %>
<%= page.title %>
diff --git a/Resources/public/dist/components/articles/edit/page-order/page-grid/main.js b/Resources/public/dist/components/articles/edit/page-order/page-grid/main.js new file mode 100644 index 000000000..816425a08 --- /dev/null +++ b/Resources/public/dist/components/articles/edit/page-order/page-grid/main.js @@ -0,0 +1 @@ +define(["jquery","text!./grid.html"],function(a,b){"use strict";var c=function(a,b,c){var d=parseInt(b.find("input").val()),e=parseInt(c.find("input").val()),f=b.data("id"),g=c.data("id");return de||f===a||g===a?1:0},d=function(a){a.addClass("highlight-animation"),a.one("animationend webkitAnimationEnd oanimationend MSAnimationEnd",function(){a.removeClass("highlight-animation")})},e=function(a){a.find("input").focus()};return{defaults:{options:{pages:[],updateCallback:function(a){}},templates:{grid:b},translations:{title:"public.title"}},initialize:function(){this.render(),this.bindDomEvents()},render:function(){this.$el.append(this.templates.grid({translations:this.translations,pages:this.options.pages}))},bindDomEvents:function(){this.$el.find("input").on("change",function(b){this.orderTable(a(b.currentTarget).parents("tr"))}.bind(this))},orderTable:function(b){var f=b.data("id"),g=this.$el.find("tbody tr").get(),h=[];g.sort(function(b,d){return c(f,a(b),a(d))}),a.each(g,function(b,c){a(c).find("input").val(b+1),h.push(a(c).data("id")),this.$el.find("table").children("tbody").append(c)}.bind(this)),d(b),e(b),this.options.updateCallback(h)}}}); \ No newline at end of file diff --git a/Resources/public/dist/services/manager.js b/Resources/public/dist/services/manager.js index ddd63c51e..23d9adcae 100644 --- a/Resources/public/dist/services/manager.js +++ b/Resources/public/dist/services/manager.js @@ -1 +1 @@ -define(["jquery","services/husky/util"],function(a,b){"use strict";var c={url:_.template('/admin/api/articles<% if (typeof id !== "undefined") { %>/<%= id %><% } %><% if (typeof postfix !== "undefined") { %>/<%= postfix %><% } %><% if (typeof version !== "undefined") { %>/<%= version %><% } %>?locale=<%= locale %><% if (typeof action !== "undefined") { %>&action=<%= action %><% } %><% if (typeof ids !== "undefined") { %>&ids=<%= ids.join(",") %><% } %>'),pageUrl:_.template('/admin/api/articles/<%= articleId %>/pages<% if (typeof pageId !== "undefined" && !!pageId) { %>/<%= pageId %><% } %>?locale=<%= locale %><% if (typeof action !== "undefined") { %>&action=<%= action %><% } %>')};return{url:c.url,load:function(a,d){return b.load(c.url({id:a,locale:d}))},loadSync:function(a,d){return b.ajax({url:c.url({id:a,locale:d}),dataType:"json",async:!1})},save:function(a,d,e,f){return b.save(c.url({id:d,locale:e,action:f}),d?"PUT":"POST",a)},savePage:function(a,d,e,f,g){return b.save(c.pageUrl({articleId:d,pageId:e,locale:f,action:g}),e?"PUT":"POST",a)},remove:function(a,d){return"string"==typeof a?b.save(c.url({id:a,locale:d}),"DELETE"):b.save(c.url({ids:a,locale:d}),"DELETE")},removePage:function(a,d,e){return b.save(c.pageUrl({articleId:a,pageId:d,locale:e}),"DELETE")},unpublish:function(a,d){return b.save(c.url({id:a,locale:d,action:"unpublish"}),"POST")},removeDraft:function(a,d){return b.save(c.url({id:a,locale:d,action:"remove-draft"}),"POST")},restoreVersion:function(a,d,e){return b.save(c.url({id:a,postfix:"versions",locale:e,version:d,action:"restore"}),"POST")},copy:function(a,d){return b.save(c.url({id:a,locale:d,action:"copy"}),"POST")},getCopyLocaleUrl:function(a,b,d){return[c.url({id:a,locale:b,action:"copy-locale"}),"&dest=",d].join("")},getVersionsUrl:function(a,b){return c.url({id:a,postfix:"versions",locale:b})}}}); \ No newline at end of file +define(["jquery","services/husky/util"],function(a,b){"use strict";var c={url:_.template('/admin/api/articles<% if (typeof id !== "undefined") { %>/<%= id %><% } %><% if (typeof postfix !== "undefined") { %>/<%= postfix %><% } %><% if (typeof version !== "undefined") { %>/<%= version %><% } %>?locale=<%= locale %><% if (typeof action !== "undefined") { %>&action=<%= action %><% } %><% if (typeof ids !== "undefined") { %>&ids=<%= ids.join(",") %><% } %>'),pageUrl:_.template('/admin/api/articles/<%= articleId %>/pages<% if (typeof pageId !== "undefined" && !!pageId) { %>/<%= pageId %><% } %>?locale=<%= locale %><% if (typeof action !== "undefined") { %>&action=<%= action %><% } %>')};return{url:c.url,load:function(a,d){return b.load(c.url({id:a,locale:d}))},loadSync:function(a,d){return b.ajax({url:c.url({id:a,locale:d}),dataType:"json",async:!1})},save:function(a,d,e,f){return b.save(c.url({id:d,locale:e,action:f}),d?"PUT":"POST",a)},savePage:function(a,d,e,f,g){return b.save(c.pageUrl({articleId:d,pageId:e,locale:f,action:g}),e?"PUT":"POST",a)},remove:function(a,d){return"string"==typeof a?b.save(c.url({id:a,locale:d}),"DELETE"):b.save(c.url({ids:a,locale:d}),"DELETE")},removePage:function(a,d,e){return b.save(c.pageUrl({articleId:a,pageId:d,locale:e}),"DELETE")},unpublish:function(a,d){return b.save(c.url({id:a,locale:d,action:"unpublish"}),"POST")},removeDraft:function(a,d){return b.save(c.url({id:a,locale:d,action:"remove-draft"}),"POST")},restoreVersion:function(a,d,e){return b.save(c.url({id:a,postfix:"versions",locale:e,version:d,action:"restore"}),"POST")},copy:function(a,d){return b.save(c.url({id:a,locale:d,action:"copy"}),"POST")},orderPages:function(a,d,e){return b.save(c.url({id:a,locale:e,action:"order"}),"POST",{pages:d})},getCopyLocaleUrl:function(a,b,d){return[c.url({id:a,locale:b,action:"copy-locale"}),"&dest=",d].join("")},getVersionsUrl:function(a,b){return c.url({id:a,postfix:"versions",locale:b})}}}); \ 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 953f0643e..08101eed5 100644 --- a/Resources/public/js/components/articles/edit/main.js +++ b/Resources/public/js/components/articles/edit/main.js @@ -64,6 +64,8 @@ define([ deletePage: 'sulu_article.edit.delete_page', pageOf: 'sulu_article.edit.page-of', newPage: 'sulu_article.edit.new-page', + orderPage: 'sulu_article.edit.order-page', + page: 'sulu_article.edit.page', openGhostOverlay: { info: 'sulu_article.settings.open-ghost-overlay.info', new: 'sulu_article.settings.open-ghost-overlay.new', @@ -650,7 +652,8 @@ define([ startPageSwitcher: function() { var page = this.options.page, - max = (this.data._embedded.pages || []).length + 1, + pages = this.data._embedded.pages || [], + max = pages.length + 1, data = []; if (!page) { @@ -669,6 +672,11 @@ define([ ]); } + // ordering is only available if more than one page exists + if (pages.length > 1) { + data.push({id: 'order', title: this.translations.orderPage}); + } + this.$dropdownElement = $(this.templates.pageSwitcher({label: Util.sprintf(this.translations.pageOf, page, max)})); var $rightContainer = $(constants.headerRightSelector); @@ -686,6 +694,8 @@ define([ clickCallback: function(item) { if (item.id === 'add') { return ArticleRouter.toPageAdd(this.options.id, this.options.locale); + } else if (item.id === 'order') { + return this.orderPages(); } else if (item.id === 1) { return ArticleRouter.toEdit(this.options.id, this.options.locale); } @@ -694,6 +704,39 @@ define([ }.bind(this) } }]); + }, + + orderPages: function() { + var $container = $('
'), + pages = (this.data._embedded.pages || []), + pageTitleProperty = this.data._pageTitlePropertyName, + data = []; + + this.$el.append($container); + + for (var i = 0, length = pages.length; i < length; i++) { + var title = this.translations.page + ' ' + pages[i].pageNumber; + if (pageTitleProperty) { + title = pages[i][pageTitleProperty]; + } + + data.push({id: pages[i].id, title: title}); + } + + this.sandbox.start([{ + name: 'articles/edit/page-order@suluarticle', + options: { + el: $container, + pages: data, + saveCallback: function(pages) { + return ArticleManager.orderPages(this.options.id, pages, this.options.locale).done(function() { + ArticleRouter.toEditForce(this.options.id, this.options.locale, this.options.content); + }.bind(this)).fail(function() { + this.sandbox.emit('sulu.labels.error.show', 'labels.error.content-save-desc', 'labels.error'); + }.bind(this)); + }.bind(this) + } + }]); } } }); diff --git a/Resources/public/js/components/articles/edit/page-order/main.js b/Resources/public/js/components/articles/edit/page-order/main.js new file mode 100644 index 000000000..b0e7cb644 --- /dev/null +++ b/Resources/public/js/components/articles/edit/page-order/main.js @@ -0,0 +1,76 @@ +/* + * This file is part of Sulu. + * + * (c) MASSIVE ART WebServices GmbH + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +define(['jquery'], function($) { + + 'use strict'; + + return { + + defaults: { + options: { + pages: [], + saveCallback: function(pages) { + } + }, + translations: { + orderPage: 'sulu_article.edit.order-page' + } + }, + + initialize: function() { + this.$container = $('
'); + this.$componentContainer = $('
'); + + this.$el.append(this.$container); + + this.sandbox.start([{ + name: 'overlay@husky', + options: { + el: this.$container, + instanceName: 'page-order', + openOnStart: true, + removeOnClose: true, + skin: 'medium', + slides: [{ + title: this.translations.orderPage, + data: this.$componentContainer, + okCallback: function() { + // force trigger update + this.$el.focus(); + + this.sandbox.emit('husky.overlay.page-order.show-loader'); + + this.options.saveCallback(this.pages).done(function() { + this.sandbox.emit('husky.overlay.page-order.close'); + }.bind(this)).fail(function() { + this.sandbox.emit('husky.overlay.page-order.hide-loader'); + }.bind(this)); + + return false; + }.bind(this) + }] + } + }]); + + this.sandbox.once('husky.overlay.page-order.opened', function() { + this.sandbox.start([{ + name: 'articles/edit/page-order/page-grid@suluarticle', + options: { + el: this.$componentContainer, + pages: this.options.pages, + updateCallback: function(pages) { + this.pages = pages; + }.bind(this) + } + }]); + }.bind(this)); + } + } +}); diff --git a/Resources/public/js/components/articles/edit/page-order/page-grid/grid.html b/Resources/public/js/components/articles/edit/page-order/page-grid/grid.html new file mode 100644 index 000000000..790d4a7d9 --- /dev/null +++ b/Resources/public/js/components/articles/edit/page-order/page-grid/grid.html @@ -0,0 +1,16 @@ + + + + + + + + + <% _.each(pages, function(page, index) { %> + + + + + <% }) %> + +
<%= translations.order %><%= translations.title %>
<%= page.title %>
diff --git a/Resources/public/js/components/articles/edit/page-order/page-grid/main.js b/Resources/public/js/components/articles/edit/page-order/page-grid/main.js new file mode 100644 index 000000000..892a70db6 --- /dev/null +++ b/Resources/public/js/components/articles/edit/page-order/page-grid/main.js @@ -0,0 +1,94 @@ +/* + * This file is part of Sulu. + * + * (c) MASSIVE ART WebServices GmbH + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +define(['jquery', 'text!./grid.html'], function($, gridTemplate) { + + 'use strict'; + + var compare = function(id, $a, $b) { + var aValue = parseInt($a.find('input').val()), + bValue = parseInt($b.find('input').val()), + aId = $a.data('id'), + bId = $b.data('id'); + + if (aValue < bValue) { + return -1; + } else if (aValue > bValue || aId === id || bId === id) { + return 1; + } + + return 0; + }, + + highlight = function($item) { + $item.addClass('highlight-animation'); + $item.one('animationend webkitAnimationEnd oanimationend MSAnimationEnd', function() { + $item.removeClass('highlight-animation'); + }); + }, + + focus = function($item) { + $item.find('input').focus(); + }; + + return { + + defaults: { + options: { + pages: [], + updateCallback: function(pages) { + } + }, + templates: { + grid: gridTemplate + }, + translations: { + title: 'public.title' + } + }, + + initialize: function() { + this.render(); + + this.bindDomEvents(); + }, + + render: function() { + this.$el.append(this.templates.grid({translations: this.translations, pages: this.options.pages})); + }, + + bindDomEvents: function() { + this.$el.find('input').on('change', function(e) { + this.orderTable($(e.currentTarget).parents('tr')); + }.bind(this)); + }, + + orderTable: function($item) { + var id = $item.data('id'), + rows = this.$el.find('tbody tr').get(), + pages = []; + + rows.sort(function(a, b) { + return compare(id, $(a), $(b)); + }); + + $.each(rows, function(index, row) { + $(row).find('input').val(index + 1); + pages.push($(row).data('id')); + + this.$el.find('table').children('tbody').append(row); + }.bind(this)); + + highlight($item); + focus($item); + + this.options.updateCallback(pages); + } + } +}); diff --git a/Resources/public/js/services/manager.js b/Resources/public/js/services/manager.js index 750a58675..87f9987b9 100644 --- a/Resources/public/js/services/manager.js +++ b/Resources/public/js/services/manager.js @@ -159,6 +159,17 @@ define(['jquery', 'services/husky/util'], function($, Util) { return Util.save(templates.url({id: id, locale: locale, action: 'copy'}), 'POST'); }, + /** + * Copy given article-pages. + * + * @param {String} id + * @param {String[]} pages + * @param {String} locale + */ + orderPages: function(id, pages, locale) { + return Util.save(templates.url({id: id, locale: locale, action: 'order'}), 'POST', {pages: pages}); + }, + /** * Returns url for copy article from a given locale to a array of other locales url. * diff --git a/Resources/translations/sulu/backend.de.xlf b/Resources/translations/sulu/backend.de.xlf index 9293da0c1..27d0e81ed 100644 --- a/Resources/translations/sulu/backend.de.xlf +++ b/Resources/translations/sulu/backend.de.xlf @@ -154,6 +154,14 @@ sulu_article.edit.new-page Seite hinzufügen + + sulu_article.edit.order-page + Seiten sortieren + + + sulu_article.edit.page + Seite + diff --git a/Resources/translations/sulu/backend.en.xlf b/Resources/translations/sulu/backend.en.xlf index fd0ae94b9..e44d8584c 100644 --- a/Resources/translations/sulu/backend.en.xlf +++ b/Resources/translations/sulu/backend.en.xlf @@ -154,6 +154,14 @@ sulu_article.edit.new-page Create new page + + sulu_article.edit.order-page + Order pages + + + sulu_article.edit.page + Page + diff --git a/Tests/Functional/Controller/ArticleControllerTest.php b/Tests/Functional/Controller/ArticleControllerTest.php index 2fd7334c3..d3ff4c7a9 100644 --- a/Tests/Functional/Controller/ArticleControllerTest.php +++ b/Tests/Functional/Controller/ArticleControllerTest.php @@ -74,6 +74,24 @@ protected function post($title = 'Test-Article', $template = 'default') return json_decode($client->getResponse()->getContent(), true); } + protected function postPage($article, $pageTitle = 'Test-Page') + { + $client = $this->createAuthenticatedClient(); + $client->request( + 'POST', + '/api/articles/' . $article['id'] . '/pages?locale=de', + [ + 'pageTitle' => $pageTitle, + 'template' => $article['template'], + 'authored' => '2016-01-01', + ] + ); + + $this->assertHttpStatusCode(200, $client->getResponse()); + + return json_decode($client->getResponse()->getContent(), true); + } + public function testPost($title = 'Test-Article', $template = 'default') { $response = $this->post($title, $template); @@ -1048,6 +1066,33 @@ public function testPostPageTreeRouteGenerateRemovePage() ); } + public function testOrderPages() + { + $article = $this->post(); + $pages = [ + $this->postPage($article, 'Page 1'), + $this->postPage($article, 'Page 2'), + $this->postPage($article, 'Page 3'), + ]; + $expectedPages = [$pages[1]['id'], $pages[2]['id'], $pages[0]['id']]; + + $client = $this->createAuthenticatedClient(); + $client->request( + 'POST', + '/api/articles/' . $article['id'] . '?action=order&locale=de', + ['pages' => $expectedPages] + ); + + $this->assertHttpStatusCode(200, $client->getResponse()); + $response = json_decode($client->getResponse()->getContent(), true); + + $responsePages = $response['_embedded']['pages']; + for ($i = 0; $i < count($expectedPages); ++$i) { + $this->assertEquals($expectedPages[$i], $responsePages[$i]['id']); + $this->assertEquals($i + 2, $responsePages[$i]['pageNumber']); + } + } + private function postPageTreeRoute($routePathData, $title = 'Test Article') { $client = $this->createAuthenticatedClient(); diff --git a/Tests/Unit/Document/Subscriber/ArticleSubscriberTest.php b/Tests/Unit/Document/Subscriber/ArticleSubscriberTest.php index 1b370c8bc..b2d5ea03c 100644 --- a/Tests/Unit/Document/Subscriber/ArticleSubscriberTest.php +++ b/Tests/Unit/Document/Subscriber/ArticleSubscriberTest.php @@ -21,6 +21,7 @@ use Sulu\Bundle\DocumentManagerBundle\Bridge\DocumentInspector; use Sulu\Bundle\DocumentManagerBundle\Bridge\PropertyEncoder; use Sulu\Component\Content\Document\LocalizationState; +use Sulu\Component\Content\Document\WorkflowStage; use Sulu\Component\DocumentManager\DocumentManagerInterface; use Sulu\Component\DocumentManager\Event\AbstractMappingEvent; use Sulu\Component\DocumentManager\Event\FlushEvent; @@ -29,6 +30,7 @@ use Sulu\Component\DocumentManager\Event\PublishEvent; use Sulu\Component\DocumentManager\Event\RemoveDraftEvent; use Sulu\Component\DocumentManager\Event\RemoveEvent; +use Sulu\Component\DocumentManager\Event\ReorderEvent; class ArticleSubscriberTest extends \PHPUnit_Framework_TestCase { @@ -451,4 +453,61 @@ public function testPersistPageData() $this->articleSubscriber->persistPageData($event->reveal()); } + + public function testPersistPageDataOnReorder() + { + $node = $this->prophesize(NodeInterface::class); + + $orderedDocument = $this->prophesize(ArticlePageDocument::class); + $orderedDocument->getParent()->willReturn($this->document->reveal()); + $this->document->getLocale()->willReturn($this->locale); + $this->documentInspector->getNode($this->document->reveal())->willReturn($node->reveal()); + + $event = $this->prophesize(ReorderEvent::class); + $event->getDocument()->willReturn($orderedDocument->reveal()); + + $pages = [ + [ + 'uuid' => '123-123-123', + 'title' => 'Test article: page 1', + 'routePath' => '/test-article', + 'pageNumber' => 1, + ], + [ + 'uuid' => '321-321-321', + 'title' => 'Test article: page 2', + 'routePath' => '/test-article/page-2', + 'pageNumber' => 2, + ], + ]; + + $this->document->getUuid()->willReturn($pages[0]['uuid']); + $this->document->getPageTitle()->willReturn($pages[0]['title']); + $this->document->getRoutePath()->willReturn($pages[0]['routePath']); + $this->document->getPageNumber()->willReturn($pages[0]['pageNumber']); + + $child = $this->prophesize(ArticlePageDocument::class); + $child->getUuid()->willReturn($pages[1]['uuid']); + $child->getPageTitle()->willReturn($pages[1]['title']); + $child->getRoutePath()->willReturn($pages[1]['routePath']); + $child->getPageNumber()->willReturn($pages[1]['pageNumber']); + $this->document->getChildren()->willReturn(new \ArrayIterator([$child->reveal()])); + + $this->documentInspector->getLocalizationState($child->reveal())->willReturn(LocalizationState::LOCALIZED); + $this->documentInspector->getLocale($this->document->reveal())->willReturn('de'); + + $propertyName = 'i18n:' . $this->locale . '-' . ArticleSubscriber::PAGES_PROPERTY; + $this->propertyEncoder->localizedSystemName(ArticleSubscriber::PAGES_PROPERTY, $this->locale)->willReturn( + $propertyName + ); + + $this->document->setPages($pages)->shouldBeCalled(); + $node->setProperty($propertyName, json_encode($pages))->shouldBeCalled(); + $this->document->setWorkflowStage(WorkflowStage::TEST)->shouldBeCalled(); + $this->document->setWorkflowStage(WorkflowStage::TEST); + + $this->documentManager->persist($this->document->reveal(), 'de')->shouldBeCalled(); + + $this->articleSubscriber->persistPageDataOnReorder($event->reveal()); + } } diff --git a/Tests/Unit/Document/Subscriber/PageSubscriberTest.php b/Tests/Unit/Document/Subscriber/PageSubscriberTest.php index b450a90fa..6b09e7989 100644 --- a/Tests/Unit/Document/Subscriber/PageSubscriberTest.php +++ b/Tests/Unit/Document/Subscriber/PageSubscriberTest.php @@ -16,9 +16,11 @@ use Sulu\Bundle\ArticleBundle\Document\Subscriber\PageSubscriber; use Sulu\Component\DocumentManager\Behavior\Mapping\ChildrenBehavior; use Sulu\Component\DocumentManager\DocumentInspector; +use Sulu\Component\DocumentManager\DocumentManagerInterface; use Sulu\Component\DocumentManager\Event\PersistEvent; use Sulu\Component\DocumentManager\Event\PublishEvent; use Sulu\Component\DocumentManager\Event\RemoveEvent; +use Sulu\Component\DocumentManager\Event\ReorderEvent; use Sulu\Component\DocumentManager\Event\RestoreEvent; use Sulu\Component\DocumentManager\PropertyEncoder; @@ -34,6 +36,11 @@ class PageSubscriberTest extends \PHPUnit_Framework_TestCase */ private $propertyEncoder; + /** + * @var DocumentManagerInterface + */ + private $documentManager; + /** * @var PageSubscriber */ @@ -56,13 +63,13 @@ protected function setUp() { $this->documentInspector = $this->prophesize(DocumentInspector::class); $this->propertyEncoder = $this->prophesize(PropertyEncoder::class); + $this->documentManager = $this->prophesize(DocumentManagerInterface::class); $this->document = $this->prophesize(PageBehavior::class); $this->node = $this->prophesize(NodeInterface::class); $this->pageSubscriber = new PageSubscriber( - $this->documentInspector->reveal(), - $this->propertyEncoder->reveal() + $this->documentInspector->reveal(), $this->propertyEncoder->reveal(), $this->documentManager->reveal() ); } @@ -167,4 +174,57 @@ public function testHandleRestore() $this->pageSubscriber->handleRestore($event->reveal()); } + + public function testHandleReorder() + { + $parentDocument = $this->prophesize(ChildrenBehavior::class); + $this->document->getParent()->willReturn($parentDocument->reveal()); + + $nodes = [ + $this->prophesize(NodeInterface::class), + $this->prophesize(NodeInterface::class), + $this->prophesize(NodeInterface::class), + ]; + + $nodes[0]->getIdentifier()->willReturn('1-1-1'); + $nodes[1]->getIdentifier()->willReturn('1-2-2'); + $nodes[2]->getIdentifier()->willReturn('1-2-3'); + + $parentNode = $this->prophesize(NodeInterface::class); + $parentNode->getNodes()->willReturn( + array_map( + function ($item) { + return $item->reveal(); + }, + $nodes + ) + ); + + $this->documentInspector->getNode($parentDocument->reveal())->willReturn($parentNode->reveal()); + + $documents = [ + $this->prophesize(PageBehavior::class), + $this->prophesize(PageBehavior::class), + $this->prophesize(PageBehavior::class), + ]; + + $this->documentManager->find('1-1-1', 'de')->willReturn($documents[0]->reveal()); + $this->documentManager->find('1-2-2', 'de')->willReturn($documents[1]->reveal()); + $this->documentManager->find('1-2-3', 'de')->willReturn($documents[2]->reveal()); + + $event = $this->prophesize(ReorderEvent::class); + $event->getDocument()->willReturn($this->document->reveal()); + $event->getLocale()->willReturn('de'); + + $this->propertyEncoder->systemName(PageSubscriber::FIELD)->willReturn('sulu:' . PageSubscriber::FIELD); + + $documents[0]->setPageNumber(2)->shouldBeCalled(); + $nodes[0]->setProperty('sulu:' . PageSubscriber::FIELD, 2)->shouldBeCalled(); + $documents[1]->setPageNumber(3)->shouldBeCalled(); + $nodes[1]->setProperty('sulu:' . PageSubscriber::FIELD, 3)->shouldBeCalled(); + $documents[2]->setPageNumber(4)->shouldBeCalled(); + $nodes[2]->setProperty('sulu:' . PageSubscriber::FIELD, 4)->shouldBeCalled(); + + $this->pageSubscriber->handleReorder($event->reveal()); + } } diff --git a/Tests/Unit/Document/Subscriber/RoutableSubscriberTest.php b/Tests/Unit/Document/Subscriber/RoutableSubscriberTest.php index 7a50e02a3..b172a4069 100644 --- a/Tests/Unit/Document/Subscriber/RoutableSubscriberTest.php +++ b/Tests/Unit/Document/Subscriber/RoutableSubscriberTest.php @@ -16,6 +16,7 @@ use Prophecy\Argument; use Sulu\Bundle\ArticleBundle\Document\ArticleDocument; use Sulu\Bundle\ArticleBundle\Document\Behavior\RoutableBehavior; +use Sulu\Bundle\ArticleBundle\Document\Behavior\RoutablePageBehavior; use Sulu\Bundle\ArticleBundle\Document\Subscriber\RoutableSubscriber; use Sulu\Bundle\DocumentManagerBundle\Bridge\DocumentInspector; use Sulu\Bundle\DocumentManagerBundle\Bridge\PropertyEncoder; @@ -25,15 +26,18 @@ use Sulu\Bundle\RouteBundle\Manager\ConflictResolverInterface; use Sulu\Bundle\RouteBundle\Manager\RouteManagerInterface; use Sulu\Bundle\RouteBundle\Model\RouteInterface; +use Sulu\Component\Content\Document\Behavior\StructureBehavior; use Sulu\Component\Content\Metadata\Factory\StructureMetadataFactoryInterface; use Sulu\Component\Content\Metadata\PropertyMetadata; use Sulu\Component\Content\Metadata\StructureMetadata; use Sulu\Component\DocumentManager\Behavior\Mapping\ChildrenBehavior; +use Sulu\Component\DocumentManager\Behavior\Mapping\ParentBehavior; use Sulu\Component\DocumentManager\DocumentManagerInterface; use Sulu\Component\DocumentManager\Event\CopyEvent; use Sulu\Component\DocumentManager\Event\HydrateEvent; use Sulu\Component\DocumentManager\Event\PersistEvent; use Sulu\Component\DocumentManager\Event\RemoveEvent; +use Sulu\Component\DocumentManager\Event\ReorderEvent; class RoutableSubscriberTest extends \PHPUnit_Framework_TestCase { @@ -372,4 +376,64 @@ public function testHandleCopy() $this->routableSubscriber->handleCopy($event->reveal()); } + + public function testHandleReorder() + { + $this->document->willImplement(ParentBehavior::class); + + $event = $this->prophesize(ReorderEvent::class); + $event->getDocument()->willReturn($this->document->reveal()); + + $parentDocument = $this->prophesize(ChildrenBehavior::class); + $parentDocument->willImplement(StructureBehavior::class); + $this->document->getParent()->willReturn($parentDocument->reveal()); + $this->documentInspector->getLocale($parentDocument)->willReturn('de'); + + $parentDocument->getStructureType()->willReturn('default'); + + $metadata = $this->prophesize(StructureMetadata::class); + $metadata->hasTag(Argument::any())->willReturn(false); + + $this->metadataFactory->getStructureMetadata('article', 'default')->willReturn($metadata->reveal()); + $this->propertyEncoder->localizedSystemName('routePath', 'de')->willReturn('i18n:de-routePath'); + + $children = [ + $this->prophesize(RoutablePageBehavior::class), + $this->prophesize(RoutablePageBehavior::class), + $this->prophesize(RoutablePageBehavior::class), + ]; + $parentDocument->getChildren() + ->willReturn([$children[0]->reveal(), $children[1]->reveal(), $children[2]->reveal()]); + + $nodes = [ + $this->prophesize(NodeInterface::class), + $this->prophesize(NodeInterface::class), + $this->prophesize(NodeInterface::class), + ]; + $this->documentInspector->getNode($children[0]->reveal())->willReturn($nodes[0]->reveal()); + $this->documentInspector->getNode($children[1]->reveal())->willReturn($nodes[1]->reveal()); + $this->documentInspector->getNode($children[2]->reveal())->willReturn($nodes[2]->reveal()); + + $routes = [ + $this->prophesize(RouteInterface::class), + $this->prophesize(RouteInterface::class), + $this->prophesize(RouteInterface::class), + ]; + $routes[0]->getPath()->willReturn('/test-1'); + $routes[1]->getPath()->willReturn('/test-2'); + $routes[2]->getPath()->willReturn('/test-3'); + $this->chainGenerator->generate($children[0]->reveal())->willReturn($routes[0]->reveal()); + $this->chainGenerator->generate($children[1]->reveal())->willReturn($routes[1]->reveal()); + $this->chainGenerator->generate($children[2]->reveal())->willReturn($routes[2]->reveal()); + + $children[0]->setRoutePath('/test-1')->shouldBeCalled(); + $children[1]->setRoutePath('/test-2')->shouldBeCalled(); + $children[2]->setRoutePath('/test-3')->shouldBeCalled(); + + $nodes[0]->setProperty('i18n:de-routePath', '/test-1')->shouldBeCalled(); + $nodes[1]->setProperty('i18n:de-routePath', '/test-2')->shouldBeCalled(); + $nodes[2]->setProperty('i18n:de-routePath', '/test-3')->shouldBeCalled(); + + $this->routableSubscriber->handleReorder($event->reveal()); + } } diff --git a/Tests/app/Resources/articles/default.xml b/Tests/app/Resources/articles/default.xml index 2f5be13b7..27114ddda 100644 --- a/Tests/app/Resources/articles/default.xml +++ b/Tests/app/Resources/articles/default.xml @@ -13,5 +13,6 @@ +