superdesk/superdesk-client-core

View on GitHub
scripts/apps/authoring/multiedit/multiedit.ts

Summary

Maintainability
D
2 days
Test Coverage
/**
 * This file is part of Superdesk.
 *
 * Copyright 2015 Sourcefabric z.u. and contributors.
 *
 * For the full copyright and license information, please see the
 * AUTHORS and LICENSE files distributed with this source code, or
 * at https://www.sourcefabric.org/superdesk/license
 */

import {gettext} from 'core/utils';
import {isPublished} from 'apps/archive/utils';
import _, {cloneDeep} from 'lodash';
import {AuthoringWorkspaceService} from '../authoring/services/AuthoringWorkspaceService';
import {isMediaType} from 'core/helpers/item';
import {InitializeMedia} from '../authoring/services/InitializeMediaService';
import {sdApi} from 'api';
import {notify} from 'core/notify/notify';

MultieditService.$inject = ['storage', 'superdesk', 'authoringWorkspace', 'referrer', '$location'];
function MultieditService(storage, superdesk, authoringWorkspace: AuthoringWorkspaceService, referrer, $location) {
    // 1. Service manages multiedit screen
    // 2. Screen has it's boards, at least 2 of them
    // 3. Every board can be popuplated with one content item

    var MIN_BOARDS = 2;
    var STORAGE_KEY = 'multiedit';

    var saved = storage.getItem(STORAGE_KEY);

    this.items = saved === null ? [] : saved;

    this.minBoards = function() {
        return MIN_BOARDS;
    };

    this.create = function(_ids) {
        var self = this;

        self.items = [];

        if (_ids) {
            _.each(_ids, (_id) => {
                self.items.push(createBoard(_id));
            });
        }
        if (self.items.length < MIN_BOARDS) {
            for (var i = 0; i < MIN_BOARDS - self.items.length; i++) {
                self.items.push(createBoard(null));
            }
        }

        self.updateItems();
        self.open();
    };

    this.exit = function(item) {
        let someFailed = false;

        Promise.all(
            this.items
                .filter((item) => item.article != null)
                .map((item) => sdApi.article.unlock(item.article)
                    .catch(() => {
                        someFailed = true;
                        return Promise.resolve();
                    })),
        ).then(() => {
            if (someFailed) {
                notify.error(gettext('Some articles failed to unlock'));
            }

            this.items = [];
            this.updateItems();
            $location.url(referrer.getReferrerUrl());
        });
    };

    this.open = function() {
        if (authoringWorkspace.getState()) {
            authoringWorkspace.close(true);
        }
        referrer.setReferrerUrl($location.url());
        superdesk.intent('author', 'multiedit');
    };

    this.updateItems = function() {
        storage.setItem(STORAGE_KEY, this.items);
    };

    this.edit = function(_id, board) {
        if (!this.items[board]) {
            this.items[board] = createBoard(_id);
        } else {
            this.items[board].article = _id;
        }
        this.updateItems();
    };

    this.remove = function(_id) {
        _.extend(_.find(this.items, {article: _id}), {article: null});
        this.updateItems();
    };

    this.close = function(board) {
        if (this.items.length > MIN_BOARDS) {
            this.items.splice(board, 1);
            this.updateItems();
        }
    };

    function createBoard(_id) {
        return {article: _id};
    }
}

MultieditController.$inject = ['$scope', 'multiEdit'];
function MultieditController($scope, multiEdit) {
    $scope.$watch(() => multiEdit.items, (items) => {
        $scope.boards = items;
    });

    $scope.minBoards = multiEdit.minBoards();

    $scope.closeBoard = function(board) {
        multiEdit.close(board);
    };

    $scope.closeMulti = function() {
        multiEdit.exit();
    };
}

MultieditDropdownDirective.$inject = ['workqueue', 'multiEdit', '$route'];
function MultieditDropdownDirective(workqueue, multiEdit, $route) {
    return {
        templateUrl: 'scripts/apps/authoring/multiedit/views/sd-multiedit-dropdown.html',
        link: function(scope) {
            scope.current = $route.current.params.item;
            scope.queue = [scope.current];

            scope.$watch(() => workqueue.items, (items) => {
                scope.items = items;
            });

            scope.toggle = function(_id) {
                if (_id === scope.current) {
                    return false;
                }
                if (scope.selected(_id)) {
                    scope.queue = _.without(scope.queue, _id);
                } else {
                    scope.queue.push(_id);
                }
            };

            scope.selected = function(_id) {
                return _.indexOf(scope.queue, _id) !== -1;
            };

            scope.open = function() {
                multiEdit.create(scope.queue);
            };
        },
    };
}

MultieditDropdownInnerDirective.$inject = ['workqueue', 'multiEdit'];
function MultieditDropdownInnerDirective(workqueue, multiEdit) {
    return {
        templateUrl: 'scripts/apps/authoring/multiedit/views/sd-multiedit-inner-dropdown.html',
        link: function(scope, elem, attrs) {
            var workqueueItems = [],
                multieditItems = [];

            scope.$watch(() => multiEdit.items, (items) => {
                multieditItems = _.map(multiEdit.items, (board) => board.article);
                filter();
            }, true);

            scope.$watch(() => workqueue.items, (items) => {
                workqueueItems = items;
                filter();
            });

            function filter() {
                scope.items = _.filter(workqueueItems, (item) => _.indexOf(multieditItems, item._id) === -1);
            }

            scope.open = function(_id) {
                multiEdit.edit(_id, attrs.board);
            };
        },
    };
}

MultieditArticleDirective.$inject = ['authoring', 'content', 'multiEdit', 'lock', '$timeout', 'notify'];
function MultieditArticleDirective(authoring, content, multiEdit, lock, $timeout, notify) {
    return {
        templateUrl: 'scripts/apps/authoring/multiedit/views/sd-multiedit-article.html',
        scope: {article: '=', focus: '='},
        link: function(scope, elem) {
            scope.requestEditor3DirectivesToGenerateHtml = [];

            scope.$watch('article', (newVal, oldVal) => {
                if (newVal && newVal !== oldVal) {
                    openItem();
                }
            });

            function openItem() {
                authoring.open(scope.article).then((item) => {
                    scope.origItem = item;
                    scope.item = JSON.parse(JSON.stringify(item));
                    scope._editable = authoring.isEditable(item);
                    scope.isMediaType = isMediaType(scope.item);
                    scope.refreshTrigger = scope.refreshTrigger + 1 || 0;
                    if (scope.focus) {
                        $timeout(() => {
                            elem.children().focus();
                        }, 0, false);
                    }
                    scope.isLocked = lock.isLocked(item);

                    content.setupAuthoring(scope.item.profile, scope, scope.item).then((contentType) => {
                        scope.contentType = contentType;
                        InitializeMedia.initMedia(scope);
                    });
                });
            }

            openItem();

            scope.autosave = function(item, timeout) {
                scope.dirty = true;
                return authoring.autosave(cloneDeep(item), scope.origItem, timeout)
                    .then(
                        () => {
                            scope.$applyAsync(() => {
                                InitializeMedia.initMedia(scope);
                            });
                        });
            };

            scope.$watch('item.flags', (newValue, oldValue) => {
                if (newValue !== oldValue) {
                    scope.item.flags = newValue;
                    scope.origItem.flags = oldValue;
                }
            }, true);

            scope.save = function() {
                return authoring.save(
                    scope.origItem,
                    cloneDeep(scope.item),
                    scope.requestEditor3DirectivesToGenerateHtml,
                ).then((res) => {
                    scope.dirty = false;
                    InitializeMedia.initMedia(scope);

                    notify.success(gettext('Item updated.'));

                    return res;
                });
            };

            scope.remove = function(item) {
                multiEdit.remove(item._id);
            };

            scope.isPublished = function(item) {
                if (!_.isNil(item)) {
                    return isPublished(item);
                }
            };
        },
    };
}

MultieditFloatMenuDirective.$inject = ['$document'];
function MultieditFloatMenuDirective($document) {
    return {
        link: function(scope, elem) {
            var open = false;

            elem.bind('click', (event) => {
                if (!open) {
                    event.preventDefault();
                    event.stopPropagation();
                    $('#multiedit-float')
                        .css(getPosition(event.pageX, event.pageY))
                        .show();
                } else {
                    $('#multiedit-float').hide();
                }
                open = !open;
            });

            $document.bind('click', closeOnClick);

            scope.$on('$destroy', () => {
                $document.unbind('click', closeOnClick);
                elem.unbind('click');
            });

            function closeOnClick() {
                open = false;
                $('#multiedit-float').hide();
            }

            function getPosition(crdL, crdT) {
                var docHeight = $document.height();
                var docWidth = $document.width();
                var position: any = {
                    right: docWidth - crdL,
                };

                if (docHeight - crdT < 400) {
                    position.bottom = docHeight - crdT;
                    position.top = 'auto';
                } else {
                    position.top = crdT;
                    position.bottom = 'auto';
                }
                return position;
            }
        },
    };
}

angular.module('superdesk.apps.authoring.multiedit', ['superdesk.core.activity', 'superdesk.apps.authoring'])
    .service('multiEdit', MultieditService)
    .directive('sdMultieditDropdown', MultieditDropdownDirective)
    .directive('sdMultieditInnerDropdown', MultieditDropdownInnerDirective)
    .directive('sdMultieditArticle', MultieditArticleDirective)
    .directive('sdMultieditFloatMenu', MultieditFloatMenuDirective)

    .config(['superdeskProvider', function(superdesk) {
        superdesk
            .activity('multiedit', {
                category: '/authoring',
                href: '/multiedit',
                when: '/multiedit',
                label: gettext('Authoring'),
                templateUrl: 'scripts/apps/authoring/multiedit/views/multiedit.html',
                topTemplateUrl: 'scripts/apps/dashboard/views/workspace-topnav.html',
                sideTemplateUrl: 'scripts/apps/workspace/views/workspace-sidenav.html',
                controller: MultieditController,
                filters: [{action: 'author', type: 'multiedit'}],
            });
    }]);