Added missing file for `UI/Confirmation`
authorAlexander Ebert <ebert@woltlab.com>
Fri, 29 May 2015 12:59:29 +0000 (14:59 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Fri, 29 May 2015 12:59:29 +0000 (14:59 +0200)
wcfsetup/install/files/js/WoltLab/WCF/Ajax.js
wcfsetup/install/files/js/WoltLab/WCF/Ajax/Request.js
wcfsetup/install/files/js/WoltLab/WCF/Core.js
wcfsetup/install/files/js/WoltLab/WCF/UI/Confirmation.js [new file with mode: 0644]
wcfsetup/install/files/js/WoltLab/WCF/UI/Dialog.js
wcfsetup/install/files/lib/data/AbstractDatabaseObjectAction.class.php

index d7337037dbe80f6af3732ce3486e87a64e71c68c..5d6e930df8093767b727c1e1842b2754117e999b 100644 (file)
@@ -56,9 +56,6 @@ define(['AjaxRequest', 'Core', 'ObjectMap'], function(AjaxRequest, Core, ObjectM
                        }
                        
                        var options = callbackObject._ajaxSetup();
-                       if (typeof data === 'object') {
-                               options.data = Core.extend(data, options.data);
-                       }
                        
                        options.pinData = true;
                        options.callbackObject = callbackObject;
@@ -70,6 +67,10 @@ define(['AjaxRequest', 'Core', 'ObjectMap'], function(AjaxRequest, Core, ObjectM
                        if (typeof success === 'function') request.setOption('success', success);
                        if (typeof failure === 'function') request.setOption('failure', failure);
                        
+                       if (typeof data === 'object') {
+                               request.setData(data);
+                       }
+                       
                        request.sendRequest();
                        
                        _requests.set(callbackObject, request);
index 5159795493a5ea142c092e91afc3edca26c43bfb..7b12064bed54bd86f8f41615de2f4ee679c64eb6 100644 (file)
@@ -53,6 +53,10 @@ define(['Core', 'Language', 'DOM/ChangeListener', 'DOM/Util', 'UI/Dialog', 'Wolt
                                callbackObject: null
                        }, options);
                        
+                       if (typeof options.callbackObject === 'object') {
+                               this._options.callbackObject = options.callbackObject;
+                       }
+                       
                        this._options.url = Core.convertLegacyUrl(this._options.url);
                        
                        if (this._options.pinData) {
index f64496a86540e246582f8e1e8a0d8da7aadae145..cd8649a37bba4e2c078300c43393305a30c80651 100644 (file)
@@ -90,6 +90,7 @@ define([], function() {
                 */
                extend: function(out) {
                        out = out || {};
+                       var newObj = this.clone(out);
                        
                        for (var i = 1, length = arguments.length; i < length; i++) {
                                var obj = arguments[i];
@@ -99,16 +100,40 @@ define([], function() {
                                for (var key in obj) {
                                        if (obj.hasOwnProperty(key)) {
                                                if (!Array.isArray(obj[key]) && typeof obj[key] === 'object') {
-                                                       this.extend(out[key], obj[key]);
+                                                       if (this.isPlainObject(obj[key])) {
+                                                               // object literals have the prototype of Object which in return has no parent prototype
+                                                               newObj[key] = this.extend(out[key], obj[key]);
+                                                       }
+                                                       else {
+                                                               newObj[key] = obj[key];
+                                                       }
                                                }
                                                else {
-                                                       out[key] = obj[key];
+                                                       newObj[key] = obj[key];
                                                }
                                        }
                                }
                        }
                        
-                       return out;
+                       return newObj;
+               },
+               
+               /**
+                * Returns true if `obj` is an object literal.
+                * 
+                * @param       {*}     obj     target object
+                * @returns     {boolean}       true if target is an object literal
+                */
+               isPlainObject: function(obj) {
+                       if (obj === window || obj.nodeType) {
+                               return false;
+                       }
+                       
+                       if (obj.constructor && !obj.constructor.prototype.hasOwnProperty('isPrototypeOf')) {
+                               return false;
+                       }
+                       
+                       return true;
                },
                
                /**
@@ -138,29 +163,23 @@ define([], function() {
                 * Recursively serializes an object into an encoded URI parameter string.
                 *  
                 * @param       {object}        obj     target object
+                * @param       {string=}       prefix  parameter prefix
                 * @return      encoded parameter string
                 */
-               serialize: function(obj) {
+               serialize: function(obj, prefix) {
                        var parameters = [];
                        
                        for (var key in obj) {
                                if (obj.hasOwnProperty(key)) {
+                                       var parameterKey = (prefix) ? prefix + '[' + key + ']' : key;
                                        var value = obj[key];
                                        
-                                       if (Array.isArray(value)) {
-                                               for (var i = 0, length = value.length; i < length; i++) {
-                                                       parameters.push(key + '[]=' + encodeURIComponent(value[i]));
-                                               }
-                                               
-                                               continue;
+                                       if (typeof value === 'object') {
+                                               parameters.push(this.serialize(value, parameterKey));
                                        }
-                                       else if (this.getType(value) === 'Object') {
-                                               parameters.push(this.serialize(value));
-                                               
-                                               continue;
+                                       else {
+                                               parameters.push(encodeURIComponent(parameterKey) + '=' + encodeURIComponent(value));
                                        }
-                                       
-                                       parameters.push(key + '=' + encodeURIComponent(value));
                                }
                        }
                        
diff --git a/wcfsetup/install/files/js/WoltLab/WCF/UI/Confirmation.js b/wcfsetup/install/files/js/WoltLab/WCF/UI/Confirmation.js
new file mode 100644 (file)
index 0000000..e7bf407
--- /dev/null
@@ -0,0 +1,151 @@
+/**
+ * Provides the confirmation dialog overlay.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module     WoltLab/WCF/UI/Confirmation
+ */
+define(['Core', 'Language', 'UI/Dialog'], function(Core, Language, UIDialog) {
+       "use strict";
+       
+       var _active = false;
+       var _confirmButton = null;
+       var _content = null;
+       var _options = {};
+       var _text = null;
+       
+       /**
+        * Confirmation dialog overlay.
+        * 
+        * @exports     WoltLab/WCF/UI/Confirmation
+        */
+       var UIConfirmation = {
+               /**
+                * Shows the confirmation dialog.
+                * 
+                * Possible options:
+                *  - cancel: callback if user cancels the dialog
+                *  - confirm: callback if user confirm the dialog
+                *  - legacyCallback: WCF 2.0/2.1 compatible callback with string parameter
+                *  - message: displayed confirmation message
+                *  - parameters: list of parameters passed to the callback on confirm
+                *  - template: optional HTML string to be inserted below the `message`
+                * 
+                * @param       {object<string, *>}     options         confirmation options
+                */
+               show: function(options) {
+                       if (_active) {
+                               return;
+                       }
+                       
+                       _options = Core.extend({
+                               cancel: null,
+                               confirm: null,
+                               legacyCallback: null,
+                               message: '',
+                               parameters: {},
+                               template: ''
+                       }, options);
+                       
+                       _options.message = (typeof _options.message === 'string') ? _options.message.trim() : '';
+                       if (!_options.message.length) {
+                               throw new Error("Expected a non-empty string for option 'message'.");
+                       }
+                       
+                       if (typeof _options.confirm !== 'function' && typeof _options.legacyCallback !== 'function') {
+                               throw new TypeError("Expected a valid callback for option 'confirm'.");
+                       }
+                       
+                       if (_content === null) {
+                               this._createDialog();
+                       }
+                       
+                       _content.innerHTML = (typeof options.template === 'string') ? options.template.trim() : '';
+                       _text.textContent = _options.message;
+                       
+                       _active = true;
+                       
+                       UIDialog.open('wcfSystemConfirmation', null, {
+                               onClose: this._onClose.bind(this),
+                               onShow: this._onShow.bind(this),
+                               title: Language.get('wcf.global.confirmation.title')
+                       });
+               },
+               
+               /**
+                * Creates the dialog DOM elements.
+                */
+               _createDialog: function() {
+                       var dialog = document.createElement('div');
+                       dialog.setAttribute('id', 'wcfSystemConfirmation');
+                       dialog.classList.add('systemConfirmation');
+                       
+                       _text = document.createElement('p');
+                       dialog.appendChild(_text);
+                       
+                       _content = document.createElement('div');
+                       _content.setAttribute('id', 'wcfSystemConfirmationContent');
+                       dialog.appendChild(_content);
+                       
+                       var formSubmit = document.createElement('div');
+                       formSubmit.classList.add('formSubmit');
+                       dialog.appendChild(formSubmit);
+                       
+                       _confirmButton = document.createElement('button');
+                       _confirmButton.classList.add('buttonPrimary');
+                       _confirmButton.textContent = Language.get('wcf.global.confirmation.confirm');
+                       _confirmButton.addEventListener('click', this._confirm.bind(this));
+                       formSubmit.appendChild(_confirmButton);
+                       
+                       var cancelButton = document.createElement('button');
+                       cancelButton.textContent = Language.get('wcf.global.confirmation.cancel');
+                       cancelButton.addEventListener('click', function() { UIDialog.close('wcfSystemConfirmation'); });
+                       formSubmit.appendChild(cancelButton);
+                       
+                       document.body.appendChild(dialog);
+               },
+               
+               /**
+                * Invoked if the user confirms the dialog.
+                */
+               _confirm: function() {
+                       if (typeof _options.legacyCallback === 'function') {
+                               _options.legacyCallback('confirm', _options.parameters);
+                       }
+                       else {
+                               _options.confirm(_options.parameters);
+                       }
+                       
+                       _active = false;
+                       UIDialog.close('wcfSystemConfirmation');
+               },
+               
+               /**
+                * Invoked on dialog close or if user cancels the dialog.
+                */
+               _onClose: function() {
+                       if (_active) {
+                               _confirmButton.blur();
+                               _active = false;
+                               
+                               if (typeof _options.legacyCallback === 'function') {
+                                       _options.legacyCallback('cancel', _options.parameters);
+                               }
+                               else if (typeof _options.cancel === 'function') {
+                                       _options.cancel(_options.parameters);
+                               }
+                       }
+               },
+               
+               /**
+                * Sets the focus on the confirm button on dialog open for proper keyboard support.
+                */
+               _onShow: function() {
+                       _confirmButton.blur();
+                       _confirmButton.focus();
+               }
+       };
+       
+       return UIConfirmation;
+});
index 1c67cf82e69e288519665b449a4a0a6703b808fa..b5ae883db5544438018556579677e7f7754f0f24 100644 (file)
@@ -365,7 +365,7 @@ define(
                                setTimeout(function() {
                                        if (data.dialog.getAttribute('aria-hidden') === 'true') {
                                                _container.removeChild(data.dialog);
-                                               _dialogs.delete(id);
+                                               _dialogs['delete'](id);
                                        }
                                }, 5000);
                        }
index 879c5f352c48915b1369a4803300449af3a750d8..ddf78a03dcdaa56f374bcf0ff26bef317e640384 100644 (file)
@@ -9,6 +9,7 @@ use wcf\system\WCF;
 use wcf\util\ClassUtil;
 use wcf\util\JSON;
 use wcf\util\StringUtil;
+use wcf\util\ArrayUtil;
 
 /**
  * Default implementation for DatabaseObject-related actions.
@@ -99,6 +100,9 @@ abstract class AbstractDatabaseObjectAction implements IDatabaseObjectAction, ID
        const TYPE_BOOLEAN = 3;
        const TYPE_JSON = 4;
        
+       const STRUCT_FLAT = 1;
+       const STRUCT_ARRAY = 2;
+       
        /**
         * Initialize a new DatabaseObject-related action.
         * 
@@ -419,7 +423,18 @@ abstract class AbstractDatabaseObjectAction implements IDatabaseObjectAction, ID
         * @param       string          $arrayIndex
         */
        protected function readInteger($variableName, $allowEmpty = false, $arrayIndex = '') {
-               $this->readValue($variableName, $allowEmpty, $arrayIndex, self::TYPE_INTEGER);
+               $this->readValue($variableName, $allowEmpty, $arrayIndex, self::TYPE_INTEGER, self::STRUCT_FLAT);
+       }
+       
+       /**
+        * Reads an integer array and validates it.
+        * 
+        * @param       string          $variableName
+        * @param       boolean         $allowEmpty
+        * @param       string          $arrayIndex
+        */
+       protected function readIntegerArray($variableName, $allowEmpty = false, $arrayIndex = '') {
+               $this->readValue($variableName, $allowEmpty, $arrayIndex, self::TYPE_INTEGER, self::STRUCT_ARRAY);
        }
        
        /**
@@ -430,7 +445,7 @@ abstract class AbstractDatabaseObjectAction implements IDatabaseObjectAction, ID
         * @param       string          $arrayIndex
         */
        protected function readString($variableName, $allowEmpty = false, $arrayIndex = '') {
-               $this->readValue($variableName, $allowEmpty, $arrayIndex, self::TYPE_STRING);
+               $this->readValue($variableName, $allowEmpty, $arrayIndex, self::TYPE_STRING, self::STRUCT_FLAT);
        }
        
        /**
@@ -441,7 +456,7 @@ abstract class AbstractDatabaseObjectAction implements IDatabaseObjectAction, ID
         * @param       string          $arrayIndex
         */
        protected function readBoolean($variableName, $allowEmpty = false, $arrayIndex = '') {
-               $this->readValue($variableName, $allowEmpty, $arrayIndex, self::TYPE_BOOLEAN);
+               $this->readValue($variableName, $allowEmpty, $arrayIndex, self::TYPE_BOOLEAN, self::STRUCT_FLAT);
        }
        
        /**
@@ -452,7 +467,7 @@ abstract class AbstractDatabaseObjectAction implements IDatabaseObjectAction, ID
         * @param       string          $arrayIndex
         */
        protected function readJSON($variableName, $allowEmpty = false, $arrayIndex = '') {
-               $this->readValue($variableName, $allowEmpty, $arrayIndex, self::TYPE_JSON);
+               $this->readValue($variableName, $allowEmpty, $arrayIndex, self::TYPE_JSON, self::STRUCT_FLAT);
        }
        
        /**
@@ -464,8 +479,9 @@ abstract class AbstractDatabaseObjectAction implements IDatabaseObjectAction, ID
         * @param       boolean         $allowEmpty
         * @param       string          $arrayIndex
         * @param       integer         $type
+        * @param       integer         $structure
         */
-       protected function readValue($variableName, $allowEmpty, $arrayIndex, $type) {
+       protected function readValue($variableName, $allowEmpty, $arrayIndex, $type, $structure) {
                if ($arrayIndex) {
                        if (!isset($this->parameters[$arrayIndex])) {
                                throw new SystemException("Corrupt parameters, index '".$arrayIndex."' is missing");
@@ -481,16 +497,30 @@ abstract class AbstractDatabaseObjectAction implements IDatabaseObjectAction, ID
                        case self::TYPE_INTEGER:
                                if (!isset($target[$variableName])) {
                                        if ($allowEmpty) {
-                                               $target[$variableName] = 0;
+                                               $target[$variableName] = ($structure === self::STRUCT_FLAT) ? 0 : array();
                                        }
                                        else {
                                                throw new UserInputException($variableName);
                                        }
                                }
                                else {
-                                       $target[$variableName] = intval($target[$variableName]);
-                                       if (!$allowEmpty && !$target[$variableName]) {
-                                               throw new UserInputException($variableName);
+                                       if ($structure === self::STRUCT_FLAT) {
+                                               $target[$variableName] = intval($target[$variableName]);
+                                               if (!$allowEmpty && !$target[$variableName]) {
+                                                       throw new UserInputException($variableName);
+                                               }
+                                       }
+                                       else {
+                                               $target[$variableName] = ArrayUtil::toIntegerArray($target[$variableName]);
+                                               if (!is_array($target[$variableName])) {
+                                                       throw new UserInputException($variableName);
+                                               }
+                                               
+                                               for ($i = 0, $length = count($target[$variableName]); $i < $length; $i++) {
+                                                       if ($target[$variableName][$i] === 0) {
+                                                               throw new UserInputException($variableName);
+                                                       }
+                                               }
                                        }
                                }
                        break;