├── .gitignore ├── LICENSE ├── README-ru.md ├── README.md ├── composer.json └── src ├── EavBehavior.php ├── EavModel.php ├── Module.php ├── admin ├── Module.php ├── assets │ ├── FbAsset.php │ └── formbuilder │ │ ├── css │ │ ├── formbuilder.css │ │ └── vendor.css │ │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── font-awesome.css │ │ ├── font-awesome.min.css │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 │ │ ├── js │ │ ├── app.js │ │ ├── backbone.js │ │ ├── formbuilder.js │ │ ├── jquery.ui.js │ │ ├── rivets.js │ │ └── underscorejs.js │ │ └── locales │ │ ├── es-ES.js │ │ └── ru-RU.js ├── controllers │ └── AjaxController.php └── widgets │ ├── Fields.php │ └── views │ └── fields.php ├── handlers ├── ArrayValueHandler.php ├── MultipleOptionsValueHandler.php ├── OptionValueHandler.php ├── RawValueHandler.php └── ValueHandler.php ├── messages ├── es │ └── eav.php └── ru │ └── eav.php ├── migrations ├── m150821_133232_init.php ├── m160501_014209_add_column_order_into_option.php ├── m160501_124615_create_table_eav_attribute_rules.php ├── m160501_230535_add_columns_into_attribute_rules.php ├── m160501_232516_add_new_field_types.php ├── m160501_234949_remove_column_required_from_attribute.php ├── m160711_132535_delete_foregin_key_from_values.php ├── m160711_162535_change_default_value.php ├── m160731_142546_alter_table_eav_attribute.php ├── m160807_162635_change_default_value_in_eav_attribute_value.php └── m160818_102815_add_entityId_index_in_eav_attribute_value.php ├── models ├── EavAttribute.php ├── EavAttributeOption.php ├── EavAttributeOptionSearch.php ├── EavAttributeRule.php ├── EavAttributeSearch.php ├── EavAttributeType.php ├── EavAttributeTypeSearch.php ├── EavAttributeValue.php ├── EavAttributeValueSearch.php ├── EavEntity.php └── EavEntitySearch.php └── widgets ├── ActiveField.php ├── AttributeHandler.php ├── CheckBoxList.php ├── DropDownList.php ├── EncodedTextInput.php ├── NumericInput.php ├── RadioList.php ├── TextInput.php └── Textarea.php /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject 2 | .idea/ 3 | !.gitkeep 4 | 5 | # IDE 6 | .idea 7 | nbproject 8 | .buildpath 9 | .project 10 | .settings -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Alexey Samoylov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README-ru.md: -------------------------------------------------------------------------------- 1 | EAV Dynamic Attributes for Yii2 2 | ======== 3 | Архитектура баз данных EAV(Enity-Attribute-Value, Сущность-Атрибут-Значение) 4 | 5 | [![Latest Stable Version](https://poser.pugx.org/mirocow/yii2-eav/v/stable)](https://packagist.org/packages/mirocow/yii2-eav) [![Latest Unstable Version](https://poser.pugx.org/mirocow/yii2-eav/v/unstable)](https://packagist.org/packages/mirocow/yii2-eav) [![Total Downloads](https://poser.pugx.org/mirocow/yii2-eav/downloads)](https://packagist.org/packages/mirocow/yii2-eav) [![License](https://poser.pugx.org/mirocow/yii2-eav/license)](https://packagist.org/packages/mirocow/yii2-eav) 6 | [![Join the chat at https://gitter.im/Mirocow/yii2-eav](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Mirocow/yii2-eav?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 7 | 8 | ### Установка 9 | При помощи композера устанавливаем расширение 10 | ``` 11 | php composer.phar require --prefer-dist "mirocow/yii2-eav" "*" 12 | ``` 13 | далее выполняем миграции 14 | ``` 15 | php ./yii migrate/up -p=@mirocow/eav/migrations 16 | ``` 17 | добавляем настройки сообщений расширения в основной конфиг 18 | 19 | ``` php 20 | 'i18n' => [ 21 | 'translations' => [ 22 | 'app*' => [ 23 | 'class' => 'yii\i18n\PhpMessageSource', 24 | //'basePath' => '@app/messages', 25 | //'sourceLanguage' => 'en-US', 26 | 'fileMap' => [ 27 | 'app' => 'app.php', 28 | 'app/error' => 'error.php', 29 | ], 30 | ], 31 | 'eav' => [ 32 | 'class' => 'yii\i18n\PhpMessageSource', 33 | 'basePath' => '@mirocow/eav/messages', 34 | ], 35 | ], 36 | ] 37 | ``` 38 | добавляем в конфиг модуль 39 | ``` php 40 | $modules = [ 41 | ..., 42 | 'eav' => [ 43 | 'class' => 'mirocow\eav\Module', 44 | ], 45 | ]; 46 | ``` 47 | 48 | ### Использование 49 | Добавляем в модель поведение, которое расширяет ее возможности методами данного расширения 50 | 51 | ``` php 52 | ......... 53 | /** 54 | * create_time, update_time to now() 55 | * crate_user_id, update_user_id to current login user id 56 | */ 57 | public function behaviors() 58 | { 59 | return [ 60 | 'eav' => [ 61 | 'class' => \mirocow\eav\EavBehavior::className(), 62 | // это модель для таблицы object_attribute_value 63 | 'valueClass' => \mirocow\eav\models\EavAttributeValue::className(), 64 | ] 65 | ]; 66 | } 67 | 68 | ......... 69 | ``` 70 | в эту же модель добавляем 71 | ``` php 72 | /** 73 | * @return \yii\db\ActiveQuery 74 | */ 75 | public function getEavAttributes() 76 | { 77 | return \mirocow\eav\models\EavAttribute::find() 78 | ->joinWith('entity') 79 | ->where([ 80 | //'categoryId' => $this->categories[0]->id, 81 | 'entityModel' => $this::className() 82 | ]); 83 | } 84 | ``` 85 | C моделью закончили. 86 | 87 | ## Создание и редактирование атрибутов 88 | ### Создание атрибутов без админки 89 | ``` php 90 | $attr = new mirocow\eav\models\EavAttribute(); 91 | $attr->attributes = [ 92 | 'entityId' => 1, // Category ID 93 | 'typeId' => 1, // ID type from eav_attribute_type 94 | 'name' => 'packing', // service name field 95 | 'label' => 'Packing', // label text for form 96 | 'defaultValue' => '10 kg', // default value 97 | 'entityModel' => Product::className(), // work model 98 | 'required' => false // add rule "required field" 99 | ]; 100 | $attr->save(); 101 | 102 | $attr->attributes = [ 103 | 'entityId' => 1, // Category ID 104 | 'typeId' => 1, // ID type from eav_attribute_type 105 | 'name' => 'color', // service name field 106 | 'label' => 'Color', // label text for form 107 | 'defaultValue' => 'white', // default value 108 | 'entityModel' => Product::className(), // work model 109 | 'required' => false // add rule "required field" 110 | ]; 111 | $attr->save(); 112 | ``` 113 | ### Создание атрибутов из админки 114 | Для использования админки достаточно будет добавить в представление следующий код 115 | ``` php 116 | $model, 118 | 'categoryId' => $model->id, 119 | 'entityName' => 'Продукт', 120 | 'entityModel' => 'app\models\Product', // ваша модель для которой подключено расширение 121 | ])?> 122 | ``` 123 | При рендере представления должна отобразиться админка в которой можно добавлять атрибуты и редактировать их. 124 | Добавляем несколько атрибутов в админке, они уже будут присутствовать в вашей модели и нужно прописать правила валидации для них 125 | ``` php 126 | public function rules() 127 | { 128 | return [ 129 | [['name'], 'string', 'max' => 255], // Product field 130 | 131 | [['c1'], 'required'], // Attribute field 132 | [['c1'], 'string', 'max' => 255], // Attribute field 133 | 134 | [['c2'], 'required'], // Attribute field 135 | [['c2'], 'string', 'max' => 255], // Attribute field 136 | ]; 137 | } 138 | ``` 139 | тут c1 и c2 - поля добавленные через EAV, данные поля можно изменять так 140 | ``` php 141 | $model = Product::find()->where(['id' => 1])->one(); 142 | $model->c1 = "blue"; 143 | $model->c2 = "red"; 144 | $model->save(); 145 | ``` 146 | для редактирования этих полей в расширении присутствует специальный виджет 147 | ### Вывод всех EAV атрибутов 148 | ``` php 149 | getEavAttributes()->all() as $attr){ 151 | echo $form->field($model, $attr->name, ['class' => '\mirocow\eav\widgets\ActiveField'])->eavInput(); 152 | } 153 | ?> 154 | ``` 155 | 156 | ### Вывод отдельного EAV атрибута 157 | ``` php 158 | field($model,'с1', ['class' => '\mirocow\eav\widgets\ActiveField'])->eavInput(); ?> 160 | ?> 161 | ``` 162 | Для вывода этих атрибутов на карточке (товара/модуля/вашей модели) можно использовать следующий код 163 | ``` php 164 | 165 | getEavAttributes()->all() as $attr) { 167 | ?> 168 | 169 | 170 | 184 | 185 | 188 |
type; ?> 171 |
    172 | renderEavAttr($attr, $model); 174 | if ($attrValue[0]['value']) { 175 | foreach ($attrValue as $attrValueItem) { 176 | echo '
  • '; 177 | echo $attrValueItem['value']; 178 | echo '
  • '; 179 | } 180 | } else echo '---'; 181 | ?> 182 |
183 |
189 | ``` 190 | Для использования этого кода, добавьте в вашу модель следующий метод, он возвращает массив с EAV атрибутами 191 | ``` php 192 | /** 193 | * @param $attr 194 | * @param null $model 195 | * @return array 196 | * 197 | * Создает массив с EAV атрибутами 198 | */ 199 | function renderEavAttr($attr, $model = NULL) 200 | { 201 | $optionValues = $model[$attr->name]->value; // Список выбранных значений 202 | $allOptionValues = $attr->getEavOptions()->asArray()->all(); // Список всех возможных значений 203 | 204 | // Если массив - все возможные значения 205 | unset($out); 206 | if (is_array($allOptionValues)) { 207 | $out = []; 208 | foreach ($allOptionValues as $allOtionValuesItem) { 209 | // Если список доступных значений - массив 210 | if (is_array($optionValues)) { 211 | foreach ($optionValues as $optionValuesItem) { 212 | if ($optionValuesItem == $allOtionValuesItem["id"]) { 213 | $out[] = $allOtionValuesItem; 214 | } 215 | } 216 | } else { 217 | if ($optionValues == $allOtionValuesItem["id"]) { 218 | $out[] = $allOtionValuesItem; 219 | } 220 | } 221 | } 222 | 223 | 224 | } 225 | 226 | if ($out) { 227 | return $out; 228 | } else return [0 => [ 229 | 'id' => 0, 230 | 'attributeId' => 0, 231 | 'value' => $model[$attr->name]['value'], 232 | 'defaultOptionId' => 0, 233 | 'order' => 0, 234 | 235 | ]]; 236 | } 237 | ``` 238 | 239 | Тут описаны основные шаги для начала работы с модулем, если что-то не получается велком в icq 124011, постараюсь помочь. 240 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | EAV Dynamic Attributes for Yii2 2 | ======== 3 | Архитектура баз данных EAV(Enity-Attribute-Value, Сущность-Атрибут-Значение) 4 | 5 | [![Latest Stable Version](https://poser.pugx.org/mirocow/yii2-eav/v/stable)](https://packagist.org/packages/mirocow/yii2-eav) [![Latest Unstable Version](https://poser.pugx.org/mirocow/yii2-eav/v/unstable)](https://packagist.org/packages/mirocow/yii2-eav) [![Total Downloads](https://poser.pugx.org/mirocow/yii2-eav/downloads)](https://packagist.org/packages/mirocow/yii2-eav) [![License](https://poser.pugx.org/mirocow/yii2-eav/license)](https://packagist.org/packages/mirocow/yii2-eav) 6 | [![Join the chat at https://gitter.im/Mirocow/yii2-eav](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Mirocow/yii2-eav?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 7 | 8 | # Screenshots 9 | 10 | ## Edit attributes 11 | 12 | ### List of attributes 13 | 14 | ![](http://images.mirocow.com/2016-05-02-23-29-39-3hcha.png) 15 | 16 | ### Edit attribute 17 | 18 | ![](http://images.mirocow.com/2016-05-02-23-39-41-5ih6u.png) 19 | 20 | ## Edit form 21 | 22 | ![](http://images.mirocow.com/2016-05-02-23-32-34-m98o1.png) 23 | 24 | # Install 25 | 26 | ## Add github repository 27 | 28 | ```json 29 | "repositories": [ 30 | { 31 | "type": "git", 32 | "url": "https://github.com/mirocow/yii2-eav.git" 33 | } 34 | ] 35 | ``` 36 | and then 37 | 38 | ``` 39 | php composer.phar require --prefer-dist "mirocow/yii2-eav" "*" 40 | ``` 41 | 42 | ## Configure 43 | 44 | ``` sh 45 | php ./yii migrate/up -p=@mirocow/eav/migrations 46 | ``` 47 | 48 | or 49 | 50 | ``` sh 51 | php ./yii migrate/up -p=@vendor/mirocow/yii2-eav/src/migrations 52 | ``` 53 | 54 | and then add messages settings 55 | 56 | ``` php 57 | 'i18n' => [ 58 | 'translations' => [ 59 | 'app*' => [ 60 | 'class' => 'yii\i18n\PhpMessageSource', 61 | //'basePath' => '@app/messages', 62 | //'sourceLanguage' => 'en-US', 63 | 'fileMap' => [ 64 | 'app' => 'app.php', 65 | 'app/error' => 'error.php', 66 | ], 67 | ], 68 | 'eav' => [ 69 | 'class' => 'yii\i18n\PhpMessageSource', 70 | 'basePath' => '@mirocow/eav/messages', 71 | ], 72 | ], 73 | ] 74 | ``` 75 | 76 | ## Use 77 | 78 | ### Model 79 | 80 | #### Simple 81 | 82 | ``` php 83 | class Product extends \yii\db\ActiveRecord 84 | { 85 | public function rules() 86 | { 87 | return [ 88 | [['name'], 'string', 'max' => 255], // Product field 89 | [['c1'], 'required'], // Attribute field 90 | [['c1'], 'string', 'max' => 255], // Attribute field 91 | ]; 92 | } 93 | 94 | public function behaviors() 95 | { 96 | return [ 97 | 'eav' => [ 98 | 'class' => \mirocow\eav\EavBehavior::className(), 99 | // это модель для таблицы object_attribute_value 100 | 'valueClass' => \mirocow\eav\models\EavAttributeValue::className(), 101 | ] 102 | ]; 103 | } 104 | 105 | /** 106 | * @return \yii\db\ActiveQuery 107 | */ 108 | public function getEavAttributes() 109 | { 110 | return \mirocow\eav\models\EavAttribute::find() 111 | ->joinWith('entity') 112 | ->where([ 113 | 'categoryId' => $this->categories[0]->id, 114 | 'entityModel' => $this::className() 115 | ]); 116 | } 117 | } 118 | ``` 119 | 120 | #### Advanced 121 | 122 | ``` php 123 | class Product extends \yii\db\ActiveRecord 124 | { 125 | 126 | public function rules() 127 | { 128 | return [ 129 | [['name'], 'string', 'max' => 255], // Product field 130 | [['c1'], 'required'], // Attribute field 131 | [['c1'], 'string', 'max' => 255], // Attribute field 132 | ]; 133 | } 134 | 135 | public function behaviors() 136 | { 137 | return [ 138 | 'eav' => [ 139 | 'class' => \mirocow\eav\EavBehavior::className(), 140 | // это модель для таблицы object_attribute_value 141 | 'valueClass' => \mirocow\eav\models\EavAttributeValue::className(), 142 | ] 143 | ]; 144 | } 145 | 146 | /** 147 | * @return \yii\db\ActiveQuery 148 | */ 149 | public function getEavAttributes($attributes = []) 150 | { 151 | return \mirocow\eav\models\EavAttribute::find() 152 | ->joinWith('entity') 153 | ->where([ 154 | //'categoryId' => $this->categories[0]->id, 155 | 'entityModel' => $this::className() 156 | ]) 157 | ->orderBy(['order' => SORT_ASC]); 158 | } 159 | } 160 | ``` 161 | 162 | ### View 163 | 164 | Insert this code for create widget or load all EAV inputs fields for model 165 | 166 | ### Form edit 167 | 168 | fo load selected field 169 | 170 | ``` php 171 | field($model,'test5', ['class' => '\mirocow\eav\widgets\ActiveField'])->eavInput(); ?> 172 | ``` 173 | or for load all fields 174 | 175 | #### Simple 176 | 177 | ``` php 178 | getEavAttributes()->all() as $attr) { 180 | echo $form->field($model, $attr->name, ['class' => '\mirocow\eav\widgets\ActiveField'])->eavInput(); 181 | } 182 | ?> 183 | ``` 184 | 185 | or add sorted 186 | 187 | 188 | ``` php 189 | getEavAttributes()->orderBy(['order' => SORT_ASC])->all() as $attr) { 191 | echo $form->field($model, $attr->name, ['class' => '\mirocow\eav\widgets\ActiveField'])->eavInput(); 192 | } 193 | ?> 194 | ``` 195 | 196 | ### Advanced 197 | 198 | ``` php 199 | getEavAttributes(['entityId' => 8, 'typeId' => 3])->all() as $attr) { 201 | echo $form->field($model, $attr->name, ['class' => '\mirocow\eav\widgets\ActiveField'])->eavInput(); 202 | } 203 | ?> 204 | ``` 205 | 206 | ### Partial template 207 | 208 | ``` php 209 |

210 | Encode 211 | 212 | getEavAttributes()->all() as $attr) { 214 | print_r($model[$attr->name]['value']); 215 | } 216 | ?> 217 |

218 | 219 |

220 | String 221 | 222 | getEavAttributes()->all() as $attr){ 224 | echo $model[$attr->name]; 225 | } 226 | ?> 227 | ``` 228 | 229 | ### Add attribute 230 | 231 | ```php 232 | $attr = new mirocow\eav\models\EavAttribute(); 233 | $attr->attributes = [ 234 | 'entityId' => 1, // Category ID 235 | 'typeId' => 1, // ID type from eav_attribute_type 236 | 'name' => 'packing', // service name field 237 | 'label' => 'Packing', // label text for form 238 | 'defaultValue' => '10 kg', // default value 239 | 'entityModel' => Product::className(), // work model 240 | 'required' => false // add rule "required field" 241 | ]; 242 | $attr->save(); 243 | 244 | $attr->attributes = [ 245 | 'entityId' => 1, // Category ID 246 | 'typeId' => 1, // ID type from eav_attribute_type 247 | 'name' => 'color', // service name field 248 | 'label' => 'Color', // label text for form 249 | 'defaultValue' => 'white', // default value 250 | 'entityModel' => Product::className(), // work model 251 | 'required' => false // add rule "required field" 252 | ]; 253 | $attr->save(); 254 | ``` 255 | 256 | ### Add/Update values 257 | 258 | ```php 259 | $model = Product::find()->where(['id' => 1])->one(); 260 | $model->color = "blue"; 261 | $model->packing = "12 kg"; 262 | $model->save(); 263 | ``` 264 | 265 | ## Administrate GUI 266 | 267 | ## Config module EAV for managment of fields 268 | In main config file: 269 | ```php 270 | $modules = [ 271 | ..., 272 | 'eav' => [ 273 | 'class' => 'mirocow\eav\Module', 274 | ], 275 | ]; 276 | ``` 277 | 278 | ## Form 279 | 280 | 281 | ## Add / Edit attribute 282 | 283 | 284 | ``` php 285 | $model, 287 | 'categoryId' => $model->id, 288 | 'entityName' => 'Продукт', 289 | 'entityModel' => 'app\models\Product', 290 | ])?> 291 | ``` 292 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mirocow/yii2-eav", 3 | "description": "EAV for Yii2", 4 | "license": "MIT", 5 | "keywords": [ 6 | "yii2", 7 | "eav", 8 | "attribute", 9 | "cck", 10 | "fields" 11 | ], 12 | "type": "yii2-extension", 13 | "authors": [ 14 | { 15 | "name": "Alien-art", 16 | "email": "alien@alien-art.ru", 17 | "role": "Developer" 18 | }, 19 | { 20 | "name": "Alexey Samoylov", 21 | "email": "alexey.samoylov@gmail.com", 22 | "homepage": "http://yiiblog.ru", 23 | "role": "Developer" 24 | }, 25 | { 26 | "name": "Mirocow", 27 | "email": "mr.mirocow@gmail.com", 28 | "homepage": "http://mirocow.com", 29 | "role": "Developer" 30 | } 31 | ], 32 | "minimum-stability": "dev", 33 | "require": { 34 | "php": ">=5.5.0", 35 | "yiisoft/yii2": ">=2.0.5" 36 | }, 37 | "autoload": { 38 | "psr-4": { 39 | "mirocow\\eav\\": "./src" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/EavBehavior.php: -------------------------------------------------------------------------------- 1 | 4 | */ 5 | 6 | namespace mirocow\eav; 7 | 8 | use mirocow\eav\models\EavAttribute; 9 | use Yii; 10 | use yii\base\Behavior; 11 | use yii\base\Exception; 12 | use yii\db\ActiveRecord; 13 | 14 | /** 15 | * Class EavBehavior 16 | * 17 | * @package mirocow\eav 18 | * @mixin ActiveRecord 19 | * @property EavModel $eav; 20 | * @property ActiveRecord $owner 21 | */ 22 | class EavBehavior extends Behavior 23 | { 24 | public function events() 25 | { 26 | return [ 27 | ActiveRecord::EVENT_BEFORE_INSERT => 'beforeSave', 28 | ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeSave', 29 | ActiveRecord::EVENT_AFTER_INSERT => 'afterSave', 30 | ActiveRecord::EVENT_AFTER_UPDATE => 'afterSave', 31 | ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate', 32 | ]; 33 | } 34 | 35 | /** @var array */ 36 | public $valueClass; 37 | 38 | protected $EavModel; 39 | 40 | protected $models = []; 41 | 42 | public function init() 43 | { 44 | assert(isset($this->valueClass)); 45 | } 46 | 47 | /** 48 | * @return EavModel 49 | */ 50 | public function __get($attribute) 51 | { 52 | return $this->createModel($attribute); 53 | } 54 | 55 | public function __set($attribute, $value) 56 | { 57 | $model = $this->createModel($attribute); 58 | $model->load(['EavModel' => [$attribute => $value]], 'EavModel'); 59 | } 60 | 61 | /** 62 | * @param $attribute 63 | */ 64 | protected function createModel($attribute) 65 | { 66 | if (empty($this->models[$attribute])) { 67 | $this->EavModel = EavModel::create( 68 | [ 69 | 'entityModel' => $this->owner, 70 | 'valueClass' => $this->valueClass, 71 | 'attribute' => $attribute, 72 | ] 73 | ); 74 | 75 | $this->models[$attribute] = $this->EavModel; 76 | } 77 | 78 | return $this->models[$attribute]; 79 | } 80 | 81 | public function canSetProperty($name, $checkVars = true) 82 | { 83 | return $this->canGetProperty($name); 84 | } 85 | 86 | public function canGetProperty($name, $checkVars = true) 87 | { 88 | return EavAttribute::find()->where(['name' => $name])->exists(); 89 | } 90 | 91 | public function beforeValidate() 92 | { 93 | static $running; 94 | 95 | if (empty($running)) { 96 | $running = true; 97 | 98 | $attributeNames = $this->owner->activeAttributes(); 99 | 100 | foreach ($attributeNames as $attributeName) { 101 | if (preg_match('~c\d+~', $attributeName)) { 102 | if (!EavAttribute::find()->where(['name' => $attributeName])->exists()) { 103 | throw new Exception(\Yii::t('eav', 'Attribute {name} not found', ['name' => $attributeName])); 104 | } 105 | } 106 | } 107 | 108 | return $this->owner->validate(); 109 | } 110 | 111 | $running = false; 112 | } 113 | 114 | public function beforeSave() 115 | { 116 | } 117 | 118 | public function getLabel($attribute) 119 | { 120 | return EavAttribute::find()->select(['label'])->where(['name' => $attribute])->scalar(); 121 | } 122 | 123 | public function afterSave() 124 | { 125 | if (\Yii::$app instanceof \yii\web\Application) { 126 | if (!$this->EavModel) { 127 | $this->createModel('eav'); 128 | } 129 | 130 | $post = []; 131 | 132 | if (Yii::$app->request->isPost) { 133 | $modelName = EavModel::getModelShortName($this->owner); 134 | $post = Yii::$app->request->post($modelName); 135 | } 136 | 137 | foreach ($this->models as $model) { 138 | $model->load($post, 'EavModel'); 139 | $model->save(false); 140 | } 141 | } 142 | } 143 | 144 | } -------------------------------------------------------------------------------- /src/EavModel.php: -------------------------------------------------------------------------------- 1 | 4 | */ 5 | 6 | namespace mirocow\eav; 7 | 8 | use mirocow\eav\widgets\AttributeHandler; 9 | use Yii; 10 | use yii\base\DynamicModel as BaseEavModel; 11 | use yii\db\ActiveRecord; 12 | use yii\widgets\ActiveForm; 13 | use mirocow\eav\models\EavAttribute; 14 | 15 | /** 16 | * Class EavModel 17 | * 18 | * @package mirocow\eav 19 | */ 20 | class EavModel extends BaseEavModel 21 | { 22 | /** @var string Class to use for storing data */ 23 | public $valueClass; 24 | 25 | /** @var ActiveRecord */ 26 | public $entityModel; 27 | 28 | /** @var AttributeHandler[] */ 29 | public $handlers; 30 | 31 | /** @var string */ 32 | public $attribute = ''; 33 | 34 | /** @var ActiveForm */ 35 | public $activeForm; 36 | 37 | /** @var string[] */ 38 | private $attributeLabels = []; 39 | 40 | /** 41 | * Constructor for creating form model from entity object 42 | * 43 | * @param array $params 44 | * @return static 45 | */ 46 | public static function create($params) 47 | { 48 | $params['class'] = static::className(); 49 | 50 | /** @var static $model */ 51 | $model = Yii::createObject($params); 52 | 53 | $params = []; 54 | 55 | /** 56 | * Event EavBehavior::afterSave 57 | * Rise after form submit 58 | */ 59 | if ($model->attribute <> 'eav') { 60 | $params = [EavAttribute::tableName() . '.name' => $model->attribute]; 61 | } 62 | 63 | $attributes = $model 64 | ->entityModel 65 | // Load data from owner model 66 | ->getEavAttributes() 67 | ->joinWith('entity') 68 | ->andWhere($params) 69 | ->all(); 70 | 71 | foreach ($attributes as $attribute) { 72 | $handler = AttributeHandler::load($model, $attribute); 73 | $attribute_name = $handler->getAttributeName(); 74 | 75 | // 76 | // Add rules 77 | // 78 | 79 | if ($attribute->required) { 80 | $model->addRule($attribute_name, 'required'); 81 | } else { 82 | $model->addRule($attribute_name, 'safe'); 83 | } 84 | 85 | $handler->valueHandler->addRules(); 86 | 87 | // 88 | // Load attribute value 89 | // 90 | 91 | $value = $handler->valueHandler->load(); 92 | if (!$value) { 93 | // Set default attribute 94 | $value = $handler->valueHandler->defaultValue(); 95 | } 96 | 97 | $model->defineAttribute($attribute_name, $value); 98 | 99 | // 100 | // Add widget handler 101 | // 102 | 103 | $model->handlers[$attribute_name] = $handler; 104 | } 105 | 106 | return $model; 107 | } 108 | 109 | /** 110 | * @inheritdoc 111 | */ 112 | public function getAttributeLabels() 113 | { 114 | return $this->attributeLabels; 115 | } 116 | 117 | public function setLabel($name, $label) 118 | { 119 | $this->attributeLabels[$name] = $label; 120 | } 121 | 122 | public function save($runValidation = true, $attributes = null) 123 | { 124 | if (!$this->handlers) { 125 | Yii::info(Yii::t('eav', 'Dynamic model data were no attributes.'), __METHOD__); 126 | 127 | return false; 128 | } 129 | 130 | if ($runValidation && !$this->validate($attributes)) { 131 | Yii::info(Yii::t('eav', 'Dynamic model data were not save due to validation error.'), __METHOD__); 132 | 133 | return false; 134 | } 135 | 136 | $db = $this->entityModel->getDb(); 137 | 138 | $transaction = $db->beginTransaction(); 139 | try { 140 | foreach ($this->handlers as $handler) { 141 | $handler->valueHandler->save(); 142 | } 143 | $transaction->commit(); 144 | } catch (\Exception $e) { 145 | $transaction->rollBack(); 146 | throw $e; 147 | } 148 | } 149 | 150 | public function __set($name, $value) 151 | { 152 | $this->defineAttribute($name, $value); 153 | } 154 | 155 | public function getValue() 156 | { 157 | if (isset($this->attributes[$this->attribute])) { 158 | return $this->attributes[$this->attribute]; 159 | } else { 160 | return ''; 161 | } 162 | } 163 | 164 | public function __toString() 165 | { 166 | if (isset($this->attributes[$this->attribute])) { 167 | if (is_string($this->attributes[$this->attribute])) { 168 | return (string)$this->attributes[$this->attribute]; 169 | } else { 170 | return (string)json_encode($this->attributes[$this->attribute]); 171 | } 172 | } else { 173 | return ''; 174 | } 175 | } 176 | 177 | public function formName() 178 | { 179 | return self::getModelShortName($this->entityModel) . '[EavModel]'; 180 | } 181 | 182 | public static function getModelShortName($model) 183 | { 184 | $reflector = new \ReflectionClass($model::className()); 185 | 186 | return $reflector->getShortName(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/Module.php: -------------------------------------------------------------------------------- 1 | setModule('admin', 'mirocow\eav\admin\Module'); 18 | $this->registerTranslations(); 19 | } 20 | 21 | public function createController($route) 22 | { 23 | if (strpos($route, 'admin/') !== false) { 24 | return $this->getModule('admin')->createController(str_replace('admin/', '', $route)); 25 | } else { 26 | return parent::createController($route); 27 | } 28 | } 29 | 30 | public function registerTranslations() 31 | { 32 | Yii::$app->i18n->translations['eav'] = 33 | [ 34 | 'class' => 'yii\i18n\PhpMessageSource', 35 | 'basePath' => "@mirocow/eav/messages", 36 | 'forceTranslation' => true, 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/admin/Module.php: -------------------------------------------------------------------------------- 1 | 15 | * @since 2.0 16 | */ 17 | class FbAsset extends AssetBundle 18 | { 19 | public $baseUrl = '@web'; 20 | public $sourcePath = '@mirocow/eav/admin/assets/formbuilder'; 21 | public $css = [ 22 | 'css/vendor.css', 23 | 'css/formbuilder.css', 24 | ]; 25 | 26 | public $js = [ 27 | 'js/underscorejs.js', 28 | 'js/backbone.js', 29 | 'js/jquery.ui.js', 30 | 'js/app.js', 31 | 'js/rivets.js', 32 | 'js/formbuilder.js', 33 | ]; 34 | 35 | public $depends = [ 36 | 'yii\web\JqueryAsset', 37 | ]; 38 | 39 | } -------------------------------------------------------------------------------- /src/admin/assets/formbuilder/css/formbuilder.css: -------------------------------------------------------------------------------- 1 | .fb-button { 2 | display: inline-block; 3 | margin: 0; 4 | border: 0 none; 5 | background: #16a085; 6 | color: #fff; 7 | text-align: center; 8 | text-decoration: none; 9 | font-size: 12px; 10 | line-height: 1.5; 11 | cursor: pointer; 12 | border-radius: .125rem; 13 | border-bottom: 2px solid #16a085; 14 | border: none; 15 | padding: 3px 6px; 16 | } 17 | 18 | .fb-button[disabled] { 19 | background: #ddd !important; 20 | border: thin solid #ccc; 21 | color: #777 !important; 22 | text-shadow: none !important; 23 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=65)"; 24 | opacity: .65; 25 | cursor: default 26 | } 27 | 28 | .fb-clear { 29 | clear: both 30 | } 31 | 32 | .fb-main { 33 | max-width: 1000px; 34 | margin: 0 auto; 35 | padding: 0 20px 0 0; 36 | position: relative; 37 | font-family: 'Source Sans Pro', 'Open Sans', Tahoma 38 | } 39 | 40 | .fb-save-wrapper { 41 | position: absolute; 42 | right: 20px; 43 | top: 10px 44 | } 45 | 46 | .fb-left { 47 | float: left; 48 | width: 320px; 49 | padding-top: 30px 50 | } 51 | 52 | .fb-right { 53 | padding-top: 70px; 54 | margin-left: 320px; 55 | border-left: 1px solid #ddd; 56 | padding-left: 20px; 57 | min-height: 100%; 58 | overflow: hidden 59 | } 60 | 61 | .fb-no-response-fields { 62 | color: #999 63 | } 64 | 65 | .fb-tabs { 66 | list-style: none; 67 | margin: 0 0 20px 0; 68 | padding: 0 0 0 20px; 69 | border-bottom: 1px solid #ccc 70 | } 71 | 72 | .fb-tabs li { 73 | display: inline-block 74 | } 75 | 76 | .fb-tabs li a { 77 | display: block; 78 | padding: 10px; 79 | border-radius: 5px 5px 0 0; 80 | font-size: 13px; 81 | cursor: pointer; 82 | border-left: 1px solid transparent; 83 | border-right: 1px solid transparent 84 | } 85 | 86 | .fb-tabs li.active a { 87 | border: 1px solid #ccc; 88 | margin-bottom: -1px; 89 | border-bottom-color: #fff 90 | } 91 | 92 | .fb-tab-content .fb-tab-pane { 93 | padding: 0 20px; 94 | display: none 95 | } 96 | 97 | .fb-tab-content .fb-tab-pane.active { 98 | display: block 99 | } 100 | 101 | .fb-add-field-types .section { 102 | padding-bottom: 5px; 103 | margin-bottom: 20px 104 | } 105 | 106 | .fb-add-field-types { 107 | font-size: 0 108 | } 109 | 110 | .fb-add-field-types a:nth-child(odd) { 111 | margin-right: 3% 112 | } 113 | 114 | .fb-response-fields { 115 | padding-bottom: 150px 116 | } 117 | 118 | .fb-response-fields a.sortable-placeholder { 119 | display: block; 120 | border: 1px dashed #ddd; 121 | min-height: 80px; 122 | height: 80px; 123 | width: 100% 124 | } 125 | 126 | .fb-field-wrapper { 127 | cursor: pointer; 128 | position: relative; 129 | margin-bottom: 20px 130 | } 131 | 132 | .fb-field-wrapper input { 133 | border-radius: 3px; 134 | border: thin solid #ddd 135 | } 136 | 137 | .fb-field-wrapper:hover .actions-wrapper, .fb-field-wrapper.editing .actions-wrapper { 138 | display: block 139 | } 140 | 141 | .fb-field-wrapper:hover .subtemplate-wrapper { 142 | border-color: #ddd; 143 | border-radius: 3px 144 | } 145 | 146 | .fb-field-wrapper.editing { 147 | background-color: #ecf0f1; 148 | border-radius: 3px 149 | } 150 | 151 | .fb-field-wrapper.editing .subtemplate-wrapper { 152 | border-color: #d9e1e3; 153 | border-style: solid; 154 | margin: 0; 155 | border-radius: 3px 156 | } 157 | 158 | .fb-field-wrapper .actions-wrapper { 159 | display: none; 160 | position: absolute; 161 | bottom: -7px; 162 | right: 5px; 163 | z-index: 3 164 | } 165 | 166 | .edit-response-field input, .edit-response-field textarea, .edit-response-field select { 167 | border: thin solid #ddd; 168 | border-radius: .25em; 169 | padding: .5em; 170 | display: inline-block; 171 | height: auto; 172 | vertical-align: middle 173 | } 174 | 175 | .edit-response-field input:focus, .edit-response-field textarea:focus, .edit-response-field select:focus { 176 | outline: none; 177 | border: thin solid #1abc9c 178 | } 179 | 180 | .edit-response-field select { 181 | font-size: 14px 182 | } 183 | 184 | .fb-field-wrapper .subtemplate-wrapper { 185 | border: 1px dashed transparent; 186 | margin-bottom: 10px; 187 | padding: 10px; 188 | position: relative 189 | } 190 | 191 | .fb-field-wrapper .subtemplate-wrapper .cover { 192 | position: absolute; 193 | top: 0; 194 | left: 0; 195 | height: 100%; 196 | width: 100%; 197 | z-index: 2 198 | } 199 | 200 | .fb-field-wrapper .subtemplate-wrapper > label { 201 | display: block; 202 | border-bottom: thin solid #eee; 203 | padding-bottom: 3px; 204 | margin-bottom: 7px 205 | } 206 | 207 | .fb-field-wrapper .subtemplate-wrapper abbr { 208 | color: #f00 209 | } 210 | 211 | .fb-field-wrapper .input-line { 212 | clear: both; 213 | margin-bottom: 10px 214 | } 215 | 216 | .fb-field-wrapper .input-line .above-line { 217 | margin-top: 7px 218 | } 219 | 220 | .fb-field-wrapper .input-line > span { 221 | display: inline-block; 222 | vertical-align: top 223 | } 224 | 225 | .fb-field-wrapper .input-line > span input { 226 | width: 100% 227 | } 228 | 229 | .fb-field-wrapper .input-line > span > label { 230 | display: block; 231 | font-size: 13px; 232 | margin-left: 3px 233 | } 234 | 235 | .fb-field-wrapper .help-block { 236 | display: block; 237 | font-size: 12px; 238 | margin-top: 5px 239 | } 240 | 241 | .fb-edit-field-wrapper { 242 | font-size: 13px 243 | } 244 | 245 | .fb-edit-field-wrapper .fb-field-label { 246 | font-weight: normal; 247 | background: #eee; 248 | padding: .75em; 249 | color: #666; 250 | font-size: 1.25em 251 | } 252 | 253 | .fb-edit-field-wrapper .fb-field-label .field-type { 254 | margin-top: .5em; 255 | display: block; 256 | font-family: 'Source Sans Pro', sans-serif; 257 | font-size: 1em 258 | } 259 | 260 | .fb-edit-field-wrapper .fb-field-label .field-type:before { 261 | content: 'Type: '; 262 | color: #999 263 | } 264 | 265 | .fb-edit-field-wrapper .fb-field-label .fa.fa-arrow-right { 266 | display: none 267 | } 268 | 269 | .fb-edit-field-wrapper .fb-edit-section-header { 270 | border-bottom: 1px solid #ddd; 271 | margin-top: 25px; 272 | margin-bottom: 10px; 273 | padding-bottom: 5px; 274 | clear: both; 275 | font-weight: 700 276 | } 277 | 278 | .fb-edit-field-wrapper .fb-bottom-add { 279 | margin-top: 8px 280 | } 281 | 282 | .fb-common-wrapper .fb-label-description { 283 | margin-bottom: 10px 284 | } 285 | 286 | .fb-common-wrapper .fb-label-description input, .fb-common-wrapper .fb-label-description textarea { 287 | width: 100% 288 | } 289 | 290 | .fb-common-wrapper .fb-label-description textarea { 291 | min-height: 5em 292 | } 293 | 294 | .response-field-draggable-helper { 295 | border: 1px dashed #ddd; 296 | background: #eee 297 | } 298 | 299 | .response-field-text input.rf-size-small { 300 | width: 130px 301 | } 302 | 303 | .response-field-text input.rf-size-medium { 304 | width: 300px 305 | } 306 | 307 | .response-field-text input.rf-size-large { 308 | width: 100% 309 | } 310 | 311 | .response-field-paragraph textarea.rf-size-small { 312 | width: 200px; 313 | min-height: 60px 314 | } 315 | 316 | .response-field-paragraph textarea.rf-size-medium { 317 | width: 400px; 318 | min-height: 100px 319 | } 320 | 321 | .response-field-paragraph textarea.rf-size-large { 322 | width: 100%; 323 | min-height: 200px 324 | } 325 | 326 | .response-field-address .street { 327 | width: 400px 328 | } 329 | 330 | .response-field-address .city, .response-field-address .state, .response-field-address .zip, .response-field-address .country { 331 | width: 198px 332 | } 333 | 334 | .response-field-date .month, .response-field-date .day, .response-field-date .year { 335 | width: 50px 336 | } 337 | 338 | .response-field-time .hours, .response-field-time .minutes, .response-field-time .seconds { 339 | width: 50px 340 | } 341 | 342 | .response-field-checkboxes .fb-option, .response-field-radio .fb-option { 343 | margin-bottom: 5px; 344 | display: inline-block 345 | } 346 | 347 | .response-field-website input { 348 | width: 200px 349 | } 350 | -------------------------------------------------------------------------------- /src/admin/assets/formbuilder/css/vendor.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'FontAwesome'; 3 | src: url('../fonts/fontawesome-webfont.eot?v=4.6.3'); 4 | src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg'); 5 | font-weight: normal; 6 | font-style: normal; 7 | } 8 | 9 | .fa { 10 | display: inline-block; 11 | font-family: FontAwesome; 12 | font-style: normal; 13 | font-weight: 400; 14 | line-height: 1; 15 | -webkit-font-smoothing: antialiased; 16 | -moz-osx-font-smoothing: grayscale 17 | } 18 | 19 | .fa-lg { 20 | font-size: 1.3333333333333333em; 21 | line-height: .75em; 22 | vertical-align: -15% 23 | } 24 | 25 | .fa-2x { 26 | font-size: 2em 27 | } 28 | 29 | .fa-3x { 30 | font-size: 3em 31 | } 32 | 33 | .fa-4x { 34 | font-size: 4em 35 | } 36 | 37 | .fa-5x { 38 | font-size: 5em 39 | } 40 | 41 | .fa-fw { 42 | width: 1.2857142857142858em; 43 | text-align: center 44 | } 45 | 46 | .fa-ul { 47 | padding-left: 0; 48 | margin-left: 2.142857142857143em; 49 | list-style-type: none 50 | } 51 | 52 | .fa-ul > li { 53 | position: relative 54 | } 55 | 56 | .fa-li { 57 | position: absolute; 58 | left: -2.142857142857143em; 59 | width: 2.142857142857143em; 60 | top: .14285714285714285em; 61 | text-align: center 62 | } 63 | 64 | .fa-li.fa-lg { 65 | left: -1.8571428571428572em 66 | } 67 | 68 | .fa-border { 69 | padding: .2em .25em .15em; 70 | border: solid .08em #eee; 71 | border-radius: .1em 72 | } 73 | 74 | .pull-right { 75 | float: right 76 | } 77 | 78 | .pull-left { 79 | float: left 80 | } 81 | 82 | .fa.pull-left { 83 | margin-right: .3em 84 | } 85 | 86 | .fa.pull-right { 87 | margin-left: .3em 88 | } 89 | 90 | .fa-spin { 91 | -webkit-animation: spin 2s infinite linear; 92 | -moz-animation: spin 2s infinite linear; 93 | -o-animation: spin 2s infinite linear; 94 | animation: spin 2s infinite linear 95 | } 96 | 97 | @-moz-keyframes spin { 98 | 0% { 99 | -moz-transform: rotate(0deg) 100 | } 101 | 100% { 102 | -moz-transform: rotate(359deg) 103 | } 104 | } 105 | 106 | @-webkit-keyframes spin { 107 | 0% { 108 | -webkit-transform: rotate(0deg) 109 | } 110 | 100% { 111 | -webkit-transform: rotate(359deg) 112 | } 113 | } 114 | 115 | @-o-keyframes spin { 116 | 0% { 117 | -o-transform: rotate(0deg) 118 | } 119 | 100% { 120 | -o-transform: rotate(359deg) 121 | } 122 | } 123 | 124 | @-ms-keyframes spin { 125 | 0% { 126 | -ms-transform: rotate(0deg) 127 | } 128 | 100% { 129 | -ms-transform: rotate(359deg) 130 | } 131 | } 132 | 133 | @keyframes spin { 134 | 0% { 135 | transform: rotate(0deg) 136 | } 137 | 100% { 138 | transform: rotate(359deg) 139 | } 140 | } 141 | 142 | .fa-rotate-90 { 143 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); 144 | -webkit-transform: rotate(90deg); 145 | -moz-transform: rotate(90deg); 146 | -ms-transform: rotate(90deg); 147 | -o-transform: rotate(90deg); 148 | transform: rotate(90deg) 149 | } 150 | 151 | .fa-rotate-180 { 152 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); 153 | -webkit-transform: rotate(180deg); 154 | -moz-transform: rotate(180deg); 155 | -ms-transform: rotate(180deg); 156 | -o-transform: rotate(180deg); 157 | transform: rotate(180deg) 158 | } 159 | 160 | .fa-rotate-270 { 161 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); 162 | -webkit-transform: rotate(270deg); 163 | -moz-transform: rotate(270deg); 164 | -ms-transform: rotate(270deg); 165 | -o-transform: rotate(270deg); 166 | transform: rotate(270deg) 167 | } 168 | 169 | .fa-flip-horizontal { 170 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); 171 | -webkit-transform: scale(-1, 1); 172 | -moz-transform: scale(-1, 1); 173 | -ms-transform: scale(-1, 1); 174 | -o-transform: scale(-1, 1); 175 | transform: scale(-1, 1) 176 | } 177 | 178 | .fa-flip-vertical { 179 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); 180 | -webkit-transform: scale(1, -1); 181 | -moz-transform: scale(1, -1); 182 | -ms-transform: scale(1, -1); 183 | -o-transform: scale(1, -1); 184 | transform: scale(1, -1) 185 | } 186 | 187 | .fa-stack { 188 | position: relative; 189 | display: inline-block; 190 | width: 2em; 191 | height: 2em; 192 | line-height: 2em; 193 | vertical-align: middle 194 | } 195 | 196 | .fa-stack-1x, .fa-stack-2x { 197 | position: absolute; 198 | left: 0; 199 | width: 100%; 200 | text-align: center 201 | } 202 | 203 | .fa-stack-1x { 204 | line-height: inherit 205 | } 206 | 207 | .fa-stack-2x { 208 | font-size: 2em 209 | } 210 | 211 | .fa-inverse { 212 | color: #fff 213 | } 214 | 215 | .fa-glass:before { 216 | content: "\f000" 217 | } 218 | 219 | .fa-music:before { 220 | content: "\f001" 221 | } 222 | 223 | .fa-search:before { 224 | content: "\f002" 225 | } 226 | 227 | .fa-envelope-o:before { 228 | content: "\f003" 229 | } 230 | 231 | .fa-heart:before { 232 | content: "\f004" 233 | } 234 | 235 | .fa-star:before { 236 | content: "\f005" 237 | } 238 | 239 | .fa-star-o:before { 240 | content: "\f006" 241 | } 242 | 243 | .fa-user:before { 244 | content: "\f007" 245 | } 246 | 247 | .fa-film:before { 248 | content: "\f008" 249 | } 250 | 251 | .fa-th-large:before { 252 | content: "\f009" 253 | } 254 | 255 | .fa-th:before { 256 | content: "\f00a" 257 | } 258 | 259 | .fa-th-list:before { 260 | content: "\f00b" 261 | } 262 | 263 | .fa-check:before { 264 | content: "\f00c" 265 | } 266 | 267 | .fa-times:before { 268 | content: "\f00d" 269 | } 270 | 271 | .fa-search-plus:before { 272 | content: "\f00e" 273 | } 274 | 275 | .fa-search-minus:before { 276 | content: "\f010" 277 | } 278 | 279 | .fa-power-off:before { 280 | content: "\f011" 281 | } 282 | 283 | .fa-signal:before { 284 | content: "\f012" 285 | } 286 | 287 | .fa-gear:before, .fa-cog:before { 288 | content: "\f013" 289 | } 290 | 291 | .fa-trash-o:before { 292 | content: "\f014" 293 | } 294 | 295 | .fa-home:before { 296 | content: "\f015" 297 | } 298 | 299 | .fa-file-o:before { 300 | content: "\f016" 301 | } 302 | 303 | .fa-clock-o:before { 304 | content: "\f017" 305 | } 306 | 307 | .fa-road:before { 308 | content: "\f018" 309 | } 310 | 311 | .fa-download:before { 312 | content: "\f019" 313 | } 314 | 315 | .fa-arrow-circle-o-down:before { 316 | content: "\f01a" 317 | } 318 | 319 | .fa-arrow-circle-o-up:before { 320 | content: "\f01b" 321 | } 322 | 323 | .fa-inbox:before { 324 | content: "\f01c" 325 | } 326 | 327 | .fa-play-circle-o:before { 328 | content: "\f01d" 329 | } 330 | 331 | .fa-rotate-right:before, .fa-repeat:before { 332 | content: "\f01e" 333 | } 334 | 335 | .fa-refresh:before { 336 | content: "\f021" 337 | } 338 | 339 | .fa-list-alt:before { 340 | content: "\f022" 341 | } 342 | 343 | .fa-lock:before { 344 | content: "\f023" 345 | } 346 | 347 | .fa-flag:before { 348 | content: "\f024" 349 | } 350 | 351 | .fa-headphones:before { 352 | content: "\f025" 353 | } 354 | 355 | .fa-volume-off:before { 356 | content: "\f026" 357 | } 358 | 359 | .fa-volume-down:before { 360 | content: "\f027" 361 | } 362 | 363 | .fa-volume-up:before { 364 | content: "\f028" 365 | } 366 | 367 | .fa-qrcode:before { 368 | content: "\f029" 369 | } 370 | 371 | .fa-barcode:before { 372 | content: "\f02a" 373 | } 374 | 375 | .fa-tag:before { 376 | content: "\f02b" 377 | } 378 | 379 | .fa-tags:before { 380 | content: "\f02c" 381 | } 382 | 383 | .fa-book:before { 384 | content: "\f02d" 385 | } 386 | 387 | .fa-bookmark:before { 388 | content: "\f02e" 389 | } 390 | 391 | .fa-print:before { 392 | content: "\f02f" 393 | } 394 | 395 | .fa-camera:before { 396 | content: "\f030" 397 | } 398 | 399 | .fa-font:before { 400 | content: "\f031" 401 | } 402 | 403 | .fa-bold:before { 404 | content: "\f032" 405 | } 406 | 407 | .fa-italic:before { 408 | content: "\f033" 409 | } 410 | 411 | .fa-text-height:before { 412 | content: "\f034" 413 | } 414 | 415 | .fa-text-width:before { 416 | content: "\f035" 417 | } 418 | 419 | .fa-align-left:before { 420 | content: "\f036" 421 | } 422 | 423 | .fa-align-center:before { 424 | content: "\f037" 425 | } 426 | 427 | .fa-align-right:before { 428 | content: "\f038" 429 | } 430 | 431 | .fa-align-justify:before { 432 | content: "\f039" 433 | } 434 | 435 | .fa-list:before { 436 | content: "\f03a" 437 | } 438 | 439 | .fa-dedent:before, .fa-outdent:before { 440 | content: "\f03b" 441 | } 442 | 443 | .fa-indent:before { 444 | content: "\f03c" 445 | } 446 | 447 | .fa-video-camera:before { 448 | content: "\f03d" 449 | } 450 | 451 | .fa-picture-o:before { 452 | content: "\f03e" 453 | } 454 | 455 | .fa-pencil:before { 456 | content: "\f040" 457 | } 458 | 459 | .fa-map-marker:before { 460 | content: "\f041" 461 | } 462 | 463 | .fa-adjust:before { 464 | content: "\f042" 465 | } 466 | 467 | .fa-tint:before { 468 | content: "\f043" 469 | } 470 | 471 | .fa-edit:before, .fa-pencil-square-o:before { 472 | content: "\f044" 473 | } 474 | 475 | .fa-share-square-o:before { 476 | content: "\f045" 477 | } 478 | 479 | .fa-check-square-o:before { 480 | content: "\f046" 481 | } 482 | 483 | .fa-arrows:before { 484 | content: "\f047" 485 | } 486 | 487 | .fa-step-backward:before { 488 | content: "\f048" 489 | } 490 | 491 | .fa-fast-backward:before { 492 | content: "\f049" 493 | } 494 | 495 | .fa-backward:before { 496 | content: "\f04a" 497 | } 498 | 499 | .fa-play:before { 500 | content: "\f04b" 501 | } 502 | 503 | .fa-pause:before { 504 | content: "\f04c" 505 | } 506 | 507 | .fa-stop:before { 508 | content: "\f04d" 509 | } 510 | 511 | .fa-forward:before { 512 | content: "\f04e" 513 | } 514 | 515 | .fa-fast-forward:before { 516 | content: "\f050" 517 | } 518 | 519 | .fa-step-forward:before { 520 | content: "\f051" 521 | } 522 | 523 | .fa-eject:before { 524 | content: "\f052" 525 | } 526 | 527 | .fa-chevron-left:before { 528 | content: "\f053" 529 | } 530 | 531 | .fa-chevron-right:before { 532 | content: "\f054" 533 | } 534 | 535 | .fa-plus-circle:before { 536 | content: "\f055" 537 | } 538 | 539 | .fa-minus-circle:before { 540 | content: "\f056" 541 | } 542 | 543 | .fa-times-circle:before { 544 | content: "\f057" 545 | } 546 | 547 | .fa-check-circle:before { 548 | content: "\f058" 549 | } 550 | 551 | .fa-question-circle:before { 552 | content: "\f059" 553 | } 554 | 555 | .fa-info-circle:before { 556 | content: "\f05a" 557 | } 558 | 559 | .fa-crosshairs:before { 560 | content: "\f05b" 561 | } 562 | 563 | .fa-times-circle-o:before { 564 | content: "\f05c" 565 | } 566 | 567 | .fa-check-circle-o:before { 568 | content: "\f05d" 569 | } 570 | 571 | .fa-ban:before { 572 | content: "\f05e" 573 | } 574 | 575 | .fa-arrow-left:before { 576 | content: "\f060" 577 | } 578 | 579 | .fa-arrow-right:before { 580 | content: "\f061" 581 | } 582 | 583 | .fa-arrow-up:before { 584 | content: "\f062" 585 | } 586 | 587 | .fa-arrow-down:before { 588 | content: "\f063" 589 | } 590 | 591 | .fa-mail-forward:before, .fa-share:before { 592 | content: "\f064" 593 | } 594 | 595 | .fa-expand:before { 596 | content: "\f065" 597 | } 598 | 599 | .fa-compress:before { 600 | content: "\f066" 601 | } 602 | 603 | .fa-plus:before { 604 | content: "\f067" 605 | } 606 | 607 | .fa-minus:before { 608 | content: "\f068" 609 | } 610 | 611 | .fa-asterisk:before { 612 | content: "\f069" 613 | } 614 | 615 | .fa-exclamation-circle:before { 616 | content: "\f06a" 617 | } 618 | 619 | .fa-gift:before { 620 | content: "\f06b" 621 | } 622 | 623 | .fa-leaf:before { 624 | content: "\f06c" 625 | } 626 | 627 | .fa-fire:before { 628 | content: "\f06d" 629 | } 630 | 631 | .fa-eye:before { 632 | content: "\f06e" 633 | } 634 | 635 | .fa-eye-slash:before { 636 | content: "\f070" 637 | } 638 | 639 | .fa-warning:before, .fa-exclamation-triangle:before { 640 | content: "\f071" 641 | } 642 | 643 | .fa-plane:before { 644 | content: "\f072" 645 | } 646 | 647 | .fa-calendar:before { 648 | content: "\f073" 649 | } 650 | 651 | .fa-random:before { 652 | content: "\f074" 653 | } 654 | 655 | .fa-comment:before { 656 | content: "\f075" 657 | } 658 | 659 | .fa-magnet:before { 660 | content: "\f076" 661 | } 662 | 663 | .fa-chevron-up:before { 664 | content: "\f077" 665 | } 666 | 667 | .fa-chevron-down:before { 668 | content: "\f078" 669 | } 670 | 671 | .fa-retweet:before { 672 | content: "\f079" 673 | } 674 | 675 | .fa-shopping-cart:before { 676 | content: "\f07a" 677 | } 678 | 679 | .fa-folder:before { 680 | content: "\f07b" 681 | } 682 | 683 | .fa-folder-open:before { 684 | content: "\f07c" 685 | } 686 | 687 | .fa-arrows-v:before { 688 | content: "\f07d" 689 | } 690 | 691 | .fa-arrows-h:before { 692 | content: "\f07e" 693 | } 694 | 695 | .fa-bar-chart-o:before { 696 | content: "\f080" 697 | } 698 | 699 | .fa-twitter-square:before { 700 | content: "\f081" 701 | } 702 | 703 | .fa-facebook-square:before { 704 | content: "\f082" 705 | } 706 | 707 | .fa-camera-retro:before { 708 | content: "\f083" 709 | } 710 | 711 | .fa-key:before { 712 | content: "\f084" 713 | } 714 | 715 | .fa-gears:before, .fa-cogs:before { 716 | content: "\f085" 717 | } 718 | 719 | .fa-comments:before { 720 | content: "\f086" 721 | } 722 | 723 | .fa-thumbs-o-up:before { 724 | content: "\f087" 725 | } 726 | 727 | .fa-thumbs-o-down:before { 728 | content: "\f088" 729 | } 730 | 731 | .fa-star-half:before { 732 | content: "\f089" 733 | } 734 | 735 | .fa-heart-o:before { 736 | content: "\f08a" 737 | } 738 | 739 | .fa-sign-out:before { 740 | content: "\f08b" 741 | } 742 | 743 | .fa-linkedin-square:before { 744 | content: "\f08c" 745 | } 746 | 747 | .fa-thumb-tack:before { 748 | content: "\f08d" 749 | } 750 | 751 | .fa-external-link:before { 752 | content: "\f08e" 753 | } 754 | 755 | .fa-sign-in:before { 756 | content: "\f090" 757 | } 758 | 759 | .fa-trophy:before { 760 | content: "\f091" 761 | } 762 | 763 | .fa-github-square:before { 764 | content: "\f092" 765 | } 766 | 767 | .fa-upload:before { 768 | content: "\f093" 769 | } 770 | 771 | .fa-lemon-o:before { 772 | content: "\f094" 773 | } 774 | 775 | .fa-phone:before { 776 | content: "\f095" 777 | } 778 | 779 | .fa-square-o:before { 780 | content: "\f096" 781 | } 782 | 783 | .fa-bookmark-o:before { 784 | content: "\f097" 785 | } 786 | 787 | .fa-phone-square:before { 788 | content: "\f098" 789 | } 790 | 791 | .fa-twitter:before { 792 | content: "\f099" 793 | } 794 | 795 | .fa-facebook:before { 796 | content: "\f09a" 797 | } 798 | 799 | .fa-github:before { 800 | content: "\f09b" 801 | } 802 | 803 | .fa-unlock:before { 804 | content: "\f09c" 805 | } 806 | 807 | .fa-credit-card:before { 808 | content: "\f09d" 809 | } 810 | 811 | .fa-rss:before { 812 | content: "\f09e" 813 | } 814 | 815 | .fa-hdd-o:before { 816 | content: "\f0a0" 817 | } 818 | 819 | .fa-bullhorn:before { 820 | content: "\f0a1" 821 | } 822 | 823 | .fa-bell:before { 824 | content: "\f0f3" 825 | } 826 | 827 | .fa-certificate:before { 828 | content: "\f0a3" 829 | } 830 | 831 | .fa-hand-o-right:before { 832 | content: "\f0a4" 833 | } 834 | 835 | .fa-hand-o-left:before { 836 | content: "\f0a5" 837 | } 838 | 839 | .fa-hand-o-up:before { 840 | content: "\f0a6" 841 | } 842 | 843 | .fa-hand-o-down:before { 844 | content: "\f0a7" 845 | } 846 | 847 | .fa-arrow-circle-left:before { 848 | content: "\f0a8" 849 | } 850 | 851 | .fa-arrow-circle-right:before { 852 | content: "\f0a9" 853 | } 854 | 855 | .fa-arrow-circle-up:before { 856 | content: "\f0aa" 857 | } 858 | 859 | .fa-arrow-circle-down:before { 860 | content: "\f0ab" 861 | } 862 | 863 | .fa-globe:before { 864 | content: "\f0ac" 865 | } 866 | 867 | .fa-wrench:before { 868 | content: "\f0ad" 869 | } 870 | 871 | .fa-tasks:before { 872 | content: "\f0ae" 873 | } 874 | 875 | .fa-filter:before { 876 | content: "\f0b0" 877 | } 878 | 879 | .fa-briefcase:before { 880 | content: "\f0b1" 881 | } 882 | 883 | .fa-arrows-alt:before { 884 | content: "\f0b2" 885 | } 886 | 887 | .fa-group:before, .fa-users:before { 888 | content: "\f0c0" 889 | } 890 | 891 | .fa-chain:before, .fa-link:before { 892 | content: "\f0c1" 893 | } 894 | 895 | .fa-cloud:before { 896 | content: "\f0c2" 897 | } 898 | 899 | .fa-flask:before { 900 | content: "\f0c3" 901 | } 902 | 903 | .fa-cut:before, .fa-scissors:before { 904 | content: "\f0c4" 905 | } 906 | 907 | .fa-copy:before, .fa-files-o:before { 908 | content: "\f0c5" 909 | } 910 | 911 | .fa-paperclip:before { 912 | content: "\f0c6" 913 | } 914 | 915 | .fa-save:before, .fa-floppy-o:before { 916 | content: "\f0c7" 917 | } 918 | 919 | .fa-square:before { 920 | content: "\f0c8" 921 | } 922 | 923 | .fa-bars:before { 924 | content: "\f0c9" 925 | } 926 | 927 | .fa-list-ul:before { 928 | content: "\f0ca" 929 | } 930 | 931 | .fa-list-ol:before { 932 | content: "\f0cb" 933 | } 934 | 935 | .fa-strikethrough:before { 936 | content: "\f0cc" 937 | } 938 | 939 | .fa-underline:before { 940 | content: "\f0cd" 941 | } 942 | 943 | .fa-table:before { 944 | content: "\f0ce" 945 | } 946 | 947 | .fa-magic:before { 948 | content: "\f0d0" 949 | } 950 | 951 | .fa-truck:before { 952 | content: "\f0d1" 953 | } 954 | 955 | .fa-pinterest:before { 956 | content: "\f0d2" 957 | } 958 | 959 | .fa-pinterest-square:before { 960 | content: "\f0d3" 961 | } 962 | 963 | .fa-google-plus-square:before { 964 | content: "\f0d4" 965 | } 966 | 967 | .fa-google-plus:before { 968 | content: "\f0d5" 969 | } 970 | 971 | .fa-money:before { 972 | content: "\f0d6" 973 | } 974 | 975 | .fa-caret-down:before { 976 | content: "\f0d7" 977 | } 978 | 979 | .fa-caret-up:before { 980 | content: "\f0d8" 981 | } 982 | 983 | .fa-caret-left:before { 984 | content: "\f0d9" 985 | } 986 | 987 | .fa-caret-right:before { 988 | content: "\f0da" 989 | } 990 | 991 | .fa-columns:before { 992 | content: "\f0db" 993 | } 994 | 995 | .fa-unsorted:before, .fa-sort:before { 996 | content: "\f0dc" 997 | } 998 | 999 | .fa-sort-down:before, .fa-sort-asc:before { 1000 | content: "\f0dd" 1001 | } 1002 | 1003 | .fa-sort-up:before, .fa-sort-desc:before { 1004 | content: "\f0de" 1005 | } 1006 | 1007 | .fa-envelope:before { 1008 | content: "\f0e0" 1009 | } 1010 | 1011 | .fa-linkedin:before { 1012 | content: "\f0e1" 1013 | } 1014 | 1015 | .fa-rotate-left:before, .fa-undo:before { 1016 | content: "\f0e2" 1017 | } 1018 | 1019 | .fa-legal:before, .fa-gavel:before { 1020 | content: "\f0e3" 1021 | } 1022 | 1023 | .fa-dashboard:before, .fa-tachometer:before { 1024 | content: "\f0e4" 1025 | } 1026 | 1027 | .fa-comment-o:before { 1028 | content: "\f0e5" 1029 | } 1030 | 1031 | .fa-comments-o:before { 1032 | content: "\f0e6" 1033 | } 1034 | 1035 | .fa-flash:before, .fa-bolt:before { 1036 | content: "\f0e7" 1037 | } 1038 | 1039 | .fa-sitemap:before { 1040 | content: "\f0e8" 1041 | } 1042 | 1043 | .fa-umbrella:before { 1044 | content: "\f0e9" 1045 | } 1046 | 1047 | .fa-paste:before, .fa-clipboard:before { 1048 | content: "\f0ea" 1049 | } 1050 | 1051 | .fa-lightbulb-o:before { 1052 | content: "\f0eb" 1053 | } 1054 | 1055 | .fa-exchange:before { 1056 | content: "\f0ec" 1057 | } 1058 | 1059 | .fa-cloud-download:before { 1060 | content: "\f0ed" 1061 | } 1062 | 1063 | .fa-cloud-upload:before { 1064 | content: "\f0ee" 1065 | } 1066 | 1067 | .fa-user-md:before { 1068 | content: "\f0f0" 1069 | } 1070 | 1071 | .fa-stethoscope:before { 1072 | content: "\f0f1" 1073 | } 1074 | 1075 | .fa-suitcase:before { 1076 | content: "\f0f2" 1077 | } 1078 | 1079 | .fa-bell-o:before { 1080 | content: "\f0a2" 1081 | } 1082 | 1083 | .fa-coffee:before { 1084 | content: "\f0f4" 1085 | } 1086 | 1087 | .fa-cutlery:before { 1088 | content: "\f0f5" 1089 | } 1090 | 1091 | .fa-file-text-o:before { 1092 | content: "\f0f6" 1093 | } 1094 | 1095 | .fa-building-o:before { 1096 | content: "\f0f7" 1097 | } 1098 | 1099 | .fa-hospital-o:before { 1100 | content: "\f0f8" 1101 | } 1102 | 1103 | .fa-ambulance:before { 1104 | content: "\f0f9" 1105 | } 1106 | 1107 | .fa-medkit:before { 1108 | content: "\f0fa" 1109 | } 1110 | 1111 | .fa-fighter-jet:before { 1112 | content: "\f0fb" 1113 | } 1114 | 1115 | .fa-beer:before { 1116 | content: "\f0fc" 1117 | } 1118 | 1119 | .fa-h-square:before { 1120 | content: "\f0fd" 1121 | } 1122 | 1123 | .fa-plus-square:before { 1124 | content: "\f0fe" 1125 | } 1126 | 1127 | .fa-angle-double-left:before { 1128 | content: "\f100" 1129 | } 1130 | 1131 | .fa-angle-double-right:before { 1132 | content: "\f101" 1133 | } 1134 | 1135 | .fa-angle-double-up:before { 1136 | content: "\f102" 1137 | } 1138 | 1139 | .fa-angle-double-down:before { 1140 | content: "\f103" 1141 | } 1142 | 1143 | .fa-angle-left:before { 1144 | content: "\f104" 1145 | } 1146 | 1147 | .fa-angle-right:before { 1148 | content: "\f105" 1149 | } 1150 | 1151 | .fa-angle-up:before { 1152 | content: "\f106" 1153 | } 1154 | 1155 | .fa-angle-down:before { 1156 | content: "\f107" 1157 | } 1158 | 1159 | .fa-desktop:before { 1160 | content: "\f108" 1161 | } 1162 | 1163 | .fa-laptop:before { 1164 | content: "\f109" 1165 | } 1166 | 1167 | .fa-tablet:before { 1168 | content: "\f10a" 1169 | } 1170 | 1171 | .fa-mobile-phone:before, .fa-mobile:before { 1172 | content: "\f10b" 1173 | } 1174 | 1175 | .fa-circle-o:before { 1176 | content: "\f10c" 1177 | } 1178 | 1179 | .fa-quote-left:before { 1180 | content: "\f10d" 1181 | } 1182 | 1183 | .fa-quote-right:before { 1184 | content: "\f10e" 1185 | } 1186 | 1187 | .fa-spinner:before { 1188 | content: "\f110" 1189 | } 1190 | 1191 | .fa-circle:before { 1192 | content: "\f111" 1193 | } 1194 | 1195 | .fa-mail-reply:before, .fa-reply:before { 1196 | content: "\f112" 1197 | } 1198 | 1199 | .fa-github-alt:before { 1200 | content: "\f113" 1201 | } 1202 | 1203 | .fa-folder-o:before { 1204 | content: "\f114" 1205 | } 1206 | 1207 | .fa-folder-open-o:before { 1208 | content: "\f115" 1209 | } 1210 | 1211 | .fa-smile-o:before { 1212 | content: "\f118" 1213 | } 1214 | 1215 | .fa-frown-o:before { 1216 | content: "\f119" 1217 | } 1218 | 1219 | .fa-meh-o:before { 1220 | content: "\f11a" 1221 | } 1222 | 1223 | .fa-gamepad:before { 1224 | content: "\f11b" 1225 | } 1226 | 1227 | .fa-keyboard-o:before { 1228 | content: "\f11c" 1229 | } 1230 | 1231 | .fa-flag-o:before { 1232 | content: "\f11d" 1233 | } 1234 | 1235 | .fa-flag-checkered:before { 1236 | content: "\f11e" 1237 | } 1238 | 1239 | .fa-terminal:before { 1240 | content: "\f120" 1241 | } 1242 | 1243 | .fa-code:before { 1244 | content: "\f121" 1245 | } 1246 | 1247 | .fa-reply-all:before { 1248 | content: "\f122" 1249 | } 1250 | 1251 | .fa-mail-reply-all:before { 1252 | content: "\f122" 1253 | } 1254 | 1255 | .fa-star-half-empty:before, .fa-star-half-full:before, .fa-star-half-o:before { 1256 | content: "\f123" 1257 | } 1258 | 1259 | .fa-location-arrow:before { 1260 | content: "\f124" 1261 | } 1262 | 1263 | .fa-crop:before { 1264 | content: "\f125" 1265 | } 1266 | 1267 | .fa-code-fork:before { 1268 | content: "\f126" 1269 | } 1270 | 1271 | .fa-unlink:before, .fa-chain-broken:before { 1272 | content: "\f127" 1273 | } 1274 | 1275 | .fa-question:before { 1276 | content: "\f128" 1277 | } 1278 | 1279 | .fa-info:before { 1280 | content: "\f129" 1281 | } 1282 | 1283 | .fa-exclamation:before { 1284 | content: "\f12a" 1285 | } 1286 | 1287 | .fa-superscript:before { 1288 | content: "\f12b" 1289 | } 1290 | 1291 | .fa-subscript:before { 1292 | content: "\f12c" 1293 | } 1294 | 1295 | .fa-eraser:before { 1296 | content: "\f12d" 1297 | } 1298 | 1299 | .fa-puzzle-piece:before { 1300 | content: "\f12e" 1301 | } 1302 | 1303 | .fa-microphone:before { 1304 | content: "\f130" 1305 | } 1306 | 1307 | .fa-microphone-slash:before { 1308 | content: "\f131" 1309 | } 1310 | 1311 | .fa-shield:before { 1312 | content: "\f132" 1313 | } 1314 | 1315 | .fa-calendar-o:before { 1316 | content: "\f133" 1317 | } 1318 | 1319 | .fa-fire-extinguisher:before { 1320 | content: "\f134" 1321 | } 1322 | 1323 | .fa-rocket:before { 1324 | content: "\f135" 1325 | } 1326 | 1327 | .fa-maxcdn:before { 1328 | content: "\f136" 1329 | } 1330 | 1331 | .fa-chevron-circle-left:before { 1332 | content: "\f137" 1333 | } 1334 | 1335 | .fa-chevron-circle-right:before { 1336 | content: "\f138" 1337 | } 1338 | 1339 | .fa-chevron-circle-up:before { 1340 | content: "\f139" 1341 | } 1342 | 1343 | .fa-chevron-circle-down:before { 1344 | content: "\f13a" 1345 | } 1346 | 1347 | .fa-html5:before { 1348 | content: "\f13b" 1349 | } 1350 | 1351 | .fa-css3:before { 1352 | content: "\f13c" 1353 | } 1354 | 1355 | .fa-anchor:before { 1356 | content: "\f13d" 1357 | } 1358 | 1359 | .fa-unlock-alt:before { 1360 | content: "\f13e" 1361 | } 1362 | 1363 | .fa-bullseye:before { 1364 | content: "\f140" 1365 | } 1366 | 1367 | .fa-ellipsis-h:before { 1368 | content: "\f141" 1369 | } 1370 | 1371 | .fa-ellipsis-v:before { 1372 | content: "\f142" 1373 | } 1374 | 1375 | .fa-rss-square:before { 1376 | content: "\f143" 1377 | } 1378 | 1379 | .fa-play-circle:before { 1380 | content: "\f144" 1381 | } 1382 | 1383 | .fa-ticket:before { 1384 | content: "\f145" 1385 | } 1386 | 1387 | .fa-minus-square:before { 1388 | content: "\f146" 1389 | } 1390 | 1391 | .fa-minus-square-o:before { 1392 | content: "\f147" 1393 | } 1394 | 1395 | .fa-level-up:before { 1396 | content: "\f148" 1397 | } 1398 | 1399 | .fa-level-down:before { 1400 | content: "\f149" 1401 | } 1402 | 1403 | .fa-check-square:before { 1404 | content: "\f14a" 1405 | } 1406 | 1407 | .fa-pencil-square:before { 1408 | content: "\f14b" 1409 | } 1410 | 1411 | .fa-external-link-square:before { 1412 | content: "\f14c" 1413 | } 1414 | 1415 | .fa-share-square:before { 1416 | content: "\f14d" 1417 | } 1418 | 1419 | .fa-compass:before { 1420 | content: "\f14e" 1421 | } 1422 | 1423 | .fa-toggle-down:before, .fa-caret-square-o-down:before { 1424 | content: "\f150" 1425 | } 1426 | 1427 | .fa-toggle-up:before, .fa-caret-square-o-up:before { 1428 | content: "\f151" 1429 | } 1430 | 1431 | .fa-toggle-right:before, .fa-caret-square-o-right:before { 1432 | content: "\f152" 1433 | } 1434 | 1435 | .fa-euro:before, .fa-eur:before { 1436 | content: "\f153" 1437 | } 1438 | 1439 | .fa-gbp:before { 1440 | content: "\f154" 1441 | } 1442 | 1443 | .fa-dollar:before, .fa-usd:before { 1444 | content: "\f155" 1445 | } 1446 | 1447 | .fa-rupee:before, .fa-inr:before { 1448 | content: "\f156" 1449 | } 1450 | 1451 | .fa-cny:before, .fa-rmb:before, .fa-yen:before, .fa-jpy:before { 1452 | content: "\f157" 1453 | } 1454 | 1455 | .fa-ruble:before, .fa-rouble:before, .fa-rub:before { 1456 | content: "\f158" 1457 | } 1458 | 1459 | .fa-won:before, .fa-krw:before { 1460 | content: "\f159" 1461 | } 1462 | 1463 | .fa-bitcoin:before, .fa-btc:before { 1464 | content: "\f15a" 1465 | } 1466 | 1467 | .fa-file:before { 1468 | content: "\f15b" 1469 | } 1470 | 1471 | .fa-file-text:before { 1472 | content: "\f15c" 1473 | } 1474 | 1475 | .fa-sort-alpha-asc:before { 1476 | content: "\f15d" 1477 | } 1478 | 1479 | .fa-sort-alpha-desc:before { 1480 | content: "\f15e" 1481 | } 1482 | 1483 | .fa-sort-amount-asc:before { 1484 | content: "\f160" 1485 | } 1486 | 1487 | .fa-sort-amount-desc:before { 1488 | content: "\f161" 1489 | } 1490 | 1491 | .fa-sort-numeric-asc:before { 1492 | content: "\f162" 1493 | } 1494 | 1495 | .fa-sort-numeric-desc:before { 1496 | content: "\f163" 1497 | } 1498 | 1499 | .fa-thumbs-up:before { 1500 | content: "\f164" 1501 | } 1502 | 1503 | .fa-thumbs-down:before { 1504 | content: "\f165" 1505 | } 1506 | 1507 | .fa-youtube-square:before { 1508 | content: "\f166" 1509 | } 1510 | 1511 | .fa-youtube:before { 1512 | content: "\f167" 1513 | } 1514 | 1515 | .fa-xing:before { 1516 | content: "\f168" 1517 | } 1518 | 1519 | .fa-xing-square:before { 1520 | content: "\f169" 1521 | } 1522 | 1523 | .fa-youtube-play:before { 1524 | content: "\f16a" 1525 | } 1526 | 1527 | .fa-dropbox:before { 1528 | content: "\f16b" 1529 | } 1530 | 1531 | .fa-stack-overflow:before { 1532 | content: "\f16c" 1533 | } 1534 | 1535 | .fa-instagram:before { 1536 | content: "\f16d" 1537 | } 1538 | 1539 | .fa-flickr:before { 1540 | content: "\f16e" 1541 | } 1542 | 1543 | .fa-adn:before { 1544 | content: "\f170" 1545 | } 1546 | 1547 | .fa-bitbucket:before { 1548 | content: "\f171" 1549 | } 1550 | 1551 | .fa-bitbucket-square:before { 1552 | content: "\f172" 1553 | } 1554 | 1555 | .fa-tumblr:before { 1556 | content: "\f173" 1557 | } 1558 | 1559 | .fa-tumblr-square:before { 1560 | content: "\f174" 1561 | } 1562 | 1563 | .fa-long-arrow-down:before { 1564 | content: "\f175" 1565 | } 1566 | 1567 | .fa-long-arrow-up:before { 1568 | content: "\f176" 1569 | } 1570 | 1571 | .fa-long-arrow-left:before { 1572 | content: "\f177" 1573 | } 1574 | 1575 | .fa-long-arrow-right:before { 1576 | content: "\f178" 1577 | } 1578 | 1579 | .fa-apple:before { 1580 | content: "\f179" 1581 | } 1582 | 1583 | .fa-windows:before { 1584 | content: "\f17a" 1585 | } 1586 | 1587 | .fa-android:before { 1588 | content: "\f17b" 1589 | } 1590 | 1591 | .fa-linux:before { 1592 | content: "\f17c" 1593 | } 1594 | 1595 | .fa-dribbble:before { 1596 | content: "\f17d" 1597 | } 1598 | 1599 | .fa-skype:before { 1600 | content: "\f17e" 1601 | } 1602 | 1603 | .fa-foursquare:before { 1604 | content: "\f180" 1605 | } 1606 | 1607 | .fa-trello:before { 1608 | content: "\f181" 1609 | } 1610 | 1611 | .fa-female:before { 1612 | content: "\f182" 1613 | } 1614 | 1615 | .fa-male:before { 1616 | content: "\f183" 1617 | } 1618 | 1619 | .fa-gittip:before { 1620 | content: "\f184" 1621 | } 1622 | 1623 | .fa-sun-o:before { 1624 | content: "\f185" 1625 | } 1626 | 1627 | .fa-moon-o:before { 1628 | content: "\f186" 1629 | } 1630 | 1631 | .fa-archive:before { 1632 | content: "\f187" 1633 | } 1634 | 1635 | .fa-bug:before { 1636 | content: "\f188" 1637 | } 1638 | 1639 | .fa-vk:before { 1640 | content: "\f189" 1641 | } 1642 | 1643 | .fa-weibo:before { 1644 | content: "\f18a" 1645 | } 1646 | 1647 | .fa-renren:before { 1648 | content: "\f18b" 1649 | } 1650 | 1651 | .fa-pagelines:before { 1652 | content: "\f18c" 1653 | } 1654 | 1655 | .fa-stack-exchange:before { 1656 | content: "\f18d" 1657 | } 1658 | 1659 | .fa-arrow-circle-o-right:before { 1660 | content: "\f18e" 1661 | } 1662 | 1663 | .fa-arrow-circle-o-left:before { 1664 | content: "\f190" 1665 | } 1666 | 1667 | .fa-toggle-left:before, .fa-caret-square-o-left:before { 1668 | content: "\f191" 1669 | } 1670 | 1671 | .fa-dot-circle-o:before { 1672 | content: "\f192" 1673 | } 1674 | 1675 | .fa-wheelchair:before { 1676 | content: "\f193" 1677 | } 1678 | 1679 | .fa-vimeo-square:before { 1680 | content: "\f194" 1681 | } 1682 | 1683 | .fa-turkish-lira:before, .fa-try:before { 1684 | content: "\f195" 1685 | } 1686 | 1687 | .fa-plus-square-o:before { 1688 | content: "\f196" 1689 | } -------------------------------------------------------------------------------- /src/admin/assets/formbuilder/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mirocow/yii2-eav/9cd752d4ecb2b32d5d501ca68d57c382e66d011a/src/admin/assets/formbuilder/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /src/admin/assets/formbuilder/fonts/font-awesome.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.6.3');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} -------------------------------------------------------------------------------- /src/admin/assets/formbuilder/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mirocow/yii2-eav/9cd752d4ecb2b32d5d501ca68d57c382e66d011a/src/admin/assets/formbuilder/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /src/admin/assets/formbuilder/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mirocow/yii2-eav/9cd752d4ecb2b32d5d501ca68d57c382e66d011a/src/admin/assets/formbuilder/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /src/admin/assets/formbuilder/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mirocow/yii2-eav/9cd752d4ecb2b32d5d501ca68d57c382e66d011a/src/admin/assets/formbuilder/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /src/admin/assets/formbuilder/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mirocow/yii2-eav/9cd752d4ecb2b32d5d501ca68d57c382e66d011a/src/admin/assets/formbuilder/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /src/admin/assets/formbuilder/js/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Scroll left panel support 3 | */ 4 | $.scrollWindowTo = function (pos, duration, cb) { 5 | if (duration == null) { 6 | duration = 0; 7 | } 8 | if (pos === $(window).scrollTop()) { 9 | $(window).trigger('scroll'); 10 | if (typeof cb === "function") { 11 | cb(); 12 | } 13 | return; 14 | } 15 | return $('html, body').animate({ 16 | scrollTop: pos 17 | }, duration, function () { 18 | return typeof cb === "function" ? cb() : void 0; 19 | }); 20 | }; 21 | 22 | /** 23 | * Deep objects support 24 | */ 25 | (function () { 26 | var arrays, 27 | basicObjects, 28 | deepClone, 29 | deepExtend, 30 | deepExtendCouple, 31 | isBasicObject, 32 | __slice = [].slice; 33 | 34 | deepClone = function (obj) { 35 | var func, isArr; 36 | if (!_.isObject(obj || _.isFunction(obj))) { 37 | return obj; 38 | } 39 | if (_.isDate(obj)) { 40 | return new Date(obj.getTime()); 41 | } 42 | if (_.isRegExp(obj)) { 43 | return new RegExp(obj.source, obj.toString().replace(/.*\//, "")); 44 | } 45 | isArr = _.isArray(obj || _.isArguments(obj)); 46 | func = function (memo, value, key) { 47 | if (isArr) { 48 | memo.push(deepClone(value)); 49 | } else { 50 | memo[key] = deepClone(value); 51 | } 52 | return memo; 53 | }; 54 | return _.reduce(obj, func, isArr ? [] : {}); 55 | }; 56 | 57 | isBasicObject = function (object) { 58 | if (object === null) return false; 59 | return (object.prototype === {}.prototype || object.prototype === Object.prototype) && _.isObject(object) && !_.isArray(object) && !_.isFunction(object) && !_.isDate(object) && !_.isRegExp(object) && !_.isArguments(object); 60 | }; 61 | 62 | basicObjects = function (object) { 63 | return _.filter(_.keys(object), function (key) { 64 | return isBasicObject(object[key]); 65 | }); 66 | }; 67 | 68 | arrays = function (object) { 69 | return _.filter(_.keys(object), function (key) { 70 | return _.isArray(object[key]); 71 | }); 72 | }; 73 | 74 | deepExtendCouple = function (destination, source, maxDepth) { 75 | var combine, recurse, sharedArrayKey, sharedArrayKeys, sharedObjectKey, sharedObjectKeys, _i, _j, _len, _len1; 76 | if (maxDepth == null) { 77 | maxDepth = 20; 78 | } 79 | if (maxDepth <= 0) { 80 | console.warn('_.deepExtend(): Maximum depth of recursion hit.'); 81 | return _.extend(destination, source); 82 | } 83 | sharedObjectKeys = _.intersection(basicObjects(destination), basicObjects(source)); 84 | recurse = function (key) { 85 | return source[key] = deepExtendCouple(destination[key], source[key], maxDepth - 1); 86 | }; 87 | for (_i = 0, _len = sharedObjectKeys.length; _i < _len; _i++) { 88 | sharedObjectKey = sharedObjectKeys[_i]; 89 | recurse(sharedObjectKey); 90 | } 91 | sharedArrayKeys = _.intersection(arrays(destination), arrays(source)); 92 | combine = function (key) { 93 | return source[key] = _.union(destination[key], source[key]); 94 | }; 95 | for (_j = 0, _len1 = sharedArrayKeys.length; _j < _len1; _j++) { 96 | sharedArrayKey = sharedArrayKeys[_j]; 97 | combine(sharedArrayKey); 98 | } 99 | return _.extend(destination, source); 100 | }; 101 | 102 | deepExtend = function () { 103 | var finalObj, maxDepth, objects, _i; 104 | objects = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), maxDepth = arguments[_i++]; 105 | if (!_.isNumber(maxDepth)) { 106 | objects.push(maxDepth); 107 | maxDepth = 20; 108 | } 109 | if (objects.length <= 1) { 110 | return objects[0]; 111 | } 112 | if (maxDepth <= 0) { 113 | return _.extend.apply(this, objects); 114 | } 115 | finalObj = objects.shift(); 116 | while (objects.length > 0) { 117 | finalObj = deepExtendCouple(finalObj, deepClone(objects.shift()), maxDepth); 118 | } 119 | return finalObj; 120 | }; 121 | 122 | _.mixin({ 123 | deepClone: deepClone, 124 | isBasicObject: isBasicObject, 125 | basicObjects: basicObjects, 126 | arrays: arrays, 127 | deepExtend: deepExtend 128 | }); 129 | 130 | }).call(this); 131 | 132 | /** 133 | * Nested models support 134 | */ 135 | (function (factory) { 136 | if (typeof define === 'function' && define.amd) { 137 | // AMD 138 | define(['underscore', 'backbone'], factory); 139 | } else { 140 | // globals 141 | factory(_, Backbone); 142 | } 143 | }(function (_, Backbone) { 144 | 145 | /** 146 | * Takes a nested object and returns a shallow object keyed with the path names 147 | * e.g. { "level1.level2": "value" } 148 | * 149 | * @param {Object} Nested object e.g. { level1: { level2: 'value' } } 150 | * @return {Object} Shallow object with path names e.g. { 'level1.level2': 'value' } 151 | */ 152 | function objToPaths(obj) { 153 | var ret = {}, 154 | separator = DeepModel.keyPathSeparator; 155 | 156 | for (var key in obj) { 157 | var val = obj[key]; 158 | 159 | if (val && val.constructor === Object && !_.isEmpty(val)) { 160 | //Recursion for embedded objects 161 | var obj2 = objToPaths(val); 162 | 163 | for (var key2 in obj2) { 164 | var val2 = obj2[key2]; 165 | 166 | ret[key + separator + key2] = val2; 167 | } 168 | } else { 169 | ret[key] = val; 170 | } 171 | } 172 | 173 | return ret; 174 | } 175 | 176 | /** 177 | * @param {Object} Object to fetch attribute from 178 | * @param {String} Object path e.g. 'user.name' 179 | * @return {Mixed} 180 | */ 181 | function getNested(obj, path, return_exists) { 182 | var separator = DeepModel.keyPathSeparator; 183 | 184 | var fields = path.split(separator); 185 | var result = obj; 186 | return_exists || (return_exists === false); 187 | for (var i = 0, n = fields.length; i < n; i++) { 188 | if (return_exists && !_.has(result, fields[i])) { 189 | return false; 190 | } 191 | result = result[fields[i]]; 192 | 193 | if (result == null && i < n - 1) { 194 | result = {}; 195 | } 196 | 197 | if (typeof result === 'undefined') { 198 | if (return_exists) { 199 | return true; 200 | } 201 | return result; 202 | } 203 | } 204 | if (return_exists) { 205 | return true; 206 | } 207 | return result; 208 | } 209 | 210 | /** 211 | * @param {Object} obj Object to fetch attribute from 212 | * @param {String} path Object path e.g. 'user.name' 213 | * @param {Object} [options] Options 214 | * @param {Boolean} [options.unset] Whether to delete the value 215 | * @param {Mixed} Value to set 216 | */ 217 | function setNested(obj, path, val, options) { 218 | options = options || {}; 219 | 220 | var separator = DeepModel.keyPathSeparator; 221 | 222 | var fields = path.split(separator); 223 | var result = obj; 224 | for (var i = 0, n = fields.length; i < n && result !== undefined; i++) { 225 | var field = fields[i]; 226 | 227 | //If the last in the path, set the value 228 | if (i === n - 1) { 229 | options.unset ? delete result[field] : result[field] = val; 230 | } else { 231 | //Create the child object if it doesn't exist, or isn't an object 232 | if (typeof result[field] === 'undefined' || !_.isObject(result[field])) { 233 | result[field] = {}; 234 | } 235 | 236 | //Move onto the next part of the path 237 | result = result[field]; 238 | } 239 | } 240 | } 241 | 242 | /** 243 | * @param {Object} obj Object to fetch attribute from 244 | * @param {String} path Object path e.g. 'user.name' 245 | */ 246 | function deleteNested(obj, path) { 247 | setNested(obj, path, null, {unset: true}); 248 | } 249 | 250 | var DeepModel = Backbone.Model.extend({ 251 | 252 | // Override constructor 253 | // Support having nested defaults by using _.deepExtend instead of _.extend 254 | constructor: function (attributes, options) { 255 | var defaults, attrs = attributes || {}; 256 | 257 | if(!attrs.cid || options.duplicate){ 258 | this.cid = _.uniqueId('c'); 259 | } else { 260 | this.cid = attrs.cid; 261 | } 262 | 263 | this.attributes = {}; 264 | if (options && options.collection) this.collection = options.collection; 265 | if (options && options.parse) attrs = this.parse(attrs, options) || {}; 266 | if (defaults = _.result(this, 'defaults')) { 267 | // 268 | // Replaced the call to _.defaults with _.deepExtend. 269 | attrs = _.deepExtend({}, defaults, attrs); 270 | // 271 | } 272 | this.set(attrs, options); 273 | this.changed = {}; 274 | this.initialize.apply(this, arguments); 275 | }, 276 | 277 | // Return a copy of the model's `attributes` object. 278 | toJSON: function (options) { 279 | return _.deepClone(this.attributes); 280 | }, 281 | 282 | // Override get 283 | // Supports nested attributes via the syntax 'obj.attr' e.g. 'author.user.name' 284 | get: function (attr) { 285 | return getNested(this.attributes, attr); 286 | }, 287 | 288 | // Override set 289 | // Supports nested attributes via the syntax 'obj.attr' e.g. 'author.user.name' 290 | set: function (key, val, options) { 291 | var attr, attrs, unset, changes, silent, changing, prev, current; 292 | 293 | if (key == null) return this; 294 | 295 | // Handle both `"key", value` and `{key: value}` -style arguments. 296 | if (typeof key === 'object') { 297 | attrs = key; 298 | options = val || {}; 299 | } else { 300 | (attrs = {})[key] = val; 301 | } 302 | 303 | options || (options = {}); 304 | 305 | // Run validation. 306 | if (!this._validate(attrs, options)) return false; 307 | 308 | // Extract attributes and options. 309 | unset = options.unset; 310 | silent = options.silent; 311 | changes = []; 312 | changing = this._changing; 313 | this._changing = true; 314 | 315 | if (!changing) { 316 | this._previousAttributes = _.deepClone(this.attributes); //: Replaced _.clone with _.deepClone 317 | this.changed = {}; 318 | } 319 | current = this.attributes, prev = this._previousAttributes; 320 | 321 | // Check for changes of `id`. 322 | if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; 323 | 324 | // 325 | attrs = objToPaths(attrs); 326 | // 327 | 328 | // For each `set` attribute, update or delete the current value. 329 | for (attr in attrs) { 330 | val = attrs[attr]; 331 | 332 | //: Using getNested, setNested and deleteNested 333 | if (!_.isEqual(getNested(current, attr), val)) changes.push(attr); 334 | if (!_.isEqual(getNested(prev, attr), val)) { 335 | setNested(this.changed, attr, val); 336 | } else { 337 | deleteNested(this.changed, attr); 338 | } 339 | unset ? deleteNested(current, attr) : setNested(current, attr, val); 340 | // 341 | } 342 | 343 | // Trigger all relevant attribute changes. 344 | if (!silent) { 345 | if (changes.length) this._pending = true; 346 | 347 | // 348 | var separator = DeepModel.keyPathSeparator; 349 | 350 | for (var i = 0, l = changes.length; i < l; i++) { 351 | var key = changes[i]; 352 | 353 | this.trigger('change:' + key, this, getNested(current, key), options); 354 | 355 | var fields = key.split(separator); 356 | 357 | //Trigger change events for parent keys with wildcard (*) notation 358 | for (var n = fields.length - 1; n > 0; n--) { 359 | var parentKey = _.first(fields, n).join(separator), 360 | wildcardKey = parentKey + separator + '*'; 361 | 362 | this.trigger('change:' + wildcardKey, this, getNested(current, parentKey), options); 363 | } 364 | // 365 | } 366 | } 367 | 368 | if (changing) return this; 369 | if (!silent) { 370 | while (this._pending) { 371 | this._pending = false; 372 | this.trigger('change', this, options); 373 | } 374 | } 375 | this._pending = false; 376 | this._changing = false; 377 | return this; 378 | }, 379 | 380 | // Clear all attributes on the model, firing `"change"` unless you choose 381 | // to silence it. 382 | clear: function (options) { 383 | var attrs = {}, shallowAttributes = objToPaths(this.attributes); 384 | 385 | for (var key in shallowAttributes) attrs[key] = void 0; 386 | return this.set(attrs, _.extend({}, options, {unset: true})); 387 | }, 388 | 389 | // Determine if the model has changed since the last `"change"` event. 390 | // If you specify an attribute name, determine if that attribute has changed. 391 | hasChanged: function (attr) { 392 | if (attr == null) return !_.isEmpty(this.changed); 393 | return getNested(this.changed, attr) !== undefined; 394 | }, 395 | 396 | // Return an object containing all the attributes that have changed, or 397 | // false if there are no changed attributes. Useful for determining what 398 | // parts of a view need to be updated and/or what attributes need to be 399 | // persisted to the server. Unset attributes will be set to undefined. 400 | // You can also pass an attributes object to diff against the model, 401 | // determining if there *would be* a change. 402 | changedAttributes: function (diff) { 403 | //: objToPaths 404 | if (!diff) return this.hasChanged() ? objToPaths(this.changed) : false; 405 | // 406 | 407 | var old = this._changing ? this._previousAttributes : this.attributes; 408 | 409 | // 410 | diff = objToPaths(diff); 411 | old = objToPaths(old); 412 | // 413 | 414 | var val, changed = false; 415 | for (var attr in diff) { 416 | if (_.isEqual(old[attr], (val = diff[attr]))) continue; 417 | (changed || (changed = {}))[attr] = val; 418 | } 419 | return changed; 420 | }, 421 | 422 | // Get the previous value of an attribute, recorded at the time the last 423 | // `"change"` event was fired. 424 | previous: function (attr) { 425 | if (attr == null || !this._previousAttributes) return null; 426 | 427 | // 428 | return getNested(this._previousAttributes, attr); 429 | // 430 | }, 431 | 432 | // Get all of the attributes of the model at the time of the previous 433 | // `"change"` event. 434 | previousAttributes: function () { 435 | // 436 | return _.deepClone(this._previousAttributes); 437 | // 438 | } 439 | 440 | }); 441 | 442 | //Config; override in your app to customise 443 | DeepModel.keyPathSeparator = '.'; 444 | 445 | //Exports 446 | Backbone.DeepModel = DeepModel; 447 | 448 | //For use in NodeJS 449 | if (typeof module != 'undefined') module.exports = DeepModel; 450 | 451 | return Backbone; 452 | 453 | })); 454 | -------------------------------------------------------------------------------- /src/admin/assets/formbuilder/locales/es-ES.js: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | var formbuilder_lang = {}; 8 | 9 | formbuilder_lang['Save'] = "Guardar"; 10 | formbuilder_lang['No response fields'] = "Campos sin respuesta"; 11 | formbuilder_lang['Add new field'] = "Agregar nuevo Campo"; 12 | formbuilder_lang['Edit field'] = "Editar Campo"; 13 | formbuilder_lang['Groups'] = "Grupos"; 14 | formbuilder_lang['Field type:'] = "Tipo de Campo:"; 15 | formbuilder_lang['Untitled'] = "Sin titulo"; 16 | formbuilder_lang['default'] = "por defecto"; 17 | formbuilder_lang['Group name'] = "Nombre de Grupo"; 18 | formbuilder_lang['Field label'] = "Etiqueta de Campo"; 19 | formbuilder_lang['Add a longer description to this field'] = "Añadir una descripción más larga a este campo"; 20 | formbuilder_lang['Required'] = "Requerido"; 21 | formbuilder_lang['Read only'] = "Solo lectura"; 22 | formbuilder_lang['Visible'] = "Visible"; 23 | formbuilder_lang['Options'] = "Opciones"; 24 | formbuilder_lang['Include "other"'] = 'Incluir "otro"'; 25 | formbuilder_lang['Add option'] = "Agregar opción"; 26 | formbuilder_lang['Include blank'] = "Incluir blanco"; 27 | formbuilder_lang['Duplicate Field'] = "Duplicar Campo"; 28 | formbuilder_lang['Remove Field'] = "Remover Campo"; 29 | formbuilder_lang['Sort up'] = "Ordenar hacia arriba"; 30 | formbuilder_lang['Sort down'] = "Ordenar hacia abajo"; 31 | formbuilder_lang['Remove Option'] = "Remover Opcion"; 32 | formbuilder_lang['checkbox'] = "checkbox"; 33 | formbuilder_lang['Save form'] = "Guardar formulario"; 34 | formbuilder_lang['All changes saved'] = "Todos los cambios guardados"; 35 | formbuilder_lang['You have unsaved changes. If you leave this page, you will lose those changes!'] = "Tiene cambios no guardados. Si abandona esta página, perderá esos cambios!"; 36 | formbuilder_lang['text'] = "texto"; 37 | formbuilder_lang['Size'] = "Tamaño"; 38 | formbuilder_lang['Small'] = "Pequeño"; 39 | formbuilder_lang['Medium'] = "Mediano"; 40 | formbuilder_lang['Large'] = "Grande"; 41 | formbuilder_lang['Length Limit'] = "Limite de largo"; 42 | formbuilder_lang['Max'] = "Max"; 43 | formbuilder_lang['characters'] = "caracteres"; 44 | formbuilder_lang['words'] = "palabras"; 45 | formbuilder_lang['Min'] = "Min"; 46 | formbuilder_lang['numiric'] = "numerico"; 47 | formbuilder_lang['Integer only'] = "Solamente Integer"; 48 | formbuilder_lang['Only accept integers'] = "Solo acepta Integer"; 49 | formbuilder_lang['Minimum / Maximum'] = "Minimo / Maximo"; 50 | formbuilder_lang['Above'] = "Encima"; 51 | formbuilder_lang['Below'] = "Abajo"; 52 | formbuilder_lang['area'] = "area"; 53 | formbuilder_lang['Attributes'] = "Atributos"; 54 | formbuilder_lang['Rows'] = "Filas"; 55 | formbuilder_lang['Cols'] = "Columnas"; 56 | formbuilder_lang['array'] = "arreglo"; 57 | formbuilder_lang['radio'] = "radio"; 58 | formbuilder_lang['option'] = "opcion"; 59 | formbuilder_lang['Other'] = "Otro"; 60 | formbuilder_lang['Checkboxes'] = 'Checkboxes'; 61 | formbuilder_lang['Dropdown'] = 'Dropdown'; 62 | formbuilder_lang['Json textarea'] = 'JSON area de texto'; 63 | formbuilder_lang['Input numericfield'] = 'Campo numerico'; 64 | formbuilder_lang['Radio'] = 'Radio'; 65 | formbuilder_lang['Input textfield'] = 'Campo de texto'; 66 | formbuilder_lang['Input textarea'] = 'Campo de area de texto'; -------------------------------------------------------------------------------- /src/admin/assets/formbuilder/locales/ru-RU.js: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | var formbuilder_lang = {}; 8 | 9 | formbuilder_lang['Save'] = "Сохранить"; 10 | formbuilder_lang['No response fields'] = "Данные для отображения полей не получены"; 11 | formbuilder_lang['Add new field'] = "Добавление поля"; 12 | formbuilder_lang['Edit field'] = "Редактирование поля"; 13 | formbuilder_lang['Groups'] = "Группы"; 14 | formbuilder_lang['Field type:'] = "Тип поля:"; 15 | formbuilder_lang['Untitled'] = "Без заголовка"; 16 | formbuilder_lang['default'] = "По умолчанию"; 17 | formbuilder_lang['Group name'] = "Название группы"; 18 | formbuilder_lang['Field label'] = "Название поля"; 19 | formbuilder_lang['Add a longer description to this field'] = "Добавить более подробное описание в этой области"; 20 | formbuilder_lang['Required'] = "Обязательное"; 21 | formbuilder_lang['Read only'] = "Только для чтения"; 22 | formbuilder_lang['Visible'] = "Видимое"; 23 | formbuilder_lang['Options'] = "Настройки"; 24 | formbuilder_lang['Include "other"'] = 'Подключить "Прочие"'; 25 | formbuilder_lang['Add option'] = "Добавить опцию"; 26 | formbuilder_lang['Include blank'] = "Добавить пустой"; 27 | formbuilder_lang['Duplicate Field'] = "Дублировать поле"; 28 | formbuilder_lang['Remove Field'] = "Удалить поле"; 29 | formbuilder_lang['Sort up'] = "Вверх"; 30 | formbuilder_lang['Sort down'] = "Вниз"; 31 | formbuilder_lang['Remove Option'] = "Удалить опцию"; 32 | formbuilder_lang['checkbox'] = "Чекбокс"; 33 | formbuilder_lang['Save form'] = "Сохранить"; 34 | formbuilder_lang['All changes saved'] = "Все сохранено"; 35 | formbuilder_lang['You have unsaved changes. If you leave this page, you will lose those changes!'] = "У вас есть несохраненные изменения. Если вы покинете эту страницу, то потеряете эти изменения!"; 36 | formbuilder_lang['text'] = "Текстовое поле"; 37 | formbuilder_lang['Size'] = "Размер"; 38 | formbuilder_lang['Small'] = "Маленький"; 39 | formbuilder_lang['Medium'] = "Средний"; 40 | formbuilder_lang['Large'] = "Большой"; 41 | formbuilder_lang['Length Limit'] = "Ограничение длины"; 42 | formbuilder_lang['Max'] = "Макс"; 43 | formbuilder_lang['characters'] = "символов"; 44 | formbuilder_lang['words'] = "слов"; 45 | formbuilder_lang['Min'] = "Мин"; 46 | formbuilder_lang['numiric'] = "Цифровое поле"; 47 | formbuilder_lang['Integer only'] = "Только целые числа"; 48 | formbuilder_lang['Only accept integers'] = "Принимать только целые числа"; 49 | formbuilder_lang['Minimum / Maximum'] = "Минимум / Максимум"; 50 | formbuilder_lang['Above'] = "Больше"; 51 | formbuilder_lang['Below'] = "Меньше"; 52 | formbuilder_lang['area'] = "Текстовое поле"; 53 | formbuilder_lang['Attributes'] = "Атрибуты"; 54 | formbuilder_lang['Rows'] = "Строки"; 55 | formbuilder_lang['Cols'] = "Столбцы"; 56 | formbuilder_lang['array'] = "Json"; 57 | formbuilder_lang['radio'] = "Список"; 58 | formbuilder_lang['option'] = "Выпадающий список"; 59 | formbuilder_lang['Other'] = "Прочее"; 60 | formbuilder_lang['Checkboxes'] = 'Чекбоксы'; 61 | formbuilder_lang['Dropdown'] = 'Выпадающий список'; 62 | formbuilder_lang['Json textarea'] = 'JSON Текстовое поле'; 63 | formbuilder_lang['Input numericfield'] = 'Цифровое поле'; 64 | formbuilder_lang['Radio'] = 'Список с выбором'; 65 | formbuilder_lang['Input textfield'] = 'Текстовая строка'; 66 | formbuilder_lang['Input textarea'] = 'Текстовое поле'; -------------------------------------------------------------------------------- /src/admin/controllers/AjaxController.php: -------------------------------------------------------------------------------- 1 | response->format = 'json'; 23 | 24 | $status = 'false'; 25 | 26 | $attribuites = []; 27 | 28 | $types = EavAttributeType::find()->all(); 29 | 30 | if ($types) { 31 | foreach ($types as $type) { 32 | $attribuites[$type->name] = $type->attributes; 33 | $attribuites[$type->name]['formBuilder'] = $type->formBuilder; 34 | } 35 | 36 | $status = 'success'; 37 | } 38 | 39 | return ['status' => $status, 'types' => $attribuites]; 40 | } 41 | 42 | public function actionSave() 43 | { 44 | if (Yii::$app->request->isPost) { 45 | $post = Yii::$app->request->post(); 46 | 47 | if ($post['payload'] && $post['entityModel']) { 48 | $payload = Json::decode($post['payload']); 49 | 50 | if (!isset($payload['fields'])) { 51 | return; 52 | } 53 | 54 | $transaction = \Yii::$app->db->beginTransaction(); 55 | 56 | try { 57 | $categoryId = isset($post['categoryId']) ? $post['categoryId'] : 0; 58 | 59 | $entityId = EavEntity::find()->select(['id'])->where( 60 | [ 61 | 'entityModel' => $post['entityModel'], 62 | 'categoryId' => $categoryId, 63 | ] 64 | )->scalar(); 65 | 66 | if (!$entityId) { 67 | $entity = new EavEntity; 68 | $entity->entityName = isset($post['entityName']) ? $post['entityName'] : 'Untitled'; 69 | $entity->entityModel = $post['entityModel']; 70 | $entity->categoryId = $categoryId; 71 | $entity->save(false); 72 | $entityId = $entity->id; 73 | } 74 | 75 | $attributes = []; 76 | 77 | foreach ($payload['fields'] as $order => $field) { 78 | if (!isset($field['cid'])) { 79 | continue; 80 | } 81 | 82 | // Attribute 83 | if (isset($field['cid'])) { 84 | $attribute = EavAttribute::findOne(['name' => $field['cid'], 'entityId' => $entityId]); 85 | } 86 | 87 | // Create new attribute 88 | if (empty($attribute)) { 89 | $attribute = new EavAttribute; 90 | $lastId = EavAttribute::find() 91 | ->select(['id']) 92 | ->orderBy(['id' => SORT_DESC]) 93 | ->limit(1) 94 | ->scalar() + 1; 95 | $attribute->name = 'c' . $lastId; 96 | } 97 | 98 | $attribute->type = $field['group_name']; 99 | $attribute->entityId = $entityId; 100 | $attribute->typeId = EavAttributeType::find()->select(['id'])->where( 101 | ['name' => $field['field_type']] 102 | )->scalar(); 103 | $attribute->label = $field['label']; 104 | $attribute->order = $order; 105 | 106 | if (isset($field['field_options']['description'])) { 107 | $attribute->description = $field['field_options']['description']; 108 | unset($field['field_options']['description']); 109 | } else { 110 | $attribute->description = ''; 111 | } 112 | 113 | $attribute->save(false); 114 | 115 | 116 | $attributes[] = $attribute->id; 117 | 118 | if (isset($field['field_options']['options'])) { 119 | $options = []; 120 | 121 | foreach ($field['field_options']['options'] as $k => $o) { 122 | $option = EavAttributeOption::find() 123 | ->where( 124 | [ 125 | 'attributeId' => $attribute->id, 126 | 'value' => $o['label'], 127 | ] 128 | ) 129 | ->one(); 130 | 131 | if (!$option) { 132 | $option = new EavAttributeOption; 133 | } 134 | $option->attributeId = $attribute->id; 135 | $option->value = $o['label']; 136 | $option->defaultOptionId = (int)$o['checked']; 137 | $option->order = $k; 138 | $option->save(); 139 | 140 | $options[] = $option->value; 141 | } 142 | 143 | EavAttributeOption::deleteAll( 144 | [ 145 | 'and', 146 | ['attributeId' => $attribute->id], 147 | ['NOT', ['IN', 'value', $options]], 148 | ] 149 | ); 150 | 151 | unset($field['field_options']['options']); 152 | } 153 | 154 | // Rule 155 | $rule = EavAttributeRule::find()->where(['attributeId' => $attribute->id])->one(); 156 | if (!$rule) { 157 | $rule = new EavAttributeRule(); 158 | } 159 | $rule->attributeId = $attribute->id; 160 | if (!empty($field['field_options'])) { 161 | foreach ($field['field_options'] as $key => $param) { 162 | $rule->{$key} = $param; 163 | } 164 | } 165 | $rule->required = isset($field['field_options']['required']) ? 166 | (int)$field['field_options']['required'] : 0; 167 | $rule->visible = isset($field['field_options']['visible']) ? 168 | (int)$field['field_options']['visible'] : 0; 169 | $rule->locked = isset($field['field_options']['locked']) ? 170 | (int)$field['field_options']['locked'] : 0; 171 | 172 | $rule->save(); 173 | } 174 | 175 | $attrArray = EavAttribute::find() 176 | ->select('id') 177 | ->where(['AND', ['NOT IN', 'id', $attributes], ['IN', 'entityId', $entityId]]) 178 | ->asArray() 179 | ->column(); 180 | 181 | if ($attrArray) { 182 | EavAttributeValue::deleteAll(['IN', 'attributeId', $attrArray]); 183 | EavAttributeOption::deleteAll(['IN', 'attributeId', $attrArray]); 184 | EavAttributeRule::deleteAll(['IN', 'attributeId', $attrArray]); 185 | EavAttribute::deleteAll(['IN', 'id', $attrArray]); 186 | 187 | // Delete entity if there are no attributes 188 | if (!EavAttribute::find() 189 | ->select('id') 190 | ->where(['IN', 'entityId', $entityId]) 191 | ->exists()) { 192 | EavEntity::deleteAll(['IN', 'id', $entityId]); 193 | } 194 | } 195 | 196 | 197 | $transaction->commit(); 198 | } catch (\Exception $e) { 199 | $transaction->rollBack(); 200 | throw $e; 201 | } 202 | } 203 | } 204 | } 205 | 206 | } 207 | -------------------------------------------------------------------------------- /src/admin/widgets/Fields.php: -------------------------------------------------------------------------------- 1 | url = Url::toRoute($this->url); 29 | 30 | $this->urlSave = Url::toRoute($this->urlSave); 31 | 32 | $this->entityModel = str_replace('\\', '\\\\', $this->entityModel); 33 | 34 | $attributes = $this->model->getEavAttributes() 35 | ->joinWith('entity') 36 | ->joinWith('attributeRule') 37 | ->all(); 38 | 39 | /** @var EavAttribute $attribute */ 40 | foreach ($attributes as $attribute) { 41 | $options = ArrayHelper::merge( 42 | [ 43 | 'description' => $attribute->description, 44 | 'required' => (bool)$attribute->required, 45 | 'visible' => (bool)$attribute->attributeRule->visible, 46 | 'locked' => (bool)$attribute->attributeRule->locked, 47 | ], 48 | is_null($attribute->attributeRule->rules) 49 | ? [] 50 | : json_decode($attribute->attributeRule->rules) 51 | ); 52 | 53 | foreach ($attribute->eavOptions as $option) { 54 | $options['options'][] = [ 55 | 'label' => $option->value, 56 | 'id' => $option->id, 57 | 'checked' => (bool)$option->defaultOptionId, 58 | ]; 59 | } 60 | 61 | $this->bootstrapData[] = [ 62 | 'group_name' => $attribute->type, 63 | 'label' => $attribute->label, 64 | 'field_type' => $attribute->eavType->name, 65 | 'field_options' => $options, 66 | 'cid' => $attribute->name, 67 | ]; 68 | } 69 | 70 | $this->bootstrapData = Json::encode($this->bootstrapData); 71 | } 72 | 73 | public function run() 74 | { 75 | return $this->render( 76 | 'fields', 77 | [ 78 | 'url' => $this->url, 79 | 'urlSave' => $this->urlSave, 80 | 'categoryId' => isset($this->categoryId) ? $this->categoryId : 0, 81 | 'entityModel' => $this->entityModel, 82 | 'entityName' => $this->entityName, 83 | 'bootstrapData' => $this->bootstrapData, 84 | ] 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/admin/widgets/views/fields.php: -------------------------------------------------------------------------------- 1 | 12 | 13 |

14 |
15 | registerJs($js_form_builder, View::POS_READY, 'js_form_builder'); 40 | if (file_exists(Yii::getAlias('@mirocow/eav') . '/admin/assets/formbuilder/locales/' . Yii::$app->language . '.js')) { 41 | $this->registerJsFile($path->baseUrl . '/locales/' . Yii::$app->language . '.js', [View::POS_READY]); 42 | } 43 | ?> 44 | -------------------------------------------------------------------------------- /src/handlers/ArrayValueHandler.php: -------------------------------------------------------------------------------- 1 | 4 | */ 5 | 6 | namespace mirocow\eav\handlers; 7 | 8 | use yii\helpers\Json; 9 | 10 | /** 11 | * Class RawValueHandler 12 | * 13 | * @package mirocow\eav 14 | */ 15 | class ArrayValueHandler extends RawValueHandler 16 | { 17 | /** 18 | * @inheritdoc 19 | */ 20 | public function load() 21 | { 22 | $value = parent::load(); 23 | 24 | return json_decode($value, true); 25 | } 26 | 27 | /** 28 | * @inheritdoc 29 | */ 30 | public function defaultValue() 31 | { 32 | $type = $this->attributeHandler->attributeModel->eavType; 33 | 34 | return false; 35 | } 36 | 37 | /** 38 | * @inheritdoc 39 | */ 40 | public function save() 41 | { 42 | $EavModel = $this->attributeHandler->owner; 43 | $valueModel = $this->getValueModel(); 44 | $attribute = $this->attributeHandler->getAttributeName(); 45 | 46 | if (isset($EavModel->attributes[$attribute])) { 47 | $value = $EavModel->attributes[$attribute]; 48 | $valueModel->value = json_encode($value); 49 | 50 | if (!$valueModel->save()) { 51 | throw new \Exception("Can't save value model"); 52 | } 53 | } 54 | } 55 | 56 | public function getTextValue() 57 | { 58 | return json_encode(parent::getTextValue()); 59 | } 60 | 61 | public function getArrayValue() 62 | { 63 | return json_decode(parent::getTextValue()); 64 | } 65 | 66 | public function addRules() 67 | { 68 | $model = &$this->attributeHandler->owner; 69 | $attribute = &$this->attributeHandler->attributeModel; 70 | $attribute_name = $this->attributeHandler->getAttributeName(); 71 | 72 | if ($attribute->eavType->storeType == ValueHandler::STORE_TYPE_ARRAY) { 73 | $model->addRule($attribute_name, 'string'); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/handlers/MultipleOptionsValueHandler.php: -------------------------------------------------------------------------------- 1 | 4 | */ 5 | 6 | namespace mirocow\eav\handlers; 7 | 8 | use yii\db\ActiveRecord; 9 | use yii\helpers\ArrayHelper; 10 | 11 | /** 12 | * Class MultipleOptionsValueHandler 13 | * 14 | * @package mirocow\eav 15 | */ 16 | class MultipleOptionsValueHandler extends ValueHandler 17 | { 18 | /** @var AttributeHandler */ 19 | public $attributeHandler; 20 | 21 | public function load() 22 | { 23 | $EavModel = $this->attributeHandler->owner; 24 | 25 | /** @var ActiveRecord $valueClass */ 26 | $valueClass = $EavModel->valueClass; 27 | 28 | $models = $valueClass::findAll( 29 | [ 30 | 'entityId' => $EavModel->entityModel->getPrimaryKey(), 31 | 'attributeId' => $this->attributeHandler->attributeModel->getPrimaryKey(), 32 | ] 33 | ); 34 | 35 | $values = ArrayHelper::getColumn( 36 | $models, 37 | function ($element) { 38 | return $element->optionId; 39 | } 40 | ); 41 | 42 | return $values; 43 | } 44 | 45 | /** 46 | * @inheritdoc 47 | */ 48 | public function defaultValue() 49 | { 50 | $defaultOptions = []; 51 | 52 | foreach ($this->attributeHandler->attributeModel->eavOptions as $option) { 53 | if ($option->defaultOptionId === 1) { 54 | $defaultOptions[] = $option->id; 55 | } 56 | } 57 | 58 | return $defaultOptions; 59 | } 60 | 61 | public function save() 62 | { 63 | $EavModel = $this->attributeHandler->owner; 64 | $attribute = $this->attributeHandler->getAttributeName(); 65 | /** @var ActiveRecord $valueClass */ 66 | $valueClass = $EavModel->valueClass; 67 | 68 | $baseQuery = $valueClass::find()->where( 69 | [ 70 | 'entityId' => $EavModel->entityModel->getPrimaryKey(), 71 | 'attributeId' => $this->attributeHandler->attributeModel->getPrimaryKey(), 72 | ] 73 | ); 74 | 75 | $allOptions = ArrayHelper::getColumn( 76 | $this->attributeHandler->attributeModel->eavOptions, 77 | function ($element) { 78 | return $element->getPrimaryKey(); 79 | } 80 | ); 81 | 82 | $query = clone $baseQuery; 83 | $query->andWhere(['NOT IN', 'optionId', $allOptions]); 84 | $valueClass::deleteAll($query->where); 85 | 86 | // then we delete unselected options 87 | $selectedOptions = ArrayHelper::getValue($EavModel->attributes, $attribute); 88 | if (!is_array($selectedOptions)) { 89 | $selectedOptions = []; 90 | } 91 | $deleteOptions = array_diff($allOptions, $selectedOptions); 92 | 93 | $query = clone $baseQuery; 94 | $query->andWhere(['IN', 'optionId', $deleteOptions]); 95 | 96 | $valueClass::deleteAll($query->where); 97 | 98 | // third we insert missing options 99 | foreach ($selectedOptions as $id) { 100 | $query = clone $baseQuery; 101 | $query->andWhere(['optionId' => $id]); 102 | 103 | $valueModel = $query->one(); 104 | 105 | if (!$valueModel instanceof ActiveRecord) { 106 | /** @var ActiveRecord $valueModel */ 107 | $valueModel = new $valueClass; 108 | $valueModel->entityId = $EavModel->entityModel->getPrimaryKey(); 109 | $valueModel->attributeId = $this->attributeHandler->attributeModel->getPrimaryKey(); 110 | $valueModel->optionId = $id; 111 | if (!$valueModel->save()) { 112 | throw new \Exception("Can't save value model"); 113 | } 114 | } 115 | } 116 | } 117 | 118 | public function getTextValue() 119 | { 120 | $EavModel = $this->attributeHandler->owner; 121 | 122 | /** @var ActiveRecord $valueClass */ 123 | $valueClass = $EavModel->valueClass; 124 | 125 | $models = $valueClass::findAll( 126 | [ 127 | 'entityId' => $EavModel->entityModel->getPrimaryKey(), 128 | 'attributeId' => $this->attributeHandler->attributeModel->getPrimaryKey(), 129 | ] 130 | ); 131 | 132 | $values = []; 133 | foreach ($models as $model) { 134 | $values[] = $model->option->value; 135 | } 136 | 137 | return implode(', ', $values); 138 | } 139 | 140 | public function addRules() 141 | { 142 | $model = &$this->attributeHandler->owner; 143 | $attribute = &$this->attributeHandler->attributeModel; 144 | $attribute_name = $this->attributeHandler->getAttributeName(); 145 | } 146 | } -------------------------------------------------------------------------------- /src/handlers/OptionValueHandler.php: -------------------------------------------------------------------------------- 1 | 4 | */ 5 | 6 | namespace mirocow\eav\handlers; 7 | 8 | /** 9 | * Class OptionValueHandler 10 | * 11 | * @package mirocow\eav 12 | */ 13 | class OptionValueHandler extends ValueHandler 14 | { 15 | /** 16 | * @inheritdoc 17 | */ 18 | public function load() 19 | { 20 | $valueModel = $this->getValueModel(); 21 | 22 | return $valueModel->optionId; 23 | } 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public function save() 29 | { 30 | $EavModel = $this->attributeHandler->owner; 31 | $valueModel = $this->getValueModel(); 32 | $attribute = $this->attributeHandler->getAttributeName(); 33 | 34 | if (isset($EavModel->attributes[$attribute])) { 35 | $valueModel->optionId = $EavModel->attributes[$attribute]; 36 | 37 | // Save empty value 38 | if (!$valueModel->optionId) { 39 | $valueModel->optionId = null; 40 | } 41 | 42 | if (!$valueModel->save()) { 43 | throw new \Exception("Can't save value model"); 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * @inheritdoc 50 | */ 51 | public function defaultValue() 52 | { 53 | $defaultOptions = []; 54 | 55 | foreach ($this->attributeHandler->attributeModel->eavOptions as $option) { 56 | if ($option->defaultOptionId === 1) { 57 | $defaultOptions[] = $option->id; 58 | } 59 | } 60 | 61 | return $defaultOptions; 62 | } 63 | 64 | public function getTextValue() 65 | { 66 | return $this->getValueModel()->option->value; 67 | } 68 | 69 | public function addRules() 70 | { 71 | $model = &$this->attributeHandler->owner; 72 | $attribute = &$this->attributeHandler->attributeModel; 73 | $attribute_name = $this->attributeHandler->getAttributeName(); 74 | 75 | if ($attribute->eavType->storeType == ValueHandler::STORE_TYPE_OPTION) { 76 | $model->addRule($attribute_name, 'default', ['value' => $attribute->defaultOptionId]); 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /src/handlers/RawValueHandler.php: -------------------------------------------------------------------------------- 1 | 4 | */ 5 | 6 | namespace mirocow\eav\handlers; 7 | 8 | /** 9 | * Class RawValueHandler 10 | * 11 | * @package mirocow\eav 12 | */ 13 | class RawValueHandler extends ValueHandler 14 | { 15 | /** 16 | * @inheritdoc 17 | */ 18 | public function load() 19 | { 20 | $valueModel = $this->getValueModel(); 21 | 22 | return $valueModel->value; 23 | } 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public function defaultValue() 29 | { 30 | return null; 31 | } 32 | 33 | /** 34 | * @inheritdoc 35 | */ 36 | public function save() 37 | { 38 | $EavModel = $this->attributeHandler->owner; 39 | $valueModel = $this->getValueModel(); 40 | $attribute = $this->attributeHandler->getAttributeName(); 41 | 42 | if (isset($EavModel->attributes[$attribute])) { 43 | $valueModel->value = $EavModel->attributes[$attribute]; 44 | if (!$valueModel->save()) { 45 | throw new \Exception("Can't save value model"); 46 | } 47 | } 48 | } 49 | 50 | public function getTextValue() 51 | { 52 | return $this->getValueModel()->value; 53 | } 54 | 55 | public function addRules() 56 | { 57 | $model = &$this->attributeHandler->owner; 58 | $attribute = &$this->attributeHandler->attributeModel; 59 | $attribute_name = $this->attributeHandler->getAttributeName(); 60 | 61 | if ($attribute->eavType->storeType == ValueHandler::STORE_TYPE_RAW) { 62 | $model->addRule($attribute_name, 'default', ['value' => $attribute->defaultValue]); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/handlers/ValueHandler.php: -------------------------------------------------------------------------------- 1 | 4 | */ 5 | 6 | namespace mirocow\eav\handlers; 7 | 8 | use mirocow\eav\widgets\AttributeHandler; 9 | use yii\db\ActiveRecord; 10 | 11 | /** 12 | * Class ValueHandler 13 | * 14 | * @package mirocow\eav 15 | * @property ActiveRecord $valueModel 16 | * @property string $textValue 17 | */ 18 | abstract class ValueHandler 19 | { 20 | const STORE_TYPE_RAW = 0; 21 | const STORE_TYPE_OPTION = 1; 22 | const STORE_TYPE_MULTIPLE_OPTIONS = 2; 23 | const STORE_TYPE_ARRAY = 3; // Json encoded 24 | 25 | /** @var AttributeHandler */ 26 | public $attributeHandler; 27 | 28 | /** 29 | * @return ActiveRecord 30 | * @throws \Exception 31 | * @throws \yii\base\InvalidConfigException 32 | */ 33 | public function getValueModel() 34 | { 35 | $EavModel = $this->attributeHandler->owner; 36 | 37 | /** @var ActiveRecord $valueClass */ 38 | $valueClass = $EavModel->valueClass; 39 | 40 | $valueModel = $valueClass::findOne( 41 | [ 42 | 'entityId' => $EavModel->entityModel->getPrimaryKey(), 43 | 'attributeId' => $this->attributeHandler->attributeModel->getPrimaryKey(), 44 | ] 45 | ); 46 | 47 | if (!$valueModel instanceof ActiveRecord) { 48 | /** @var ActiveRecord $valueModel */ 49 | $valueModel = new $valueClass; 50 | $valueModel->entityId = $EavModel->entityModel->getPrimaryKey(); 51 | $valueModel->attributeId = $this->attributeHandler->attributeModel->getPrimaryKey(); 52 | } 53 | 54 | return $valueModel; 55 | } 56 | 57 | abstract public function defaultValue(); 58 | 59 | abstract public function load(); 60 | 61 | abstract public function save(); 62 | 63 | abstract public function getTextValue(); 64 | 65 | // TODO 7: Add rules from $attributeModel->getEavOptions() 66 | abstract public function addRules(); 67 | } -------------------------------------------------------------------------------- /src/messages/es/eav.php: -------------------------------------------------------------------------------- 1 | 'Constructor de campos EAV', 5 | 'Descriptions' => 'Descripciones', 6 | 'Route' => 'Ruta', 7 | 'Operations' => 'Operaciones', 8 | 'Fields constructor' => 'Constructor de Campos', 9 | 'Options constructor' => 'Constructor de Opciones', 10 | 'Types constructor' => 'Constructor de Tipos', 11 | 'Entities constructor' => 'Constructor de Entidades', 12 | 'Values constructor' => 'Constructor de Valores', 13 | 'Create' => 'Crear', 14 | 'Update' => 'Actualizar', 15 | 'Delete' => 'Eliminar', 16 | 'List' => 'Listar', 17 | 18 | 'Type ID' => 'Id de Tipo', 19 | 'Type' => 'Tipo', 20 | 'Name' => 'Nombre', 21 | 'Label' => 'Etiqueta', 22 | 'Default Value' => 'Valor por defecto', 23 | 'Default Option ID' => 'Id de Opción por defecto', 24 | 'Required' => 'Requerido', 25 | 'Order' => 'Orden', 26 | 'Description' => 'Descripcion', 27 | 28 | 'Sort order' => 'orden de clasificación', 29 | 'Value' => 'Valor', 30 | 'Default option Id' => 'Id de Opción por defecto', 31 | 32 | 'Rules' => 'Reglas', 33 | 'Locked' => 'Bloqueado', 34 | 'Visible' => 'Visible', 35 | 'Attribute name' => 'Nombre de Atributo', 36 | 37 | 'Handler Class' => 'Clase Manejadora', 38 | 'Store Type' => 'Tipo de Almacenado', 39 | 40 | 'Entity ID' => 'Id de Entidad', 41 | 'Attribute ID' => 'Id de Atributo', 42 | 'Option ID' => 'Id de Opción', 43 | 44 | 'Entity Model' => 'Modelo Entidad', 45 | 'ID Category' => 'Id de Categoria', 46 | 'ID Model' => 'Id de Modelo', 47 | 48 | 'Dynamic model data were no attributes.' => 'datos de los modelos dinámicos sin atributos', 49 | 'Dynamic model data were not save due to validation error.' => 'datos de los modelos dinámicos no se guardaron debido a un error de validación.', 50 | 'The description of field' => 'Descripcion del campo', 51 | 52 | 'The name of field' => 'El nombre del campo', 53 | 'The label of field' => 'El etiqueta del Campo', 54 | 'Entity name' => 'Nombre de Entidad', 55 | 'The default value of field' => 'El valor por defecto del Campo', 56 | 57 | 'Search' => 'Buscar', 58 | 'Reset' => 'Reiniciar', 59 | 60 | 'EAV' => 'EAV', 61 | 'Fields' => 'Campos', 62 | 'Create Eav Attribute' => 'Crear Atibuto EAV', 63 | 'Update Eav Attribute' => 'Actualizar Atributo EAV', 64 | 'The type of entity' => 'El tipo de la Entidad', 65 | 66 | 67 | 'Are you sure you want to delete this item?' => '¿Está seguro de que desea eliminar este item?', 68 | 69 | 'The name of entity' => 'El nombre de la Entidad', 70 | 'This is the ActiveRecord class associated with the table that EavEntity will be built upon. 71 | You should provide a fully qualified class name, e.g., app\models\Post.' => 72 | 'Esta es la clase ActiveRecord asociado a la tabla que EavEntity será creada. 73 |                  Debe proporcionar un nombre de clase completo, por ejemplo, app\models\Post.', 74 | 'This category id to bind to the directory. The default is not used and may be 0.' => 'Este identificador de categoría se una al directorio. El valor por defecto no se utiliza y puede ser 0.', 75 | 76 | 'Create Eav Entity' => 'Crear Entidad EAV', 77 | 'Eav Entities' => 'Entidades EAV', 78 | 'Update Eav Entity' => 'Actualizar Entidad EAV', 79 | 80 | 'Create Eav Attribute Option' => 'Crear Opcion de Atributo EAV', 81 | 'Eav Attribute Options' => 'Opciones de Atributo EAV', 82 | 'Update Eav Attribute Option' => 'Actualizar Opcion de Atributo EAV', 83 | 84 | 'Create Eav Attribute Type' => 'Crear Tipo de Atributo EAV', 85 | 'Eav Attribute Types' => 'Tipos de Atributo EAV', 86 | 'Update Eav Attribute Type' => 'Actualizar Tipo de Atributo EAV', 87 | 88 | 'Create Eav Attribute Value' => 'Crear Valor de Atributo EAV', 89 | 'Eav Attribute Values' => 'Valores de Atributo EAV', 90 | 'Update Eav Attribute Value' => 'Actualizar Valor de Atributo EAV', 91 | 92 | 'Attribute name must contain latin word characters only.' => 'Nombre del atributo debe contener solamente caracteres de palabras latinas.', 93 | 'Type name must contain latin word characters only.' => 'Nombre del tipo debe contener solamente caracteres de palabras latinas.', 94 | 95 | 'Checkboxes' => 'Checkboxes', 96 | 'Dropdown' => 'Lista Desplegable', 97 | 'Json textarea' => 'Area de Texto JSON', 98 | 'Input numericfield' => 'Ingreso de campo numerico', 99 | 'Radio' => 'Radio', 100 | 'Input textfield' => 'Ingreso de campo texto', 101 | 'Input textarea' => 'Ingreso de area de texto', 102 | 103 | 'Attribute {name} not found' => 'No existe Atributo {name}', 104 | ]; 105 | -------------------------------------------------------------------------------- /src/messages/ru/eav.php: -------------------------------------------------------------------------------- 1 | 'Конструктор EAV-атрибутов', 5 | 'Descriptions' => 'Описания', 6 | 'Route' => 'Маршрут', 7 | 'Operations' => 'Действия', 8 | 'Fields constructor' => 'Конструктор атрибутов', 9 | 'Options constructor' => 'Конструктор параметров', 10 | 'Types constructor' => 'Конструктор типов', 11 | 'Entities constructor' => 'Конструктор сущностей', 12 | 'Values constructor' => 'Конструктор значений', 13 | 'Create' => 'Создать', 14 | 'Update' => 'Редактировать', 15 | 'Delete' => 'Удалить', 16 | 'List' => 'Список', 17 | 18 | 'Type ID' => 'Идентификатор типа', 19 | 'Type' => 'Тип', 20 | 'Name' => 'Имя', 21 | 'Label' => 'Метка', 22 | 'Default Value' => 'Значение по умолчанию', 23 | 'Default Option ID' => 'Идентификатор параметра по умолчанию', 24 | 'Required' => 'Обязательный', 25 | 'Order' => 'Порядок', 26 | 'Description' => 'Описание', 27 | 28 | 'Sort order' => 'Порядок сортировки', 29 | 'Value' => 'Значение', 30 | 'Default option Id' => 'Идентификатор параметра по умолчанию', 31 | 32 | 'Rules' => 'Правила', 33 | 'Locked' => 'Заблокирован', 34 | 'Visible' => 'Видимый', 35 | 'Attribute name' => 'Название атрибута', 36 | 37 | 'Handler Class' => 'Класс-обработчик', 38 | 'Store Type' => 'Тип хранилища', 39 | 40 | 'Entity ID' => 'Идентификатор сущности', 41 | 'Attribute ID' => 'Идентификатор атрибута', 42 | 'Option ID' => 'Идентификатор параметра', 43 | 44 | 'Entity Model' => 'Модель сущности', 45 | 'ID Category' => 'Идентификатор категории', 46 | 'ID Model' => 'Идентификатор модели', 47 | 48 | 'Dynamic model data were no attributes.' => 'В модели нет EAV-атрибутов', 49 | 'Dynamic model data were not save due to validation error.' => 'EAV-атрибуты не были сохранены из-за ошибки валидации.', 50 | 'The description of field' => 'Описание атрибута', 51 | 52 | 'The name of field' => 'Название атрибута', 53 | 'The label of field' => 'Метка атрибута', 54 | 'Entity name' => 'Название сущности', 55 | 'The default value of field' => 'Значение атрибута по умолчанию', 56 | 57 | 'Search' => 'Поиск', 58 | 'Reset' => 'Очистить', 59 | 60 | 'EAV' => 'EAV', 61 | 'Fields' => 'Атрибуты', 62 | 'Create Eav Attribute' => 'Создание EAV-атрибута', 63 | 'Update Eav Attribute' => 'Редактирование EAV-атрибута', 64 | 'The type of entity' => 'Тип атрибута', 65 | 66 | 67 | 'Are you sure you want to delete this item?' => 'Вы уверены, что хотите удалить этот элемент?', 68 | 69 | 'The name of entity' => 'Имя сущности', 70 | 'This is the ActiveRecord class associated with the table that EavEntity will be built upon. 71 | You should provide a fully qualified class name, e.g., app\models\Post.' => 72 | 'Это класс ActiveRecord, связанный с таблицей, для которого создается сущность. Вы должны указать полное имя класса, например, app\models\Post.', 73 | 'This category id to bind to the directory. The default is not used and may be 0.' => 'Это идентификатор категории для объединения сущностей. По умолчанию не используется и может быть равен 0.', 74 | 75 | 'Create Eav Entity' => 'Создание EAV-сущности', 76 | 'Eav Entities' => 'EAV-сущности', 77 | 'Update Eav Entity' => 'Редактирование EAV-сущности', 78 | 79 | 'Create Eav Attribute Option' => 'Создание параметра EAV-атрибута', 80 | 'Eav Attribute Options' => 'Параметры EAV-атрибута', 81 | 'Update Eav Attribute Option' => 'Редактирование параметра EAV-атрибута', 82 | 83 | 'Create Eav Attribute Type' => 'Создание типа EAV-атрибута', 84 | 'Eav Attribute Types' => 'Типы EAV-атрибутов', 85 | 'Update Eav Attribute Type' => 'Редактирование типа EAV-атрибута', 86 | 87 | 'Create Eav Attribute Value' => 'Создание значения EAV-атрибута', 88 | 'Eav Attribute Values' => 'Значения EAV-атрибутов', 89 | 'Update Eav Attribute Value' => 'Редактирование значения EAV-атрибута', 90 | 91 | 'Attribute name must contain latin word characters only.' => 'Имя атрибута должно содержать только латинские буквы.', 92 | 'Type name must contain latin word characters only.' => 'Имя атрибута должно содержать только латинские буквы.', 93 | 94 | 'Checkboxes' => 'Чекбоксы', 95 | 'Dropdown' => 'Выпадающий список', 96 | 'Json textarea' => 'JSON Текствое поле', 97 | 'Input numericfield' => 'Цифровое поле', 98 | 'Radio' => 'Список с выбором', 99 | 'Input textfield' => 'Текстовая строка', 100 | 'Input textarea' => 'Текстовое поле', 101 | 102 | 'Attribute {name} not found' => 'Attribute {name} not found', 103 | ]; 104 | -------------------------------------------------------------------------------- /src/migrations/m150821_133232_init.php: -------------------------------------------------------------------------------- 1 | tables = [ 14 | 'entity' => "{{%eav_entity}}", 15 | 'attribute' => "{{%eav_attribute}}", 16 | 'attribute_type' => "{{%eav_attribute_type}}", 17 | 'value' => "{{%eav_attribute_value}}", 18 | 'option' => "{{%eav_attribute_option}}", 19 | ]; 20 | 21 | $this->attributeTypes = [ 22 | [ 23 | 'name' => 'text', 24 | 'storeType' => ValueHandler::STORE_TYPE_RAW, 25 | 'handlerClass' => '\mirocow\eav\widgets\TextInput', 26 | ], 27 | [ 28 | 'name' => 'option', 29 | 'storeType' => ValueHandler::STORE_TYPE_OPTION, 30 | 'handlerClass' => '\mirocow\eav\widgets\DropDownList', 31 | ], 32 | [ 33 | 'name' => 'checkbox', 34 | 'storeType' => ValueHandler::STORE_TYPE_MULTIPLE_OPTIONS, 35 | 'handlerClass' => '\mirocow\eav\widgets\CheckBoxList', 36 | ], 37 | [ 38 | 'name' => 'array', 39 | 'storeType' => ValueHandler::STORE_TYPE_ARRAY, 40 | 'handlerClass' => '\mirocow\eav\widgets\EncodedTextInput', 41 | ], 42 | [ 43 | 'name' => 'radio', 44 | 'storeType' => ValueHandler::STORE_TYPE_OPTION, 45 | 'handlerClass' => '\mirocow\eav\widgets\RadioList', 46 | ], 47 | [ 48 | 'name' => 'area', 49 | 'storeType' => ValueHandler::STORE_TYPE_RAW, 50 | 'handlerClass' => '\mirocow\eav\widgets\Textarea', 51 | ], 52 | ]; 53 | } 54 | 55 | public function safeUp() 56 | { 57 | $options = $this->db->driverName == 'mysql' 58 | ? 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB' 59 | : null; 60 | 61 | $this->createTable( 62 | $this->tables['entity'], 63 | [ 64 | 'id' => $this->primaryKey(11), 65 | 'entityName' => $this->string(50), 66 | 'entityModel' => $this->string(100), 67 | 'categoryId' => $this->integer(11), 68 | ], 69 | $options 70 | ); 71 | 72 | $this->createTable( 73 | $this->tables['attribute'], 74 | [ 75 | 'id' => $this->primaryKey(11), 76 | 'entityId' => $this->integer(11)->defaultValue(null), 77 | 'typeId' => $this->integer(11)->defaultValue(null), 78 | 'type' => $this->string(50)->defaultValue(''), 79 | 'name' => $this->string(255)->defaultValue('NULL'), 80 | 'label' => $this->string(255)->defaultValue('NULL'), 81 | 'defaultValue' => $this->string(255)->defaultValue('NULL'), 82 | 'defaultOptionId' => $this->integer(11)->defaultValue(0), 83 | 'description' => $this->string(255)->defaultValue('NULL'), 84 | 'required' => $this->smallInteger(1)->defaultValue(0), 85 | 'order' => $this->integer(11)->defaultValue(0), 86 | ], 87 | $options 88 | ); 89 | 90 | $this->createTable( 91 | $this->tables['attribute_type'], 92 | [ 93 | 'id' => $this->primaryKey(11), 94 | 'name' => $this->string(255)->defaultValue('NULL'), 95 | 'handlerClass' => $this->string(255)->defaultValue('NULL'), 96 | 'storeType' => $this->smallInteger(6)->defaultValue(0), 97 | ], 98 | $options 99 | ); 100 | 101 | $this->createTable( 102 | $this->tables['value'], 103 | [ 104 | 'id' => $this->primaryKey(11), 105 | 'entityId' => $this->integer(11)->defaultValue(0), 106 | 'attributeId' => $this->integer(11)->defaultValue(0), 107 | 'value' => $this->string(255)->defaultValue('NULL'), 108 | 'optionId' => $this->integer(11)->defaultValue(0), 109 | ], 110 | $options 111 | ); 112 | 113 | $this->createTable( 114 | $this->tables['option'], 115 | [ 116 | 'id' => $this->primaryKey(11), 117 | 'attributeId' => $this->integer(11)->defaultValue(0), 118 | 'value' => $this->string(255)->defaultValue('NULL'), 119 | 'defaultOptionId' => $this->smallInteger(1)->defaultValue(0), 120 | ], 121 | $options 122 | ); 123 | 124 | if ($this->db->driverName != "sqlite") { 125 | $this->addForeignKey( 126 | 'FK_Attribute_typeId', 127 | $this->tables['attribute'], 128 | 'typeId', 129 | $this->tables['attribute_type'], 130 | 'id', 131 | "CASCADE", 132 | "NO ACTION" 133 | ); 134 | 135 | $this->addForeignKey( 136 | 'FK_EntityId', 137 | $this->tables['attribute'], 138 | 'entityId', 139 | $this->tables['entity'], 140 | 'id', 141 | "CASCADE", 142 | "NO ACTION" 143 | ); 144 | 145 | $this->addForeignKey( 146 | 'FK_Value_entityId', 147 | $this->tables['value'], 148 | 'entityId', 149 | $this->tables['entity'], 150 | 'id', 151 | "CASCADE", 152 | "NO ACTION" 153 | ); 154 | 155 | $this->addForeignKey( 156 | 'FK_Value_attributeId', 157 | $this->tables['value'], 158 | 'attributeId', 159 | $this->tables['attribute'], 160 | 'id', 161 | "CASCADE", 162 | "NO ACTION" 163 | ); 164 | 165 | $this->addForeignKey( 166 | 'FK_Value_optionId', 167 | $this->tables['value'], 168 | 'optionId', 169 | $this->tables['option'], 170 | 'id', 171 | "CASCADE", 172 | "NO ACTION" 173 | ); 174 | 175 | $this->addForeignKey( 176 | 'FK_Option_attributeId', 177 | $this->tables['option'], 178 | 'attributeId', 179 | $this->tables['attribute'], 180 | 'id', 181 | "CASCADE", 182 | "NO ACTION" 183 | ); 184 | } 185 | 186 | foreach ($this->attributeTypes as $columns) { 187 | $this->insert($this->tables['attribute_type'], $columns); 188 | } 189 | } 190 | 191 | public function safeDown() 192 | { 193 | if ($this->db->driverName != "sqlite") { 194 | $this->dropForeignKey('FK_Attribute_typeId', $this->tables['attribute']); 195 | $this->dropForeignKey('FK_EntityId', $this->tables['attribute']); 196 | $this->dropForeignKey('FK_Value_entityId', $this->tables['value']); 197 | $this->dropForeignKey('FK_Value_attributeId', $this->tables['value']); 198 | $this->dropForeignKey('FK_Value_optionId', $this->tables['value']); 199 | $this->dropForeignKey('FK_Option_attributeId', $this->tables['option']); 200 | } 201 | 202 | $this->dropTable($this->tables['attribute']); 203 | $this->dropTable($this->tables['attribute_type']); 204 | $this->dropTable($this->tables['value']); 205 | $this->dropTable($this->tables['option']); 206 | $this->dropTable($this->tables['entity']); 207 | } 208 | 209 | } 210 | -------------------------------------------------------------------------------- /src/migrations/m160501_014209_add_column_order_into_option.php: -------------------------------------------------------------------------------- 1 | addColumn(self::TABLE_NAME, 'order', $this->integer(11)->defaultValue(0)); 12 | } 13 | 14 | public function safeDown() 15 | { 16 | $this->dropColumn(self::TABLE_NAME, 'order'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/migrations/m160501_124615_create_table_eav_attribute_rules.php: -------------------------------------------------------------------------------- 1 | db->driverName == 'mysql' 18 | ? 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB' 19 | : null; 20 | 21 | $this->createTable( 22 | self::TABLE_NAME, 23 | [ 24 | 'id' => $this->primaryKey(), 25 | 'attributeId' => $this->integer(11)->defaultValue(0), 26 | 'rules' => $this->text()->defaultValue(''), 27 | ], 28 | $options 29 | ); 30 | 31 | $this->addForeignKey( 32 | 'FK_Rules_attributeId', 33 | self::TABLE_NAME, 34 | 'attributeId', 35 | '{{%eav_attribute}}', 36 | 'id', 37 | 'CASCADE', 38 | 'NO ACTION' 39 | ); 40 | } 41 | 42 | /** 43 | * @inheritdoc 44 | */ 45 | public function safeDown() 46 | { 47 | $this->dropForeignKey('FK_Rules_attributeId', self::TABLE_NAME); 48 | $this->dropTable('eav_attribute_rules'); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/migrations/m160501_230535_add_columns_into_attribute_rules.php: -------------------------------------------------------------------------------- 1 | addColumn(self::TABLE_NAME, 'required', $this->smallInteger(1)->defaultValue(0)); 12 | $this->addColumn(self::TABLE_NAME, 'visible', $this->smallInteger(1)->defaultValue(0)); 13 | $this->addColumn(self::TABLE_NAME, 'locked', $this->smallInteger(1)->defaultValue(0)); 14 | } 15 | 16 | public function safeDown() 17 | { 18 | $this->dropColumn(self::TABLE_NAME, 'required'); 19 | $this->dropColumn(self::TABLE_NAME, 'visible'); 20 | $this->dropColumn(self::TABLE_NAME, 'locked'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/migrations/m160501_232516_add_new_field_types.php: -------------------------------------------------------------------------------- 1 | insert( 13 | self::TABLE_NAME, 14 | [ 15 | 'name' => 'numiric', 16 | 'storeType' => ValueHandler::STORE_TYPE_RAW, 17 | 'handlerClass' => '\mirocow\eav\widgets\NumericInput', 18 | ] 19 | ); 20 | } 21 | 22 | public function safeDown() 23 | { 24 | $this->delete( 25 | self::TABLE_NAME, 26 | [ 27 | 'name' => 'numiric', 28 | 'storeType' => ValueHandler::STORE_TYPE_RAW, 29 | 'handlerClass' => '\mirocow\eav\widgets\NumericInput', 30 | ] 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/migrations/m160501_234949_remove_column_required_from_attribute.php: -------------------------------------------------------------------------------- 1 | dropColumn(self::TABLE_NAME, 'required'); 12 | } 13 | 14 | public function safeDown() 15 | { 16 | $this->addColumn(self::TABLE_NAME, 'required', $this->smallInteger(1)->defaultValue(0)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/migrations/m160711_132535_delete_foregin_key_from_values.php: -------------------------------------------------------------------------------- 1 | dropForeignKey('FK_Value_optionId', self::TABLE_NAME); 12 | 13 | $this->dropForeignKey('FK_Value_entityId', self::TABLE_NAME); 14 | } 15 | 16 | public function safeDown() 17 | { 18 | $this->addForeignKey( 19 | 'FK_Value_optionId', 20 | self::TABLE_NAME, 21 | 'optionId', 22 | '{{%eav_attribute_option}}', 23 | 'id', 24 | 'CASCADE', 25 | 'NO ACTION' 26 | ); 27 | 28 | $this->addForeignKey( 29 | 'FK_Value_entityId', 30 | self::TABLE_NAME, 31 | 'entityId', 32 | '{{%eav_entity}}', 33 | 'id', 34 | 'CASCADE', 35 | 'NO ACTION' 36 | ); 37 | } 38 | } -------------------------------------------------------------------------------- /src/migrations/m160711_162535_change_default_value.php: -------------------------------------------------------------------------------- 1 | alterColumn('{{%eav_attribute}}', 'description', $this->string(255)->defaultValue('')); 10 | } 11 | 12 | public function safeDown() 13 | { 14 | $this->alterColumn('{{%eav_attribute}}', 'description', $this->string(255)->defaultValue('NULL')); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/migrations/m160731_142546_alter_table_eav_attribute.php: -------------------------------------------------------------------------------- 1 | addColumn('{{%eav_attribute}}', 'categoryId', $this->integer(11)->null()); 10 | } 11 | 12 | public function safeDown() 13 | { 14 | // Just pass. It's was already nullable before this migration 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/migrations/m160807_162635_change_default_value_in_eav_attribute_value.php: -------------------------------------------------------------------------------- 1 | alterColumn('{{%eav_attribute_value}}', 'value', $this->text()->null()); 10 | } 11 | 12 | public function safeDown() 13 | { 14 | $this->alterColumn('{{%eav_attribute_value}}', 'value', $this->string(255)->defaultValue('NULL')); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/migrations/m160818_102815_add_entityId_index_in_eav_attribute_value.php: -------------------------------------------------------------------------------- 1 | createIndex('idx_eav_attribute_value_entityId', $this->tableName, 'entityId'); 12 | } 13 | 14 | public function safeDown() 15 | { 16 | $this->dropIndex('idx_eav_attribute_value_entityId', $this->tableName); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/models/EavAttribute.php: -------------------------------------------------------------------------------- 1 | 255], 45 | [ 46 | ['name'], 47 | 'match', 48 | 'pattern' => '/(^|.*\])([\w\.]+)(\[.*|$)/', 49 | 'message' => Yii::t('eav', 'Attribute name must contain latin word characters only.'), 50 | ], 51 | [['type'], 'string', 'max' => 50], 52 | [['entityId', 'typeId', 'order'], 'integer'], 53 | ]; 54 | } 55 | 56 | /** 57 | * @inheritdoc 58 | */ 59 | public function attributeLabels() 60 | { 61 | return [ 62 | 'id' => 'ID', 63 | 'typeId' => Yii::t('eav', 'Type ID'), 64 | 'entityId' => Yii::t('eav', 'Entity ID'), 65 | 'type' => Yii::t('eav', 'Type'), 66 | 'name' => Yii::t('eav', 'Name'), 67 | 'label' => Yii::t('eav', 'Label'), 68 | 'defaultValue' => Yii::t('eav', 'Default Value'), 69 | 'defaultOptionId' => Yii::t('eav', 'Default Option ID'), 70 | 'order' => Yii::t('eav', 'Order'), 71 | 'description' => Yii::t('eav', 'Description'), 72 | ]; 73 | } 74 | 75 | /** 76 | * @return \yii\db\ActiveQuery 77 | */ 78 | public function getDefaultOption() 79 | { 80 | return $this->hasOne(EavAttributeOption::className(), ['id' => 'defaultOptionId']); 81 | } 82 | 83 | /** 84 | * @return \yii\db\ActiveQuery 85 | */ 86 | public function getEavType() 87 | { 88 | return $this->hasOne(EavAttributeType::className(), ['id' => 'typeId']); 89 | } 90 | 91 | /** 92 | * @return \yii\db\ActiveQuery 93 | */ 94 | public function getEntity() 95 | { 96 | return $this->hasOne(EavEntity::className(), ['id' => 'entityId']); 97 | } 98 | 99 | /** 100 | * @return \yii\db\ActiveQuery 101 | */ 102 | public function getEavOptions() 103 | { 104 | return $this->hasMany(EavAttributeOption::className(), ['attributeId' => 'id']) 105 | ->orderBy(['order' => SORT_ASC]); 106 | } 107 | 108 | /** 109 | * @return \yii\db\ActiveQuery 110 | */ 111 | public function getEavAttributeValues() 112 | { 113 | return $this->hasMany(EavAttributeValue::className(), ['attributeId' => 'id']); 114 | } 115 | 116 | /** 117 | * @return \yii\db\ActiveQuery 118 | */ 119 | public function getAttributeRule() 120 | { 121 | return $this->hasOne(EavAttributeRule::className(), ['attributeId' => 'id']); 122 | } 123 | 124 | public function getRequired() 125 | { 126 | return $this->attributeRule->required; 127 | } 128 | 129 | public function getbootstrapData() 130 | { 131 | return [ 132 | 'cid' => '', 133 | 'label' => '', 134 | 'field_type' => '', 135 | 'required' => '', 136 | 'field_options' => [], 137 | ]; 138 | } 139 | 140 | public function getListTypes() 141 | { 142 | $models = EavAttributeType::find()->select(['id', 'name'])->asArray()->all(); 143 | 144 | return ArrayHelper::map($models, 'id', 'name'); 145 | } 146 | 147 | public function getListEntities() 148 | { 149 | $models = EavEntity::find()->select(['id', 'entityName'])->asArray()->all(); 150 | 151 | return ArrayHelper::map($models, 'id', 'entityName'); 152 | } 153 | 154 | } -------------------------------------------------------------------------------- /src/models/EavAttributeOption.php: -------------------------------------------------------------------------------- 1 | 255], 38 | [ 39 | ['attributeId'], 40 | 'exist', 41 | 'skipOnError' => true, 42 | 'targetClass' => EavAttribute::className(), 43 | 'targetAttribute' => ['attributeId' => 'id'], 44 | ], 45 | ]; 46 | } 47 | 48 | /** 49 | * @inheritdoc 50 | */ 51 | public function attributeLabels() 52 | { 53 | return [ 54 | 'id' => 'ID', 55 | 'order' => Yii::t('eav', 'Sort order'), 56 | 'attributeId' => Yii::t('eav', 'Attribute ID'), 57 | 'value' => Yii::t('eav', 'Value'), 58 | 'defaultOptionId' => Yii::t('eav', 'Default option Id'), 59 | ]; 60 | } 61 | 62 | /** 63 | * @return \yii\db\ActiveQuery 64 | */ 65 | public function getEavAttributes() 66 | { 67 | return $this->hasMany(EavAttribute::className(), ['defaultOptionId' => 'id']) 68 | ->orderBy(['order' => SORT_DESC]); 69 | } 70 | 71 | /** 72 | * @return \yii\db\ActiveQuery 73 | */ 74 | public function getAttribute($name = '') 75 | { 76 | return $this->hasOne(EavAttribute::className(), ['id' => 'attributeId']); 77 | } 78 | 79 | /** 80 | * @return \yii\db\ActiveQuery 81 | */ 82 | public function getValues() 83 | { 84 | return $this->hasMany(EavAttributeValue::className(), ['optionId' => 'id']); 85 | } 86 | 87 | public function getListAttributes() 88 | { 89 | $models = EavAttribute::find()->select(['id', 'label'])->asArray()->all(); 90 | 91 | return ArrayHelper::map($models, 'id', 'label'); 92 | } 93 | } -------------------------------------------------------------------------------- /src/models/EavAttributeOptionSearch.php: -------------------------------------------------------------------------------- 1 | $query, 49 | ] 50 | ); 51 | 52 | $this->load($params); 53 | 54 | if (!$this->validate()) { 55 | // uncomment the following line if you do not want to return any records when validation fails 56 | // $query->where('0=1'); 57 | return $dataProvider; 58 | } 59 | 60 | // grid filtering conditions 61 | $query->andFilterWhere( 62 | [ 63 | 'id' => $this->id, 64 | 'attributeId' => $this->attributeId, 65 | ] 66 | ); 67 | 68 | $query->andFilterWhere(['like', 'value', $this->value]); 69 | 70 | return $dataProvider; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/models/EavAttributeRule.php: -------------------------------------------------------------------------------- 1 | 1], 33 | [['rules'], 'string'], 34 | ]; 35 | } 36 | 37 | /** 38 | * @inheritdoc 39 | */ 40 | public function attributeLabels() 41 | { 42 | return [ 43 | 'id' => 'ID', 44 | 'attributeId' => Yii::t('eav', 'Attribute ID'), 45 | 'rules' => Yii::t('eav', 'Rules'), 46 | 'required' => Yii::t('eav', 'Required'), 47 | 'locked' => Yii::t('eav', 'Locked'), 48 | 'visible' => Yii::t('eav', 'Visible'), 49 | ]; 50 | } 51 | 52 | /** 53 | * @return \yii\db\ActiveQuery 54 | */ 55 | public function getEavAttribute() 56 | { 57 | return $this->hasOne(EavAttribute::className(), ['id' => 'attributeId']); 58 | } 59 | 60 | private function getDefaultFields() 61 | { 62 | return [ 63 | 'id', 64 | 'attributeId', 65 | 'rules', 66 | 'required', 67 | 'locked', 68 | 'visible', 69 | 'eavAttribute', 70 | ]; 71 | } 72 | 73 | private function getSkipFields() 74 | { 75 | return [ 76 | 'cid', 77 | 'label', 78 | 'type', 79 | 'field_type', 80 | 'description', 81 | ]; 82 | } 83 | 84 | public function __get($name) 85 | { 86 | if (in_array($name, $this->getDefaultFields())) { 87 | return parent::__get($name); 88 | } 89 | 90 | if (in_array($name, $this->getSkipFields())) { 91 | $rules = Json::decode($this->rules); 92 | 93 | if (isset($rules[$name])) { 94 | return $rules[$name]; 95 | } 96 | } 97 | 98 | return []; 99 | } 100 | 101 | public function __set($name, $value) 102 | { 103 | if (in_array($name, $this->getDefaultFields())) { 104 | return parent::__set($name, $value); 105 | } 106 | 107 | if (in_array($name, $this->getSkipFields())) { 108 | return []; 109 | } 110 | 111 | $rules = Json::decode($this->rules); 112 | 113 | if (!$rules) { 114 | $rules = []; 115 | } 116 | 117 | $rules[$name] = $value; 118 | 119 | $this->rules = Json::encode($rules); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/models/EavAttributeSearch.php: -------------------------------------------------------------------------------- 1 | $query, 49 | ] 50 | ); 51 | 52 | $this->load($params); 53 | 54 | if (!$this->validate()) { 55 | // uncomment the following line if you do not want to return any records when validation fails 56 | // $query->where('0=1'); 57 | return $dataProvider; 58 | } 59 | 60 | // grid filtering conditions 61 | $query->andFilterWhere( 62 | [ 63 | 'id' => $this->id, 64 | 'entityId' => $this->entityId, 65 | 'typeId' => $this->typeId, 66 | 'defaultOptionId' => $this->defaultOptionId, 67 | ] 68 | ); 69 | 70 | $query->andFilterWhere(['like', 'name', $this->name]) 71 | ->andFilterWhere(['like', 'label', $this->label]) 72 | ->andFilterWhere(['like', 'defaultValue', $this->defaultValue]); 73 | 74 | return $dataProvider; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/models/EavAttributeType.php: -------------------------------------------------------------------------------- 1 | 255], 34 | [ 35 | 'name', 36 | 'match', 37 | 'pattern' => '/(^|.*\])([\w\.]+)(\[.*|$)/', 38 | 'message' => Yii::t('eav', 'Type name must contain latin word characters only.'), 39 | ], 40 | ]; 41 | } 42 | 43 | /** 44 | * @inheritdoc 45 | */ 46 | public function attributeLabels() 47 | { 48 | return [ 49 | 'id' => 'ID', 50 | 'name' => Yii::t('eav', 'Name'), 51 | 'handlerClass' => Yii::t('eav', 'Handler Class'), 52 | 'storeType' => Yii::t('eav', 'Store Type'), 53 | ]; 54 | } 55 | 56 | /** 57 | * @return \yii\db\ActiveQuery 58 | */ 59 | public function getEavAttributes() 60 | { 61 | return $this->hasMany(EavAttribute::className(), ['typeId' => 'id']) 62 | ->orderBy(['order' => SORT_DESC]); 63 | } 64 | 65 | public function getFormBuilder() 66 | { 67 | $class = $this->handlerClass; 68 | 69 | return [ 70 | 'order' => isset($class::$order) ? $class::$order : 0, 71 | 'view' => isset($class::$fieldView) ? $class::$fieldView : Yii::t('eav', 'Template view'), 72 | 'edit' => isset($class::$fieldSettings) ? $class::$fieldSettings : Yii::t('eav', 'Template settings'), 73 | 'addButton' => isset($class::$fieldButton) ? $class::$fieldButton : Yii::t('eav', 'Template field button'), 74 | 'defaultAttributes' => isset($class::$defaultAttributes) 75 | ? $class::$defaultAttributes 76 | : Yii::t( 77 | 'eav', 78 | 'Template default attributes' 79 | ), 80 | ]; 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /src/models/EavAttributeTypeSearch.php: -------------------------------------------------------------------------------- 1 | $query, 49 | ] 50 | ); 51 | 52 | $this->load($params); 53 | 54 | if (!$this->validate()) { 55 | // uncomment the following line if you do not want to return any records when validation fails 56 | // $query->where('0=1'); 57 | return $dataProvider; 58 | } 59 | 60 | // grid filtering conditions 61 | $query->andFilterWhere( 62 | [ 63 | 'id' => $this->id, 64 | 'storeType' => $this->storeType, 65 | ] 66 | ); 67 | 68 | $query->andFilterWhere(['like', 'name', $this->name]) 69 | ->andFilterWhere(['like', 'handlerClass', $this->handlerClass]); 70 | 71 | return $dataProvider; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/models/EavAttributeValue.php: -------------------------------------------------------------------------------- 1 | 1023], 39 | ]; 40 | } 41 | 42 | /** 43 | * @inheritdoc 44 | */ 45 | public function attributeLabels() 46 | { 47 | return [ 48 | 'id' => 'ID', 49 | 'entityId' => Yii::t('eav', 'Entity ID'), 50 | 'attributeId' => Yii::t('eav', 'Attribute ID'), 51 | 'value' => Yii::t('eav', 'Value'), 52 | 'optionId' => Yii::t('eav', 'Option ID'), 53 | ]; 54 | } 55 | 56 | /** 57 | * @return \yii\db\ActiveQuery 58 | */ 59 | public function getEavAttribute() 60 | { 61 | return $this->hasOne(EavAttribute::className(), ['id' => 'attributeId']); 62 | } 63 | 64 | /** 65 | * @return \yii\db\ActiveQuery 66 | */ 67 | public function getEntity() 68 | { 69 | return $this->hasOne(Eav::className(), ['id' => 'entityId']); 70 | } 71 | 72 | /** 73 | * @return \yii\db\ActiveQuery 74 | */ 75 | public function getOption() 76 | { 77 | return $this->hasOne(EavAttributeOption::className(), ['id' => 'optionId']); 78 | } 79 | 80 | public function getValue() 81 | { 82 | return 'XXX'; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/models/EavAttributeValueSearch.php: -------------------------------------------------------------------------------- 1 | $query, 49 | ] 50 | ); 51 | 52 | $this->load($params); 53 | 54 | if (!$this->validate()) { 55 | // uncomment the following line if you do not want to return any records when validation fails 56 | // $query->where('0=1'); 57 | return $dataProvider; 58 | } 59 | 60 | // grid filtering conditions 61 | $query->andFilterWhere( 62 | [ 63 | 'id' => $this->id, 64 | 'entityId' => $this->entityId, 65 | 'attributeId' => $this->attributeId, 66 | 'optionId' => $this->optionId, 67 | ] 68 | ); 69 | 70 | $query->andFilterWhere(['like', 'value', $this->value]); 71 | 72 | return $dataProvider; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/models/EavEntity.php: -------------------------------------------------------------------------------- 1 | 100], 32 | [['entityModel', 'entityName'], 'required'], 33 | [['categoryId'], 'integer'], 34 | ]; 35 | } 36 | 37 | /** 38 | * @inheritdoc 39 | */ 40 | public function attributeLabels() 41 | { 42 | return [ 43 | 'id' => 'ID', 44 | 'entityName' => Yii::t('eav', 'Entity name'), 45 | 'entityModel' => Yii::t('eav', 'Entity Model'), 46 | 'categoryId' => Yii::t('eav', 'ID Category'), 47 | 'modelId' => Yii::t('eav', 'ID Model'), 48 | ]; 49 | } 50 | 51 | /** 52 | * @return \yii\db\ActiveQuery 53 | */ 54 | public function getEavAttributes() 55 | { 56 | return $this->hasMany(EavAttribute::className(), ['entityId' => 'id']) 57 | ->orderBy(['order' => SORT_DESC]); 58 | } 59 | } -------------------------------------------------------------------------------- /src/models/EavEntitySearch.php: -------------------------------------------------------------------------------- 1 | $query, 49 | ] 50 | ); 51 | 52 | $this->load($params); 53 | 54 | if (!$this->validate()) { 55 | // uncomment the following line if you do not want to return any records when validation fails 56 | // $query->where('0=1'); 57 | return $dataProvider; 58 | } 59 | 60 | // grid filtering conditions 61 | $query->andFilterWhere( 62 | [ 63 | 'id' => $this->id, 64 | 'categoryId' => $this->categoryId, 65 | ] 66 | ); 67 | 68 | $query->andFilterWhere(['=', 'entityName', $this->entityName]) 69 | ->andFilterWhere(['=', 'entityModel', $this->entityModel]); 70 | 71 | return $dataProvider; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/widgets/ActiveField.php: -------------------------------------------------------------------------------- 1 | inputOptions, $options); 14 | $options['form'] = $this->form; 15 | 16 | return $this->renderField($this->model, $this->attribute, $options); 17 | } 18 | 19 | private function renderField($model, $attribute, $options) 20 | { 21 | $this->adjustLabelFor($options); 22 | $eavModel = EavModel::create( 23 | [ 24 | 'entityModel' => $model, 25 | 'attribute' => $attribute, 26 | 'valueClass' => \mirocow\eav\models\EavAttributeValue::className(), 27 | ] 28 | ); 29 | $handler = $eavModel->handlers[$attribute]; 30 | $handler->owner->activeForm = $options['form']; 31 | unset($options['form']); 32 | $handler->options = $options; 33 | 34 | /** @var EavAttribute $attributeModel */ 35 | $attributeModel = $handler->attributeModel; 36 | 37 | /** @var ActiveField $model */ 38 | $model = $handler->run(); 39 | $model->label($attributeModel->label); 40 | $model->hint($attributeModel->description); 41 | $this->parts = $model->parts; 42 | 43 | /** Add required attribute */ 44 | if ($attributeModel->required) { 45 | $this->options['class'] .= ' ' . $this->form->requiredCssClass; 46 | } 47 | 48 | return $this; 49 | } 50 | } -------------------------------------------------------------------------------- /src/widgets/AttributeHandler.php: -------------------------------------------------------------------------------- 1 | 4 | */ 5 | 6 | namespace mirocow\eav\widgets; 7 | 8 | use mirocow\eav\EavModel; 9 | use mirocow\eav\handlers\ValueHandler; 10 | use Yii; 11 | use yii\base\InvalidParamException; 12 | use yii\base\Widget; 13 | use yii\db\ActiveRecord; 14 | 15 | /** 16 | * Class AttributeHandler 17 | * 18 | * @package mirocow\eav 19 | */ 20 | class AttributeHandler extends Widget 21 | { 22 | const VALUE_HANDLER_CLASS = '\mirocow\eav\handlers\RawValueHandler'; 23 | 24 | /** @var EavModel */ 25 | public $owner; 26 | 27 | /** @var ValueHandler */ 28 | public $valueHandler; 29 | 30 | /** @var ActiveRecord */ 31 | public $attributeModel; 32 | 33 | public $nameField = 'name'; 34 | public $labelField = 'label'; 35 | public $options = []; 36 | 37 | /** 38 | * @param EavModel $owner 39 | * @param ActiveRecord $attributeModel 40 | * @return AttributeHandler 41 | * @throws \yii\base\InvalidConfigException 42 | */ 43 | public static function load($owner, $attributeModel) 44 | { 45 | if (!class_exists($class = $attributeModel->eavType->handlerClass)) { 46 | throw new InvalidParamException('Unknown handler class: ' . $class); 47 | } 48 | 49 | $handler = Yii::createObject( 50 | [ 51 | 'class' => $class, 52 | 'owner' => $owner, 53 | 'attributeModel' => $attributeModel, 54 | ] 55 | ); 56 | $handler->init(); 57 | 58 | return $handler; 59 | } 60 | 61 | /** 62 | * @inheritdoc 63 | */ 64 | public function init() 65 | { 66 | $this->valueHandler = Yii::createObject( 67 | [ 68 | 'class' => static::VALUE_HANDLER_CLASS, 69 | 'attributeHandler' => $this, 70 | ] 71 | ); 72 | } 73 | 74 | /** 75 | * @return string 76 | */ 77 | public function getAttributeName() 78 | { 79 | return (string)($this->attributeModel->{$this->nameField}); 80 | } 81 | 82 | public function getAttributeLabel() 83 | { 84 | return (string)($this->attributeModel->{$this->labelField}); 85 | } 86 | 87 | public function getOptions() 88 | { 89 | $result = []; 90 | foreach ($this->attributeModel->eavOptions as $option) { 91 | $result[] = $option->getPrimaryKey(); 92 | } 93 | 94 | return $result; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/widgets/CheckBoxList.php: -------------------------------------------------------------------------------- 1 | 4 | */ 5 | 6 | namespace mirocow\eav\widgets; 7 | 8 | use Yii; 9 | use yii\helpers\ArrayHelper; 10 | 11 | class CheckBoxList extends AttributeHandler 12 | { 13 | const VALUE_HANDLER_CLASS = '\mirocow\eav\handlers\MultipleOptionsValueHandler'; 14 | 15 | static $order = 10; 16 | 17 | static $fieldView = <<