diff --git a/core/modules/layout/grunt.js b/core/modules/layout/grunt.js
index f566742..79bb9e6 100644
--- a/core/modules/layout/grunt.js
+++ b/core/modules/layout/grunt.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * Grunt file for the layout JS app. Allows for linting and build processes.
+ *
+ * @see http://gruntjs.com/
+ */
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
@@ -44,6 +50,7 @@ module.exports = function(grunt) {
jQuery: true,
Backbone: true,
Drupal: true,
+ drupalSettings: true,
VIE: true,
_: true
}
diff --git a/core/modules/layout/js/collections/collections.js b/core/modules/layout/js/collections/collections.js
index 9020725..b71c783 100644
--- a/core/modules/layout/js/collections/collections.js
+++ b/core/modules/layout/js/collections/collections.js
@@ -1,5 +1,8 @@
/**
* @file
+ * This file contains the collections of models for the layout js-app.
+ *
+ * @todo: split into separate files.
*/
(function ($, _, Backbone, Drupal, drupalSettings) {
"use strict";
diff --git a/core/modules/layout/js/models/app-model.js b/core/modules/layout/js/models/app-model.js
index 8830e6c..b7535b4 100644
--- a/core/modules/layout/js/models/app-model.js
+++ b/core/modules/layout/js/models/app-model.js
@@ -1,5 +1,9 @@
/**
* @file
+ * This model hold application state and corresponds to the layout containing
+ * regions (and block instances).
+ *
+ * @todo: probably split this AppModel into AppModel and LayoutModel.
*/
(function ($, _, Backbone, Drupal) {
"use strict";
diff --git a/core/modules/layout/js/models/block-model.js b/core/modules/layout/js/models/block-model.js
index 167b030..e746ef5 100644
--- a/core/modules/layout/js/models/block-model.js
+++ b/core/modules/layout/js/models/block-model.js
@@ -1,5 +1,6 @@
/**
* @file
+ * This model corresponds to a block that can be placed
*/
(function ($, _, Backbone, Drupal, drupalSettings) {
"use strict";
@@ -14,10 +15,10 @@
'description': '',
'config': {}
},
+ // @todo: create a new BlockInstanceModel instance via a webservice (or
+ // another way of ensuring that the id of the BlockInstanceMdoel is unique).
createBlockInstance: function() {
return new Drupal.layout.BlockInstanceModel({
- // in real life this would need to come from somewhere else ..
- // maybe time for UUID.js
'id': this.get('id'),
'label': this.get('id'),
'blockId': this.get('id'),
@@ -26,5 +27,4 @@
}
});
-
})(jQuery, _, Backbone, Drupal, drupalSettings);
diff --git a/core/modules/layout/js/models/blockinstance-model.js b/core/modules/layout/js/models/blockinstance-model.js
index 26d3587..d39caf1 100644
--- a/core/modules/layout/js/models/blockinstance-model.js
+++ b/core/modules/layout/js/models/blockinstance-model.js
@@ -1,5 +1,7 @@
/**
* @file
+ * This model corresponds to the instance of a block placed in a region of a
+ * layout.
*/
(function ($, _, Backbone, Drupal, drupalSettings) {
"use strict";
@@ -11,14 +13,14 @@
return drupalSettings.layout.webserviceURL + '/block';
},
defaults: {
+ // Unique id of the block instance.
'id': null,
'weight': null,
+ // Unique id of the block (e.g CMI key).
'blockId': null,
'region': '',
'config': {}
}
});
-
-
})(jQuery, _, Backbone, Drupal, drupalSettings);
diff --git a/core/modules/layout/js/models/region-model.js b/core/modules/layout/js/models/region-model.js
index 6e0b22d..0cb35e3 100644
--- a/core/modules/layout/js/models/region-model.js
+++ b/core/modules/layout/js/models/region-model.js
@@ -1,5 +1,6 @@
/**
* @file
+ * This model corresponds to a region in a layout.
*/
(function ($, _, Backbone, Drupal) {
"use strict";
@@ -17,5 +18,4 @@
}
});
-
})(jQuery, _, Backbone, Drupal);
diff --git a/core/modules/layout/js/theme.js b/core/modules/layout/js/theme.js
index f4af36c..7e76446 100644
--- a/core/modules/layout/js/theme.js
+++ b/core/modules/layout/js/theme.js
@@ -1,5 +1,6 @@
/**
* @file
+ * Theme functions for the layout js-app.
*/
(function ($) {
/**
@@ -29,9 +30,9 @@
/**
* Theme function to get the html for a block instance.
- * Poor man's _.template ...
- * @return
- * The corresponding HTML.
+ * @param id
+ * @param label
+ * @return {String}
*/
Drupal.theme.layoutBlock = function (id, label, attributes) {
return '
' +
diff --git a/core/modules/layout/js/views/app-view.js b/core/modules/layout/js/views/app-view.js
index 8fcaca9..79f9b58 100644
--- a/core/modules/layout/js/views/app-view.js
+++ b/core/modules/layout/js/views/app-view.js
@@ -1,27 +1,32 @@
+/**
+ * @file
+ * This file holds the master view for the layout js app.
+ */
(function ($, _, Backbone, Drupal) {
"use strict";
- // sad panda.
- Backbone.emulateJSON = true;
+
+ // @todo: be RESTful. Not sure whether the router/rest.module are in place.
+ // Instead of using REST verbs, POST will be used and $_POST['_method'] will
+ // contain the verb.
Backbone.emulateHTTP = true;
+ // Payloads will be sent as application/x-www-form-urlencoded, in
+ // $_POST['model'].
+ Backbone.emulateJSON = true;
Drupal.layout = Drupal.layout || {};
Drupal.layout.AppView = Backbone.View.extend({
- events: {
- 'change': 'onChange'
- },
initialize: function(options) {
this.regionsView = new Drupal.layout.RegionsView({
collection: this.model.get('regions'),
el: this.$el.find('.layout-display')
});
- this.model.get('regions').on('change', function() {
- console.log('Change!');
- });
this.settings = options.settings;
},
render: function() {
- // @todo: let's do this in a better way.
+ // @todo: this should move to layout.admin.js and provide better handling.
+ // Do not setup the js app if another user is currently operating on this
+ // layout (locked on the server via TempStore).
if (this.settings.locked) {
return false;
}
@@ -40,9 +45,6 @@
this.$el.sortable('destroy');
this.regionsView.remove();
this.$el.remove();
- },
- onChange: function() {
-
}
});
diff --git a/core/modules/layout/js/views/blockinstance-view.js b/core/modules/layout/js/views/blockinstance-view.js
index a407c3e..aded8a8 100644
--- a/core/modules/layout/js/views/blockinstance-view.js
+++ b/core/modules/layout/js/views/blockinstance-view.js
@@ -1,3 +1,7 @@
+/**
+ * @file
+ * This view controls a single BlockInstance.
+ */
(function ($, _, Backbone, Drupal) {
"use strict";
@@ -10,7 +14,7 @@
'drop':'onDrop'
},
onDrop:function (event, index) {
- // Trigger reorder, will be handle in Drupal.layout.RegionView
+ // Trigger reorder, will be handled in Drupal.layout.RegionView.
this.$el.trigger('reorder', [this.model, index]);
// @todo: handle dropping onto enpty region.
event.preventDefault();
diff --git a/core/modules/layout/js/views/blockinstancemodal-view.js b/core/modules/layout/js/views/blockinstancemodal-view.js
index ee3a521..7746d03 100644
--- a/core/modules/layout/js/views/blockinstancemodal-view.js
+++ b/core/modules/layout/js/views/blockinstancemodal-view.js
@@ -1,5 +1,6 @@
/**
* @file
+ * This view controls and instantiates the dialog configuring a BlockInstance.
*/
(function ($, _, Backbone, Drupal) {
diff --git a/core/modules/layout/js/views/blockselectormodal-view.js b/core/modules/layout/js/views/blockselectormodal-view.js
index 56d12d3..5e6a3c4 100644
--- a/core/modules/layout/js/views/blockselectormodal-view.js
+++ b/core/modules/layout/js/views/blockselectormodal-view.js
@@ -1,5 +1,11 @@
/**
* @file
+ * BlockSelectorModalView displays Blocks available for placement in a given
+ * region, creates BlockInstance from a selected Block adds it to the region.
+ *
+ * @todo: use a form/server-side generated listing or a proper webservice to
+ * retrieve the "placeable" Blocks.
+ * @todo: split of Drupal.layout.BlockListItemView into separate file.
*/
(function ($, _, Backbone, Drupal) {
diff --git a/core/modules/layout/js/views/modal-view.js b/core/modules/layout/js/views/modal-view.js
index ea38807..26eba39 100644
--- a/core/modules/layout/js/views/modal-view.js
+++ b/core/modules/layout/js/views/modal-view.js
@@ -1,3 +1,9 @@
+/**
+ * @file
+ * Base view wrapping a dialog.js based dialog.
+ *
+ * @todo: maybe provide meaningful form-loading?
+ */
(function ($, _, Backbone, Drupal) {
"use strict";
diff --git a/core/modules/layout/js/views/region-view.js b/core/modules/layout/js/views/region-view.js
index 705f68b..f79ce05 100644
--- a/core/modules/layout/js/views/region-view.js
+++ b/core/modules/layout/js/views/region-view.js
@@ -1,3 +1,8 @@
+/**
+ * @file
+ * This view controls a region and the blockInstances contained in it. Opens
+ * the BlockSelectorModalView on request.
+ */
(function ($, _, Backbone, Drupal) {
"use strict";
@@ -31,7 +36,8 @@
e.stopPropagation();
return ;
},
- // @todo: this should be on app-view.js but for now ...
+
+ // @todo: listen to collection events in app-view.js instead/propagate event.
saveFullLayout: function() {
// Show the "changed" notice.
$('.display-changed').removeClass('js-hide');
@@ -46,12 +52,11 @@
nestedViewTagName:'div'
});
-
- // make sure we gather a bunch (300ms) of change events before we push the
- // last to the server. @todo: make sure that the submit button is disabled
- // so that we don't have crazy race condition thingies.
+ // @todo: be selective about what change-events trigger requests to the
+ // server.
+ // "Debounce" rapid sequences of change events to avoid unnecessary requests.
blockInstances.on('change', _.debounce(function() {
- Drupal.layout.appModel.save();
+ this.saveFullLayout();
}, 100), this);
blockInstances.on('add', this.saveFullLayout, this);
blockInstances.on('remove', this.saveFullLayout, this);
@@ -63,10 +68,12 @@
this._collectionView.render();
return this;
},
+
remove:function () {
this.$el.empty();
this._collectionView.remove();
},
+
reorderInstances:function (event, model, position) {
var collection = this.model.get('blockInstances');
// handle cross collection.
@@ -93,5 +100,4 @@
}
});
-
})(jQuery, _, Backbone, Drupal);
diff --git a/core/modules/layout/layout.admin.inc b/core/modules/layout/layout.admin.inc
index 20b6f15..8bc2270 100644
--- a/core/modules/layout/layout.admin.inc
+++ b/core/modules/layout/layout.admin.inc
@@ -97,7 +97,16 @@ function layout_master_edit(Display $display) {
}
/**
- * Page callback: temporary webservice.
+ * Page callback: webservice handling RESTful(-ish) requests from the JS app.
+ * Currently it only updates the *whole*
+ *
+ * @todo: convert this to use the routing system (currently we use
+ * Backbone.emulateHTTP).
+ * @todo: allow this to consume application/json-payloads (currently we need
+ * Backbone.emulateJSON)
+ * @todo: protect against CSRF. We should use a session-based token. This issue
+ * will reoccur with all RESTful services, because a per-request token (à la
+ * form-token just is not RESTful).
*
* @param Drupal\layout\Plugin\Core\Entity\Display $display
* @param null $a
@@ -160,17 +169,27 @@ function layout_master_break_lock_confirm($form, &$form_state, Display $display)
$cancel = drupal_container()->get('request')->query->get('cancel');
if (empty($cancel)) {
- $cancel = 'admin/structure/layouts/' . $display->get('name') . '/edit';
+ $cancel = 'admin/structure/layouts/manage/' . $display->get('id') . '/edit';
}
$account = user_load($display->locked->owner);
$form = confirm_form($form,
t('Do you want to break the lock on display %name?',
- array('%name' => $display->get('name'))),
+ array('%name' => $display->get('label'))),
$cancel,
t('By breaking this lock, any unsaved changes made by !user will be lost.', array('!user' => theme('username', array('account' => $account)))),
t('Break lock'),
t('Cancel'));
- $form['actions']['submit']['#submit'][] = array($display, 'submitBreakLock');
+ $form['actions']['submit']['#submit'][] = 'layout_master_break_lock_confirm_submit';
return $form;
}
+
+/**
+ * Form submit handler to break_lock on a display.
+ */
+function layout_master_break_lock_confirm_submit(&$form, &$form_state) {
+ $display = $form_state['display'];
+ drupal_container()->get('user.tempstore')->get('layout')->delete($display->get('id'));
+ $form_state['redirect'] = 'admin/structure/layouts/manage/' . $display->get('id') . '/edit';
+ drupal_set_message(t('The lock has been broken and you may now edit this display.'));
+}
diff --git a/core/modules/layout/lib/Drupal/layout/DisplayFormController.php b/core/modules/layout/lib/Drupal/layout/DisplayFormController.php
index 05306a4..f505d69 100644
--- a/core/modules/layout/lib/Drupal/layout/DisplayFormController.php
+++ b/core/modules/layout/lib/Drupal/layout/DisplayFormController.php
@@ -54,12 +54,12 @@ public function form(array $form, array &$form_state, EntityInterface $display)
$form['locked'] = array(
'#type' => 'container',
'#attributes' => array('class' => array('view-locked', 'messages', 'warning')),
- '#children' => t('This display is being edited by user !user, and is therefore locked from editing by others. This lock is !age old. Click here to
break this lock.',
+ '#children' => t('This display is being edited by user !user, and is therefore locked from editing by others. This lock is @age old. Click here to
break this lock.',
// @todo: add callback to break lock.
array(
'!user' => theme('username', array('account' => user_load($display->locked->owner))),
- '!age' => format_interval(REQUEST_TIME - $display->locked->updated),
- '!break' => url('admin/structure/layouts/manage/' . $display->get('id') . '/break-lock')
+ '@age' => format_interval(REQUEST_TIME - $display->locked->updated),
+ '@break' => url('admin/structure/layouts/manage/' . $display->get('id') . '/break-lock')
)
),
'#weight' => -10,
@@ -195,14 +195,9 @@ protected function actions(array $form, array &$form_state) {
public function save(array $form, array &$form_state) {
$display = $this->getEntity($form_state);
$cached_display = layout_master_cache_load($display->id);
- // @todo: properly handle storing template / other form values
- // this is a all bohemian villages to me ...
- $cached_display->set('layout', $form_state['values']['layout']);
-
- // As the changes are cached in TempStore loading the cached instance and
- // saving it should be enough.
+ // All changes are already in TempStore. So a save commit all changes.
$cached_display->save();
- // Cache busting. Remove this view from cache so we can edit it properly.
+ // Bust the TempStore. Remove the display form TempStore so it will be reloaded.
drupal_container()->get('user.tempstore')->get('layout')->delete($cached_display->id);
watchdog('display', 'Layout @label saved.', array('@label' => $display->label()), WATCHDOG_NOTICE);
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php
index 4875c1e..bd3e52d 100644
--- a/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php
@@ -183,14 +183,4 @@ public function getLayoutInstance() {
return $this->layoutInstance;
}
-
- /**
- * Submit handler to break_lock on a display.
- * @todo: is this the best place? copied from ViewsUI.
- */
- public function submitBreakLock(&$form, &$form_state) {
- drupal_container()->get('user.tempstore')->get('layout')->delete($this->get('id'));
- $form_state['redirect'] = 'admin/structure/layouts/manage/' . $this->get('id') . '/edit';
- drupal_set_message(t('The lock has been broken and you may now edit this display.'));
- }
}