From 617c2a8c026239eca7347bd830e326852899c88d Mon Sep 17 00:00:00 2001
From: Henning Leutz <leutz@pcsg.de>
Date: Fri, 8 Dec 2017 14:09:06 +0100
Subject: [PATCH] feat: quiqqer/package-bricks#62

---
 ajax/getPanelCategories.php |  26 ++
 ajax/getPanelCategory.php   |  17 ++
 bin/BrickEdit.css           |  10 +
 bin/BrickEdit.js            | 581 ++++++++++++++++++++----------------
 src/QUI/Bricks/Brick.php    |   7 +
 src/QUI/Bricks/Manager.php  |  48 +--
 src/QUI/Bricks/Panel.php    | 146 +++++++++
 src/QUI/Bricks/Utils.php    | 108 +++++++
 8 files changed, 653 insertions(+), 290 deletions(-)
 create mode 100644 ajax/getPanelCategories.php
 create mode 100644 ajax/getPanelCategory.php
 create mode 100644 src/QUI/Bricks/Panel.php

diff --git a/ajax/getPanelCategories.php b/ajax/getPanelCategories.php
new file mode 100644
index 0000000..89aa1a8
--- /dev/null
+++ b/ajax/getPanelCategories.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * This file contains package_quiqqer_bricks_ajax_getPanelCategories
+ */
+
+/**
+ * Returns the Brick categories for a brick panel
+ *
+ * @param {String|Integer} $brickId - Brick-ID
+ * @return array
+ */
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_bricks_ajax_getPanelCategories',
+    function ($brickId) {
+        $categories = QUI\Bricks\Panel::getInstance()->getCategoriesFromBrick($brickId);
+
+        foreach ($categories as $key => $category) {
+            unset($categories[$key]['items']);
+        }
+
+        return $categories;
+    },
+    array('brickId'),
+    'Permission::checkAdminUser'
+);
diff --git a/ajax/getPanelCategory.php b/ajax/getPanelCategory.php
new file mode 100644
index 0000000..79425fc
--- /dev/null
+++ b/ajax/getPanelCategory.php
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * Return a xml category
+ *
+ * @param array $file - list of xml files
+ * @param $category
+ * @return String
+ */
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_bricks_ajax_getPanelCategory',
+    function ($brickId, $category) {
+        return QUI\Bricks\Panel::getInstance()->getCategoryFromBrick($brickId, $category);
+    },
+    array('brickId', 'category'),
+    'Permission::checkAdminUser'
+);
diff --git a/bin/BrickEdit.css b/bin/BrickEdit.css
index f3f0225..76d576e 100644
--- a/bin/BrickEdit.css
+++ b/bin/BrickEdit.css
@@ -5,6 +5,16 @@
     width: 100%;
 }
 
+.quiqqer-bricks-container {
+    float: left;
+    position: relative;
+    width: 100%;
+}
+
+.brick-edit-content {
+    margin-bottom: 0;
+}
+
 .quiqqer-bricks-brickedit label {
     clear: both;
     float: left;
diff --git a/bin/BrickEdit.js b/bin/BrickEdit.js
index 7b80090..af23a47 100644
--- a/bin/BrickEdit.js
+++ b/bin/BrickEdit.js
@@ -40,10 +40,18 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
             '$onInject',
             '$onCreate',
             '$onDestroy',
+
+            'showInformation',
+            'showSettings',
+            'showExtras',
+            'showContent',
+
             '$load',
             '$unload',
             'save',
-            'del'
+            'del',
+            '$onCategoryEnter',
+            '$onCategoryLeave'
         ],
 
         options: {
@@ -58,23 +66,27 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
             this.$availableBricks   = [];
             this.$availableSettings = [];
             this.$customfields      = [];
+            this.$loaded            = false;
 
-            this.$Editor = false;
-            this.$Areas  = false;
-            this.$Active = false;
+            this.$Container = null;
+            this.$Editor    = false;
+            this.$Areas     = false;
 
             this.addEvents({
-                onInject : this.$onInject,
-                onCreate : this.$onCreate,
-                onDestroy: this.$onDestroy,
-                onResize : function () {
+                onInject       : this.$onInject,
+                onCreate       : this.$onCreate,
+                onDestroy      : this.$onDestroy,
+                onResize       : function () {
                     var controls = QUI.Controls.getControlsInElement(this.getContent());
+
                     controls.each(function (Control) {
                         if ("resize" in Control) {
                             Control.resize();
                         }
                     });
-                }.bind(this)
+                }.bind(this),
+                onCategoryEnter: this.$onCategoryEnter,
+                onCategoryLeave: this.$onCategoryLeave
             });
         },
 
@@ -113,7 +125,7 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
                 icon  : 'fa fa-file-o',
                 text  : QUILocale.get('quiqqer/system', 'information'),
                 events: {
-                    onActive: this.$load
+                    onClick: this.showInformation
                 }
             });
 
@@ -122,7 +134,7 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
                 icon  : 'fa fa-magic',
                 text  : QUILocale.get('quiqqer/system', 'properties'),
                 events: {
-                    onActive: this.$load
+                    onClick: this.showSettings
                 }
             });
 
@@ -131,7 +143,7 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
                 icon  : 'fa fa-gears',
                 text  : QUILocale.get('quiqqer/system', 'settings'),
                 events: {
-                    onActive: this.$load
+                    onClick: this.showExtras
                 }
             });
 
@@ -140,7 +152,7 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
                 icon  : 'fa fa-file-text-o',
                 text  : QUILocale.get('quiqqer/system', 'content'),
                 events: {
-                    onActive: this.$load
+                    onClick: this.showContent
                 }
             });
         },
@@ -153,8 +165,9 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
 
             QUIAjax.get([
                 'package_quiqqer_bricks_ajax_getBrick',
-                'package_quiqqer_bricks_ajax_getAvailableBricks'
-            ], function (brick, bricks) {
+                'package_quiqqer_bricks_ajax_getAvailableBricks',
+                'package_quiqqer_bricks_ajax_getPanelCategories'
+            ], function (brick, bricks, categories) {
                 /**
                  * @param {{availableSettings:object}} data
                  * @param {{attributes:object}} data
@@ -174,6 +187,14 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
                     })
                 });
 
+                this.getContent().setStyles({
+                    position: 'relative'
+                });
+
+                this.$Container = new Element('div', {
+                    'class': 'quiqqer-bricks-container'
+                }).inject(this.getContent());
+
                 // brick xml settings
                 var type = brick.attributes.type;
                 var data = bricks.filter(function (entry) {
@@ -184,11 +205,15 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
                     this.getCategory('content').hide();
                 }
 
+                for (var i = 0, len = categories.length; i < len; i++) {
+                    this.addCategory(categories[i]);
+                }
+
                 this.refresh();
 
                 this.fireEvent('loaded', [this]);
                 this.getCategory('information').click();
-
+                this.$loaded = true;
             }.bind(this), {
                 'package': 'quiqqer/brick',
                 brickId  : this.getAttribute('id')
@@ -214,26 +239,23 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
          * @return Promise
          */
         save: function () {
-            var self   = this,
-                Active = this.$Active;
-
             this.Loader.show();
             this.$unload();
 
-            return this.$load(Active).then(function () {
-                var data          = self.getAttribute('data');
-                data.customfields = self.$customfields;
+            var self = this,
+                data = self.getAttribute('data');
 
-                return Bricks.saveBrick(self.getAttribute('id'), data).then(function () {
-                    QUI.getMessageHandler().then(function (MH) {
-                        MH.addSuccess(
-                            QUILocale.get(lg, 'message.brick.save.success')
-                        );
-                    });
+            data.customfields = self.$customfields;
 
-                    self.fireEvent('save', [self]);
-                    self.Loader.hide();
+            return Bricks.saveBrick(self.getAttribute('id'), data).then(function () {
+                QUI.getMessageHandler().then(function (MH) {
+                    MH.addSuccess(
+                        QUILocale.get(lg, 'message.brick.save.success')
+                    );
                 });
+
+                self.fireEvent('save', [self]);
+                self.Loader.hide();
             });
         },
 
@@ -272,100 +294,55 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
         /**
          * event on button active
          *
-         * @param {Object} Button - qui/controls/buttons/Button
-         *
          * @return Promise
          */
-        $load: function (Button) {
-            this.Loader.show();
-
-            return new Promise(function (resolve, reject) {
-
-                var Prom = false,
-                    self = this;
-
-                if (Button === this.$Active) {
-                    reject();
-                    self.Loader.hide();
-                    return;
-                }
-
-                var data = this.getAttribute('data');
-
-                this.$unload();
-
-                this.setAttribute('data', data);
-                this.$Active = Button;
-
-                switch (Button.getAttribute('name')) {
-                    case 'information':
-                        Prom = this.$showInformation();
-                        break;
-
-                    case 'settings':
-                        Prom = this.$showSettings();
-                        break;
-
-                    case 'extra':
-                        Prom = this.$showExtras();
-                        break;
-
-                    case 'content':
-                        Prom = this.$showContent();
-                        break;
-
-                    default:
-                        reject();
-                        return;
-                }
-
-                if (!Prom) {
-                    reject();
-                    self.Loader.hide();
-                    return;
-                }
-
-                Prom.then(function () {
-
-                    resolve();
-                    self.Loader.hide();
-
-                }).catch(function () {
-                    reject();
-                    self.Loader.hide();
-                });
-
-            }.bind(this));
+        $load: function () {
+            QUIFormUtils.setDataToForm(
+                this.getAttribute('data').attributes,
+                this.$Container.getElement('form')
+            );
+
+            QUIFormUtils.setDataToForm(
+                this.getAttribute('data').settings,
+                this.$Container.getElement('form')
+            );
         },
 
         /**
          * event unload category
          */
         $unload: function () {
-            if (!this.$Active) {
+            if (!this.$loaded) {
+                return;
+            }
+
+            if (!this.getActiveCategory()) {
                 return;
             }
 
             var Form   = this.getContent().getElement('form'),
-                unload = this.$Active.getAttribute('name'),
+                unload = this.getActiveCategory().getAttribute('name'),
                 data   = this.getAttribute('data');
 
-            if (unload === 'information') {
-                data.attributes = Object.merge(
-                    data.attributes,
-                    QUIFormUtils.getFormData(Form)
-                );
+            switch (unload) {
+                case 'extra':
+                case 'settings':
+                case 'content':
+                    break;
+
+                default:
+                    data.attributes = Object.merge(
+                        data.attributes,
+                        QUIFormUtils.getFormData(Form)
+                    );
             }
 
-            if (Form.getElement('[name="frontendTitle"]')) {
+            if (Form && Form.getElement('[name="frontendTitle"]')) {
                 data.attributes.frontendTitle = Form.getElement('[name="frontendTitle"]').value;
             }
 
-            if (unload === 'settings') {
+            if (unload === 'settings' && this.$Areas) {
                 data.attributes.areas = this.$Areas.getAreas().join(',');
-                //data.attributes.width   = Form.elements.width.value;
-                //data.attributes.height  = Form.elements.height.value;
-                //data.attributes.classes = Form.elements.classes.value;
 
                 var flexibleList = [],
                     fieldData    = QUIFormUtils.getFormData(Form);
@@ -386,9 +363,6 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
                 }
 
                 this.$customfields = flexibleList;
-
-                this.$Areas.destroy();
-                this.$Areas = false;
             }
 
             if (unload === 'extra') {
@@ -398,15 +372,13 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
                 );
             }
 
-            if (unload === 'content') {
+            if (unload === 'content' && this.$Editor) {
                 data.attributes.content = this.$Editor.getContent();
 
                 this.$Editor.destroy();
                 this.$Editor = false;
             }
 
-            this.$Active = null;
-
             this.setAttribute('data', data);
         },
 
@@ -415,25 +387,21 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
          *
          * @returns {Promise}
          */
-        $showInformation: function () {
-            return new Promise(function (resolve, reject) {
-
-                Template.get('ajax/brick/templates/information', function (result) {
-                    this.setContent(result);
-
-                    QUIFormUtils.setDataToForm(
-                        this.getAttribute('data').attributes,
-                        this.getContent().getElement('form')
-                    );
-
-                    resolve();
+        showInformation: function () {
+            var self = this;
 
-                }.bind(this), {
-                    'package': 'quiqqer/bricks',
-                    onError  : reject
+            return this.$hideCategory().then(function () {
+                return Template.get('ajax/brick/templates/information', false, {
+                    'package': 'quiqqer/bricks'
                 });
-
-            }.bind(this));
+            }).then(function (html) {
+                self.$Container.set('html', html);
+                self.$load();
+            }).then(function () {
+                return self.$showCategory();
+            }).then(function () {
+                self.Loader.hide();
+            });
         },
 
         /**
@@ -441,83 +409,88 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
          *
          * @returns {Promise}
          */
-        $showSettings: function () {
-            return new Promise(function (resolve, reject) {
-
-                Template.get('ajax/brick/templates/settings', function (result) {
-                    this.setContent(result);
-
-                    // areas
-                    var Content      = this.getContent(),
-                        areas        = [],
-                        attributes   = this.getAttribute('data').attributes,
-                        customfields = this.$customfields;
-
-                    if (attributes.areas) {
-                        areas = attributes.areas
-                                          .replace(/^,*/, '')
-                                          .replace(/,*$/, '')
-                                          .split(',');
-                    }
-
-                    // areas
-                    this.$Areas = new BrickAreas({
-                        brickId    : this.getAttribute('id'),
-                        projectName: this.getAttribute('projectName'),
-                        projectLang: this.getAttribute('projectLang'),
-                        areas      : areas,
-                        styles     : {
-                            height: 120
+        showSettings: function () {
+            var self = this;
+
+            return this.$hideCategory().then(function () {
+                return new Promise(function (resolve, reject) {
+                    Template.get('ajax/brick/templates/settings', function (result) {
+                        self.$Container.set('html', result);
+
+                        // areas
+                        var Content      = self.getContent(),
+                            areas        = [],
+                            attributes   = self.getAttribute('data').attributes,
+                            customfields = self.$customfields;
+
+                        if (attributes.areas) {
+                            areas = attributes.areas
+                                              .replace(/^,*/, '')
+                                              .replace(/,*$/, '')
+                                              .split(',');
                         }
-                    }).inject(Content.getElement('.quiqqer-bricks-areas'));
-
 
-                    // flexble settings
-                    var i, len, data;
-                    var TBody = Content.getElement('.brick-table-flexible tbody');
-
-                    for (i = 0, len = this.$availableSettings.length; i < len; i++) {
+                        // areas
+                        self.$Areas = new BrickAreas({
+                            brickId    : self.getAttribute('id'),
+                            projectName: self.getAttribute('projectName'),
+                            projectLang: self.getAttribute('projectLang'),
+                            areas      : areas,
+                            styles     : {
+                                height: 120
+                            }
+                        }).inject(Content.getElement('.quiqqer-bricks-areas'));
 
-                        data = this.$availableSettings[i];
 
-                        new Element('tr', {
-                            'class': i % 2 ? 'odd' : 'even',
-                            html   : '<td>' +
-                            '<label>' +
-                            '<input type="checkbox" name="flexible-' + data.name + '" />' +
-                            '<span>' + QUILocale.get(data.text[0], data.text[1]) + '</span>' +
-                            '</label>' +
-                            '</td>'
-                        }).inject(TBody);
-                    }
+                        // flexble settings
+                        var i, len, data;
+                        var TBody = Content.getElement('.brick-table-flexible tbody');
 
-                    if (customfields) {
+                        for (i = 0, len = self.$availableSettings.length; i < len; i++) {
+                            data = self.$availableSettings[i];
 
-                        var name;
-                        var Form = Content.getElement('form');
+                            new Element('tr', {
+                                'class': i % 2 ? 'odd' : 'even',
+                                html   : '<td>' +
+                                '<label>' +
+                                '<input type="checkbox" name="flexible-' + data.name + '" />' +
+                                '<span>' + QUILocale.get(data.text[0], data.text[1]) + '</span>' +
+                                '</label>' +
+                                '</td>'
+                            }).inject(TBody);
+                        }
 
-                        for (i = 0, len = customfields.length; i < len; i++) {
+                        if (customfields) {
+                            var name;
+                            var Form = Content.getElement('form');
 
-                            name = customfields[i];
+                            for (i = 0, len = customfields.length; i < len; i++) {
+                                name = customfields[i];
 
-                            if (typeof Form.elements[name] !== 'undefined') {
-                                Form.elements[name].checked = true;
-                            }
+                                if (typeof Form.elements[name] !== 'undefined') {
+                                    Form.elements[name].checked = true;
+                                }
 
-                            if (typeof Form.elements['flexible-' + name] !== 'undefined') {
-                                Form.elements['flexible-' + name].checked = true;
+                                if (typeof Form.elements['flexible-' + name] !== 'undefined') {
+                                    Form.elements['flexible-' + name].checked = true;
+                                }
                             }
                         }
-                    }
-
-                    resolve();
 
-                }.bind(this), {
-                    'package': 'quiqqer/bricks',
-                    onError  : reject
+                        resolve();
+                    }, {
+                        'package': 'quiqqer/bricks',
+                        onError  : reject
+                    });
                 });
-
-            }.bind(this));
+            }).then(function () {
+                return self.$showCategory();
+            }).then(function () {
+                self.Loader.hide();
+            }).catch(function (err) {
+                console.error(err);
+                self.Loader.hide();
+            });
         },
 
         /**
@@ -525,22 +498,23 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
          *
          * @returns {Promise}
          */
-        $showExtras: function () {
-            return new Promise(function (resolve, reject) {
-
-                Template.get('ajax/brick/templates/extras', function (result) {
-                    this.setContent(result);
+        showExtras: function () {
+            var self = this;
 
-                    this.$createExtraData().then(function () {
-                        resolve();
-                    });
-
-                }.bind(this), {
-                    'package': 'quiqqer/bricks',
-                    onError  : reject
+            return this.$hideCategory().then(function () {
+                return Template.get('ajax/brick/templates/extras', false, {
+                    'package': 'quiqqer/bricks'
                 });
-
-            }.bind(this));
+            }).then(function (html) {
+                self.$Container.set('html', html);
+                self.$load();
+
+                return self.$createExtraData();
+            }).then(function () {
+                return self.$showCategory();
+            }).then(function () {
+                self.Loader.hide();
+            });
         },
 
         /**
@@ -548,34 +522,36 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
          *
          * @returns {Promise}
          */
-        $showContent: function () {
-            return new Promise(function (resolve, reject) {
-
-                Template.get('ajax/brick/templates/content', function (result) {
-                    this.setContent(result);
-
-                    this.$createContentEditor().then(function () {
-                        resolve();
-                    });
+        showContent: function () {
+            var self = this;
 
-                }.bind(this), {
-                    'package': 'quiqqer/bricks',
-                    onError  : reject
+            return this.$hideCategory().then(function () {
+                return Template.get('ajax/brick/templates/content', false, {
+                    'package': 'quiqqer/bricks'
                 });
-
-            }.bind(this));
+            }).then(function (html) {
+                self.$Container.set('html', html);
+                return self.$createContentEditor();
+            }).then(function () {
+                return self.$showCategory();
+            }).then(function () {
+                self.Loader.hide();
+            }).catch(function (err) {
+                console.error(err);
+                self.Loader.hide();
+            });
         },
 
         /**
          * Create the editor, if the brick type is a content type
          *
-         * @param {Function} [callback]
          * @return Promise
          */
-        $createContentEditor: function (callback) {
-            return new Promise(function (resolve) {
+        $createContentEditor: function () {
+            var self = this;
 
-                var TableBody = this.$Elm.getElement('table.brick-edit-content tbody'),
+            return new Promise(function (resolve) {
+                var TableBody = self.$Container.getElement('table.brick-edit-content tbody'),
                     TD        = new Element('td'),
                     TR        = new Element('tr', {
                         'class': 'odd'
@@ -584,25 +560,25 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
                 TD.inject(TR);
                 TR.inject(TableBody);
 
-                var contenSize = this.getContent().getSize();
+                var contentSize = self.getContent().getSize();
 
                 // load ckeditor
                 require(['classes/editor/Manager'], function (EditorManager) {
                     new EditorManager().getEditor(null, function (Editor) {
-                        this.$Editor = Editor;
-                        this.$Editor.setAttribute('showLoader', false);
+                        self.$Editor = Editor;
+                        self.$Editor.setAttribute('showLoader', false);
 
                         var Project = Projects.get(
-                            this.getAttribute('projectName'),
-                            this.getAttribute('projectLang')
+                            self.getAttribute('projectName'),
+                            self.getAttribute('projectLang')
                         );
 
-                        this.$Editor.setProject(Project);
+                        self.$Editor.setProject(Project);
 
                         var height = 300;
 
-                        if ((contenSize.y - 100) > height) {
-                            height = contenSize.y - 100;
+                        if ((contentSize.y - 100) > height) {
+                            height = contentSize.y - 100;
                         }
 
 
@@ -615,24 +591,16 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
                             }
                         }).inject(TD);
 
-                        this.$Editor.addEvent('onLoaded', function () {
-                            if (typeof callback === 'function') {
-                                callback();
-                            }
-
-                            resolve();
-                        });
-
-                        this.$Editor.inject(EditorContainer);
-                        this.$Editor.setHeight(EditorContainer.getSize().y);
-                        this.$Editor.setWidth(EditorContainer.getSize().x);
-                        this.$Editor.setContent(
-                            this.getAttribute('data').attributes.content
+                        self.$Editor.addEvent('onLoaded', resolve);
+                        self.$Editor.inject(EditorContainer);
+                        self.$Editor.setHeight(EditorContainer.getSize().y);
+                        self.$Editor.setWidth(EditorContainer.getSize().x);
+                        self.$Editor.setContent(
+                            self.getAttribute('data').attributes.content
                         );
-
-                    }.bind(this));
-                }.bind(this));
-            }.bind(this));
+                    });
+                });
+            });
         },
 
         /**
@@ -645,8 +613,8 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
                 var TableExtra = this.$Elm.getElement('table.brick-edit-extra-header'),
                     TableBody  = TableExtra.getElement('tbody');
 
-                TableBody.getElement('[name="frontendTitle"]').value = this.getAttribute(
-                    'data').attributes.frontendTitle;
+                TableBody.getElement('[name="frontendTitle"]').value =
+                    this.getAttribute('data').attributes.frontendTitle;
 
                 if (!this.$availableSettings || !this.$availableSettings.length) {
                     TableExtra.setStyle('display', 'none');
@@ -661,12 +629,11 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
 
                 TableExtra.setStyle('display', null);
 
-                var Form = this.getContent().getElement('form');
-
                 var i, len, Row, text, Value, setting, extraFieldId;
 
                 var self = this,
-                    id   = this.getId();
+                    id   = this.getId(),
+                    Form = this.getContent().getElement('form');
 
                 // extra settings
                 for (i = 0, len = this.$availableSettings.length; i < len; i++) {
@@ -740,7 +707,6 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
                 // parse controls
                 QUI.parse(TableExtra).then(function () {
                     return ControlUtils.parse(TableExtra);
-
                 }).then(function () {
                     // set project to the controls
                     TableExtra.getElements('[data-quiid]').each(function (Elm) {
@@ -761,6 +727,115 @@ define('package/quiqqer/bricks/bin/BrickEdit', [
                 }).catch(reject);
 
             }.bind(this));
+        },
+
+        /**
+         * event: on category enter
+         *
+         * @return Promise
+         */
+        $onCategoryEnter: function (Panel, Category) {
+            if (this.$loaded === false) {
+                return Promise.resolve();
+            }
+
+            switch (Category.getAttribute('name')) {
+                case 'information':
+                case 'extra':
+                case 'settings':
+                case 'content':
+                    return Promise.resolve();
+            }
+
+            var self = this;
+
+            this.Loader.show();
+
+            return this.$hideCategory().then(function () {
+                return new Promise(function (resolve, reject) {
+                    QUIAjax.get('package_quiqqer_bricks_ajax_getPanelCategory', function (result) {
+                        self.$Container.set('html', '<form>' + result + '</form>');
+                        self.$load();
+                        resolve();
+                    }, {
+                        'package': 'quiqqer/bricks',
+                        brickId  : self.getAttribute('id'),
+                        category : Category.getAttribute('name'),
+                        onError  : reject
+                    });
+                });
+            }).then(function () {
+                return QUI.parse();
+            }).then(function () {
+                return self.$showCategory();
+            }).then(function () {
+                self.Loader.hide();
+
+            }).catch(function (err) {
+                console.error(err);
+                self.Loader.hide();
+            });
+        },
+
+        /**
+         * event: on category leave
+         */
+        $onCategoryLeave: function () {
+            this.$unload();
+
+            if (this.$Areas) {
+                this.$Areas.destroy();
+                this.$Areas = false;
+            }
+
+            if (this.$Editor) {
+                this.$Editor.destroy();
+                this.$Editor = false;
+            }
+        },
+
+        /**
+         * show the container
+         *
+         * @return Promise
+         */
+        $showCategory: function () {
+            var self = this;
+
+            return new Promise(function (resolve) {
+                moofx(self.$Container).animate({
+                    opacity: 1,
+                    top    : 0
+                }, {
+                    duration: 250,
+                    callback: resolve
+                });
+            });
+        },
+
+        /**
+         * hide the container
+         *
+         * @return Promise
+         */
+        $hideCategory: function () {
+            var self = this;
+
+            // unload
+            this.$unload();
+
+            return new Promise(function (resolve) {
+                moofx(self.$Container).animate({
+                    opacity: 0,
+                    top    : -20
+                }, {
+                    duration: 250,
+                    callback: function () {
+                        self.$Container.set('html', '');
+                        resolve();
+                    }
+                });
+            });
         }
     });
 });
diff --git a/src/QUI/Bricks/Brick.php b/src/QUI/Bricks/Brick.php
index 6f207eb..17ec43a 100644
--- a/src/QUI/Bricks/Brick.php
+++ b/src/QUI/Bricks/Brick.php
@@ -125,6 +125,13 @@ public function __construct($params = array())
             $this->settings[$entry['name']] = false;
         }
 
+        $availableAttributes = Utils::getAttributesForBrick($this);
+
+        foreach ($availableAttributes as $attribute) {
+            $this->settings[$attribute] = false;
+        }
+
+
         // control default settings
         if (is_object($Control)) {
             $controlSettings = $Control->getAttributes();
diff --git a/src/QUI/Bricks/Manager.php b/src/QUI/Bricks/Manager.php
index 495a3e0..be5fc54 100644
--- a/src/QUI/Bricks/Manager.php
+++ b/src/QUI/Bricks/Manager.php
@@ -755,7 +755,6 @@ public function saveBrick($brickId, array $brickData)
             $brickData['areas'] = $brickData['attributes']['areas'];
         }
 
-
         if (isset($brickData['areas'])) {
             $parts = explode(',', $brickData['areas']);
 
@@ -788,6 +787,13 @@ public function saveBrick($brickId, array $brickData)
             $Brick->setSettings($brickData['settings']);
         }
 
+        $brickAttributes = Utils::getAttributesForBrick($Brick);
+
+        foreach ($brickAttributes as $attribute) {
+            if (isset($brickData['attributes'][$attribute])) {
+                $Brick->setSetting($attribute, $brickData['attributes'][$attribute]);
+            }
+        }
 
         // custom fields
         $customfields = array();
@@ -811,6 +817,9 @@ public function saveBrick($brickId, array $brickData)
             }
         }
 
+        QUI\System\Log::writeRecursive($brickData);
+        QUI\System\Log::writeRecursive($Brick->getSettings());
+
         // update
         QUI::getDataBase()->update($this->getTable(), array(
             'title'         => $Brick->getAttribute('title'),
@@ -915,42 +924,7 @@ public function copyBrick($brickId, $params = array())
      */
     protected function getBricksXMLFiles()
     {
-        $cache = 'quiqqer/bricks/availableBrickFiles';
-
-        try {
-            return QUI\Cache\Manager::get($cache);
-        } catch (QUI\Exception $Exception) {
-        }
-
-        $PKM      = QUI::getPackageManager();
-        $Projects = QUI::getProjectManager();
-        $packages = $PKM->getInstalled();
-        $result   = array();
-
-        // package bricks
-        foreach ($packages as $package) {
-            $bricksXML = OPT_DIR.$package['name'].'/bricks.xml';
-
-            if (file_exists($bricksXML)) {
-                $result[] = $bricksXML;
-            }
-        }
-
-        // project bricks
-        $projects = $Projects->getProjects();
-
-        foreach ($projects as $project) {
-            $bricksXML = USR_DIR.$project.'/bricks.xml';
-
-            if (file_exists($bricksXML)) {
-                $result[] = $bricksXML;
-            }
-        }
-
-
-        QUI\Cache\Manager::set($cache, $result);
-
-        return $result;
+        return Utils::getBricksXMLFiles();
     }
 
     /**
diff --git a/src/QUI/Bricks/Panel.php b/src/QUI/Bricks/Panel.php
new file mode 100644
index 0000000..cc547c9
--- /dev/null
+++ b/src/QUI/Bricks/Panel.php
@@ -0,0 +1,146 @@
+<?php
+
+/**
+ * This file contains QUI\Bricks\Panel
+ */
+
+namespace QUI\Bricks;
+
+use QUI;
+
+/**
+ * Class Panel
+ * - Helper class for the brick panel in the administration
+ *
+ * @package QUI\Bricks
+ */
+class Panel extends QUI\Utils\Singleton
+{
+    /**
+     * @param integer $brickId
+     * @param string $category
+     *
+     * @return string
+     */
+    public function getCategoryFromBrick($brickId, $category)
+    {
+        $BrickManager = QUI\Bricks\Manager::init();
+        $Brick        = $BrickManager->getBrickById($brickId);
+        $type         = $Brick->getAttribute('type');
+
+        $cacheName = 'quiqqer/bricks/categories/category/'.$type.'/'.$category;
+
+        try {
+            return QUI\Cache\Manager::get($cacheName);
+        } catch (QUI\Exception $Exception) {
+        }
+
+
+        $files = Utils::getBricksXMLFiles();
+        $path  = $this->getPath($Brick);
+        $path  = $path.'/window';
+
+        $Settings = QUI\Utils\XML\Settings::getInstance();
+        $Settings->setXMLPath($path);
+
+        $result = '';
+
+        try {
+            $result = $Settings->getCategoriesHtml($files, $category);
+            QUI\Cache\Manager::set($cacheName, $result);
+        } catch (\Exception $Exception) {
+            QUI\System\Log::writeException($Exception);
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param string|integer $brickId
+     * @return array
+     */
+    public function getCategoriesFromBrick($brickId)
+    {
+        $BrickManager = QUI\Bricks\Manager::init();
+        $Brick        = $BrickManager->getBrickById($brickId);
+
+        $xmlFiles = $this->getXMLFilesForBricks($Brick);
+        $path     = $this->getPath($Brick);
+        $path     = $path.'/window';
+
+        $Settings = QUI\Utils\XML\Settings::getInstance();
+        $Settings->setXMLPath($path);
+
+        $categories = array();
+
+        foreach ($xmlFiles as $file) {
+            $panel      = $Settings->getPanel($file);
+            $categories = array_merge(
+                $categories,
+                $panel['categories']->toArray()
+            );
+        }
+
+        // locale
+        foreach ($categories as $key => $category) {
+            if (isset($category['title']) && is_array($category['title'])) {
+                $categories[$key]['text'] = QUI::getLocale()->get(
+                    $category['title'][0],
+                    $category['title'][1]
+                );
+
+                $categories[$key]['title'] = QUI::getLocale()->get(
+                    $category['title'][0],
+                    $category['title'][1]
+                );
+            }
+
+            if (empty($category['text']) && !empty($category['title'])) {
+                $categories[$key]['text'] = $category['title'];
+            }
+        }
+
+        return $categories;
+    }
+
+    /**
+     * @param Brick $Brick
+     * @return array
+     */
+    public function getXMLFilesForBricks(Brick $Brick)
+    {
+        $path = $this->getPath($Brick);
+
+        $xmlFiles = Utils::getBricksXMLFiles();
+        $result   = array();
+
+        foreach ($xmlFiles as $xmlFile) {
+            try {
+                $Dom    = QUI\Utils\Text\XML::getDomFromXml($xmlFile);
+                $Path   = new \DOMXPath($Dom);
+                $bricks = $Path->query($path);
+
+                if ($bricks->length) {
+                    $result[] = $xmlFile;
+                }
+            } catch (QUI\Exception $Exception) {
+                continue;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param Brick $Brick
+     * @return string
+     */
+    protected function getPath(Brick $Brick)
+    {
+        $type = $Brick->getAttribute('type');
+        $type = '\\'.trim($type, '\\');
+        $path = '//quiqqer/bricks/brick[@control="'.$type.'"]';
+
+        return $path;
+    }
+}
diff --git a/src/QUI/Bricks/Utils.php b/src/QUI/Bricks/Utils.php
index e8e44ea..fd25302 100644
--- a/src/QUI/Bricks/Utils.php
+++ b/src/QUI/Bricks/Utils.php
@@ -191,4 +191,112 @@ public static function hasInheritance(Project $Project, $areaName)
 
         return true;
     }
+
+    /**
+     * List of available bricks.xml files
+     *
+     * @return array
+     */
+    public static function getBricksXMLFiles()
+    {
+        $cache = 'quiqqer/bricks/availableBrickFiles';
+
+        try {
+            return QUI\Cache\Manager::get($cache);
+        } catch (QUI\Exception $Exception) {
+        }
+
+        $PKM      = QUI::getPackageManager();
+        $Projects = QUI::getProjectManager();
+        $packages = $PKM->getInstalled();
+        $result   = array();
+
+        // package bricks
+        foreach ($packages as $package) {
+            $bricksXML = OPT_DIR.$package['name'].'/bricks.xml';
+
+            if (file_exists($bricksXML)) {
+                $result[] = $bricksXML;
+            }
+        }
+
+        // project bricks
+        $projects = $Projects->getProjects();
+
+        foreach ($projects as $project) {
+            $bricksXML = USR_DIR.$project.'/bricks.xml';
+
+            if (file_exists($bricksXML)) {
+                $result[] = $bricksXML;
+            }
+        }
+
+
+        QUI\Cache\Manager::set($cache, $result);
+
+        return $result;
+    }
+
+    /**
+     * Return all available attributes for a brick
+     *
+     * @param Brick $Brick
+     * @return array
+     */
+    public static function getAttributesForBrick(Brick $Brick)
+    {
+        $attributes = array();
+        $files      = Panel::getInstance()->getXMLFilesForBricks($Brick);
+
+        // main path
+        $type = $Brick->getAttribute('type');
+        $type = '\\'.trim($type, '\\');
+
+        $path  = '//quiqqer/bricks/brick[@control="'.$type.'"]';
+        $cache = 'quiqqer/bricks/'.md5($type).'/attributes';
+
+        try {
+            return QUI\Cache\Manager::get($cache);
+        } catch (QUI\Exception $Exception) {
+        }
+
+
+        $settingsPath   = $path.'/settings/setting';
+        $categoriesPath = $path.'/window/categories/category/settings';
+
+        foreach ($files as $file) {
+            $Dom  = QUI\Utils\Text\XML::getDomFromXml($file);
+            $Path = new \DOMXPath($Dom);
+
+            // settings
+            $settings = $Path->query($settingsPath);
+
+            /* @var $Setting \DOMElement */
+            foreach ($settings as $Setting) {
+                $attributes[] = $Setting->getAttribute('name');
+            }
+
+            // categories
+            $categories = $Path->query($categoriesPath);
+
+            /* @var $Settings \DOMElement */
+            foreach ($categories as $Settings) {
+                $children = $Settings->childNodes;
+
+                /* @var $Child \DOMElement */
+                foreach ($children as $Child) {
+                    switch ($Child->nodeName) {
+                        case 'input':
+                        case 'textarea':
+                            $attributes[] = $Child->getAttribute('conf');
+                            break;
+                    }
+                }
+            }
+        }
+
+        QUI\Cache\Manager::set($cache, $attributes);
+
+        return $attributes;
+    }
 }
-- 
GitLab