owncloud/core

View on GitHub
apps/files/js/filelockplugin.js

Summary

Maintainability
B
4 hrs
Test Coverage
/*
 * Copyright (c) 2018 Thomas Müller <thomas.mueller@tmit.eu>
 *
 * This file is licensed under the Affero General Public License version 3
 * or later.
 *
 * See the COPYING-README file.
 *
 */

(function(OCA) {
    var NS_DAV = OC.Files.Client.NS_DAV;

    var TEMPLATE_LOCK_STATUS_ACTION =
        '<a class="action action-lock-status permanent" title="{{message}}" href="#">' +
        '<span class="icon icon-lock-closed" />' +
        '</a>';

    /**
     * Parses an XML lock node
     *
     * @param {Node} xmlvalue node to parse
     * @return {Object} parsed values in associative array
     */
    function parseLockNode(xmlvalue) {
        var lockInfo = {
            lockscope: getChildNodeLocalName(xmlvalue.getElementsByTagNameNS(NS_DAV, 'lockscope')[0]),
            locktype: getChildNodeLocalName(xmlvalue.getElementsByTagNameNS(NS_DAV, 'locktype')[0]),
            lockroot: getHrefNodeContents(xmlvalue.getElementsByTagNameNS(NS_DAV, 'lockroot')[0]),
            // string, as it can also be "infinite"
            depth: xmlvalue.getElementsByTagNameNS(NS_DAV, 'depth')[0].textContent,
            timeout: xmlvalue.getElementsByTagNameNS(NS_DAV, 'timeout')[0].textContent,
            locktoken: getHrefNodeContents(xmlvalue.getElementsByTagNameNS(NS_DAV, 'locktoken')[0])
        };

        var owner = null;
        var ownerEl = xmlvalue.getElementsByTagNameNS(NS_DAV, 'owner');
        if (ownerEl && ownerEl.length) {
            owner = ownerEl[0].textContent;
        }

        lockInfo.owner = owner || t('files', 'Unknown user');
        return lockInfo;
    }

    function getHrefNodeContents(node) {
        var nodes = node.getElementsByTagNameNS(NS_DAV, 'href');
        if (!nodes.length) {
            return null;
        }
        return nodes[0].textContent;
    }

    /**
     * Filter out text nodes from a list of XML nodes
     *
     * @param {Array.<Node>} nodes nodes to filter
     * @return {Array.<Node>} filtered array of nodes
     */
    function getChildNodeLocalName(node) {
        for (var i = 0; i < node.childNodes.length; i++) {
            // skip pure text nodes
            if (node.childNodes[i].nodeType === 1) {
                return node.childNodes[i].localName;
            }
        }
        return null;
    }

    OCA.Files = OCA.Files || {};

    /**
     * @namespace OCA.Files.LockPlugin
     */
    OCA.Files.LockPlugin = {

        /**
         * @param fileList
         */
        attach: function(fileList) {
            this._extendFileActions(fileList);

            var oldCreateRow = fileList._createRow;
            fileList._createRow = function(fileData) {
                var $tr = oldCreateRow.apply(this, arguments);
                if (fileData.activeLocks) {
                    $tr.attr('data-activelocks', JSON.stringify(fileData.activeLocks));
                }
                return $tr;
            };
            var oldElementToFile = fileList.elementToFile;
            fileList.elementToFile = function($el) {
                var fileInfo = oldElementToFile.apply(this, arguments);
                var activeLocks = $el.attr('data-activelocks');
                if (_.isUndefined(activeLocks)) {
                    activeLocks = '[]';
                }
                fileInfo.activeLocks = JSON.parse(activeLocks);
                return fileInfo;
            };

            var oldGetWebdavProperties = fileList._getWebdavProperties;
            fileList._getWebdavProperties = function() {
                var props = oldGetWebdavProperties.apply(this, arguments);
                props.push('{DAV:}lockdiscovery');
                return props;
            };

            var lockTab = new OCA.Files.LockTabView('lockTabView', {order: -20});
            fileList.registerTabView(lockTab);

            fileList.filesClient.addFileInfoParser(function(response) {
                var data = {};
                var props = response.propStat[0].properties;
                var activeLocks = props['{DAV:}lockdiscovery'];
                if (!_.isUndefined(activeLocks) && activeLocks !== '') {
                    data.activeLocks = _.chain(activeLocks).filter(function(xmlvalue) {
                        return (xmlvalue.namespaceURI === NS_DAV && xmlvalue.nodeName.split(':')[1] === 'activelock');
                    }).map(function(xmlvalue) {
                        return parseLockNode(xmlvalue);
                    }).value();
                }
                return data;
            });
        },

        /**
         * @param fileList
         * @private
         */
        _extendFileActions: function(fileList) {
            var self = this;
            fileList.fileActions.registerAction({
                name: 'lock-status',
                displayName: t('files', 'Lock status'),
                mime: 'all',
                permissions: OC.PERMISSION_READ,
                type: OCA.Files.FileActions.TYPE_INLINE,
                render: function(actionSpec, isDefault, context) {
                    var $file = context.$file;
                    var isLocked = $file.data('activelocks');
                    if (isLocked && isLocked.length > 0) {
                        var $actionLink = $(self.renderLink());
                        context.$file.find('a.name>span.fileactions').append($actionLink);
                        return $actionLink;
                    }
                    return '';
                },
                actionHandler: function(fileName) {
                    fileList.showDetailsView(fileName, 'lockTabView');
                }
            });

            if (oc_appconfig.files.enable_lock_file_action) {
                fileList.fileActions.registerAction({
                    name: 'lock',
                    mime: 'all',
                    displayName: t('files', 'Lock file'),
                    permissions: OC.PERMISSION_UPDATE,
                    type: OCA.Files.FileActions.TYPE_DROPDOWN,
                    iconClass: 'icon-lock-closed',
                    actionHandler: function (filename, context) {
                        const file = context.fileInfoModel.getFullPath();
                        context.fileInfoModel._filesClient.lock(file).then(function (result, response) {
                            const xml = response.xhr.responseXML;
                            const activelock = xml.getElementsByTagNameNS('DAV:', 'activelock');
                            const lock = parseLockNode(activelock[0]);
                            context.fileInfoModel.set('activeLocks', [lock]);
                        }, function (error) {
                            console.log(error)
                            OC.Notification.show(t('files', 'Failed to lock.'));
                        });
                    }
                });

                fileList.fileActions.addAdvancedFilter(function (actions, context) {
                    var $file = context.$file;
                    if (context.fileInfoModel && context.fileInfoModel.attributes.mimetype === 'httpd/unix-directory') {
                        delete (actions.lock);
                        return actions;
                    }
                    var isLocked = $file.data('activelocks');
                    if (isLocked && isLocked.length > 0) {
                        delete (actions.lock);
                    }
                    return actions;
                });
            }

        },

        renderLink: function () {
            if (!this._template) {
                this._template = Handlebars.compile(TEMPLATE_LOCK_STATUS_ACTION);
            }
            return this._template({
                message: t('files', 'This resource is locked. Click to see more details.')
        });
    }

};

})(OCA);

OC.Plugins.register('OCA.Files.FileList', OCA.Files.LockPlugin);