From ca9b1bcf224959b55dbbd92dfed9acbfbb0a6c4a Mon Sep 17 00:00:00 2001 From: Julia Radzhabova Date: Wed, 21 Sep 2022 17:15:33 +0300 Subject: [PATCH] [DE] Add document protection --- apps/documenteditor/main/app.js | 2 + .../main/app/controller/DocProtection.js | 203 +++++++++++++++ .../main/app/controller/Main.js | 4 +- .../main/app/controller/Toolbar.js | 23 +- .../main/app/view/DocProtection.js | 130 ++++++++++ .../main/app/view/ProtectDialog.js | 237 ++++++++++++++++++ apps/documenteditor/main/app_dev.js | 2 + 7 files changed, 593 insertions(+), 8 deletions(-) create mode 100644 apps/documenteditor/main/app/controller/DocProtection.js create mode 100644 apps/documenteditor/main/app/view/DocProtection.js create mode 100644 apps/documenteditor/main/app/view/ProtectDialog.js diff --git a/apps/documenteditor/main/app.js b/apps/documenteditor/main/app.js index 190281919..4d7b05f6d 100644 --- a/apps/documenteditor/main/app.js +++ b/apps/documenteditor/main/app.js @@ -157,6 +157,7 @@ require([ 'Main', 'ViewTab', 'Search', + 'DocProtection', 'Common.Controllers.Fonts', 'Common.Controllers.History' /** coauthoring begin **/ @@ -191,6 +192,7 @@ require([ 'documenteditor/main/app/controller/Main', 'documenteditor/main/app/controller/ViewTab', 'documenteditor/main/app/controller/Search', + 'documenteditor/main/app/controller/DocProtection', 'documenteditor/main/app/view/FileMenuPanels', 'documenteditor/main/app/view/ParagraphSettings', 'documenteditor/main/app/view/HeaderFooterSettings', diff --git a/apps/documenteditor/main/app/controller/DocProtection.js b/apps/documenteditor/main/app/controller/DocProtection.js new file mode 100644 index 000000000..c5202176f --- /dev/null +++ b/apps/documenteditor/main/app/controller/DocProtection.js @@ -0,0 +1,203 @@ +/* + * + * (c) Copyright Ascensio System SIA 2010-2022 + * + * This program is a free software product. You can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License (AGPL) + * version 3 as published by the Free Software Foundation. In accordance with + * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect + * that Ascensio System SIA expressly excludes the warranty of non-infringement + * of any third-party rights. + * + * This program is distributed WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For + * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html + * + * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha + * street, Riga, Latvia, EU, LV-1050. + * + * The interactive user interfaces in modified source and object code versions + * of the Program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU AGPL version 3. + * + * Pursuant to Section 7(b) of the License you must retain the original Product + * logo when distributing the program. Pursuant to Section 7(e) we decline to + * grant you any rights under trademark law for use of our trademarks. + * + * All the Product's GUI elements, including illustrations and icon sets, as + * well as technical writing content are licensed under the terms of the + * Creative Commons Attribution-ShareAlike 4.0 International. See the License + * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + * + */ + +/** + * DocProtection.js + * + * Created by Julia Radzhabova on 21.09.2022 + * Copyright (c) 2022 Ascensio System SIA. All rights reserved. + * + */ +define([ + 'core', + 'common/main/lib/view/Protection', + 'documenteditor/main/app/view/DocProtection', + 'documenteditor/main/app/view/ProtectDialog' +], function () { + 'use strict'; + + if (!Common.enumLock) + Common.enumLock = {}; + + var enumLock = { + docLockView: 'lock-mode-view', + docLockForms: 'lock-mode-forms', + docLockReview: 'lock-mode-review', + docLockComments: 'lock-mode-comments' + }; + for (var key in enumLock) { + if (enumLock.hasOwnProperty(key)) { + Common.enumLock[key] = enumLock[key]; + } + } + + DE.Controllers.DocProtection = Backbone.Controller.extend(_.extend({ + models : [], + collections : [ + ], + views : [ + 'DocProtection' + ], + + initialize: function () { + + this.addListeners({ + 'DocProtection': { + 'protect:document': _.bind(this.onProtectDocClick, this) + } + }); + }, + onLaunch: function () { + this._state = {}; + Common.NotificationCenter.on('app:ready', this.onAppReady.bind(this)); + }, + setConfig: function (data, api) { + this.setApi(api); + }, + setApi: function (api) { + if (api) { + this.api = api; + this.api.asc_registerCallback('asc_onChangeProtectDocument',_.bind(this.onChangeProtectDocument, this)); + } + }, + + setMode: function(mode) { + this.appConfig = mode; + + this.appConfig.isEdit && (this.view = this.createView('DocProtection', { + mode: mode + })); + + return this; + }, + + createToolbarPanel: function() { + if (this.view) + return this.view.getPanel(); + }, + + getView: function(name) { + return !name && this.view ? + this.view : Backbone.Controller.prototype.getView.call(this, name); + }, + + onProtectDocClick: function(state) { + this.view.btnProtectDoc.toggle(!state, true); + if (state) { + var me = this, + btn, + win = new DE.Views.ProtectDialog({ + handler: function(result, value, props) { + btn = result; + if (result == 'ok') { + // var props = me.api.asc_getProtectedDocument(); + // props.asc_setLockMode(props); + // props.asc_setLockPwd(value); + // me.api.asc_setProtectedDocument(props); + } + Common.NotificationCenter.trigger('edit:complete'); + } + }).on('close', function() { + if (btn!=='ok') + me.view.btnProtectDoc.toggle(false, true); + }); + + win.show(); + } else { + var me = this, + btn, + props = me.api.asc_getProtectedDocument(); + if (props.asc_isPassword()) { + var win = new Common.Views.OpenDialog({ + title: me.view.txtWBUnlockTitle, + closable: true, + type: Common.Utils.importTextType.DRM, + txtOpenFile: me.view.txtWBUnlockDescription, + validatePwd: false, + handler: function (result, value) { + btn = result; + if (result == 'ok') { + if (me.api) { + // props.asc_setLockPwd(value && value.drmOptions ? value.drmOptions.asc_getPassword() : undefined); + // me.api.asc_setProtectedDocument(props); + } + Common.NotificationCenter.trigger('edit:complete'); + } + } + }).on('close', function() { + if (btn!=='ok') + me.view.btnProtectDoc.toggle(true, true); + }); + + win.show(); + } else { + props.asc_setLockPwd(); + me.api.asc_setProtectedDocument(props); + } + } + }, + + onAppReady: function (config) { + if (!this.view) return; + + var me = this; + (new Promise(function (resolve) { + resolve(); + })).then(function () { + // me.view.btnProtectDoc.toggle(me.api.asc_isProtectedDocument(), true); + }); + }, + + onChangeProtectDocument: function() { + // this.view && this.view.btnProtectDoc.toggle(this.api.asc_isProtectedDocument(), true); + }, + + getDocProps: function(update) { + if (!this.appConfig || !this.appConfig.isEdit && !this.appConfig.isRestrictedEdit) return; + + if (update || !this._state.protection) { + var docProtected = !!this.api.asc_isProtectedDocument(), + type; + + if (docProtected) { + var props = this.api.asc_getProtectedDocument(); + type = props.asc_getLockMode(); + } + this._state.protection = {docLock: docProtected, lockMode: type}; + } + + return this._state.protection; + } + + }, DE.Controllers.DocProtection || {})); +}); \ No newline at end of file diff --git a/apps/documenteditor/main/app/controller/Main.js b/apps/documenteditor/main/app/controller/Main.js index 9af86ff56..a0fdd0c70 100644 --- a/apps/documenteditor/main/app/controller/Main.js +++ b/apps/documenteditor/main/app/controller/Main.js @@ -1700,8 +1700,8 @@ define([ fontsControllers && fontsControllers.setApi(me.api); rightmenuController && rightmenuController.setApi(me.api); - if (this.appOptions.canProtect) - application.getController('Common.Controllers.Protection').setMode(me.appOptions).setConfig({config: me.editorConfig}, me.api); + application.getController('Common.Controllers.Protection').setMode(me.appOptions).setConfig({config: me.editorConfig}, me.api); + application.getController('DocProtection').setMode(me.appOptions).setConfig({config: me.editorConfig}, me.api); var viewport = this.getApplication().getController('Viewport').getView('Viewport'); diff --git a/apps/documenteditor/main/app/controller/Toolbar.js b/apps/documenteditor/main/app/controller/Toolbar.js index 6812ef505..94aaaf698 100644 --- a/apps/documenteditor/main/app/controller/Toolbar.js +++ b/apps/documenteditor/main/app/controller/Toolbar.js @@ -3203,13 +3203,24 @@ define([ me.toolbar.processPanelVisible(null, true, true); } - if ( config.isDesktopApp ) { - if ( config.canProtect ) { - tab = {action: 'protect', caption: me.toolbar.textTabProtect, dataHintTitle: 'T', layoutname: 'toolbar-protect'}; - $panel = me.getApplication().getController('Common.Controllers.Protection').createToolbarPanel(); + // if ( config.isDesktopApp ) { + // if ( config.canProtect ) { + // tab = {action: 'protect', caption: me.toolbar.textTabProtect, dataHintTitle: 'T', layoutname: 'toolbar-protect'}; + // $panel = me.getApplication().getController('Common.Controllers.Protection').createToolbarPanel(); + // + // if ($panel) me.toolbar.addTab(tab, $panel, 6); + // } + // } - if ($panel) me.toolbar.addTab(tab, $panel, 6); - } + tab = {action: 'protect', caption: me.toolbar.textTabProtect, layoutname: 'toolbar-protect', dataHintTitle: 'T'}; + $panel = me.getApplication().getController('Common.Controllers.Protection').createToolbarPanel(); + if ($panel) { + config.canProtect && $panel.append($('
')); + var doctab = me.getApplication().getController('DocProtection'); + $panel.append(doctab.createToolbarPanel()); + me.toolbar.addTab(tab, $panel, 6); + me.toolbar.setVisible('protect', Common.UI.LayoutManager.isElementVisible('toolbar-protect')); + Array.prototype.push.apply(me.toolbar.lockControls, doctab.getView('DocProtection').getButtons()); } var links = me.getApplication().getController('Links'); diff --git a/apps/documenteditor/main/app/view/DocProtection.js b/apps/documenteditor/main/app/view/DocProtection.js new file mode 100644 index 000000000..3f4916175 --- /dev/null +++ b/apps/documenteditor/main/app/view/DocProtection.js @@ -0,0 +1,130 @@ +/* + * + * (c) Copyright Ascensio System SIA 2010-2022 + * + * This program is a free software product. You can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License (AGPL) + * version 3 as published by the Free Software Foundation. In accordance with + * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect + * that Ascensio System SIA expressly excludes the warranty of non-infringement + * of any third-party rights. + * + * This program is distributed WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For + * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html + * + * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha + * street, Riga, Latvia, EU, LV-1050. + * + * The interactive user interfaces in modified source and object code versions + * of the Program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU AGPL version 3. + * + * Pursuant to Section 7(b) of the License you must retain the original Product + * logo when distributing the program. Pursuant to Section 7(e) we decline to + * grant you any rights under trademark law for use of our trademarks. + * + * All the Product's GUI elements, including illustrations and icon sets, as + * well as technical writing content are licensed under the terms of the + * Creative Commons Attribution-ShareAlike 4.0 International. See the License + * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + * + */ + +/** + * DocProtection.js + * + * Created by Julia Radzhabova on 21.09.2022 + * Copyright (c) 2022 Ascensio System SIA. All rights reserved. + * + */ +define([ + 'common/main/lib/util/utils', + 'common/main/lib/component/BaseView', + 'common/main/lib/component/Layout', + 'common/main/lib/component/Window' +], function (template) { + 'use strict'; + + DE.Views.DocProtection = Common.UI.BaseView.extend(_.extend((function(){ + var template = + '
' + + '' + + '
'; + + function setEvents() { + var me = this; + + this.btnProtectDoc.on('click', function (btn, e) { + me.fireEvent('protect:document', [btn.pressed]); + }); + me._isSetEvents = true; + } + + return { + + options: {}, + + initialize: function (options) { + Common.UI.BaseView.prototype.initialize.call(this, options); + + this.appConfig = options.mode; + + var _set = Common.enumLock; + this.lockedControls = []; + this._state = {disabled: false}; + + this.btnProtectDoc = new Common.UI.Button({ + cls: 'btn-toolbar x-huge icon-top', + iconCls: 'toolbar__icon protect-workbook', + enableToggle: true, + caption: this.txtProtectDoc, + lock : [_set.lostConnect, _set.coAuth], + dataHint : '1', + dataHintDirection: 'bottom', + dataHintOffset: 'small' + }); + this.lockedControls.push(this.btnProtectDoc); + + Common.NotificationCenter.on('app:ready', this.onAppReady.bind(this)); + }, + + render: function (el) { + return this; + }, + + onAppReady: function (config) { + var me = this; + (new Promise(function (accept, reject) { + accept(); + })).then(function(){ + me.btnProtectDoc.updateHint(me.hintProtectDoc); + setEvents.call(me); + }); + }, + + getPanel: function () { + this.$el = $(_.template(template)( {} )); + + this.btnProtectDoc.render(this.$el.find('#slot-btn-protect-doc')); + return this.$el; + }, + + getButtons: function(type) { + if (type===undefined) + return this.lockedControls; + return []; + }, + + show: function () { + Common.UI.BaseView.prototype.show.call(this); + this.fireEvent('show', this); + }, + + txtProtectDoc: 'Protect Document', + txtDocUnlockTitle: 'Unprotect Document', + hintProtectDoc: 'Protect document', + txtDocUnlockDescription: 'Enter a password to unprotect document' + } + }()), DE.Views.DocProtection || {})); +}); \ No newline at end of file diff --git a/apps/documenteditor/main/app/view/ProtectDialog.js b/apps/documenteditor/main/app/view/ProtectDialog.js new file mode 100644 index 000000000..bebe37ef4 --- /dev/null +++ b/apps/documenteditor/main/app/view/ProtectDialog.js @@ -0,0 +1,237 @@ +/* + * + * (c) Copyright Ascensio System SIA 2010-2022 + * + * This program is a free software product. You can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License (AGPL) + * version 3 as published by the Free Software Foundation. In accordance with + * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect + * that Ascensio System SIA expressly excludes the warranty of non-infringement + * of any third-party rights. + * + * This program is distributed WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For + * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html + * + * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha + * street, Riga, Latvia, EU, LV-1050. + * + * The interactive user interfaces in modified source and object code versions + * of the Program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU AGPL version 3. + * + * Pursuant to Section 7(b) of the License you must retain the original Product + * logo when distributing the program. Pursuant to Section 7(e) we decline to + * grant you any rights under trademark law for use of our trademarks. + * + * All the Product's GUI elements, including illustrations and icon sets, as + * well as technical writing content are licensed under the terms of the + * Creative Commons Attribution-ShareAlike 4.0 International. See the License + * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + * + */ +/** + * ProtectDialog.js + * + * Created by Julia Radzhabova on 21.09.2022 + * Copyright (c) 2022 Ascensio System SIA. All rights reserved. + * + */ + +define([ + 'common/main/lib/component/Window' +], function () { + 'use strict'; + + Asc.c_oAscProtection = { + View: 1, + Forms: 2, + Review: 3, + Comments: 4 + }; + DE.Views.ProtectDialog = Common.UI.Window.extend(_.extend({ + + initialize : function (options) { + var t = this, + _options = {}; + + _.extend(_options, { + title: options.title ? options.title : this.txtTitle, + cls: 'modal-dlg', + width: 350, + height: 'auto', + buttons: options.buttons ? options.buttons : [{ + value: 'ok', + caption: this.txtProtect + }, 'cancel'] + }, options); + + this.handler = options.handler; + this.txtDescription = options.txtDescription || ''; + this.props = options.props; + this.isEdit = options.isEdit; + this.api = options.api; + + this.template = options.template || [ + '
', + '
', + '', + '
', + '
', + '
', + '', + '
', + '
', + '
', + '', + '
', + '
', + '
', + '
', + '
', + '', + '
' + ].join(''); + + _options.tpl = _.template(this.template)(_options); + + Common.UI.Window.prototype.initialize.call(this, _options); + }, + render: function () { + Common.UI.Window.prototype.render.call(this); + + var me = this; + this.$window.find('.dlg-btn').on('click', _.bind(this.onBtnClick, this)); + + this.repeatPwd = new Common.UI.InputField({ + el: this.$window.find('#id-protect-repeat-txt'), + type: 'password', + allowBlank : true, + style : 'width: 100%;', + maxLength: 255, + validateOnBlur: false, + validation : function(value) { + return me.txtIncorrectPwd; + } + }); + + this.inputPwd = new Common.UI.InputFieldBtnPassword({ + el: this.$window.find('#id-protect-password-txt'), + type: 'password', + allowBlank : true, + style : 'width: 100%;', + maxLength: 255, + validateOnBlur: false, + repeatInput: this.repeatPwd, + showPwdOnClick: true + }); + + this.rbView = new Common.UI.RadioBox({ + el: this.$window.find('#id-protect-radio-view'), + labelText: this.textView, + name: 'asc-radio-protect-mode', + value: Asc.c_oAscProtection.View, + checked: true + }); + + this.rbForms = new Common.UI.RadioBox({ + el: this.$window.find('#id-protect-radio-forms'), + labelText: this.textForms, + name: 'asc-radio-protect-mode', + value: Asc.c_oAscProtection.Forms + }); + + this.rbReview = new Common.UI.RadioBox({ + el: this.$window.find('#id-protect-radio-review'), + labelText: this.textReview, + name: 'asc-radio-protect-mode', + value: Asc.c_oAscProtection.Review + }); + + this.rbComments = new Common.UI.RadioBox({ + el: this.$window.find('#id-protect-radio-comment'), + labelText: this.textComments, + name: 'asc-radio-protect-mode', + value: Asc.c_oAscProtection.Comments + }); + + this.afterRender(); + }, + + getFocusedComponents: function() { + return [this.inputPwd, this.repeatPwd, this.rbView, this.rbForms, this.rbReview, this.rbComments]; + }, + + getDefaultFocusableComponent: function () { + return this.inputPwd; + }, + + afterRender: function() { + this._setDefaults(this.props); + }, + + onPrimary: function(event) { + this._handleInput('ok'); + return false; + }, + + onBtnClick: function(event) { + this._handleInput(event.currentTarget.attributes['result'].value); + }, + + _handleInput: function(state) { + if (this.handler) { + if (state == 'ok') { + if (this.inputPwd.checkValidate() !== true) { + this.inputPwd.focus(); + return; + } + if (this.inputPwd.getValue() !== this.repeatPwd.getValue()) { + this.repeatPwd.checkValidate(); + this.repeatPwd.focus(); + return; + } + } + this.handler.call(this, state, this.inputPwd.getValue(), (state == 'ok') ? this.getSettings() : undefined); + } + + this.close(); + }, + + _setDefaults: function (props) { + if (props) { + var type = props.asc_getType(); + switch (type) { + case Asc.c_oAscProtection.Forms: this.rbForms.setValue(true, true); break; + case Asc.c_oAscProtection.Review: this.rbReview.setValue(true, true); break; + case Asc.c_oAscProtection.Comments: this.rbComments.setValue(true, true); break; + } + } + }, + + getSettings: function() { + if (this.rbView.getValue()) + return Asc.c_oAscProtection.View; + if (this.rbForms.getValue()) + return Asc.c_oAscProtection.Forms; + if (this.rbReview.getValue()) + return Asc.c_oAscProtection.Review; + if (this.rbComments.getValue()) + return Asc.c_oAscProtection.Comments; + }, + + txtPassword : "Password", + txtRepeat: 'Repeat password', + txtOptional: 'optional', + txtIncorrectPwd: 'Confirmation password is not identical', + txtWarning: 'Warning: If you lose or forget the password, it cannot be recovered. Please keep it in a safe place.', + txtProtect: 'Protect', + txtTitle: 'Protect', + txtAllow: 'Allow only this type of editing in the document', + textView: 'No changes (Read only)', + textForms: 'Filling forms', + textReview: 'Tracked changes', + textComments: 'Comments' + + }, DE.Views.ProtectDialog || {})); +}); diff --git a/apps/documenteditor/main/app_dev.js b/apps/documenteditor/main/app_dev.js index 242a4d947..89276f32b 100644 --- a/apps/documenteditor/main/app_dev.js +++ b/apps/documenteditor/main/app_dev.js @@ -147,6 +147,7 @@ require([ 'Main', 'ViewTab', 'Search', + 'DocProtection', 'Common.Controllers.Fonts', 'Common.Controllers.History' /** coauthoring begin **/ @@ -181,6 +182,7 @@ require([ 'documenteditor/main/app/controller/Main', 'documenteditor/main/app/controller/ViewTab', 'documenteditor/main/app/controller/Search', + 'documenteditor/main/app/controller/DocProtection', 'documenteditor/main/app/view/FileMenuPanels', 'documenteditor/main/app/view/ParagraphSettings', 'documenteditor/main/app/view/HeaderFooterSettings',