diff --git a/Controller/ArticleController.php b/Controller/ArticleController.php index 5c9d73aef..d64c23ca9 100644 --- a/Controller/ArticleController.php +++ b/Controller/ArticleController.php @@ -23,6 +23,7 @@ use Sulu\Bundle\ArticleBundle\Document\Form\ArticleDocumentType; use Sulu\Bundle\ArticleBundle\Metadata\ArticleViewDocumentIdTrait; use Sulu\Component\Content\Form\Exception\InvalidFormException; +use Sulu\Component\Content\Mapper\ContentMapperInterface; use Sulu\Component\DocumentManager\DocumentManagerInterface; use Sulu\Component\Rest\Exception\MissingParameterException; use Sulu\Component\Rest\Exception\RestException; @@ -321,6 +322,7 @@ public function postTriggerAction($uuid, Request $request) // prepare vars $view = null; $data = null; + $userId = $this->getUser()->getId(); try { switch ($action) { @@ -336,6 +338,10 @@ public function postTriggerAction($uuid, Request $request) $this->getDocumentManager()->removeDraft($data, $locale); $this->getDocumentManager()->flush(); break; + case 'copy-locale': + $destLocales = $this->getRequestParameter($request, 'dest', true); + $data = $this->getMapper()->copyLanguage($uuid, $userId, null, $locale, explode(',', $destLocales)); + break; default: throw new RestException('Unrecognized action: ' . $action); } @@ -413,6 +419,14 @@ protected function getDocumentManager() return $this->get('sulu_document_manager.document_manager'); } + /** + * @return ContentMapperInterface + */ + protected function getMapper() + { + return $this->get('sulu.content.mapper'); + } + /** * Delegates actions by given actionParameter, which can be retrieved from the request. * diff --git a/Resources/public/dist/components/articles/edit/main.js b/Resources/public/dist/components/articles/edit/main.js index 0de9b6561..3307519df 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/suluarticle/article-manager","sulusecurity/services/user-manager","services/sulupreview/preview","sulusecurity/services/security-checker"],function(a,b,c,d,e,f,g){"use strict";return{defaults:{options:{config:{}},templates:{url:"/admin/api/articles<% if (!!id) { %>/<%= id %><% } %>?locale=<%= locale %>"},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"}},layout:function(){return{navigation:{collapsed:!0},content:{shrinkable:!!this.options.id},sidebar:!!this.options.id&&"max"}},header:function(){var a={},d={},e={};return g.hasPermission(this.data,"edit")&&(e.saveDraft={},g.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)}.bind(this)}}}),g.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}}),g.hasPermission(this.data,"delete")&&(d["delete"]={options:{disabled:!this.options.id,callback:this.deleteArticle.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,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},componentOptions:{values:b.defaults(this.data,{type:null})}},toolbar:{buttons:a,languageChanger:{data:this.options.config.languageChanger,preSelected:this.options.locale}}}},initialize:function(){this.bindCustomEvents(),this.showDraftLabel(),this.setHeaderBar(!0)},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",function(a){this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey,a.id),this.toEdit(a.id)}.bind(this))},tabChanged:function(a){this.options.content=a.id},handleError:function(a,b,c){switch(a){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&&d["delete"](this.options.id).then(function(){this.toList()}.bind(this))}.bind(this))},toEdit:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(a||this.options.locale)+"/edit:"+(b||this.options.id)+"/"+(this.options.content||"details"),!0,!0)},toList:function(){1===this.options.config.typeNames.length?this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale):this.sandbox.emit("sulu.router.navigate","articles:"+(this.options.type||this.data.articleType)+"/"+this.options.locale)},toAdd:function(){1===this.options.config.typeNames.length?this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale+"/add",!0,!0):this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale+"/add:"+(this.options.type||this.data.articleType),!0,!0)},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,b){"back"===a?this.toList():"new"===a?this.toAdd():b&&this.toEdit(this.options.locale,this.data.id)},showDraftLabel:function(){this.sandbox.emit("sulu.header.tabs.label.hide"),this.hasDraft(this.data)||e.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"),d.removeDraft(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.router.navigate",this.sandbox.mvc.history.fragment,!0,!0),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(){var b=a.Deferred();return this.options.id?(this.sandbox.util.load(this.getUrl()).done(function(a){this.preview=f.initialize({}),this.preview.start("Sulu\\Bundle\\ArticleBundle\\Document\\ArticleDocument",this.options.id,this.options.locale,a),b.resolve(a)}.bind(this)),b):(b.resolve({}),b)},destroy:function(){this.preview&&f.destroy(this.preview)},showState:function(a){a?(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"),d.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})},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,!this.options.id)}}}); \ No newline at end of file +define(["jquery","underscore","config","services/suluarticle/article-manager","sulusecurity/services/user-manager","services/sulupreview/preview","sulusecurity/services/security-checker","sulucontent/components/copy-locale-overlay/main","sulucontent/components/open-ghost-overlay/main"],function(a,b,c,d,e,f,g,h,i){"use strict";return{defaults:{options:{config:{}},templates:{url:"/admin/api/articles<% if (!!id) { %>/<%= id %><% } %>?locale=<%= locale %>"},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"}},layout:function(){return{navigation:{collapsed:!0},content:{shrinkable:!!this.options.id},sidebar:!!this.options.id&&"max"}},header:function(){var a={},d={},e={};return g.hasPermission(this.data,"edit")&&(e.saveDraft={},g.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)}.bind(this)}}}),g.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}}),g.hasPermission(this.data,"delete")&&(d["delete"]={options:{disabled:!this.options.id,callback:this.deleteArticle.bind(this)}}),d.copyLocale={options:{title:this.sandbox.translate("toolbar.copy-locale"),callback:function(){h.startCopyLocalesOverlay.call(this).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)}},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,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},componentOptions:{values:b.defaults(this.data,{type:null})}},toolbar:{buttons:a,languageChanger:{data:this.options.config.languageChanger,preSelected:this.options.locale}}}},initialize:function(){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){a.id!==this.options.locale&&(this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey,a.id),-1===b(this.data.concreteLanguages).indexOf(a.id)?i.openGhost.call(this,this.data).then(function(b,c){b?h.copyLocale.call(this,this.data.id,c,[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){switch(a){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&&d.remove(this.options.id,this.options.locale).then(function(){this.toList()}.bind(this))}.bind(this))},toEdit:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(a||this.options.locale)+"/edit:"+(b||this.options.id)+"/"+(this.options.content||"details"),!0,!0)},toList:function(){1===this.options.config.typeNames.length?this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale):this.sandbox.emit("sulu.router.navigate","articles:"+(this.options.type||this.data.articleType)+"/"+this.options.locale)},toAdd:function(){1===this.options.config.typeNames.length?this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale+"/add",!0,!0):this.sandbox.emit("sulu.router.navigate","articles/"+this.options.locale+"/add:"+(this.options.type||this.data.articleType),!0,!0)},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,b){"back"===a?this.toList():"new"===a?this.toAdd():b&&this.toEdit(this.options.locale,this.data.id)},showDraftLabel:function(){this.sandbox.emit("sulu.header.tabs.label.hide"),this.hasDraft(this.data)||e.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"),d.removeDraft(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.router.navigate",this.sandbox.mvc.history.fragment,!0,!0),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(){var b=a.Deferred();return this.options.id?(this.sandbox.util.load(this.getUrl()).done(function(a){this.preview=f.initialize({}),this.preview.start("Sulu\\Bundle\\ArticleBundle\\Document\\ArticleDocument",this.options.id,this.options.locale,a),b.resolve(a)}.bind(this)),b):(b.resolve({}),b)},destroy:function(){this.preview&&f.destroy(this.preview)},showState:function(a){a?(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"),d.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})},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,!this.options.id)},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 d.getCopyLocaleUrl(a,b,c)}}}); \ No newline at end of file diff --git a/Resources/public/dist/components/articles/list/main.js b/Resources/public/dist/components/articles/list/main.js index bd86560b1..be9a329a3 100644 --- a/Resources/public/dist/components/articles/list/main.js +++ b/Resources/public/dist/components/articles/list/main.js @@ -1 +1 @@ -define(["underscore"],function(a){"use strict";var b={options:{config:{}},templates:{list:['
','
','
','
'].join(""),draftIcon:'',publishedIcon:'',route:["articles","<% if (!!type) { %>:<%=type%><% } %>","/<%=locale%>"].join("")},translations:{headline:"sulu_article.list.title",unpublished:"public.unpublished",publishedWithDraft:"public.published-with-draft"}};return{defaults:b,header:function(){var b,c=this.options.config.types,d=this.options.config.typeNames,e={icon:"plus-circle",title:"public.add-new"},f=!1,g=null;return 1===d.length?e.callback=function(){this.toAdd(d[0])}.bind(this):(e.dropdownItems=a.map(d,function(a){return{title:c[a].title,callback:function(){this.toAdd(a)}.bind(this)}}.bind(this)),b=[],this.options.config.displayTabAll===!0&&b.push({name:"public.all",key:null}),a.each(d,function(a){b.push({id:a,name:c[a].title,key:a}),a===this.options.type&&(g=c[a].title)}.bind(this)),f={componentOptions:{callback:this.typeChange.bind(this),preselector:"name",preselect:g},data:b}),{noBack:!0,tabs:f,toolbar:{buttons:{addArticle:{options:e},deleteSelected:{}},languageChanger:{data:this.options.config.languageChanger,preSelected:this.options.locale}}}},layout:{content:{width:"max"}},initialize:function(){this.render(),this.bindCustomEvents()},render:function(){this.$el.html(this.templates.list()),this.sandbox.sulu.initListToolbarAndList.call(this,"article","/admin/api/articles/fields",{el:this.$find(".list-toolbar-container"),instanceName:"articles",template:this.sandbox.sulu.buttons.get({settings:{options:{dropdownItems:[{type:"columnOptions"}]}}})},{el:this.sandbox.dom.find(".datagrid-container"),url:"/admin/api/articles?sortBy=authored&sortOrder=desc&locale="+this.options.locale+(this.options.type?"&type="+this.options.type:""),searchInstanceName:"articles",searchFields:["title"],resultKey:"articles",idKey:"uuid",instanceName:"articles",actionCallback:function(a){this.toEdit(a)}.bind(this),viewOptions:{table:{actionIconColumn:"title",badges:[{column:"title",callback:function(a,b){return!(!a.localizationState||"ghost"!==a.localizationState.state||a.localizationState.locale===this.options.language)&&(b.title=a.localizationState.locale,b)}.bind(this)},{column:"title",callback:function(a,b){var c="",d=this.translations.unpublished;return a.published&&!a.publishedState&&(d=this.translations.publishedWithDraft,c+=this.templates.publishedIcon({title:d})),a.publishedState||(c+=this.templates.draftIcon({title:d})),b.title=c,b.cssClass="badge-none",b}.bind(this)}]}}})},toEdit:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(b||this.options.locale)+"/edit:"+a+"/details")},toAdd:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(b||this.options.locale)+"/add"+(this.options.config.typeNames.length>1?":"+a:""))},toList:function(a){1!==this.options.config.typeNames.length&&this.options.type?this.sandbox.emit("sulu.router.navigate","articles:"+this.options.type+"/"+(a||this.options.locale)):this.sandbox.emit("sulu.router.navigate","articles/"+(a||this.options.locale))},deleteItems:function(b){this.sandbox.util.save("/admin/api/articles?ids="+b.join(","),"DELETE").then(function(){a.each(b,function(a){this.sandbox.emit("husky.datagrid.articles.record.remove",a)}.bind(this))}.bind(this))},typeChange:function(a){var b=this.templates.route({type:a.key,locale:this.options.locale});this.options.type=a.key,this.sandbox.emit("husky.datagrid.articles.url.update",{type:a.key}),this.sandbox.emit("sulu.router.navigate",b,!1,!1)},bindCustomEvents:function(){this.sandbox.on("husky.datagrid.articles.number.selections",function(a){var b=a>0?"enable":"disable";this.sandbox.emit("sulu.header.toolbar.item."+b,"deleteSelected",!1)}.bind(this)),this.sandbox.on("sulu.toolbar.delete",function(){this.sandbox.emit("husky.datagrid.articles.items.get-selected",this.deleteItems.bind(this))}.bind(this)),this.sandbox.on("sulu.header.language-changed",function(a){this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey,a.id),this.toList(a.id)}.bind(this))}}}); \ No newline at end of file +define(["underscore","sulucontent/components/copy-locale-overlay/main","sulucontent/components/open-ghost-overlay/main","services/suluarticle/article-manager"],function(a,b,c,d){"use strict";var e={options:{config:{}},templates:{list:['
','
','
','
'].join(""),draftIcon:'',publishedIcon:'',route:["articles","<% if (!!type) { %>:<%=type%><% } %>","/<%=locale%>"].join("")},translations:{headline:"sulu_article.list.title",unpublished:"public.unpublished",publishedWithDraft:"public.published-with-draft"}};return{defaults:e,header:function(){var b,c=this.options.config.types,d=this.options.config.typeNames,e={icon:"plus-circle",title:"public.add-new"},f=!1,g=null;return 1===d.length?e.callback=function(){this.toAdd(d[0])}.bind(this):(e.dropdownItems=a.map(d,function(a){return{title:c[a].title,callback:function(){this.toAdd(a)}.bind(this)}}.bind(this)),b=[],this.options.config.displayTabAll===!0&&b.push({name:"public.all",key:null}),a.each(d,function(a){b.push({id:a,name:c[a].title,key:a}),a===this.options.type&&(g=c[a].title)}.bind(this)),f={componentOptions:{callback:this.typeChange.bind(this),preselector:"name",preselect:g},data:b}),{noBack:!0,tabs:f,toolbar:{buttons:{addArticle:{options:e},deleteSelected:{}},languageChanger:{data:this.options.config.languageChanger,preSelected:this.options.locale}}}},layout:{content:{width:"max"}},initialize:function(){this.render(),this.bindCustomEvents()},render:function(){this.$el.html(this.templates.list()),this.sandbox.sulu.initListToolbarAndList.call(this,"article","/admin/api/articles/fields",{el:this.$find(".list-toolbar-container"),instanceName:"articles",template:this.sandbox.sulu.buttons.get({settings:{options:{dropdownItems:[{type:"columnOptions"}]}}})},{el:this.sandbox.dom.find(".datagrid-container"),url:"/admin/api/articles?sortBy=authored&sortOrder=desc&locale="+this.options.locale+(this.options.type?"&type="+this.options.type:""),searchInstanceName:"articles",searchFields:["title"],resultKey:"articles",idKey:"uuid",instanceName:"articles",actionCallback:function(a,e){"ghost"===e.localizationState.state?d.load(a,this.options.locale).then(function(d){c.openGhost.call(this,d).then(function(c,d){c?b.copyLocale.call(this,a,d,[this.options.locale],function(){this.toEdit(a)}.bind(this)):this.toEdit(a)}.bind(this))}.bind(this)).fail(function(a){this.sandbox.emit("sulu.article.error",a.status,data)}.bind(this)):this.toEdit(a)}.bind(this),viewOptions:{table:{actionIconColumn:"title",badges:[{column:"title",callback:function(a,b){return!(!a.localizationState||"ghost"!==a.localizationState.state||a.localizationState.locale===this.options.locale)&&(b.title=a.localizationState.locale,b)}.bind(this)},{column:"title",callback:function(a,b){var c="",d=this.translations.unpublished;return a.published&&!a.publishedState&&(d=this.translations.publishedWithDraft,c+=this.templates.publishedIcon({title:d})),a.publishedState||(c+=this.templates.draftIcon({title:d})),b.title=c,b.cssClass="badge-none",b}.bind(this)}]}}})},toEdit:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(b||this.options.locale)+"/edit:"+a+"/details")},toAdd:function(a,b){this.sandbox.emit("sulu.router.navigate","articles/"+(b||this.options.locale)+"/add"+(this.options.config.typeNames.length>1?":"+a:""))},toList:function(a){1!==this.options.config.typeNames.length&&this.options.type?this.sandbox.emit("sulu.router.navigate","articles:"+this.options.type+"/"+(a||this.options.locale)):this.sandbox.emit("sulu.router.navigate","articles/"+(a||this.options.locale))},deleteItems:function(b){this.sandbox.util.save("/admin/api/articles?ids="+b.join(","),"DELETE").then(function(){a.each(b,function(a){this.sandbox.emit("husky.datagrid.articles.record.remove",a)}.bind(this))}.bind(this))},typeChange:function(a){var b=this.templates.route({type:a.key,locale:this.options.locale});this.options.type=a.key,this.sandbox.emit("husky.datagrid.articles.url.update",{type:a.key}),this.sandbox.emit("sulu.router.navigate",b,!1,!1)},getCopyLocaleUrl:function(a,b,c){return d.getCopyLocaleUrl(a,b,c)},bindCustomEvents:function(){this.sandbox.on("husky.datagrid.articles.number.selections",function(a){var b=a>0?"enable":"disable";this.sandbox.emit("sulu.header.toolbar.item."+b,"deleteSelected",!1)}.bind(this)),this.sandbox.on("sulu.toolbar.delete",function(){this.sandbox.emit("husky.datagrid.articles.items.get-selected",this.deleteItems.bind(this))}.bind(this)),this.sandbox.on("sulu.header.language-changed",function(a){a.id!==this.options.locale&&(this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey,a.id),this.toList(a.id))}.bind(this))}}}); \ No newline at end of file diff --git a/Resources/public/dist/services/manager.js b/Resources/public/dist/services/manager.js index 7beecf1bb..491bd028f 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 %><% } %>?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}))},save:function(a,d,e,f){return b.save(c.url({id:d,locale:e,action:f}),d?"PUT":"POST",a)},remove:function(a,d){return b.save(c.url({id:a,locale:d}),"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")}}}); \ 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 %><% } %>?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}))},save:function(a,d,e,f){return b.save(c.url({id:d,locale:e,action:f}),d?"PUT":"POST",a)},remove:function(a,d){return b.save(c.url({id:a,locale:d}),"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")},getCopyLocaleUrl:function(a,b,d){return[c.url({id:a,locale:b,action:"copy-locale"}),"&dest=",d].join("")}}}); \ 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 39ee09ed9..59e634ee0 100644 --- a/Resources/public/js/components/articles/edit/main.js +++ b/Resources/public/js/components/articles/edit/main.js @@ -14,8 +14,10 @@ define([ 'services/suluarticle/article-manager', 'sulusecurity/services/user-manager', 'services/sulupreview/preview', - 'sulusecurity/services/security-checker' -], function($, _, config, ArticleManager, UserManager, Preview, SecurityChecker) { + 'sulusecurity/services/security-checker', + 'sulucontent/components/copy-locale-overlay/main', + 'sulucontent/components/open-ghost-overlay/main' +], function($, _, config, ArticleManager, UserManager, Preview, SecurityChecker, CopyLocale, OpenGhost) { 'use strict'; @@ -127,6 +129,26 @@ define([ }; } + editDropdown.copyLocale = { + options: { + title: this.sandbox.translate('toolbar.copy-locale'), + callback: function() { + CopyLocale.startCopyLocalesOverlay.call(this).then(function(newLocales) { + // reload form when the current locale is in newLocales + if (_.contains(newLocales, this.options.locale)) { + this.toEdit(this.options.locale); + + return; + } + + // save new created locales to data and show success label + this.data.concreteLanguages = _.uniq(this.data.concreteLanguages.concat(newLocales)); + this.sandbox.emit('sulu.labels.success.show', 'labels.success.copy-locale-desc', 'labels.success'); + }.bind(this)); + }.bind(this) + } + }; + if (!this.sandbox.util.isEmpty(editDropdown)) { buttons.edit = { options: { @@ -170,6 +192,10 @@ define([ this.bindCustomEvents(); this.showDraftLabel(); this.setHeaderBar(true); + this.loadLocalizations(); + + // the open ghost overlay component needs the current locale in `this.options.language` + this.options.language = this.options.locale; }, bindCustomEvents: function() { @@ -179,11 +205,44 @@ define([ 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)); + }, - this.sandbox.on('sulu.header.language-changed', function(item) { - this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey, item.id); + /** + * Language changed event. + * + * @param {Object} item + */ + languageChanged: function(item) { + if (item.id === this.options.locale) { + return; + } + + this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey, item.id); + + if (-1 === _(this.data.concreteLanguages).indexOf(item.id)) { + OpenGhost.openGhost.call(this, this.data).then(function(copy, src) { + if (!!copy) { + CopyLocale.copyLocale.call( + this, + this.data.id, + src, + [item.id], + function() { + this.toEdit(item.id); + }.bind(this) + ); + } else { + // new article will be created + this.toEdit(item.id); + } + }.bind(this)).fail(function() { + // the open-ghost page got canceled, so reset the language changer + this.sandbox.emit('sulu.header.change-language', this.options.language); + }.bind(this)); + } else { this.toEdit(item.id); - }.bind(this)); + } }, /** @@ -214,7 +273,7 @@ define([ deleteArticle: function() { this.sandbox.sulu.showDeleteDialog(function(wasConfirmed) { if (wasConfirmed) { - ArticleManager.delete(this.options.id).then(function() { + ArticleManager.remove(this.options.id, this.options.locale).then(function() { this.toList(); }.bind(this)); } @@ -477,6 +536,30 @@ define([ } this.afterSaveAction(action, !this.options.id); + }, + + loadLocalizations: function() { + this.sandbox.util.load('/admin/api/localizations').then(function(data) { + this.localizations = data._embedded.localizations.map(function(localization) { + return { + id: localization.localization, + title: localization.localization + }; + }); + }.bind(this)); + }, + + /** + * Returns copy article from a given locale to a array of other locales url. + * + * @param {string} id + * @param {string} src + * @param {string[]} dest + * + * @returns {string} + */ + getCopyLocaleUrl: function(id, src, dest) { + return ArticleManager.getCopyLocaleUrl(id, src, dest); } } }); diff --git a/Resources/public/js/components/articles/list/main.js b/Resources/public/js/components/articles/list/main.js index 28083b345..3ce3cf2f4 100644 --- a/Resources/public/js/components/articles/list/main.js +++ b/Resources/public/js/components/articles/list/main.js @@ -7,7 +7,12 @@ * with this source code in the file LICENSE. */ -define(['underscore'], function(_) { +define([ + 'underscore', + 'sulucontent/components/copy-locale-overlay/main', + 'sulucontent/components/open-ghost-overlay/main', + 'services/suluarticle/article-manager' +], function(_, CopyLocale, OpenGhost, ArticleManager) { 'use strict'; @@ -166,8 +171,30 @@ define(['underscore'], function(_) { resultKey: 'articles', idKey: 'uuid', instanceName: 'articles', - actionCallback: function(id) { - this.toEdit(id); + actionCallback: function(id, article) { + if ('ghost' === article.localizationState.state) { + ArticleManager.load(id, this.options.locale).then(function(response) { + OpenGhost.openGhost.call(this, response).then(function(copy, src) { + if (!!copy) { + CopyLocale.copyLocale.call( + this, + id, + src, + [this.options.locale], + function() { + this.toEdit(id); + }.bind(this) + ); + } else { + this.toEdit(id); + } + }.bind(this)); + }.bind(this)).fail(function(xhr) { + this.sandbox.emit('sulu.article.error', xhr.status, data); + }.bind(this)); + } else { + this.toEdit(id); + } }.bind(this), viewOptions: { table: { @@ -178,7 +205,7 @@ define(['underscore'], function(_) { callback: function(item, badge) { if (!!item.localizationState && item.localizationState.state === 'ghost' && - item.localizationState.locale !== this.options.language + item.localizationState.locale !== this.options.locale ) { badge.title = item.localizationState.locale; @@ -249,6 +276,19 @@ define(['underscore'], function(_) { this.sandbox.emit('sulu.router.navigate', url, false, false); }, + /** + * Returns copy article from a given locale to a array of other locales url. + * + * @param {string} id + * @param {string} src + * @param {string[]} dest + * + * @returns {string} + */ + getCopyLocaleUrl: function(id, src, dest) { + return ArticleManager.getCopyLocaleUrl(id, src, dest); + }, + bindCustomEvents: function() { this.sandbox.on('husky.datagrid.articles.number.selections', function(number) { var postfix = number > 0 ? 'enable' : 'disable'; @@ -260,6 +300,10 @@ define(['underscore'], function(_) { }.bind(this)); this.sandbox.on('sulu.header.language-changed', function(item) { + if (item.id === this.options.locale) { + return; + } + this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey, item.id); this.toList(item.id); }.bind(this)); diff --git a/Resources/public/js/services/manager.js b/Resources/public/js/services/manager.js index a921c8144..b2c013c35 100644 --- a/Resources/public/js/services/manager.js +++ b/Resources/public/js/services/manager.js @@ -78,6 +78,21 @@ define(['jquery', 'services/husky/util'], function($, Util) { templates.url({id: id, locale: locale, action: 'remove-draft'}), 'POST' ); + }, + + /** + * Returns copy article from a given locale to a array of other locales url. + * + * @param {string} id + * @param {string} src + * @param {string[]} dest + * + * @returns {string} + */ + getCopyLocaleUrl: function(id, src, dest) { + return [ + templates.url({id: id, locale: src, action: 'copy-locale'}), '&dest=', dest + ].join(''); } }; }); diff --git a/Tests/Functional/Controller/ArticleControllerTest.php b/Tests/Functional/Controller/ArticleControllerTest.php index fd65f4d19..ead1107b7 100644 --- a/Tests/Functional/Controller/ArticleControllerTest.php +++ b/Tests/Functional/Controller/ArticleControllerTest.php @@ -510,6 +510,76 @@ public function testCDelete() $this->assertCount(0, $response['_embedded']['articles']); } + public function testCopyLocale() + { + // prepare vars + $client = $this->createAuthenticatedClient(); + $locale = 'de'; + $destLocale = 'en'; + + $this->purgeIndex(); + + // create article in default locale + $article1 = $this->testPost('Sulu ist toll - Artikel 1'); + $article2 = $this->testPost('Sulu ist toll - Artikel 2'); + $this->flush(); + + // get all articles in default locale + $client->request('GET', '/api/articles?locale=' . $locale . '&type=blog'); + $this->assertHttpStatusCode(200, $client->getResponse()); + $response = json_decode($client->getResponse()->getContent(), true); + $this->assertEquals(2, $response['total']); + $this->assertCount(2, $response['_embedded']['articles']); + + $items = array_map( + function ($item) { + return [$item['uuid'], $item['title']]; + }, + $response['_embedded']['articles'] + ); + + $this->assertContains([$article1['id'], $article1['title']], $items); + $this->assertContains([$article2['id'], $article2['title']], $items); + + // get all articles in dest locale (both should be ghosts) + $client->request('GET', '/api/articles?locale=' . $destLocale . '&type=blog'); + $this->assertHttpStatusCode(200, $client->getResponse()); + $response = json_decode($client->getResponse()->getContent(), true); + $this->assertEquals(2, $response['total']); + $this->assertCount(2, $response['_embedded']['articles']); + + $items = array_map( + function ($item) { + return [$item['uuid'], $item['title'], $item['localizationState']]; + }, + $response['_embedded']['articles'] + ); + + $this->assertContains([$article1['id'], $article1['title'], ['state' => 'ghost', 'locale' => 'de']], $items); + $this->assertContains([$article2['id'], $article2['title'], ['state' => 'ghost', 'locale' => 'de']], $items); + + // request copy-locale post action for article1 + $client->request('POST', '/api/articles/' . $article1['id'] . '?locale=' . $locale . '&dest=' . $destLocale . '&action=copy-locale'); + $this->assertHttpStatusCode(200, $client->getResponse()); + + // get all articles in dest locale (now only one should be a ghost) + $client->request('GET', '/api/articles?locale=' . $destLocale . '&type=blog'); + $this->assertHttpStatusCode(200, $client->getResponse()); + $response = json_decode($client->getResponse()->getContent(), true); + $this->assertEquals(2, $response['total']); + $this->assertCount(2, $response['_embedded']['articles']); + + $items = array_map( + function ($item) { + return [$item['uuid'], $item['title'], $item['localizationState']]; + }, + $response['_embedded']['articles'] + ); + + $this->assertContains([$article1['id'], $article1['title'], ['state' => 'localized']], $items); + $this->assertContains([$article2['id'], $article2['title'], ['state' => 'ghost', 'locale' => 'de']], $items); + } + /** * @return Media */