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 @@
+
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 @@
+
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
+