From f43f34d96fa7143cef7f66e02a4b70a8bc316997 Mon Sep 17 00:00:00 2001 From: Johannes Wachter Date: Tue, 16 Aug 2016 12:11:37 +0200 Subject: [PATCH] addded authored date and settings tab --- Admin/ArticleContententNavigationProvider.php | 9 +- Controller/ArticleController.php | 3 + Document/ArticleDocument.php | 31 +++ Document/ArticleOngrDocument.php | 31 +++ Document/Index/ArticleIndexer.php | 1 + Document/Subscriber/ArticleSubscriber.php | 7 + .../serializer/Document.ArticleDocument.xml | 1 + .../articles/edit/settings/form.html | 39 ++++ .../components/articles/edit/settings/main.js | 1 + .../articles/edit/settings/form.html | 39 ++++ .../components/articles/edit/settings/main.js | 184 ++++++++++++++++++ .../Controller/ArticleControllerTest.php | 11 +- Tests/app/Resources/webspaces/sulu.io.xml | 3 - 13 files changed, 353 insertions(+), 7 deletions(-) create mode 100644 Resources/public/dist/components/articles/edit/settings/form.html create mode 100644 Resources/public/dist/components/articles/edit/settings/main.js create mode 100644 Resources/public/js/components/articles/edit/settings/form.html create mode 100644 Resources/public/js/components/articles/edit/settings/main.js diff --git a/Admin/ArticleContententNavigationProvider.php b/Admin/ArticleContententNavigationProvider.php index 12e50075d..f88d97fac 100644 --- a/Admin/ArticleContententNavigationProvider.php +++ b/Admin/ArticleContententNavigationProvider.php @@ -43,6 +43,13 @@ public function getNavigationItems(array $options = []) $excerpt->setComponent('articles/edit/excerpt@suluarticle'); $excerpt->setDisplay(['edit']); - return [$details, $seo, $excerpt]; + $settings = new ContentNavigationItem('content-navigation.contents.settings'); + $settings->setId('settings'); + $settings->setPosition(40); + $settings->setAction('settings'); + $settings->setComponent('articles/edit/settings@suluarticle'); + $settings->setDisplay(['edit']); + + return [$details, $seo, $excerpt, $settings]; } } diff --git a/Controller/ArticleController.php b/Controller/ArticleController.php index 08f10cc99..0cfcf03e9 100644 --- a/Controller/ArticleController.php +++ b/Controller/ArticleController.php @@ -268,6 +268,9 @@ private function persistDocument($data, $document, $locale) ] ); $form->submit($data, false); + if (array_key_exists('authored', $data)) { + $document->setAuthored(new \DateTime($data['authored'])); + } if (!$form->isValid()) { throw new InvalidFormException($form); diff --git a/Document/ArticleDocument.php b/Document/ArticleDocument.php index 01bd64b2a..b09fd6e90 100644 --- a/Document/ArticleDocument.php +++ b/Document/ArticleDocument.php @@ -141,6 +141,13 @@ class ArticleDocument implements */ protected $published; + /** + * Timestamp of authoring (can be set by user). + * + * @var \DateTime + */ + protected $authored; + public function __construct() { $this->structure = new Structure(); @@ -399,4 +406,28 @@ public function getPublished() { return $this->published; } + + /** + * Set authored date-time. + * + * @param \DateTime $authored + * + * @return $this + */ + public function setAuthored($authored) + { + $this->authored = $authored; + + return $this; + } + + /** + * Returns date-time of authoring this article. + * + * @return \DateTime + */ + public function getAuthored() + { + return $this->authored; + } } diff --git a/Document/ArticleOngrDocument.php b/Document/ArticleOngrDocument.php index c556f0957..eb5dfdc0f 100644 --- a/Document/ArticleOngrDocument.php +++ b/Document/ArticleOngrDocument.php @@ -93,6 +93,13 @@ class ArticleOngrDocument */ protected $seo; + /** + * @var \DateTime + * + * @Property(type="date") + */ + protected $authored; + /** * @param string $uuid */ @@ -340,4 +347,28 @@ public function setSeo($seo) return $this; } + + /** + * Returns authored. + * + * @return \DateTime + */ + public function getAuthored() + { + return $this->authored; + } + + /** + * Set authored date. + * + * @param \DateTime $authored + * + * @return $this + */ + public function setAuthored(\DateTime $authored = null) + { + $this->authored = $authored; + + return $this; + } } diff --git a/Document/Index/ArticleIndexer.php b/Document/Index/ArticleIndexer.php index 8af3e5299..06cc87a3a 100644 --- a/Document/Index/ArticleIndexer.php +++ b/Document/Index/ArticleIndexer.php @@ -90,6 +90,7 @@ public function index(ArticleDocument $document) $article->setTitle($document->getTitle()); $article->setChanged($document->getChanged()); $article->setCreated($document->getCreated()); + $article->setAuthored($document->getAuthored()); $article->setChanger($this->userManager->getFullNameByUserId($document->getChanger())); $article->setCreator($this->userManager->getFullNameByUserId($document->getCreator())); $article->setType($this->getType($structure->getStructure())); diff --git a/Document/Subscriber/ArticleSubscriber.php b/Document/Subscriber/ArticleSubscriber.php index 542bd2e11..027d3174b 100644 --- a/Document/Subscriber/ArticleSubscriber.php +++ b/Document/Subscriber/ArticleSubscriber.php @@ -207,5 +207,12 @@ public function handleMetadataLoad(MetadataLoadEvent $event) 'property' => 'routePath', ] ); + $metadata->addFieldMapping( + 'authored', + [ + 'encoding' => 'system_localized', + 'property' => 'authored', + ] + ); } } diff --git a/Resources/config/serializer/Document.ArticleDocument.xml b/Resources/config/serializer/Document.ArticleDocument.xml index 4258f37d7..eae8ffa61 100644 --- a/Resources/config/serializer/Document.ArticleDocument.xml +++ b/Resources/config/serializer/Document.ArticleDocument.xml @@ -26,6 +26,7 @@ + diff --git a/Resources/public/dist/components/articles/edit/settings/form.html b/Resources/public/dist/components/articles/edit/settings/form.html new file mode 100644 index 000000000..fdae5eaca --- /dev/null +++ b/Resources/public/dist/components/articles/edit/settings/form.html @@ -0,0 +1,39 @@ +
+
+
+ + +
+
+
+ + + diff --git a/Resources/public/dist/components/articles/edit/settings/main.js b/Resources/public/dist/components/articles/edit/settings/main.js new file mode 100644 index 000000000..768db94ad --- /dev/null +++ b/Resources/public/dist/components/articles/edit/settings/main.js @@ -0,0 +1 @@ +define(["underscore","jquery","sulusecurity/components/users/models/user","text!./form.html"],function(a,b,c,d){"use strict";var e={templates:{form:d},translations:{authored:"sulu_article.authored",changelog:"sulu.content.form.settings.changelog",changed:"sulu.content.form.settings.changelog.changed",created:"sulu.content.form.settings.changelog.created",userNotFound:"sulu.content.form.settings.changelog.user-not-found"}};return{type:"form-tab",defaults:e,parseData:function(a){return a},tabInitialize:function(){this.sandbox.emit("sulu.tab.initialize",this.name),this.sandbox.on("sulu.tab.saved",this.saved.bind(this))},submit:function(b){if(this.sandbox.form.validate(this.formId)){var c=this.sandbox.form.getData(this.formId);a.each(c,function(a,b){this.data[b]=a}.bind(this)),this.save(this.data,b)}},save:function(a,b){this.sandbox.emit("sulu.articles.save",a,b)},saved:function(a){this.data=a},getTemplate:function(){return this.templates.form({translations:this.translations})},getFormId:function(){return"#settings-form"},listenForChange:function(){this.sandbox.dom.on(this.formId,"change keyup",this.setDirty.bind(this)),this.sandbox.on("husky.ckeditor.changed",this.setDirty.bind(this)),this.updateChangelog(this.data)},updateChangelog:function(a){var c=function(a){this.sandbox.dom.text("#created .name",a),e.resolve()},d=function(a){this.sandbox.dom.text("#changed .name",a),f.resolve()},e=b.Deferred(),f=b.Deferred();a.creator===a.changer?this.loadUser(a.creator).done(function(a){d.call(this,a),c.call(this,a)}.bind(this)).fail(function(){d.call(this,this.translations.userNotFound),c.call(this,this.translations.userNotFound)}.bind(this)):(this.loadUser(a.creator).done(function(a){c.call(this,a)}.bind(this)).fail(function(){c.call(this,this.translations.userNotFound)}.bind(this)),this.loadUser(a.changer).done(function(a){d.call(this,a)}.bind(this)).fail(function(){d.call(this,this.translations.userNotFound)}.bind(this))),this.sandbox.dom.text("#created .date",this.sandbox.date.format(a.created,!0)),this.sandbox.dom.text("#changed .date",this.sandbox.date.format(a.changed,!0)),this.sandbox.data.when([e,f]).then(function(){this.sandbox.dom.show("#changelog-container")}.bind(this))},loadUser:function(a){var d=b.Deferred(),e=new c({id:a});return e.fetch({global:!1,success:function(a){d.resolve(a.get("fullName"))}.bind(this),error:function(){d.reject()}.bind(this)}),d}}}); \ No newline at end of file diff --git a/Resources/public/js/components/articles/edit/settings/form.html b/Resources/public/js/components/articles/edit/settings/form.html new file mode 100644 index 000000000..fdae5eaca --- /dev/null +++ b/Resources/public/js/components/articles/edit/settings/form.html @@ -0,0 +1,39 @@ +
+
+
+ + +
+
+
+ + + diff --git a/Resources/public/js/components/articles/edit/settings/main.js b/Resources/public/js/components/articles/edit/settings/main.js new file mode 100644 index 000000000..33408a284 --- /dev/null +++ b/Resources/public/js/components/articles/edit/settings/main.js @@ -0,0 +1,184 @@ +/* + * This file is part of the Sulu CMS. + * + * (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([ + 'underscore', + 'jquery', + 'sulusecurity/components/users/models/user', + 'text!./form.html' +], function(_, $, User, form) { + + 'use strict'; + + var defaults = { + templates: { + form: form + }, + translations: { + authored: 'sulu_article.authored', + changelog: 'sulu.content.form.settings.changelog', + changed: 'sulu.content.form.settings.changelog.changed', + created: 'sulu.content.form.settings.changelog.created', + userNotFound: 'sulu.content.form.settings.changelog.user-not-found' + } + }; + + return { + + type: 'form-tab', + + defaults: defaults, + + /** + * This method function has to be overwritten by the implementation to convert the data from "options.data". + * + * @param {object} data + */ + parseData: function(data) { + return data; + }, + + /** + * This method function can be overwritten by the implementation to initialize the component. + * + * For best-practice the default implementation should be used. + */ + tabInitialize: function() { + this.sandbox.emit('sulu.tab.initialize', this.name); + + this.sandbox.on('sulu.tab.saved', this.saved.bind(this)); + }, + + /** + * Submit form data. + * + * @param {String} action + */ + submit: function(action) { + if (!this.sandbox.form.validate(this.formId)) { + return; + } + + var data = this.sandbox.form.getData(this.formId); + _.each(data, function(value, key) { + this.data[key] = value; + }.bind(this)); + + this.save(this.data, action); + }, + + /** + * This method function has to be overwritten by the implementation to save the data. + * + * @param {object} data + * @param {object} action + */ + save: function(data, action) { + this.sandbox.emit('sulu.articles.save', data, action); + }, + + /** + * This method function can be overwritten by the implementation to process the data which was returned + * by the rest-api. + * + * For best-practice the default implementation should be used. + * + * @param {object} data + */ + saved: function(data) { + this.data = data; + }, + + /** + * This method function has to be overwritten by the implementation to generate the form-template. + */ + getTemplate: function() { + return this.templates.form({translations: this.translations}) + }, + + /** + * This method function has to be overwritten by the implementation. It should return the id for the form. + */ + getFormId: function() { + return '#settings-form'; + }, + + listenForChange: function() { + this.sandbox.dom.on(this.formId, 'change keyup', this.setDirty.bind(this)); + this.sandbox.on('husky.ckeditor.changed', this.setDirty.bind(this)); + + this.updateChangelog(this.data); + }, + + /** + * Update changelog. + * + * @param data + */ + updateChangelog: function(data) { + var setCreator = function(fullName) { + this.sandbox.dom.text('#created .name', fullName); + creatorDef.resolve(); + }, + setChanger = function(fullName) { + this.sandbox.dom.text('#changed .name', fullName); + changerDef.resolve(); + }, + creatorDef = $.Deferred(), + changerDef = $.Deferred(); + + if (data.creator === data.changer) { + this.loadUser(data.creator).done(function(fullName) { + setChanger.call(this, fullName); + setCreator.call(this, fullName); + }.bind(this)).fail(function() { + setChanger.call(this, this.translations.userNotFound); + setCreator.call(this, this.translations.userNotFound); + }.bind(this)); + } else { + this.loadUser(data.creator).done(function(fullName) { + setCreator.call(this, fullName); + }.bind(this)).fail(function() { + setCreator.call(this, this.translations.userNotFound); + }.bind(this)); + this.loadUser(data.changer).done(function(fullName) { + setChanger.call(this, fullName); + }.bind(this)).fail(function() { + setChanger.call(this, this.translations.userNotFound); + }.bind(this)); + } + + this.sandbox.dom.text('#created .date', this.sandbox.date.format(data.created, true)); + this.sandbox.dom.text('#changed .date', this.sandbox.date.format(data.changed, true)); + + this.sandbox.data.when([creatorDef, changerDef]).then(function() { + this.sandbox.dom.show('#changelog-container'); + }.bind(this)); + }, + + loadUser: function(id) { + var deferred = $.Deferred(), + user = new User({id: id}); + + user.fetch({ + global: false, + + success: function(model) { + deferred.resolve(model.get('fullName')) + }.bind(this), + + error: function() { + deferred.reject(); + }.bind(this) + }); + + return deferred; + } + }; +}); diff --git a/Tests/Functional/Controller/ArticleControllerTest.php b/Tests/Functional/Controller/ArticleControllerTest.php index 5e1e4c991..2166c3295 100644 --- a/Tests/Functional/Controller/ArticleControllerTest.php +++ b/Tests/Functional/Controller/ArticleControllerTest.php @@ -31,7 +31,7 @@ public function setUp() public function testPost($title = 'Test-Article', $template = 'default') { $client = $this->createAuthenticatedClient(); - $client->request('POST', '/api/articles?locale=de', ['title' => $title, 'template' => $template]); + $client->request('POST', '/api/articles?locale=de', ['title' => $title, 'template' => $template, 'authored' => '2016-01-01']); $this->assertHttpStatusCode(200, $client->getResponse()); @@ -39,6 +39,7 @@ public function testPost($title = 'Test-Article', $template = 'default') $this->assertEquals($title, $response['title']); $this->assertEquals(self::$typeMap[$template], $response['type']); $this->assertEquals($template, $response['template']); + $this->assertEquals('2016-01-01T00:00:00+0100', $response['authored']); return $response; } @@ -53,7 +54,10 @@ public function testGet() $this->assertHttpStatusCode(200, $client->getResponse()); $response = json_decode($client->getResponse()->getContent(), true); - $this->assertEquals($article, $response); + + foreach ($article as $name => $value) { + $this->assertEquals($value, $response[$name]); + } } public function testPut($title = 'Sulu is awesome') @@ -64,7 +68,7 @@ public function testPut($title = 'Sulu is awesome') $client->request( 'PUT', '/api/articles/' . $article['id'] . '?locale=de', - ['title' => $title, 'template' => 'default'] + ['title' => $title, 'template' => 'default', 'authored' => '2016-01-01'] ); $this->assertHttpStatusCode(200, $client->getResponse()); @@ -72,6 +76,7 @@ public function testPut($title = 'Sulu is awesome') $response = json_decode($client->getResponse()->getContent(), true); $this->assertNotEquals($article['title'], $response['title']); $this->assertEquals($title, $response['title']); + $this->assertEquals('2016-01-01T00:00:00+0100', $response['authored']); } public function testPutExtensions( diff --git a/Tests/app/Resources/webspaces/sulu.io.xml b/Tests/app/Resources/webspaces/sulu.io.xml index d4eca1633..5f1b6f44e 100644 --- a/Tests/app/Resources/webspaces/sulu.io.xml +++ b/Tests/app/Resources/webspaces/sulu.io.xml @@ -31,9 +31,6 @@ Sulu CMF AT sulucmf_at - - tree -