Completed
Pull Request — develop (#147)
by Wachter
14:07
created

main.js ➔ ... ➔ .startPageSwitcher   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 47

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 47
rs 8.6845
c 1
b 0
f 0
cc 4
nc 8
nop 0
1
/*
2
 * This file is part of Sulu.
3
 *
4
 * (c) MASSIVE ART WebServices GmbH
5
 *
6
 * This source file is subject to the MIT license that is bundled
7
 * with this source code in the file LICENSE.
8
 */
9
10
define([
11
    'jquery',
12
    'underscore',
13
    'config',
14
    'services/husky/util',
15
    'services/suluarticle/article-manager',
16
    'services/suluarticle/article-router',
17
    'sulusecurity/services/user-manager',
18
    'sulusecurity/services/security-checker',
19
    'sulucontent/components/copy-locale-overlay/main',
20
    'sulucontent/components/open-ghost-overlay/main',
21
    './adapter/article',
22
    './adapter/article-page'
23
], function($, _, config, Util, ArticleManager, ArticleRouter, UserManager, SecurityChecker, CopyLocale, OpenGhost, Article, ArticlePage) {
24
25
    'use strict';
26
27
    var constants = {
28
        headerRightSelector: '.right-container'
29
    };
30
31
    return {
32
33
        defaults: {
34
            options: {
35
                page: 1,
36
                config: {}
37
            },
38
39
            templates: {
40
                url: '/admin/api/articles<% if (!!id) { %>/<%= id %><% } %>?locale=<%= locale %>',
41
                pageSwitcher: [
42
                    '<div class="page-changer">',
43
                    '   <span class="title"><%= label %></span>',
44
                    '   <span class="dropdown-toggle"></span>',
45
                    '</div>'
46
                ].join('')
47
            },
48
49
            translations: {
50
                headline: 'sulu_article.edit.title',
51
                draftLabel: 'sulu-document-manager.draft-label',
52
                removeDraft: 'sulu-content.delete-draft',
53
                unpublish: 'sulu-document-manager.unpublish',
54
                unpublishConfirmTextNoDraft: 'sulu-content.unpublish-confirm-text-no-draft',
55
                unpublishConfirmTextWithDraft: 'sulu-content.unpublish-confirm-text-with-draft',
56
                unpublishConfirmTitle: 'sulu-content.unpublish-confirm-title',
57
                deleteDraftConfirmTitle: 'sulu-content.delete-draft-confirm-title',
58
                deleteDraftConfirmText: 'sulu-content.delete-draft-confirm-text',
59
                deletePage: 'sulu_article.edit.delete_page',
60
                pageOf: 'sulu_article.edit.page-of',
61
                newPage: 'sulu_article.edit.new-page',
62
                openGhostOverlay: {
63
                    info: 'sulu_article.settings.open-ghost-overlay.info',
64
                    new: 'sulu_article.settings.open-ghost-overlay.new',
65
                    copy: 'sulu_article.settings.open-ghost-overlay.copy',
66
                    ok: 'sulu_article.settings.open-ghost-overlay.ok'
67
                },
68
                copyLocaleOverlay: {
69
                    info: 'sulu_article.settings.copy-locale-overlay.info'
70
                }
71
            }
72
        },
73
74
        layout: function(){
75
            return {
76
                navigation: {
77
                    collapsed: true
78
                },
79
                content: {
80
                    shrinkable: !!this.options.id
81
                },
82
                sidebar: (!!this.options.id) ? 'max' : false
83
            };
84
        },
85
86
        header: function() {
87
            var buttons = {}, editDropdown = {}, saveDropdown = {};
88
89
            if (SecurityChecker.hasPermission(this.data, 'edit')) {
90
                saveDropdown.saveDraft = {};
91
92
                if (SecurityChecker.hasPermission(this.data, 'live')) {
93
                    saveDropdown.savePublish = {};
94
                    saveDropdown.publish = {};
95
                }
96
97
                if (!!config.has('sulu_automation.enabled')) {
98
                    saveDropdown.automationInfo = {
99
                        options: {
100
                            entityId: this.options.id,
101
                            entityClass: 'Sulu\\Bundle\\ArticleBundle\\Document\\ArticleDocument',
102
                            handlerClass: [
103
                                'Sulu\\Bundle\\ContentBundle\\Automation\\DocumentPublishHandler',
104
                                'Sulu\\Bundle\\ContentBundle\\Automation\\DocumentUnpublishHandler'
105
                            ]
106
                        }
107
                    };
108
                }
109
110
                buttons.save = {
111
                    parent: 'saveWithDraft',
112
                    options: {
113
                        callback: function() {
114
                            this.sandbox.emit('sulu.toolbar.save', 'publish');
115
                        }.bind(this),
116
                        dropdownItems: saveDropdown
117
                    }
118
                };
119
120
                buttons.template = {
121
                    options: {
122
                        dropdownOptions: {
123
                            url: '/admin/articles/templates?type=' + (this.options.type || this.data.articleType),
124
                            callback: function(item) {
125
                                this.template = item.template;
126
                                this.sandbox.emit('sulu.tab.template-change', item);
127
                                this.setHeaderBar();
128
                            }.bind(this)
129
                        }
130
                    }
131
                }
132
            }
133
134
            if (SecurityChecker.hasPermission(this.data, 'live')) {
135
                editDropdown.unpublish = {
136
                    options: {
137
                        title: this.translations.unpublish,
138
                        disabled: !this.data.published,
139
                        callback: this.unpublish.bind(this)
140
                    }
141
                };
142
143
                editDropdown.divider = {
144
                    options: {
145
                        divider: true
146
                    }
147
                };
148
            }
149
150
            if (SecurityChecker.hasPermission(this.data, 'delete')) {
151
                editDropdown.delete = {
152
                    options: {
153
                        disabled: !this.options.id,
154
                        callback: this.deleteArticle.bind(this)
155
                    }
156
                };
157
158
                editDropdown.deletePage = {
159
                    options: {
160
                        title: this.translations.deletePage,
161
                        disabled: (!this.options.page || this.options.page === 1),
162
                        callback: this.deleteArticlePage.bind(this)
163
                    }
164
                };
165
            }
166
167
            editDropdown.copyLocale = {
168
                options: {
169
                    title: this.sandbox.translate('toolbar.copy-locale'),
170
                        callback: function() {
171
                        CopyLocale.startCopyLocalesOverlay.call(
172
                            this,
173
                            this.translations.copyLocaleOverlay
174
                        ).then(function(newLocales) {
175
                            // reload form when the current locale is in newLocales
176
                            if (_.contains(newLocales, this.options.locale)) {
177
                                this.toEdit(this.options.locale);
178
179
                                return;
180
                            }
181
182
                            // save new created locales to data and show success label
183
                            this.data.concreteLanguages = _.uniq(this.data.concreteLanguages.concat(newLocales));
184
                            this.sandbox.emit('sulu.labels.success.show', 'labels.success.copy-locale-desc', 'labels.success');
185
                        }.bind(this));
186
                    }.bind(this)
187
                }
188
            };
189
190
            if (!this.sandbox.util.isEmpty(editDropdown)) {
191
                buttons.edit = {
192
                    options: {
193
                        dropdownItems: editDropdown
194
                    }
195
                };
196
            }
197
198
            buttons.statePublished = {};
199
            buttons.stateTest = {};
200
201
            return {
202
                tabs: {
203
                    url: '/admin/content-navigations?alias=article&id=' + this.options.id + '&locale=' + this.options.locale + (this.options.page ? '&page=' + this.options.page : ''),
204
                    options: {
205
                        data: function() {
206
                            return this.sandbox.util.deepCopy(this.data);
207
                        }.bind(this),
208
                        url: function() {
209
                            return this.templates.url({id: this.options.id, locale: this.options.locale});
210
                        }.bind(this),
211
                        config: this.options.config,
212
                        preview: this.preview,
213
                        adapter: this.getAdapter(),
214
                        page: this.options.page,
215
                        id: this.options.id
216
                    },
217
                    componentOptions: {
218
                        values: _.defaults(this.data, {type: null})
219
                    }
220
                },
221
222
                toolbar: {
223
                    buttons: buttons,
224
                    languageChanger: {
225
                        data: this.options.config.languageChanger,
226
                        preSelected: this.options.locale
227
                    }
228
                }
229
            };
230
        },
231
232
        initialize: function() {
233
            this.$el.addClass('article-form');
234
235
            this.startPageSwitcher();
236
237
            this.bindCustomEvents();
238
            this.showDraftLabel();
239
            this.setHeaderBar(true);
240
            this.loadLocalizations();
241
242
            // the open ghost overlay component needs the current locale in `this.options.language`
243
            this.options.language = this.options.locale;
244
        },
245
246
        bindCustomEvents: function() {
247
            this.sandbox.on('sulu.header.back', this.toList.bind(this));
248
            this.sandbox.on('sulu.tab.dirty', this.setHeaderBar.bind(this));
249
            this.sandbox.on('sulu.toolbar.save', this.save.bind(this));
250
            this.sandbox.on('sulu.tab.data-changed', this.setData.bind(this));
251
            this.sandbox.on('sulu.article.error', this.handleError.bind(this));
252
            this.sandbox.on('husky.tabs.header.item.select', this.tabChanged.bind(this));
253
            this.sandbox.on('sulu.header.language-changed', this.languageChanged.bind(this));
254
        },
255
256
        /**
257
         * Language changed event.
258
         *
259
         * @param {Object} item
260
         */
261
        languageChanged: function(item) {
262
            if (item.id === this.options.locale) {
263
                return;
264
            }
265
266
            this.sandbox.sulu.saveUserSetting(this.options.config.settingsKey, item.id);
267
268
            var data = this.getAdapter().prepareData(this.data, this);
269
            if (-1 === _(data.concreteLanguages).indexOf(item.id)) {
270
                OpenGhost.openGhost.call(this, data, this.translations.openGhostOverlay).then(function(copy, src) {
271
                    if (!!copy) {
272
                        CopyLocale.copyLocale.call(
273
                            this,
274
                            data.id,
275
                            src,
276
                            [item.id],
277
                            function() {
278
                                this.toEdit(item.id);
279
                            }.bind(this)
280
                        );
281
                    } else {
282
                        // new article will be created
283
                        this.toEdit(item.id);
284
                    }
285
                }.bind(this)).fail(function() {
286
                    // the open-ghost page got canceled, so reset the language changer
287
                    this.sandbox.emit('sulu.header.change-language', this.options.language);
288
                }.bind(this));
289
            } else {
290
                this.toEdit(item.id);
291
            }
292
        },
293
294
        /**
295
         * Tab changed event, save the new tab id to `this.options.content`.
296
         * Can be removed when issue #72 is solved: https://github.com/sulu/SuluArticleBundle/issues/72
297
         *
298
         * @param {Object} item
299
         */
300
        tabChanged: function(item) {
301
            this.options.content = item.id;
302
        },
303
304
        /**
305
         * Handles the error based on its error code.
306
         *
307
         * @param {Number} errorCode
308
         * @param {Object} data
309
         * @param {string} action
310
         */
311
        handleError: function(errorCode, data, action) {
0 ignored issues
show
Unused Code introduced by
The parameter action is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter data is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
312
            switch (errorCode) {
0 ignored issues
show
Unused Code introduced by
You have a switch statement with only a default case. This statement can be turned into a normal statement.
Loading history...
313
                default:
314
                    this.sandbox.emit('sulu.labels.error.show', 'labels.error.content-save-desc', 'labels.error');
315
                    this.sandbox.emit('sulu.header.toolbar.item.enable', 'save');
316
            }
317
        },
318
319
        deleteArticle: function() {
320
            this.sandbox.sulu.showDeleteDialog(function(wasConfirmed) {
321
                if (!wasConfirmed) {
322
                    return;
323
                }
324
325
                ArticleManager.remove(this.options.id, this.options.locale).then(function() {
326
                    this.toList();
327
                }.bind(this));
328
            }.bind(this));
329
        },
330
331
        deleteArticlePage: function() {
332
            this.sandbox.sulu.showDeleteDialog(function(wasConfirmed) {
333
                if (!wasConfirmed) {
334
                    return;
335
                }
336
337
                var pageData = this.getAdapter().prepareData(this.data, this);
338
                ArticleManager.removePage(this.options.id, pageData.id, this.options.locale).then(function() {
339
                    ArticleRouter.toEditForce(this.options.id, this.options.locale);
340
                }.bind(this));
341
            }.bind(this));
342
        },
343
344
        toEdit: function(locale, id) {
345
            if (!!this.options.page && this.options.page !== 1) {
346
                return ArticleRouter.toPageEdit((id || this.options.id), this.options.page, (locale || this.options.locale))
347
            }
348
349
            ArticleRouter.toEdit((id || this.options.id), (locale || this.options.locale), this.options.content);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
350
        },
351
352
        toList: function(locale) {
353
            ArticleRouter.toList((locale || this.options.locale), (this.options.type || this.data.articleType));
354
        },
355
356
        toAdd: function(locale) {
357
            ArticleRouter.toAdd((locale || this.options.locale), (this.options.type || this.data.articleType));
358
        },
359
360
        save: function(action) {
361
            this.loadingSave();
362
363
            this.saveTab(action).then(function(data) {
364
                this.saved(data.id, data, action);
365
            }.bind(this));
366
        },
367
368
        setData: function(data) {
369
            this.data = data;
370
        },
371
372
        saveTab: function(action) {
373
            var promise = $.Deferred();
374
375
            // Display loading animation.
376
            this.sandbox.emit('sulu.header.toolbar.item.loading', 'save');
377
378
            this.sandbox.once('sulu.tab.saved', function(id, data) {
379
                promise.resolve(_.defaults(data, {type: null}));
380
            }.bind(this));
0 ignored issues
show
unused-code introduced by
The call to bind does not seem necessary since the function does not use this. Consider calling it directly.
Loading history...
381
382
            this.sandbox.emit('sulu.tab.save', action);
383
384
            return promise;
385
        },
386
387
        setHeaderBar: function(saved) {
388
            var saveDraft = !saved,
389
                savePublish = !saved,
390
                publish = !!saved && !this.data.publishedState;
391
392
            this.setSaveToolbarItems.call(this, 'saveDraft', saveDraft);
393
            this.setSaveToolbarItems.call(this, 'savePublish', savePublish);
394
            this.setSaveToolbarItems.call(this, 'publish', publish);
395
            this.setSaveToolbarItems.call(this, 'unpublish', !!this.data.published);
396
397
            if (!!saveDraft || !!savePublish || !!publish) {
398
                this.sandbox.emit('sulu.header.toolbar.item.enable', 'save', false);
399
            } else {
400
                this.sandbox.emit('sulu.header.toolbar.item.disable', 'save', false);
401
            }
402
403
            this.showState(!!this.data.published);
404
        },
405
406
        setSaveToolbarItems: function(item, value) {
407
            this.sandbox.emit('sulu.header.toolbar.item.' + (!!value ? 'enable' : 'disable'), item, false);
408
        },
409
410
        loadingSave: function() {
411
            this.sandbox.emit('sulu.header.toolbar.item.loading', 'save');
412
        },
413
414
        /**
415
         * Routes either to the list, article-add or article-edit, depending on the passed parameter.
416
         *
417
         * @param action {String} 'new', 'add' or null
418
         */
419
        afterSaveAction: function(action) {
420
            if (action === 'back') {
421
                this.toList();
422
            } else if (action === 'new') {
423
                this.toAdd();
424
            } else if (!this.options.id) {
425
                this.toEdit(this.options.locale, this.data.id);
426
            } else if (!this.options.page) {
427
                ArticleRouter.toPageEdit(this.data.id, this.data._embedded.pages.length + 1, this.options.locale);
428
            }
429
        },
430
431
        showDraftLabel: function() {
432
            this.sandbox.emit('sulu.header.tabs.label.hide');
433
434
            if (this.hasDraft(this.data)) {
435
                return;
436
            }
437
438
            UserManager.find(this.data.changer).then(function(response) {
439
                this.sandbox.emit(
440
                    'sulu.header.tabs.label.show',
441
                    this.sandbox.util.sprintf(
442
                        this.translations.draftLabel,
443
                        {
444
                            changed: this.sandbox.date.format(this.data.changed, true),
445
                            user: response.username
446
                        }
447
                    ),
448
                    [
449
                        {
450
                            id: 'delete-draft',
451
                            title: this.translations.removeDraft,
452
                            skin: 'critical',
453
                            onClick: this.deleteDraft.bind(this)
454
                        }
455
                    ]
456
                );
457
            }.bind(this));
458
        },
459
460
        deleteDraft: function() {
461
            this.sandbox.sulu.showDeleteDialog(
462
                function(wasConfirmed) {
463
                    if (!wasConfirmed) {
464
                        return;
465
                    }
466
467
                    this.sandbox.emit('husky.label.header.loading');
468
469
                    ArticleManager.removeDraft(this.data.id, this.options.locale).always(function() {
470
                        this.sandbox.emit('sulu.header.toolbar.item.enable', 'edit');
471
                    }.bind(this)).then(function(response) {
472
                        // reload page
473
                        this.sandbox.emit(
474
                            'sulu.router.navigate',
475
                            this.sandbox.mvc.history.fragment,
476
                            true,
477
                            true
478
                        );
479
                        this.saved(response.id, response);
480
                    }.bind(this)).fail(function() {
481
                        this.sandbox.emit('husky.label.header.reset');
482
                        this.sandbox.emit(
483
                            'sulu.labels.error.show',
484
                            'labels.error.remove-draft-desc',
485
                            'labels.error'
486
                        );
487
                    }.bind(this));
488
                }.bind(this),
489
                this.translations.deleteDraftConfirmTitle,
490
                this.translations.deleteDraftConfirmText
491
            )
492
        },
493
494
        hasDraft: function(data) {
495
            return !data.id || !!data.publishedState || !data.published;
496
        },
497
498
        getUrl: function(action) {
499
            var url = _.template(this.defaults.templates.url, {
500
                id: this.options.id,
501
                locale: this.options.locale
502
            });
503
504
            if (action) {
505
                url += '&action=' + action;
506
            }
507
508
            return url;
509
        },
510
511
        loadComponentData: function() {
512
            if (!this.options.id) {
513
                return {_embedded: {pages: []}};
514
            }
515
516
            var promise = $.Deferred();
517
            this.sandbox.util.load(this.getUrl()).done(function(data) {
518
                this.preview = this.getAdapter().startPreview(this, data);
519
520
                promise.resolve(data);
521
            }.bind(this));
522
523
            return promise;
524
        },
525
526
        getAdapter: function() {
527
            if (this.adapter) {
528
                return this.adapter;
529
            }
530
531
            return this.adapter = (this.options.page === 1 ? Article : ArticlePage);
532
        },
533
534
        destroy: function() {
535
            if (!!this.preview) {
536
                this.getAdapter().destroyPreview(this.preview);
537
            }
538
539
            if (!!this.$dropdownElement) {
540
                this.sandbox.stop(this.$dropdownElement);
541
            }
542
        },
543
544
        showState: function(published) {
545
            if (!!published && !this.data.type) {
546
                this.sandbox.emit('sulu.header.toolbar.item.hide', 'stateTest');
547
                this.sandbox.emit('sulu.header.toolbar.item.show', 'statePublished');
548
            } else {
549
                this.sandbox.emit('sulu.header.toolbar.item.hide', 'statePublished');
550
                this.sandbox.emit('sulu.header.toolbar.item.show', 'stateTest');
551
            }
552
        },
553
554
        unpublish: function() {
555
            this.sandbox.sulu.showConfirmationDialog({
556
                callback: function(wasConfirmed) {
557
                    if (!wasConfirmed) {
558
                        return;
559
                    }
560
561
                    this.sandbox.emit('sulu.header.toolbar.item.loading', 'edit');
562
563
                    ArticleManager.unpublish(this.data.id, this.options.locale).always(function() {
564
                        this.sandbox.emit('sulu.header.toolbar.item.enable', 'edit');
565
                    }.bind(this)).then(function(response) {
566
                        this.sandbox.emit(
567
                            'sulu.labels.success.show',
568
                            'labels.success.content-unpublish-desc',
569
                            'labels.success'
570
                        );
571
                        this.saved(response.id, response);
572
                    }.bind(this)).fail(function() {
573
                        this.sandbox.emit(
574
                            'sulu.labels.error.show',
575
                            'labels.error.content-unpublish-desc',
576
                            'labels.error'
577
                        );
578
                    }.bind(this));
579
                }.bind(this),
580
                title: this.translations.unpublishConfirmTitle,
581
                description: !!this.hasDraft(this.data) ?
582
                    this.translations.unpublishConfirmTextNoDraft :
583
                    this.translations.unpublishConfirmTextWithDraft
584
            });
585
        },
586
587
        saved: function(id, data, action) {
588
            this.setData(data);
589
590
            if (!this.options.id) {
591
                this.sandbox.sulu.viewStates.justSaved = true;
592
            } else {
593
                this.setHeaderBar(true);
594
                this.showDraftLabel();
595
596
                this.sandbox.emit('sulu.header.saved', data);
597
                this.sandbox.emit('sulu.labels.success.show', 'labels.success.content-save-desc', 'labels.success');
598
            }
599
600
            this.afterSaveAction(action);
601
        },
602
603
        loadLocalizations: function() {
604
            this.sandbox.util.load('/admin/api/localizations').then(function(data) {
605
                this.localizations = data._embedded.localizations.map(function(localization) {
606
                    return {
607
                        id: localization.localization,
608
                        title: localization.localization
609
                    };
610
                });
611
            }.bind(this));
612
        },
613
614
        /**
615
         * Returns copy article from a given locale to a array of other locales url.
616
         *
617
         * @param {string} id
618
         * @param {string} src
619
         * @param {string[]} dest
620
         *
621
         * @returns {string}
622
         */
623
        getCopyLocaleUrl: function(id, src, dest) {
624
            var data = this.getAdapter().prepareData(this.data, this);
625
626
            return this.getAdapter().getCopyLocaleUrl(id, data.id, src, dest);
627
        },
628
629
        startPageSwitcher: function() {
630
            var page = this.options.page,
631
                max = (this.data._embedded.pages || []).length + 1,
632
                data = [];
633
634
            if (!page) {
635
                page = ++max;
636
            }
637
638
            for (var i = 1; i <= max; i++) {
639
                data.push({id: i, title: Util.sprintf(this.translations.pageOf, i, max)});
640
            }
641
642
            // new page is only available for existing articles
643
            if (this.options.id) {
644
                data = data.concat([
645
                    {divider: true},
646
                    {id: 'add', title: this.translations.newPage}
647
                ]);
648
            }
649
650
            this.$dropdownElement = $(this.templates.pageSwitcher({label: Util.sprintf(this.translations.pageOf, page, max)}));
651
652
            var $rightContainer = $(constants.headerRightSelector);
653
            $rightContainer.prepend(this.$dropdownElement);
654
            $rightContainer.addClass('wide');
655
656
            this.sandbox.start([{
657
                name: 'dropdown@husky',
658
                options: {
659
                    el: this.$dropdownElement,
660
                    instanceName: 'header-pages',
661
                    alignment: 'right',
662
                    valueName: 'title',
663
                    data: data,
664
                    clickCallback: function(item) {
665
                        if (item.id === 'add') {
666
                            return ArticleRouter.toPageAdd(this.options.id, this.options.locale);
667
                        } else if (item.id === 1) {
668
                            return ArticleRouter.toEdit(this.options.id, this.options.locale);
669
                        }
670
671
                        return ArticleRouter.toPageEdit(this.options.id, item.id, this.options.locale);
672
                    }.bind(this)
673
                }
674
            }]);
675
        }
676
    }
677
});
678