pageflow 15.1.0.beta6 → 15.1.0.rc0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of pageflow might be problematic. Click here for more details.

Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/app/assets/javascripts/pageflow/dist/editor.js +613 -94
  4. data/app/assets/javascripts/pageflow/dist/ui.js +120 -3
  5. data/app/assets/stylesheets/pageflow/editor/base.scss +1 -0
  6. data/app/assets/stylesheets/pageflow/editor/composables.scss +9 -0
  7. data/app/assets/stylesheets/pageflow/editor/file_import.scss +7 -8
  8. data/app/helpers/pageflow/config_helper.rb +1 -1
  9. data/app/helpers/pageflow/entries_helper.rb +6 -1
  10. data/app/helpers/pageflow/social_share_links_helper.rb +5 -1
  11. data/config/locales/de.yml +34 -16
  12. data/config/locales/en.yml +34 -16
  13. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +613 -93
  14. data/entry_types/paged/app/views/layouts/pageflow_paged/application.html.erb +2 -1
  15. data/entry_types/paged/lib/pageflow_paged/engine.rb +1 -0
  16. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/chapters_controller.rb +9 -1
  17. data/entry_types/scrolled/app/helpers/pageflow_scrolled/entry_json_seed_helper.rb +2 -0
  18. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry.json.jbuilder +28 -0
  19. data/entry_types/scrolled/config/locales/new/de.yml +46 -0
  20. data/entry_types/scrolled/config/locales/new/en.yml +46 -0
  21. data/entry_types/scrolled/lib/pageflow_scrolled/engine.rb +1 -0
  22. data/entry_types/scrolled/package/editor.js +2844 -78
  23. data/entry_types/scrolled/package/frontend.js +955 -443
  24. data/entry_types/scrolled/package/package.json +1 -0
  25. data/lib/pageflow/version.rb +1 -1
  26. data/packages/pageflow/editor.js +485 -90
  27. data/packages/pageflow/ui.js +120 -3
  28. metadata +5 -4
  29. data/config/locales/new/entry_metadata_configuration.de.yml +0 -17
  30. data/config/locales/new/entry_metadata_configuration.en.yml +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 589502715806a47772d831c8446f1fdc319d72b411e1c13fc00b0e5e4d5aec48
4
- data.tar.gz: f656cafc219dc4507206bec637e03d876d98045627779fe0de904c8d270e6ae4
3
+ metadata.gz: 08558155f5c70177a1eed286764157dae4b05d8fe5d5e4386f76c31727395a82
4
+ data.tar.gz: 2854663e54564f273bf779760b22636f26bcbf2644cdb5a28eed3133be6324b8
5
5
  SHA512:
6
- metadata.gz: eb958ba5f3e90b14dc67b23c8a7913da07d911a0397fb801171be92567af8ffec46b271ff917ce252ac3909def665884a5f060f33da7b663499501d24d1f3baf
7
- data.tar.gz: 714ff5f118de41a7820a2b8dbd355cedbba6dd33c13222ba8cd0684c6438bac0ae10807183302c1e45d556ac74dff4a8bfe6c1369b611a547f9c14c4a2c805e8
6
+ metadata.gz: 36b63f17d6d9408dc2519509b960354fb243a0038d58f53eab3b640bbe6b602af1a99dc9a9d55ec1a615b7f5ce349a84ee47f97adb7c4f8ddbad145dfa232bc2
7
+ data.tar.gz: 3cdfa12ed5e775ac7f998a15dcb9152c3243de25c837dbb8302768635994107c578d4be308f33d0cf0ce3b89573b7b85a56eb6dba1f3a546bda04347fb2059d4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # CHANGELOG
2
2
 
3
+ ### Version 15.1.0.rc0
4
+
5
+ 2020-02-05
6
+
7
+ [Compare changes](https://github.com/codevise/pageflow/compare/15-0-stable...v15.1.0.rc0)
8
+
9
+ - Changed file importer box size and margins of footer
10
+ ([#1323](https://github.com/codevise/pageflow/pull/1323))
11
+ - Add meta charset tag in application layout of paged entries
12
+ ([#1322](https://github.com/codevise/pageflow/pull/1322))
13
+ - Re-add help entries for appearance options
14
+ ([#1321](https://github.com/codevise/pageflow/pull/1321))
15
+ - Sharing, imprint, privacy in navigation
16
+ ([#1319](https://github.com/codevise/pageflow/pull/1319))
17
+ - Outline editing for scrolled entries
18
+ ([#1318](https://github.com/codevise/pageflow/pull/1318))
19
+
3
20
  ### Version 15.1.0.beta6
4
21
 
5
22
  2020-01-29
@@ -186,6 +186,48 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
186
186
  findKeyWithTranslation: findKeyWithTranslation,
187
187
  translationKeysWithSuffix: translationKeysWithSuffix
188
188
  });
189
+
190
+ function _arrayWithHoles(arr) {
191
+ if (Array.isArray(arr)) return arr;
192
+ }
193
+
194
+ function _iterableToArrayLimit(arr, i) {
195
+ if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) {
196
+ return;
197
+ }
198
+
199
+ var _arr = [];
200
+ var _n = true;
201
+ var _d = false;
202
+ var _e = undefined;
203
+
204
+ try {
205
+ for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
206
+ _arr.push(_s.value);
207
+
208
+ if (i && _arr.length === i) break;
209
+ }
210
+ } catch (err) {
211
+ _d = true;
212
+ _e = err;
213
+ } finally {
214
+ try {
215
+ if (!_n && _i["return"] != null) _i["return"]();
216
+ } finally {
217
+ if (_d) throw _e;
218
+ }
219
+ }
220
+
221
+ return _arr;
222
+ }
223
+
224
+ function _nonIterableRest() {
225
+ throw new TypeError("Invalid attempt to destructure non-iterable instance");
226
+ }
227
+
228
+ function _slicedToArray(arr, i) {
229
+ return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
230
+ }
189
231
  /**
190
232
  * Create object that can be passed to Marionette ui property from CSS
191
233
  * module object.
@@ -216,7 +258,7 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
216
258
  * <div class=${styles.container}></div>
217
259
  * `,
218
260
  *
219
- * ui: cssModulesUtils.ui(styles, 'container');
261
+ * ui: cssModulesUtils.ui(styles, 'container'),
220
262
  *
221
263
  * onRender() {
222
264
  * this.ui.container // => JQuery wrapper for container element
@@ -226,22 +268,98 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
226
268
  * @memberof cssModulesUtils
227
269
  */
228
270
 
271
+
229
272
  function ui(styles) {
230
273
  for (var _len = arguments.length, classNames = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
231
274
  classNames[_key - 1] = arguments[_key];
232
275
  }
233
276
 
234
277
  return classNames.reduce(function (result, className) {
235
- result[className] = ".".concat(styles[className]);
278
+ result[className] = selector(styles, className);
236
279
  return result;
237
280
  }, {});
238
281
  }
282
+ /**
283
+ * Create object that can be passed to Marionette events property from CSS
284
+ * module object.
285
+ *
286
+ * @param {Object} styles
287
+ * Class name mapping imported from `.module.css` file.
288
+ *
289
+ * @param {Object} mapping
290
+ * Events mapping using keys from the `styles` instead of CSS class names.
291
+ *
292
+ * @return {Object}
293
+ *
294
+ * @example
295
+ *
296
+ * // MyView.module.css
297
+ *
298
+ * .addButton {}
299
+ *
300
+ * // MyView.js
301
+ *
302
+ * import Marionette from 'marionette';
303
+ * import {cssModulesUtils} from 'pageflow/ui';
304
+ *
305
+ * import styles from './MyView.module.css';
306
+ *
307
+ * export const MyView = Marionette.ItemView({
308
+ * template: () => `
309
+ * <button class=${styles.addButton}></button>
310
+ * `,
311
+ *
312
+ * events: cssModulesUtils.ui(styles, {
313
+ * 'click addButton': () => console.log('clicked add button');
314
+ * })
315
+ * });
316
+ *
317
+ * @memberof cssModulesUtils
318
+ */
319
+
320
+
321
+ function events(styles, mapping) {
322
+ return Object.keys(mapping).reduce(function (result, key) {
323
+ var _key$split = key.split(' '),
324
+ _key$split2 = _slicedToArray(_key$split, 2),
325
+ event = _key$split2[0],
326
+ className = _key$split2[1];
327
+
328
+ result["".concat(event, " ").concat(selector(styles, className))] = mapping[key];
329
+ return result;
330
+ }, {});
331
+ }
332
+ /**
333
+ * Generates a CSS selector from a CSS module rule.
334
+ *
335
+ * @param {Object} styles
336
+ * Class name mapping imported from `.module.css` file.
337
+ *
338
+ * @param {String} className
339
+ * Key from the `styles` object.
340
+ *
341
+ * @return {String} CSS Selector
342
+ * @memberof cssModulesUtils
343
+ */
344
+
345
+
346
+ function selector(styles, className) {
347
+ var classNames = styles[className];
348
+
349
+ if (!classNames) {
350
+ throw new Error("Unknown class name ".concat(className, " in mapping. Knwon names: ").concat(Object.keys(styles).join(', '), "."));
351
+ }
352
+
353
+ return ".".concat(classNames.replace(/ /g, '.'));
354
+ }
239
355
 
240
356
  var cssModulesUtils =
241
357
  /*#__PURE__*/
242
358
  Object.freeze({
243
359
  __proto__: null,
244
- ui: ui
360
+ ui: ui,
361
+ events: events,
362
+ selector: selector
245
363
  }); // https://github.com/jashkenas/backbone/issues/2601
246
364
 
247
365
  function BaseObject(options) {
@@ -1088,6 +1206,8 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
1088
1206
  this.$el.addClass('input');
1089
1207
  this.$el.addClass(this.model.modelName + '_' + this.options.propertyName);
1090
1208
  this.$el.data('inputPropertyName', this.options.propertyName);
1209
+ this.$el.data('labelText', this.labelText());
1210
+ this.$el.data('inlineHelpText', this.inlineHelpText());
1091
1211
  this.ui.labelText.text(this.labelText());
1092
1212
  this.ui.inlineHelp.html(this.inlineHelpText());
1093
1213
 
@@ -3388,6 +3508,62 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
3388
3508
  };
3389
3509
  });
3390
3510
 
3511
+ // different model types. Backbone.Collection tries to merge records
3512
+ // if they have the same id.
3513
+
3514
+ var MultiCollection = function MultiCollection() {
3515
+ this.records = {};
3516
+ this.length = 0;
3517
+ };
3518
+
3519
+ _$1.extend(MultiCollection.prototype, {
3520
+ add: function add(record) {
3521
+ if (!this.records[record.cid]) {
3522
+ this.records[record.cid] = record;
3523
+ this.length = _$1.keys(this.records).length;
3524
+ this.trigger('add', record);
3525
+ }
3526
+ },
3527
+ remove: function remove(record) {
3528
+ if (this.records[record.cid]) {
3529
+ delete this.records[record.cid];
3530
+ this.length = _$1.keys(this.records).length;
3531
+ this.trigger('remove', record);
3532
+ }
3533
+ },
3534
+ isEmpty: function isEmpty() {
3535
+ return this.length === 0;
3536
+ }
3537
+ });
3538
+
3539
+ _$1.extend(MultiCollection.prototype, Backbone.Events);
3540
+
3541
+ MultiCollection.extend = Backbone.Collection.extend;
3542
+
3543
+ /**
3544
+ * Watch Backbone collections to track which models are currently
3545
+ * being saved. Used to update the notifications view displaying
3546
+ * saving status/failutes.
3547
+ */
3548
+
3549
+ var SavingRecordsCollection = MultiCollection.extend({
3550
+ /**
3551
+ * Listen to events of models in collection to track when they are
3552
+ * being saved.
3553
+ *
3554
+ * @param {Backbone.Collection} collection - Collection to watch.
3555
+ */
3556
+ watch: function watch(collection) {
3557
+ var that = this;
3558
+ this.listenTo(collection, 'request', function (model, xhr) {
3559
+ that.add(model);
3560
+ xhr.always(function () {
3561
+ that.remove(model);
3562
+ });
3563
+ });
3564
+ }
3565
+ });
3566
+
3391
3567
  var WidgetType = BaseObject.extend({
3392
3568
  initialize: function initialize(serverSideConfig, clientSideConfig) {
3393
3569
  this.name = serverSideConfig.name;
@@ -3477,6 +3653,15 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
3477
3653
  */
3478
3654
 
3479
3655
  this.failures = new FailuresAPI();
3656
+ /**
3657
+ * Tracking records that are currently being saved.
3658
+ *
3659
+ * @returns {SavingRecordsCollection}
3660
+ * @memberof editor
3661
+ * @since edge
3662
+ */
3663
+
3664
+ this.savingRecords = new SavingRecordsCollection();
3480
3665
  /**
3481
3666
  * Set up editor integration for page types.
3482
3667
  * @memberof editor
@@ -3523,7 +3708,9 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
3523
3708
  * Backbone view that will be rendered in the side bar.
3524
3709
  */
3525
3710
  registerEntryType: function registerEntryType(name, options) {
3526
- this.entryType = options;
3711
+ this.entryType = _objectSpread({
3712
+ name: name
3713
+ }, options);
3527
3714
  },
3528
3715
  createEntryModel: function createEntryModel(seed, options) {
3529
3716
  var entry = new this.entryType.entryModel(seed.entry, options);
@@ -3737,6 +3924,79 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
3737
3924
  });
3738
3925
  };
3739
3926
 
3927
+ /**
3928
+ * Mixins for Backbone models and collections that use entry type
3929
+ * specific editor controllers registered via the `editor_app` entry
3930
+ * type option.
3931
+ */
3932
+
3933
+ var entryTypeEditorControllerUrls = {
3934
+ /**
3935
+ * Mixins for Backbone collections that defines `url` method.
3936
+ *
3937
+ * @param {Object} options
3938
+ * @param {String} options.resources - Path suffix of the controller route
3939
+ *
3940
+ * @example
3941
+ *
3942
+ * import {editor, entryTypeEditorControllerUrls} from 'pageflow/editor';
3943
+ *
3944
+ * editor.registerEntryType('test', {
3945
+ // ...
3946
+ });
3947
+ *
3948
+ * export const ItemsCollection = Backbone.Collection.extend({
3949
+ * mixins: [entryTypeEditorControllerUrls.forCollection({resources: 'items'})
3950
+ * });
3951
+ *
3952
+ * new ItemsCollection().url() // => '/editor/entries/10/test/items'
3953
+ */
3954
+ forCollection: function forCollection(_ref) {
3955
+ var resources = _ref.resources;
3956
+ return {
3957
+ url: function url() {
3958
+ return entryTypeEditorControllerUrl(resources);
3959
+ },
3960
+ urlSuffix: function urlSuffix() {
3961
+ return "/".concat(resources);
3962
+ }
3963
+ };
3964
+ },
3965
+
3966
+ /**
3967
+ * Mixins for Backbone models that defines `urlRoot` method.
3968
+ *
3969
+ * @param {Object} options
3970
+ * @param {String} options.resources - Path suffix of the controller route
3971
+ *
3972
+ * @example
3973
+ *
3974
+ * import {editor, entryTypeEditorControllerUrls} from 'pageflow/editor';
3975
+ *
3976
+ * editor.registerEntryType('test', {
3977
+ // ...
3978
+ });
3979
+ *
3980
+ * export const Item = Backbone.Model.extend({
3981
+ * mixins: [entryTypeEditorControllerUrls.forModel({resources: 'items'})
3982
+ * });
3983
+ *
3984
+ * new Item({id: 20}).url() // => '/editor/entries/10/test/items/20'
3985
+ */
3986
+ forModel: function forModel(_ref2) {
3987
+ var resources = _ref2.resources;
3988
+ return {
3989
+ urlRoot: function urlRoot() {
3990
+ return this.isNew() ? this.collection.url() : entryTypeEditorControllerUrl(resources);
3991
+ }
3992
+ };
3993
+ }
3994
+ };
3995
+
3996
+ function entryTypeEditorControllerUrl(resources) {
3997
+ return [state.entry.url(), editor$1.entryType.name, resources].join('/');
3998
+ }
3999
+
3740
4000
  var formDataUtils = {
3741
4001
  fromModel: function fromModel(model) {
3742
4002
  var object = {};
@@ -3844,7 +4104,7 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
3844
4104
  this.reset();
3845
4105
  },
3846
4106
  url: function url() {
3847
- return this.parentModel.url() + _$1.result(this.parent, 'url');
4107
+ return this.parentModel.url() + (_$1.result(this.parent, 'urlSuffix') || _$1.result(this.parent, 'url'));
3848
4108
  },
3849
4109
  dispose: function dispose() {
3850
4110
  this.stopListening();
@@ -4153,10 +4413,23 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
4153
4413
  Cocktail.mixin(Configuration, mixin);
4154
4414
  });
4155
4415
 
4416
+ /**
4417
+ * Remove model from collection only after the `DELETE` request has
4418
+ * succeeded. Still allow tracking that the model is being destroyed
4419
+ * by triggering a `destroying` event and adding a `isDestroying`
4420
+ * method.
4421
+ */
4422
+
4156
4423
  var delayedDestroying = {
4157
4424
  initialize: function initialize() {
4158
4425
  this._destroying = false;
4426
+ this._destroyed = false;
4159
4427
  },
4428
+
4429
+ /**
4430
+ * Trigger `destroying` event and send `DELETE` request. Only remove
4431
+ * model from collection once the request is done.
4432
+ */
4160
4433
  destroyWithDelay: function destroyWithDelay() {
4161
4434
  var model = this;
4162
4435
  this._destroying = true;
@@ -4165,17 +4438,33 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
4165
4438
  wait: true,
4166
4439
  success: function success() {
4167
4440
  model._destroying = false;
4441
+ model._destroyed = true;
4168
4442
  },
4169
4443
  error: function error() {
4170
4444
  model._destroying = false;
4171
4445
  }
4172
4446
  });
4173
4447
  },
4448
+
4449
+ /**
4450
+ * Get whether the model is currently being destroyed.
4451
+ */
4174
4452
  isDestroying: function isDestroying() {
4175
4453
  return this._destroying;
4454
+ },
4455
+
4456
+ /**
4457
+ * Get whether the model has been destroyed.
4458
+ */
4459
+ isDestroyed: function isDestroyed() {
4460
+ return this._destroyed;
4176
4461
  }
4177
4462
  };
4178
4463
 
4464
+ /**
4465
+ * Mixin for Backbone models that shall be watched by {@link
4466
+ * modelLifecycleTrackingView} mixin.
4467
+ */
4179
4468
  var failureTracking = {
4180
4469
  initialize: function initialize() {
4181
4470
  this._saveFailed = false;
@@ -5634,6 +5923,76 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
5634
5923
  };
5635
5924
  editor$1.registerFileSelectionHandler('pageLink', PageLinkFileSelectionHandler);
5636
5925
 
5926
+ /**
5927
+ * Mixins for models with a nested configuration model.
5928
+ *
5929
+ * Triggers events on the parent model of the form
5930
+ * `change:configuration` and `change:configuration:<attribute>`, when
5931
+ * the configuration changes.
5932
+ *
5933
+ * @param {Object} [options]
5934
+ * @param {Function} [options.configurationModel] -
5935
+ * Backbone model to use for nested configuration model.
5936
+ * @param {Boolean} [options.autoSave] -
5937
+ * Save model when configuration changes.
5938
+ * @param {Boolean|Array<String>} [options.includeAttributesInJSON] -
5939
+ * Include all or specific attributes of the parent model in the
5940
+ * data returned by `toJSON` besides the `configuration` property.
5941
+ * @returns {Object} - Mixin to be included in model.
5942
+ *
5943
+ * @example
5944
+ *
5945
+ * import {configurationContainer} from 'pageflow/editor';
5946
+ *
5947
+ * const Section = Backbone.Model.extend({
5948
+ * mixins: [configurationContainer({autoSave: true})]
5949
+ * });
5950
+ *
5951
+ * const section = new Section({configuration: {some: 'value'}});
5952
+ * section.configuration.get('some') // => 'value';
5953
+ */
5954
+
5955
+ function configurationContainer() {
5956
+ var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
5957
+ configurationModel = _ref.configurationModel,
5958
+ autoSave = _ref.autoSave,
5959
+ includeAttributesInJSON = _ref.includeAttributesInJSON;
5960
+
5961
+ configurationModel = configurationModel || Configuration.extend({
5962
+ defaults: {}
5963
+ });
5964
+ return {
5965
+ initialize: function initialize() {
5966
+ this.configuration = new configurationModel(this.get('configuration'));
5967
+ this.configuration.parent = this;
5968
+ this.listenTo(this.configuration, 'change', function () {
5969
+ if (!this.isNew() && (!this.isDestroying || !this.isDestroying()) && (!this.isDestroyed || !this.isDestroyed()) && autoSave) {
5970
+ this.save();
5971
+ }
5972
+
5973
+ this.trigger('change:configuration', this);
5974
+
5975
+ _$1.chain(this.configuration.changed).keys().each(function (name) {
5976
+ this.trigger('change:configuration:' + name, this, this.configuration.get(name));
5977
+ }, this);
5978
+ });
5979
+ },
5980
+ toJSON: function toJSON() {
5981
+ var attributes = {};
5982
+
5983
+ if (includeAttributesInJSON === true) {
5984
+ attributes = _$1.clone(this.attributes);
5985
+ } else if (includeAttributesInJSON) {
5986
+ attributes = _$1.pick(this.attributes, includeAttributesInJSON);
5987
+ }
5988
+
5989
+ return _$1.extend(attributes, {
5990
+ configuration: this.configuration.toJSON()
5991
+ });
5992
+ }
5993
+ };
5994
+ }
5995
+
5637
5996
  var persistedPromise = {
5638
5997
  persisted: function persisted() {
5639
5998
  var model = this;
@@ -5997,6 +6356,61 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
5997
6356
  }
5998
6357
  });
5999
6358
 
6359
+ /**
6360
+ * A Backbone collection that is automatically updated to only
6361
+ * contain models with a foreign key matching the id of a parent
6362
+ * model.
6363
+ *
6364
+ * @param {Object} options
6365
+ * @param {Backbone.Model} options.parentModel -
6366
+ * Model whose id is compared to foreign keys.
6367
+ * @param {Backbone.Collection} options.parent -
6368
+ * Collection to filter items with matching foreign key from.
6369
+ * @param {String} options.foreignKeyAttribute -
6370
+ * Attribute to compare to id of parent model.
6371
+ * @param {String} options.parentReferenceAttribute -
6372
+ * Set reference to parent model on models in collection.
6373
+ *
6374
+ * @since edge
6375
+ */
6376
+
6377
+ var ForeignKeySubsetCollection = SubsetCollection.extend({
6378
+ mixins: [orderedCollection],
6379
+ constructor: function constructor(options) {
6380
+ var parent = options.parent;
6381
+ var parentModel = options.parentModel;
6382
+ SubsetCollection.prototype.constructor.call(this, {
6383
+ parent: parent,
6384
+ parentModel: parentModel,
6385
+ filter: function filter(item) {
6386
+ return !parentModel.isNew() && item.get(options.foreignKeyAttribute) === parentModel.id;
6387
+ },
6388
+ comparator: function comparator(item) {
6389
+ return item.get('position');
6390
+ }
6391
+ });
6392
+ this.listenTo(this, 'add', function (model) {
6393
+ if (options.parentReferenceAttribute) {
6394
+ model[options.parentReferenceAttribute] = parentModel;
6395
+ }
6396
+
6397
+ model.set(options.foreignKeyAttribute, parentModel.id);
6398
+ });
6399
+ this.listenTo(parentModel, 'destroy', function () {
6400
+ this.clear();
6401
+ });
6402
+
6403
+ if (options.parentReferenceAttribute) {
6404
+ this.each(function (model) {
6405
+ return model[options.parentReferenceAttribute] = parentModel;
6406
+ });
6407
+ this.listenTo(this, 'remove', function (model) {
6408
+ model[options.parentReferenceAttribute] = null;
6409
+ });
6410
+ }
6411
+ }
6412
+ });
6413
+
6000
6414
  var PageLinksCollection = Backbone.Collection.extend({
6001
6415
  model: PageLink,
6002
6416
  initialize: function initialize(models, options) {
@@ -6216,50 +6630,6 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
6216
6630
  };
6217
6631
  Cocktail.mixin(Backbone.Collection, addAndReturnModel);
6218
6632
 
6219
- // different model types. Backbone.Collection tries to merge records
6220
- // if they have the same id.
6221
-
6222
- var MultiCollection = function MultiCollection() {
6223
- this.records = {};
6224
- this.length = 0;
6225
- };
6226
-
6227
- _$1.extend(MultiCollection.prototype, {
6228
- add: function add(record) {
6229
- if (!this.records[record.cid]) {
6230
- this.records[record.cid] = record;
6231
- this.length = _$1.keys(this.records).length;
6232
- this.trigger('add', record);
6233
- }
6234
- },
6235
- remove: function remove(record) {
6236
- if (this.records[record.cid]) {
6237
- delete this.records[record.cid];
6238
- this.length = _$1.keys(this.records).length;
6239
- this.trigger('remove', record);
6240
- }
6241
- },
6242
- isEmpty: function isEmpty() {
6243
- return this.length === 0;
6244
- }
6245
- });
6246
-
6247
- _$1.extend(MultiCollection.prototype, Backbone.Events);
6248
-
6249
- MultiCollection.extend = Backbone.Collection.extend;
6250
-
6251
- var SavingRecordsCollection = MultiCollection.extend({
6252
- watch: function watch(collection) {
6253
- var that = this;
6254
- this.listenTo(collection, 'request', function (model, xhr) {
6255
- that.add(model);
6256
- xhr.always(function () {
6257
- that.remove(model);
6258
- });
6259
- });
6260
- }
6261
- });
6262
-
6263
6633
  var SidebarRouter = Marionette.AppRouter.extend({
6264
6634
  appRoutes: {
6265
6635
  'page_links/:id': 'pageLink',
@@ -6440,24 +6810,87 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
6440
6810
  });
6441
6811
  };
6442
6812
 
6443
- var failureIndicatingView = {
6444
- modelEvents: {
6445
- 'change:failed': 'updateFailIndicator'
6446
- },
6447
- events: {
6448
- 'click .retry': function clickRetry() {
6813
+ /**
6814
+ * Mixin for Marionette Views that sets css class names according to
6815
+ * life cycle events of its model.
6816
+ *
6817
+ * @param {Object} options
6818
+ * @param {Object} options.classNames
6819
+ * @param {String} options.classNames.creating -
6820
+ * Class name to add to root element while model is still being created.
6821
+ * @param {String} options.classNames.destroying -
6822
+ * Class name to add to root element while model is being destroyed.
6823
+ * @param {String} options.classNames.failed -
6824
+ * Class name to add to root element while model is in failed state.
6825
+ * Model needs to include {@link failureTracking} mixin.
6826
+ * @param {String} options.classNames.failureMessage -
6827
+ * Class name of the element that shall be updated with the failure
6828
+ * message. Model needs to include {@link failureTracking} mixin.
6829
+ * @param {String} options.classNames.retryButton -
6830
+ * Class name of the element that shall act as a retry button.
6831
+ */
6832
+
6833
+ function modelLifecycleTrackingView(_ref) {
6834
+ var classNames = _ref.classNames;
6835
+ return {
6836
+ events: _defineProperty({}, "click .".concat(classNames.retryButton), function click() {
6449
6837
  editor$1.failures.retry();
6450
6838
  return false;
6839
+ }),
6840
+ initialize: function initialize() {
6841
+ var _this = this;
6842
+
6843
+ if (classNames.creating) {
6844
+ this.listenTo(this.model, 'change:id', function () {
6845
+ this.$el.removeClass(classNames.creating);
6846
+ });
6847
+ }
6848
+
6849
+ if (classNames.destroying) {
6850
+ this.listenTo(this.model, 'destroying', function () {
6851
+ this.$el.addClass(classNames.destroying);
6852
+ });
6853
+ this.listenTo(this.model, 'error', function () {
6854
+ this.$el.removeClass(classNames.destroying);
6855
+ });
6856
+ }
6857
+
6858
+ if (classNames.failed || classNames.failureMessage) {
6859
+ this.listenTo(this.model, 'change:failed', function () {
6860
+ return _this.updateFailIndicator();
6861
+ });
6862
+ }
6863
+ },
6864
+ render: function render() {
6865
+ if (this.model.isNew()) {
6866
+ this.$el.addClass(classNames.creating);
6867
+ }
6868
+
6869
+ if (this.model.isDestroying && this.model.isDestroying()) {
6870
+ this.$el.addClass(classNames.destroying);
6871
+ }
6872
+
6873
+ this.updateFailIndicator();
6874
+ },
6875
+ updateFailIndicator: function updateFailIndicator() {
6876
+ if (classNames.failed) {
6877
+ this.$el.toggleClass(classNames.failed, this.model.isFailed());
6878
+ }
6879
+
6880
+ if (classNames.failureMessage) {
6881
+ this.$el.find(".".concat(classNames.failureMessage)).text(this.model.getFailureMessage());
6882
+ }
6451
6883
  }
6452
- },
6453
- onRender: function onRender() {
6454
- this.updateFailIndicator();
6455
- },
6456
- updateFailIndicator: function updateFailIndicator() {
6457
- this.$el.toggleClass('failed', this.model.isFailed());
6458
- this.$el.find('.failure .message').text(this.model.getFailureMessage());
6884
+ };
6885
+ }
6886
+
6887
+ var failureIndicatingView = modelLifecycleTrackingView({
6888
+ classNames: {
6889
+ failed: 'failed',
6890
+ failureMessage: 'failure .message',
6891
+ retryButton: 'retry'
6459
6892
  }
6460
- };
6893
+ });
6461
6894
 
6462
6895
  function template$k(data) {
6463
6896
  var __t, __p = '';
@@ -7650,7 +8083,8 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
7650
8083
  var editor = this.options.editor || {};
7651
8084
  var configurationEditor = new ConfigurationEditorView({
7652
8085
  model: entry.metadata.configuration,
7653
- tab: this.options.tab
8086
+ tab: this.options.tab,
8087
+ attributeTranslationKeyPrefixes: ['pageflow.entry_types.' + editor.entryType.name + '.editor.entry_metadata_configuration_attributes']
7654
8088
  });
7655
8089
  configurationEditor.tab('general', function () {
7656
8090
  this.input('title', TextInputView, {
@@ -7683,7 +8117,10 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
7683
8117
  });
7684
8118
  });
7685
8119
  configurationEditor.tab('widgets', function () {
7686
- editor.entryType.appearanceInputs && editor.entryType.appearanceInputs(this, entry, state.theming);
8120
+ editor.entryType.appearanceInputs && editor.entryType.appearanceInputs(this, {
8121
+ entry: entry,
8122
+ theming: state.theming
8123
+ });
7687
8124
  entry.widgets && this.view(EditWidgetsView, {
7688
8125
  model: entry,
7689
8126
  widgetTypes: editor.widgetTypes
@@ -7992,28 +8429,12 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
7992
8429
  }
7993
8430
  });
7994
8431
 
7995
- var loadable = {
7996
- modelEvents: {
7997
- 'change:id': function changeId() {
7998
- this.$el.removeClass('creating');
7999
- },
8000
- destroying: function destroying() {
8001
- this.$el.addClass('destroying');
8002
- },
8003
- error: function error() {
8004
- this.$el.removeClass('destroying');
8005
- }
8006
- },
8007
- render: function render() {
8008
- if (this.model.isNew()) {
8009
- this.$el.addClass('creating');
8010
- }
8011
-
8012
- if (this.model.isDestroying && this.model.isDestroying()) {
8013
- this.$el.addClass('destroying');
8014
- }
8432
+ var loadable = modelLifecycleTrackingView({
8433
+ classNames: {
8434
+ creating: 'creating',
8435
+ destroying: 'destroying'
8015
8436
  }
8016
- };
8437
+ });
8017
8438
 
8018
8439
  function template$F(data) {
8019
8440
  var __p = '';
@@ -10130,8 +10551,8 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
10130
10551
  onRender: function onRender() {
10131
10552
  this.listenTo(state.entry, 'change:uploading_files_count', this.notifyUploadCount);
10132
10553
  this.listenTo(state.entry, 'change:confirmable_files_count', this.notifyConfirmableFilesCount);
10133
- this.listenTo(state.savingRecords, 'add', this.update);
10134
- this.listenTo(state.savingRecords, 'remove', this.update);
10554
+ this.listenTo(editor$1.savingRecords, 'add', this.update);
10555
+ this.listenTo(editor$1.savingRecords, 'remove', this.update);
10135
10556
  this.listenTo(editor$1.failures, 'add', this.update);
10136
10557
  this.listenTo(editor$1.failures, 'remove', this.update);
10137
10558
  this.update();
@@ -10139,7 +10560,7 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
10139
10560
  },
10140
10561
  update: function update() {
10141
10562
  this.$el.toggleClass('failed', !editor$1.failures.isEmpty());
10142
- this.$el.toggleClass('saving', !state.savingRecords.isEmpty());
10563
+ this.$el.toggleClass('saving', !editor$1.savingRecords.isEmpty());
10143
10564
  this.ui.failedCount.text(editor$1.failures.count());
10144
10565
  },
10145
10566
  notifyUploadCount: function notifyUploadCount(model, uploadCount) {
@@ -10518,6 +10939,101 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
10518
10939
  app.dialogRegion.show(new ConfirmUploadView(options));
10519
10940
  };
10520
10941
 
10942
+ /**
10943
+ * Base view to edit configuration container models. Extend and
10944
+ * override the `configure` method which receives a {@link
10945
+ * ConfigurationEditorView} to define the tabs and inputs that shall
10946
+ * be displayed.
10947
+ *
10948
+ * Add a `translationKeyPrefix` property to the prototype and define
10949
+ * the following translations:
10950
+ *
10951
+ * * `<translationKeyPrefix>.tabs`: used as `tabTranslationKeyPrefix`
10952
+ * of the `ConfigurationEditorView`.
10953
+ *
10954
+ * * `<translationKeyPrefix>.attributes`: used as one of the
10955
+ * `attributeTranslationKeyPrefixes` of the
10956
+ * `ConfigurationEditorView`.
10957
+ *
10958
+ * * `<translationKeyPrefix>.back` (optional): Back button label.
10959
+ *
10960
+ * * `<translationKeyPrefix>.destroy` (optional): Destroy button
10961
+ * label.
10962
+ *
10963
+ * * `<translationKeyPrefix>.confirm_destroy` (optional): Confirm
10964
+ * message displayed before destroying.
10965
+ *
10966
+ * * `<translationKeyPrefix>.save_error` (optional): Header of the
10967
+ * failure message that is displayed if the model cannot be saved.
10968
+ *
10969
+ * * `<translationKeyPrefix>.retry` (optional): Label of the retry
10970
+ * button of the failure message.
10971
+ *
10972
+ * @param {Object} options
10973
+ * @param {Backbone.Model} options.model -
10974
+ * Model including the {@link configurationContainer},
10975
+ * {@link failureTracking} and {@link delayedDestroying} mixins.
10976
+ *
10977
+ * @since edge
10978
+ */
10979
+
10980
+ var EditConfigurationView = Marionette.Layout.extend({
10981
+ className: 'edit_configuration_view',
10982
+ template: function template(_ref) {
10983
+ var t = _ref.t;
10984
+ return "\n <a class=\"back\">".concat(t('back'), "</a>\n <a class=\"destroy\">").concat(t('destroy'), "</a>\n\n <div class=\"failure\">\n <p>").concat(t('save_error'), "</p>\n <p class=\"message\"></p>\n <a class=\"retry\" href=\"\">").concat(t('retry'), "</a>\n </div>\n\n <div class=\"configuration_container\"></div>\n ");
10985
+ },
10986
+ serializeData: function serializeData() {
10987
+ var _this = this;
10988
+
10989
+ return {
10990
+ t: function t(key) {
10991
+ return _this.t(key);
10992
+ }
10993
+ };
10994
+ },
10995
+ mixins: [failureIndicatingView],
10996
+ regions: {
10997
+ configurationContainer: '.configuration_container'
10998
+ },
10999
+ events: {
11000
+ 'click a.back': 'goBack',
11001
+ 'click a.destroy': 'destroy'
11002
+ },
11003
+ onRender: function onRender() {
11004
+ var translationKeyPrefix = _$1.result(this, 'translationKeyPrefix');
11005
+
11006
+ this.configurationEditor = new ConfigurationEditorView({
11007
+ tabTranslationKeyPrefix: "".concat(translationKeyPrefix, ".tabs"),
11008
+ attributeTranslationKeyPrefixes: ["".concat(translationKeyPrefix, ".attributes")],
11009
+ model: this.model.configuration
11010
+ });
11011
+ this.configure(this.configurationEditor);
11012
+ this.configurationContainer.show(this.configurationEditor);
11013
+ },
11014
+ onShow: function onShow() {
11015
+ this.configurationEditor.refreshScroller();
11016
+ },
11017
+ destroy: function destroy() {
11018
+ if (window.confirm(this.t('confirm_destroy'))) {
11019
+ this.model.destroyWithDelay();
11020
+ this.goBack();
11021
+ }
11022
+ },
11023
+ goBack: function goBack() {
11024
+ editor$1.navigate('/', {
11025
+ trigger: true
11026
+ });
11027
+ },
11028
+ t: function t(suffix) {
11029
+ var translationKeyPrefix = _$1.result(this, 'translationKeyPrefix');
11030
+
11031
+ return I18n$1.t("".concat(translationKeyPrefix, ".").concat(suffix), {
11032
+ defaultValue: I18n$1.t("pageflow.editor.views.edit_configuration.".concat(suffix))
11033
+ });
11034
+ }
11035
+ });
11036
+
10521
11037
  ConfigurationEditorView.register('audio', {
10522
11038
  configure: function configure() {
10523
11039
  this.tab('general', function () {
@@ -11023,9 +11539,8 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
11023
11539
  editor$1.failures.watch(state.entry);
11024
11540
  editor$1.failures.watch(state.pages);
11025
11541
  editor$1.failures.watch(state.chapters);
11026
- state.savingRecords = new SavingRecordsCollection();
11027
- state.savingRecords.watch(state.pages);
11028
- state.savingRecords.watch(state.chapters);
11542
+ editor$1.savingRecords.watch(state.pages);
11543
+ editor$1.savingRecords.watch(state.chapters);
11029
11544
  pageflow.events.trigger('seed:loaded');
11030
11545
  });
11031
11546
 
@@ -11191,6 +11706,7 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
11191
11706
  exports.DropDownButtonItemView = DropDownButtonItemView;
11192
11707
  exports.DropDownButtonView = DropDownButtonView;
11193
11708
  exports.EditChapterView = EditChapterView;
11709
+ exports.EditConfigurationView = EditConfigurationView;
11194
11710
  exports.EditEntryView = EditEntryView;
11195
11711
  exports.EditFileView = EditFileView;
11196
11712
  exports.EditLock = EditLock;
@@ -11214,6 +11730,7 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
11214
11730
  exports.EnumTableCellView = EnumTableCellView;
11215
11731
  exports.ExplorerFileItemView = ExplorerFileItemView;
11216
11732
  exports.ExtendedSelectInputView = ExtendedSelectInputView;
11733
+ exports.Failure = Failure;
11217
11734
  exports.FileConfiguration = FileConfiguration;
11218
11735
  exports.FileImport = FileImport;
11219
11736
  exports.FileInputView = FileInputView;
@@ -11234,6 +11751,7 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
11234
11751
  exports.FilesImporterView = FilesImporterView;
11235
11752
  exports.FilesView = FilesView;
11236
11753
  exports.FilteredFilesView = FilteredFilesView;
11754
+ exports.ForeignKeySubsetCollection = ForeignKeySubsetCollection;
11237
11755
  exports.HelpButtonView = HelpButtonView;
11238
11756
  exports.HelpImageView = HelpImageView;
11239
11757
  exports.HelpView = HelpView;
@@ -11248,7 +11766,6 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
11248
11766
  exports.LoadingView = LoadingView;
11249
11767
  exports.LockedView = LockedView;
11250
11768
  exports.ModelThumbnailView = ModelThumbnailView;
11251
- exports.MultiCollection = MultiCollection;
11252
11769
  exports.NestedFilesCollection = NestedFilesCollection;
11253
11770
  exports.NestedFilesView = NestedFilesView;
11254
11771
  exports.NestedTypeError = NestedTypeError;
@@ -11276,7 +11793,6 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
11276
11793
  exports.PublishEntryView = PublishEntryView;
11277
11794
  exports.ReferenceInputView = ReferenceInputView;
11278
11795
  exports.ReusableFile = ReusableFile;
11279
- exports.SavingRecordsCollection = SavingRecordsCollection;
11280
11796
  exports.Scaffold = Scaffold;
11281
11797
  exports.ScrollingView = ScrollingView;
11282
11798
  exports.SelectButtonView = SelectButtonView;
@@ -11330,10 +11846,12 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
11330
11846
  exports.addAndReturnModel = addAndReturnModel;
11331
11847
  exports.app = app;
11332
11848
  exports.authenticationProvider = authenticationProvider;
11849
+ exports.configurationContainer = configurationContainer;
11333
11850
  exports.cssModulesUtils = cssModulesUtils;
11334
11851
  exports.delayedDestroying = delayedDestroying;
11335
11852
  exports.dialogView = dialogView;
11336
11853
  exports.editor = editor$1;
11854
+ exports.entryTypeEditorControllerUrls = entryTypeEditorControllerUrls;
11337
11855
  exports.failureIndicatingView = failureIndicatingView;
11338
11856
  exports.failureTracking = failureTracking;
11339
11857
  exports.fileWithType = fileWithType;
@@ -11343,6 +11861,7 @@ this.pageflow._editorGlobalInterop = (function (exports, Backbone, _$1, Marionet
11343
11861
  exports.inputView = inputView;
11344
11862
  exports.inputWithPlaceholderText = inputWithPlaceholderText;
11345
11863
  exports.loadable = loadable;
11864
+ exports.modelLifecycleTrackingView = modelLifecycleTrackingView;
11346
11865
  exports.orderedCollection = orderedCollection;
11347
11866
  exports.persistedPromise = persistedPromise;
11348
11867
  exports.polling = polling;