0013098: Password field
authorMichael Spahn <m.spahn@metaways.de>
Mon, 22 May 2017 07:42:34 +0000 (09:42 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Tue, 23 May 2017 13:06:51 +0000 (15:06 +0200)
https://forge.tine20.org/view.php?id=13098

Change-Id: I7d45e8bcaf0da7de7f2b6dcfd933b7eeca78f52b
Reviewed-on: http://gerrit.tine20.com/customers/4714
Tested-by: Jenkins CI (http://ci.tine20.com/)
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
Tested-by: Philipp Schüle <p.schuele@metaways.de>
tine20/Filemanager/Filemanager.jsb2
tine20/Filemanager/js/FilePublishedDialog.js [new file with mode: 0644]
tine20/Filemanager/js/nodeActions.js
tine20/Tinebase/Config.php
tine20/Tinebase/Tinebase.jsb2
tine20/Tinebase/js/PasswordGenerator.js [new file with mode: 0644]
tine20/Tinebase/js/widgets/dialog/PasswordDialog.js

index 319bf7e..740666e 100644 (file)
         {
           "text": "DocumentPreview.js",
           "path": "js/"
+        },
+        {
+          "text": "FilePublishedDialog.js",
+          "path": "js/"
         }
       ]
     },
diff --git a/tine20/Filemanager/js/FilePublishedDialog.js b/tine20/Filemanager/js/FilePublishedDialog.js
new file mode 100644 (file)
index 0000000..0d1412d
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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.FilePublishedDialog = Ext.extend(Ext.Panel, {
+    /**
+     * Tine.Filemanager.Model.DownloadLink
+     */
+    record: null,
+
+    /**
+     * Password used to protect downloadlink
+     */
+    password: null,
+
+    /**
+     * Filemanager
+     */
+    app: null,
+
+    windowNamePrefix: 'FilePublishedDialog_',
+
+    layout: 'fit',
+    border: false,
+    frame: false,
+
+    /**
+     * Constructor.
+     */
+    initComponent: function () {
+        if (!this.app) {
+            this.app = Tine.Tinebase.appMgr.get('Filemanager');
+        }
+
+        this.items = [{
+            border: false,
+            frame: true,
+            layout: 'border',
+            items: [{
+                region: 'center',
+                xtype: 'columnform',
+                labelAlign: 'top',
+                formDefaults: {
+                    xtype: 'textfield',
+                    anchor: '100%',
+                    labelSeparator: '',
+                    columnWidth: .333
+                },
+                items: [
+                    [{
+                        columnWidth: 1,
+                        fieldLabel: i18n._('Url'),
+                        name: 'url',
+                        value: this.record.get('url'),
+                        maxLength: 100,
+                        allowBlank: true,
+                        readOnly: true
+                    }, {
+                        columnWidth: 1,
+                        fieldLabel: i18n._('Password'),
+                        name: 'url',
+                        value: this.password,
+                        xtype: 'tw-passwordTriggerField',
+                        allowBlank: true,
+                        editable: false
+                    }, {
+                        columnWidth: 1,
+                        fieldLabel: i18n._('Valid until'),
+                        name: 'url',
+                        xtype: 'datefield',
+                        editable: false,
+                        value: this.record.get('expiry_time')
+                    }]
+                ]
+            }]
+        }];
+
+        var me = this;
+
+        this.tbar = [];
+
+        if (Tine.Tinebase.appMgr.isEnabled('Felamimail')) {
+            this.sendByMailAction = new Ext.Action({
+                disabled: false,
+                text: this.app.i18n._('Send as e-mail'),
+                iconCls: 'action_composeEmail',
+                minWidth: 70,
+                handler: this.onSendAsMail.createDelegate(me),
+                scope: this
+            });
+            this.tbar.push(this.sendByMailAction);
+        }
+
+
+        Tine.Filemanager.FilePublishedDialog.superclass.initComponent.call(this);
+    },
+
+    onSendAsMail: function () {
+        var body =  this.app.i18n._("Download") + ": " + this.record.get('url');
+
+        if (this.password) {
+            body += "\n" + this.app.i18n._("Password") + ": " + this.password;
+        }
+
+        var record = new Tine.Felamimail.Model.Message({
+            'body': body
+        });
+
+        Tine.Felamimail.MessageEditDialog.openWindow({
+            record: record
+        });
+    }
+});
+
+Tine.Filemanager.FilePublishedDialog.openWindow = function (config) {
+    var id = (config.record && config.record.id) ? config.record.id : 0;
+    return Tine.WindowFactory.getWindow({
+        width: 350,
+        height: 200,
+        name: Tine.Filemanager.FilePublishedDialog.prototype.windowNamePrefix + id,
+        contentPanelConstructor: 'Tine.Filemanager.FilePublishedDialog',
+        contentPanelConstructorConfig: config,
+        modal: true
+    });
+};
index 57200c0..65546c2 100644 (file)
@@ -330,15 +330,13 @@ Tine.Filemanager.nodeActions.Publish = {
                 expiry_time: date,
                 password: password
             });
+
             Tine.Filemanager.downloadLinkRecordBackend.saveRecord(record, {
                 success: function (record) {
-                    // TODO: add mail-button
-                    Ext.MessageBox.show({
+                    Tine.Filemanager.FilePublishedDialog.openWindow({
                         title: selections[0].data.type == 'folder' ? app.i18n._('Folder has been published successfully') : app.i18n._('File has been published successfully'),
-                        msg: String.format(app.i18n._("Url: {0}") + '<br />' + app.i18n._("Valid Until: {1}"), record.get('url'), record.get('expiry_time')),
-                        minWidth: 900,
-                        buttons: Ext.Msg.OK,
-                        icon: Ext.MessageBox.INFO,
+                        record: record,
+                        password: password
                     });
                 }, failure: Tine.Tinebase.ExceptionHandler.handleRequestException, scope: this
             });
index 8435ce5..53c6348 100644 (file)
@@ -1135,7 +1135,7 @@ class Tinebase_Config extends Tinebase_Config_Abstract
         //_('Enable password policy')
             'description'           => 'Enable password policy',
             'type'                  => 'bool',
-            'clientRegistryInclude' => FALSE,
+            'clientRegistryInclude' => TRUE,
             'setByAdminModule'      => FALSE,
             'setBySetupModule'      => TRUE,
         ),
@@ -1155,7 +1155,7 @@ class Tinebase_Config extends Tinebase_Config_Abstract
         //_('Minimum password length')
             'description'           => 'Minimum password length.',
             'type'                  => 'int',
-            'clientRegistryInclude' => FALSE,
+            'clientRegistryInclude' => TRUE,
             'setByAdminModule'      => FALSE,
             'setBySetupModule'      => TRUE,
         ),
@@ -1165,7 +1165,7 @@ class Tinebase_Config extends Tinebase_Config_Abstract
         //_('Minimum word chars in password')
             'description'           => 'Minimum word chars in password',
             'type'                  => 'int',
-            'clientRegistryInclude' => FALSE,
+            'clientRegistryInclude' => TRUE,
             'setByAdminModule'      => FALSE,
             'setBySetupModule'      => TRUE,
         ),
@@ -1175,7 +1175,7 @@ class Tinebase_Config extends Tinebase_Config_Abstract
         //_('Minimum uppercase chars in password')
             'description'           => 'Minimum uppercase chars in password',
             'type'                  => 'int',
-            'clientRegistryInclude' => FALSE,
+            'clientRegistryInclude' => TRUE,
             'setByAdminModule'      => FALSE,
             'setBySetupModule'      => TRUE,
         ),
@@ -1185,7 +1185,7 @@ class Tinebase_Config extends Tinebase_Config_Abstract
         //_('Minimum special chars in password')
             'description'           => 'Minimum special chars in password',
             'type'                  => 'int',
-            'clientRegistryInclude' => FALSE,
+            'clientRegistryInclude' => TRUE,
             'setByAdminModule'      => FALSE,
             'setBySetupModule'      => TRUE,
         ),
@@ -1195,7 +1195,7 @@ class Tinebase_Config extends Tinebase_Config_Abstract
         //_('Minimum numbers in password')
             'description'           => 'Minimum numbers in password',
             'type'                  => 'int',
-            'clientRegistryInclude' => FALSE,
+            'clientRegistryInclude' => TRUE,
             'setByAdminModule'      => FALSE,
             'setBySetupModule'      => TRUE,
         ),
index db1fa8e..e97daaf 100644 (file)
           "path": "js/"
         },
         {
+          "text": "PasswordGenerator.js",
+          "path": "js/"
+        },
+        {
           "text": "tineInit.js",
           "path": "js/"
         }
diff --git a/tine20/Tinebase/js/PasswordGenerator.js b/tine20/Tinebase/js/PasswordGenerator.js
new file mode 100644 (file)
index 0000000..ca6b494
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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.Tinebase');
+
+/**
+ * PasswordGenerator.
+ *
+ * @param config
+ * @constructor
+ */
+Tine.Tinebase.PasswordGenerator = function (config) {
+    Ext.applyIf(this, config || {
+        minLength: 12,
+        minWordChars: 5,
+        minUppercaseChars: 1,
+        minSpecialChars: 1,
+        minNumericalChars: 1
+    });
+};
+
+Tine.Tinebase.PasswordGenerator.prototype = {
+    /**
+     * Ascii range for uppercase characters
+     * @private
+     */
+    uppercaseChars: [65, 90],
+    /**
+     * Ascii range for lowercase characters
+     * @private
+     */
+    lowercaseChars: [97, 122],
+    /**
+     * Ascii range for special chars
+     * @private
+     */
+    specialChars: [33, 46],
+    /**
+     * Ascii range for numerical chars
+     * @private
+     */
+    numericalChars: [48, 57],
+
+    /**
+     * Characters to be used in password
+     * @private
+     */
+    characters: [],
+
+    /**
+     * Generates a random password based on the minimal criteria defined in config
+     */
+    generatePassword: function () {
+        this.characters = [];
+
+        this.addCharsOfType(this.uppercaseChars, this.minUppercaseChars);
+        this.addCharsOfType(this.specialChars, this.minSpecialChars);
+        this.addCharsOfType(this.numericalChars, this.minNumericalChars);
+        this.addCharsOfType(this.lowercaseChars, this.minWordChars);
+        this.addCharsOfType(this.lowercaseChars, this.minLength - this.characters.length);
+
+        var _ = window.lodash;
+
+        return _.join(_.shuffle(this.characters), '');
+    },
+
+    /**
+     * @private
+     * @param range
+     * @param min
+     */
+    addCharsOfType: function(range, min) {
+        for(var i = 0; i < min; i++) {
+            this.characters.push(this.getRandomCharOfRange(range));
+        }
+    },
+
+    /**
+     * @private
+     * @param range
+     * @return {string}
+     */
+    getRandomCharOfRange: function (range) {
+        var _ = window.lodash;
+        return String.fromCharCode(_.random(range[0], range[1]));
+    }
+};
\ No newline at end of file
index 3a26108..ecc0dae 100644 (file)
@@ -24,9 +24,9 @@ Tine.Tinebase.widgets.dialog.PasswordDialog = Ext.extend(Ext.Panel, {
     allowEmptyPassword: false,
 
     /**
-     * Entered password
+     * Password field
      */
-    password: null,
+    passwordField: null,
 
     /**
      * Constructor.
@@ -62,6 +62,7 @@ Tine.Tinebase.widgets.dialog.PasswordDialog = Ext.extend(Ext.Panel, {
                         name: 'password',
                         maxLength: 100,
                         allowBlank: false,
+                        ref: '../../../../passwordField',
                         listeners: {
                             scope: this,
                             keyup: this.onChange.createDelegate(this)
@@ -81,6 +82,19 @@ Tine.Tinebase.widgets.dialog.PasswordDialog = Ext.extend(Ext.Panel, {
             scope: this
         });
 
+        this.pwgenAction = new Ext.Action({
+            disabled: false,
+            text: 'Generate password',
+            minWidth: 70,
+            iconCls: 'action_managePermissions',
+            handler: this.onPWGen.createDelegate(me),
+            scope: this
+        });
+
+        this.tbar = [
+            this.pwgenAction
+        ];
+
         this.bbar = [
             '->',
             this.okAction
@@ -90,11 +104,31 @@ Tine.Tinebase.widgets.dialog.PasswordDialog = Ext.extend(Ext.Panel, {
     },
 
     /**
+     * Generate pw
+     */
+    onPWGen: function () {
+        var config = null;
+
+        if (Tine.Tinebase.configManager.get('pwPolicyActive')) {
+            config = {
+                minLength: Tine.Tinebase.configManager.get('pwPolicyMinLength'),
+                minWordChars: Tine.Tinebase.configManager.get('pwPolicyMinWordChars'),
+                minUppercaseChars: Tine.Tinebase.configManager.get('pwPolicyMinUppercaseChars'),
+                minSpecialChars: Tine.Tinebase.configManager.get('pwPolicyMinSpecialChars'),
+                minNumericalChars: Tine.Tinebase.configManager.get('pwPolicyMinNumbers')
+            }
+        }
+
+        var gen = new Tine.Tinebase.PasswordGenerator(config);
+
+        this.passwordField.setValue(gen.generatePassword());
+    },
+
+    /**
      * Disable ok button if no password entered
      * @param el
      */
     onChange: function (el) {
-        this.password = el.getValue();
         this.okAction.setDisabled(!this.allowEmptyPassword && el.getValue().length === 0)
     },
 
@@ -102,7 +136,7 @@ Tine.Tinebase.widgets.dialog.PasswordDialog = Ext.extend(Ext.Panel, {
      * button handler
      */
     onOk: function () {
-        this.fireEvent('passwordEntered', this.password);
+        this.fireEvent('passwordEntered', this.passwordField.getValue());
         this.window.close();
     },