0013172: Introduce notifications into filemanager
authorMichael Spahn <m.spahn@metaways.de>
Mon, 29 May 2017 16:28:11 +0000 (18:28 +0200)
committersstamer <s.stamer@metaways.de>
Tue, 6 Jun 2017 12:56:26 +0000 (14:56 +0200)
https://forge.tine20.org/view.php?id=13172

Change-Id: I7acc3abb949d014a0d6c334c42a1038eb33c7d58
Reviewed-on: http://gerrit.tine20.com/customers/4811
Tested-by: sstamer <s.stamer@metaways.de>
Tested-by: Jenkins CI (http://ci.tine20.com/)
Reviewed-by: sstamer <s.stamer@metaways.de>
tine20/Filemanager/Filemanager.jsb2
tine20/Filemanager/js/NodeEditDialog.js
tine20/Filemanager/js/NotificationGridPanel.js [new file with mode: 0644]
tine20/Filemanager/js/NotificationPanel.js [new file with mode: 0644]
tine20/Tinebase/js/Models.js
tine20/Tinebase/js/ux/grid/CheckColumn.js

index 740666e..f0f8ee6 100644 (file)
         {
           "text": "FilePublishedDialog.js",
           "path": "js/"
+        },
+        {
+          "text": "NotificationPanel.js",
+          "path": "js/"
+        },
+        {
+          "text": "NotificationGridPanel.js",
+          "path": "js/"
         }
       ]
     },
index 0a367d7..08063ea 100644 (file)
@@ -115,6 +115,11 @@ Tine.Filemanager.NodeEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
             editDialog: this
         });
 
+        var notificationPanel = new Tine.Filemanager.NotificationPanel({
+            app: this.app,
+            editDialog: this
+        });
+
         return {
             xtype: 'tabpanel',
             border: false,
@@ -244,7 +249,7 @@ Tine.Filemanager.NodeEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
                         })
                     ]
                 }]
-            }, 
+            },
             new Tine.widgets.activities.ActivitiesTabPanel({
                 app: this.appName,
                 record_id: this.record.id,
@@ -252,7 +257,8 @@ Tine.Filemanager.NodeEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
                 }),
                 this.downloadLinkGrid,
                 {xtype: 'Tine.Filemanager.UsagePanel'},
-                grantsPanel
+                grantsPanel,
+                notificationPanel
             ]
         };
     }
diff --git a/tine20/Filemanager/js/NotificationGridPanel.js b/tine20/Filemanager/js/NotificationGridPanel.js
new file mode 100644 (file)
index 0000000..894ac2e
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Tine 2.0
+ * 
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Michael Spahn <m.spahn@metaways.de>
+ * @copyright   Copyright (c) 2017 Metaways Infosystems GmbH (http://www.metaways.de)
+ */
+Ext.ns('Tine.Filemanager');
+
+Tine.Filemanager.NotificationGridPanel = Ext.extend(Tine.widgets.account.PickerGridPanel, {
+    selectType: 'both',
+    selectAnyone: false,
+
+    userCombo: null,
+    groupCombo: null,
+
+    currentUser: null,
+
+    editDialog: null,
+
+    initComponent: function () {
+        this.currentUser = Tine.Tinebase.registry.get('currentAccount');
+        this.initColumns();
+        this.supr().initComponent.call(this);
+
+        this.on('beforeedit', this.onBeforeEdit.createDelegate(this));
+    },
+
+    initColumns: function () {
+        var me = this;
+        this.configColumns = [
+            new Ext.ux.grid.CheckColumn({
+                id: 'active',
+                header: i18n._('Notification'),
+                tooltip: i18n._('Notactiveification'),
+                dataIndex: 'active',
+                width: 55,
+                onBeforeCheck: function (checkbox, record) {
+                    return this.checkGrant(record);
+                }.createDelegate(me)
+            }), {
+                id: 'summary',
+                dataIndex: 'summary',
+                width: 100,
+                sortable: true,
+                header: i18n._('Summary'),
+                renderer: function (value) {
+                    if (value === 1) {
+                        return value + ' ' + i18n._('Day');
+                    }
+
+                    if (value > 1) {
+                        return value + ' ' + i18n._('Days');
+                    }
+
+                    return '';
+                },
+                editor: {
+                    xtype: 'numberfield'
+                }
+            }
+        ];
+    },
+
+    onBeforeEdit: function (e) {
+        return this.checkGrant(e.record);
+    },
+
+    checkGrant: function (record) {
+        var _ = window.lodash;
+
+        var userHasAdminGrant = _.get(this.editDialog, 'record.data.account_grants.adminGrant', false);
+
+        // get id if it's from notification props, if its a record which was added or if it's a group which was added
+        var id = _.get(record, 'data.account_id', _.get(record, 'data.accountId')) || _.get(record, 'data.group_id');
+
+        if (!userHasAdminGrant && id !== this.currentUser.accountId) {
+            return false;
+        }
+
+        return true;
+    },
+
+    getColumnModel: function () {
+        if (!this.colModel) {
+            this.colModel = new Ext.grid.ColumnModel({
+                defaults: {
+                    sortable: true
+                },
+                columns: [
+                    {
+                        id: 'name',
+                        header: i18n._('Name'),
+                        dataIndex: this.recordPrefix + 'name',
+                        renderer: this.accountRenderer.createDelegate(this)
+                    }
+                ].concat(this.configColumns)
+            });
+        }
+
+        return this.colModel;
+    },
+
+    accountRenderer: function (value, meta, record) {
+        if (!record) {
+            return '';
+        }
+
+        var _ = window.lodash;
+
+        var iconCls = _.get(record, 'data.accountType') === 'user' ? 'renderer renderer_accountUserIcon' : 'renderer renderer_accountGroupIcon';
+
+        return '<div class="' + iconCls + '">&#160;</div>' + Ext.util.Format.htmlEncode(_.get(record, 'data.accountName') || '');
+    },
+
+    resetCombobox: function (combo) {
+        combo.collapse();
+        combo.clearValue();
+        combo.reset();
+    },
+
+    onAddRecordFromCombo: function (recordToAdd, index, combo) {
+        var _ = window.lodash;
+
+        var id = _.get(recordToAdd, 'data.account_id') || _.get(recordToAdd, 'data.group_id');
+
+        // If there is no admin grant, only allow to edit the own record
+        if (!_.get(this.editDialog, 'record.data.account_grants.adminGrant', false) && id !== this.currentUser.accountId) {
+            Ext.Msg.alert(i18n._('No permission'), 'You are only allowed to edit your own notifications.');
+
+            this.resetCombobox(combo);
+            return false;
+        }
+
+        var record = {
+            'active': true,
+            'summary': null,
+            'accountId': id,
+            'accountType': _.get(recordToAdd, 'data.type', null),
+            'accountName': _.get(recordToAdd, 'data.n_fileas') || _.get(recordToAdd, 'data.name') || i18n._('all')
+        };
+
+        if (this.store.getById(id)) {
+            this.resetCombobox(combo);
+            return false;
+        }
+
+        this.store.loadData([record], true);
+
+        this.resetCombobox(combo);
+
+    },
+
+    getContactSearchCombo: function () {
+        var combo = new Tine.Addressbook.SearchCombo({
+            accountsStore: this.store,
+            emptyText: i18n._('Search for users ...'),
+            newRecordClass: this.recordClass,
+            newRecordDefaults: this.recordDefaults,
+            recordPrefix: this.recordPrefix,
+            userOnly: true,
+            additionalFilters: (this.showHidden) ? [{field: 'showDisabled', operator: 'equals', value: true}] : []
+        });
+
+        combo.onSelect = this.onAddRecordFromCombo.createDelegate(this, [combo], true);
+
+        this.userCombo = combo;
+
+        return combo;
+    },
+
+    onRemove: function () {
+        var selectedRows = this.getSelectionModel().getSelections();
+        for (var i = 0; i < selectedRows.length; ++i) {
+            if (this.checkGrant(selectedRows[i])) {
+                this.store.remove(selectedRows[i]);
+            }
+        }
+    },
+
+    getGroupSearchCombo: function () {
+        var combo = new Tine.Tinebase.widgets.form.RecordPickerComboBox({
+            accountsStore: this.store,
+            blurOnSelect: true,
+            recordClass: this.groupRecordClass,
+            newRecordClass: this.recordClass,
+            newRecordDefaults: this.recordDefaults,
+            recordPrefix: this.recordPrefix,
+            emptyText: i18n._('Search for groups ...')
+        });
+
+        combo.onSelect = this.onAddRecordFromCombo.createDelegate(this, [combo], true);
+
+        this.groupCombo = combo;
+
+        return combo;
+    }
+})
+;
\ No newline at end of file
diff --git a/tine20/Filemanager/js/NotificationPanel.js b/tine20/Filemanager/js/NotificationPanel.js
new file mode 100644 (file)
index 0000000..1253256
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Tine 2.0
+ *
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Michael Spahn <m.spahn@metaways.de>
+ * @copyright   Copyright (c) 2017 Metaways Infosystems GmbH (http://www.metaways.de)
+ */
+Ext.ns('Tine.Filemanager');
+
+Tine.Filemanager.NotificationPanel = Ext.extend(Ext.Panel, {
+    editDialog: null,
+    app: null,
+
+    layout: 'fit',
+    border: false,
+
+    notificationGrid: null,
+
+    initComponent: function () {
+        var _ = window.lodash;
+
+        this.app = this.app || Tine.Tinebase.appMgr.get('Filemanager');
+        this.title = this.title || this.app.i18n._('Notifications');
+
+        this.editDialog.on('load', this.onRecordLoad, this);
+        this.editDialog.on('save', this.onSave, this);
+
+        var store = new Ext.data.JsonStore({
+            fields: ['active', 'summary', 'accountId', 'accountType', 'accountName'],
+            idProperty: 'accountId'
+        });
+
+        var disable = window.lodash.get(this.editDialog.record, 'data.notificationProps', []).length === 0;
+
+        this.notificationGrid = new Tine.Filemanager.NotificationGridPanel({
+            store: store,
+            readOnly: disable,
+            flex: 1,
+            editDialog: this.editDialog
+        });
+
+        this.hasOwnNotificationSettings = new Ext.form.Checkbox({
+            checked: !disable,
+            disabled: !_.get(this.editDialog, 'record.data.account_grants.adminGrant', false) && !disable,
+            boxLabel: this.app.i18n._('This folder has own notification settings'),
+            listeners: {scope: this, check: this.onOwnNotificationCheck}
+        });
+
+        this.items = [{
+            layout: 'vbox',
+            align: 'stretch',
+            pack: 'start',
+            border: false,
+            items: [{
+                layout: 'form',
+                frame: true,
+                hideLabels: true,
+                width: '100%',
+                items: [
+                    this.hasOwnNotificationSettings
+                ]
+            },
+                this.notificationGrid
+            ]
+        }];
+
+        this.supr().initComponent.call(this);
+    },
+
+    onOwnNotificationCheck: function (cb, checked) {
+        this.notificationGrid.setReadOnly(!checked);
+
+        if (!checked) {
+            this.notificationGrid.getStore().removeAll();
+        }
+    },
+
+    onRecordLoad: function (editDialog, record, ticketFn) {
+        this.notificationGrid.getStore().loadData(window.lodash.get(record, 'data.notificationProps', []), false);
+    },
+
+    onSave: function (editDialog, record, ticketFn) {
+        var _ = window.lodash;
+
+        // Remove properties
+        _.get(record, 'data.notificationProps', []);
+
+        var data = [];
+
+        this.notificationGrid.getStore().each(function (record) {
+            // prevent to send accountName here
+            data.push({
+                'active': record.data.active,
+                'summary': record.data.summary,
+                'accountId': record.data.accountId,
+                'accountType': record.data.accountType
+            });
+        });
+
+        record.set('notificationProps', data);
+    }
+});
\ No newline at end of file
index 91c1464..89b2b26 100644 (file)
@@ -283,13 +283,13 @@ Tine.Tinebase.Model.Alarm = Tine.Tinebase.data.Record.create([
     recordsName: 'Alarms',
     getOption: function(name) {
         var encodedOptions = this.get('options'),
-            options = encodedOptions ? Ext.decode(encodedOptions) : {}
+            options = encodedOptions ? Ext.decode(encodedOptions) : {};
         
         return options[name];
     },
     setOption: function(name, value) {
         var encodedOptions = this.get('options'),
-            options = encodedOptions ? Ext.decode(encodedOptions) : {}
+            options = encodedOptions ? Ext.decode(encodedOptions) : {};
         
         options[name] = value;
         this.set('options', Ext.encode(options));
@@ -506,7 +506,10 @@ Tine.Tinebase.Model.Tree_NodeArray = Tine.Tinebase.Model.modlogFields.concat([
     { name: 'relations' },
     { name: 'customfields' },
     { name: 'notes' },
-    { name: 'tags' }
+    { name: 'tags' },
+
+    { name: 'revisionProps' },
+    { name: 'notificationProps' }
 ]);
 /**
  * @namespace   Tine.Tinebase.Model
index a39cf94..0e5f72e 100644 (file)
@@ -46,21 +46,11 @@ Ext.ux.grid.CheckColumn = function(config){
     if(!this.id){
         this.id = Ext.id();
     }
-    // this.addEvents(
-    //     /**
-    //      * @event beforecheck
-    //      * is fired when user clicks the checkbox
-    //      * return false to abort checking
-    //      * @param {Ext.ux.grid.CheckColumn}
-    //      * @param {record} row record
-    //      * @param {bool} current Value
-    //      */
-    //     'beforecheck'
-    // );
+
     this.renderer = this.renderer.createDelegate(this);
 };
 
-Ext.ux.grid.CheckColumn.prototype ={
+Ext.ux.grid.CheckColumn.prototype = {
     init : function(grid){
         this.grid = grid;
         this.grid.on('render', function(){
@@ -69,14 +59,28 @@ Ext.ux.grid.CheckColumn.prototype ={
         }, this);
     },
 
+    /**
+     * Validate action is valid or not here
+     *
+     * If returned false, the setting won't be changed.
+     *
+     * @param checkbox
+     * @param record
+     * @return {boolean}
+     */
+    onBeforeCheck: function(checkbox, record) {
+        return true;
+    },
+
     onMouseDown : function(e, t){
         if(Ext.fly(t).hasClass(this.createId())){
             e.stopEvent();
             var index = this.grid.getView().findRowIndex(t);
             var record = this.grid.store.getAt(index);
-            // if (this.fireEvent('beforecheck', this, record, record.data[this.dataIndex]) !== false) {
+
+            if (this.onBeforeCheck(this, record)) {
                 record.set(this.dataIndex, !record.data[this.dataIndex]);
-            // }
+            }
         }
     },