├── .gitignore ├── resources ├── assets │ └── images │ │ └── timg.jpg └── views │ ├── tabs.blade.php │ ├── script.blade.php │ ├── field │ └── testtext.blade.php │ ├── tree.blade.php │ └── tips.blade.php ├── .github └── ISSUE_TEMPLATE │ └── custom.md ├── routes └── web.php ├── src ├── Field │ ├── Outer.php │ └── TestText.php ├── FormWgt.php ├── Configx.php ├── ConfigxServiceProvider.php ├── ConfigxModel.php ├── Http │ └── Controllers │ │ └── ConfigxController.php └── Tools │ ├── Displayer.php │ ├── Tree.php │ ├── Tool.php │ ├── Updater.php │ └── Builder.php ├── composer.json ├── LICENSE ├── ChangeLog.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | phpunit.phar 3 | /vendor 4 | composer.phar 5 | composer.lock 6 | *.project 7 | .idea/ -------------------------------------------------------------------------------- /resources/assets/images/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ichynul/configx/HEAD/resources/assets/images/timg.jpg -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | html = $html; 18 | } 19 | 20 | public function render() 21 | { 22 | return $this->html; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Field/TestText.php: -------------------------------------------------------------------------------- 1 | 2, 16 | 'field' => 4, 17 | ]; 18 | 19 | /** 20 | * Determine if form fields has files. 21 | * 22 | * @return bool 23 | */ 24 | public function hasFile() 25 | { 26 | return true; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Configx.php: -------------------------------------------------------------------------------- 1 | views()) { 22 | $this->loadViewsFrom($views, 'configx'); 23 | } 24 | 25 | $this->app->booted(function () { 26 | Configx::routes(__DIR__ . '/../routes/web.php'); 27 | }); 28 | 29 | Admin::booting(function () { 30 | 31 | Form::extend('test_text', TestText::class); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /resources/views/tabs.blade.php: -------------------------------------------------------------------------------- 1 | Example: 2 |
 3 | base : Base setting
 4 | shop : Shop setting
 5 | uplaod : Upload setting
 6 | image :
 7 | 
8 | This will override `extensions.iframe-tabs.tabs` in config /config/admin.php: 9 |
10 | 'extensions' => [
11 |         'configx' => [
12 |             // Set to `false` if you want to disable this extension
13 |             'enable' => true,
14 |             'tabs' => [
15 |                 'base' => 'Base',
16 |                 'shop' => 'Shop',
17 |                 'uplaod' => 'Uplaod',
18 |                 'image' => '' // if tab name is empty, get from trans : trans('admin.configx.tabs.image');
19 |             ],
20 |             // Whether check group permissions. 
21 |             //if (!Admin::user()->can('confix.tab.base')) {/*hide base tab*/ } .
22 |             'check_permission' => false
23 |         ],
24 |     ],
25 | 
-------------------------------------------------------------------------------- /resources/views/script.blade.php: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ichynul/configx", 3 | "description": "laravel-admin extension configx", 4 | "type": "library", 5 | "keywords": ["laravel-admin", "extension"], 6 | "homepage": "https://github.com/ichynul/configx", 7 | "license": "MIT", 8 | "authors": [{ 9 | "name": "ichynul", 10 | "email": "ichynul@outlook.com" 11 | }], 12 | "require": { 13 | "php": ">=7.0.0", 14 | "encore/laravel-admin": "~1.6", 15 | "laravel-admin-ext/config": "^1.0", 16 | "ichynul/row-table": "^1.1.0" 17 | }, 18 | "require-dev": { 19 | "phpunit/phpunit": "~6.0" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "Ichynul\\Configx\\": "src/" 24 | } 25 | }, 26 | "extra": { 27 | "laravel": { 28 | "providers": [ 29 | "Ichynul\\Configx\\ConfigxServiceProvider" 30 | ] 31 | 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jens Segers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/ConfigxModel.php: -------------------------------------------------------------------------------- 1 | get()->toArray(); 21 | } 22 | 23 | /** 24 | * Get name prefix 25 | * 26 | * @return string 27 | */ 28 | public function getPrefix() 29 | { 30 | $arr = explode('.', $this->name); 31 | return count($arr) ? $arr[0] : ''; 32 | } 33 | 34 | /** 35 | * Create prefix permission 36 | * 37 | * @param [string] $prefix 38 | * @return void 39 | */ 40 | public static function createPermission($prefix, $name) 41 | { 42 | $slug = 'confix.tab.' . $prefix; 43 | $name = trans('admin.configx.header') . '-' . $name; 44 | if ($pm = Permission::where('slug', $slug)->first()) { 45 | if ($pm->name != $name) { 46 | $pm->update(['name' => $name]); 47 | } 48 | return; 49 | } 50 | Permission::create([ 51 | 'name' => $name, 52 | 'slug' => $slug, 53 | 'http_path' => "/configx/tab-{$prefix}", 54 | ]); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /resources/views/field/testtext.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
6 | 7 | @include('admin::form.error') 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | @include('admin::form.help-block') 20 |
21 |
22 | This is a demo.
23 | //Create App/Admin/Extensions/MyText.php
24 | --------------------
25 | <?php
26 | 
27 | namespace App\Admin\Extensions;
28 | 
29 | use Encore\Admin\Form\Field\Text;
30 | 
31 | class MyText extends Text
32 | {
33 |     protected $icon = 'fa-github-alt';
34 | 
35 |     protected $view = 'myview::mytext';
36 | 
37 |     public function prepare($value)
38 |     {
39 |         // do something
40 |         return $value;
41 |     }
42 | 
43 |     public function render()
44 |     {
45 |         // do something
46 | 
47 |         return parent::render();
48 |     }
49 | 
50 |     public function fill($data)
51 |     {
52 |         // do something
53 |     }
54 | }
55 | --------------------
56 | //Extend element in Admin/bootstrap.php :
57 | Encore\Admin\Form::extend('mytext', App/Admin/Extensions/MyText::class);
58 | --------------------
59 | //Useage
60 | __element__ : mytext
61 | 
62 | 63 | 64 |  Github Configx 65 | 66 |
67 |
68 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | ## 2019 年 4 月 10 日 2 | 3 | #1.0.18 4 | 5 | - 1. If tab name is empty , get from trans : `trans("admin.configx.tabs.{$tabkey}")`; 6 | 7 | tab 名称留空则从翻译中获取 8 | 9 | ```php 10 | 'extensions' => [ 11 | 'configx' => [ 12 | // ***** 13 | 'tabs' => [ 14 | 'base' => '基本设置', 15 | 'shop' => '店铺设置', 16 | 'uplaod' => '上传设置', 17 | 'image' => '' // if tab name is empty , get from trans : trans('admin.configx.tabs.image'); 留空则从翻译中获取名称 18 | ], 19 | // ***** 20 | ], 21 | ], 22 | 23 | ``` 24 | 25 | - 2. When adding or editing a config , you can type in a `config_name` optionaly, 26 | 27 | if `new_config_name` is empty, get from trans : `trans("admin.configx.{$tab}.{$config_key}")`; 28 | 29 | 添加或编辑配置信息时,可以输入配置名称,如若留空,则从翻译中获取 30 | 31 | ## 2019 年 3 月 22 日 32 | 33 | New feature support varable key some\_$admin$\_str. 34 | 新特性,支持在 key 中插入可变的$admin$ 35 | 36 | # demo 37 | 38 | Add 2 config keys : 39 | 40 | ``` 41 | base.skin_$admin$ 42 | element type : radio_group 43 | options : 44 | skin-blue 45 | skin-blue-light 46 | skin-yellow 47 | skin-yellow-light 48 | skin-green 49 | skin-green-light 50 | skin-purple 51 | skin-purple-light 52 | skin-red 53 | skin-red-light 54 | skin-black 55 | skin-black-light 56 | 57 | base.layout_$admin$ 58 | element type : checkbox_group 59 | options : 60 | fixed 61 | layout-boxed 62 | layout-top-nav 63 | sidebar-collapse 64 | sidebar-mini 65 | ``` 66 | 67 | Then add some codes at `Admin/bootstrap.php` : 68 | 69 | ```php 70 | if(Admin::user()) 71 | { 72 | config( 73 | [ 74 | 'admin.skin' => config('base.skin_admin_' . Admin::user()->id, 'skin-blue'), 75 | 'admin.layout' => explode(',', config('base.layout_admin_' . Admin::user()->id, 'fixed')), 76 | ] 77 | ); 78 | } 79 | ``` 80 | -------------------------------------------------------------------------------- /src/Http/Controllers/ConfigxController.php: -------------------------------------------------------------------------------- 1 | input('do'); 19 | 20 | $__configx__ = Tool::getConfigx(); 21 | 22 | $cx_options = []; 23 | 24 | if ($__configx__ && $__configx__['description']) { 25 | 26 | $cx_options = json_decode($__configx__['description'], 1); 27 | } 28 | 29 | $tabs = Tool::tabs($cx_options); 30 | 31 | if ($do == 'backup') { 32 | 33 | $this->backUp($__configx__); 34 | 35 | $do = 'new_config'; 36 | } 37 | 38 | if ($do == 'new_config') { 39 | 40 | return Displayer::newConfig($id, $cx_options, $tabs); 41 | 42 | } else if ($do == 'tabs_config') { 43 | 44 | return Displayer::tabsConfig($id, $cx_options, $tabs); 45 | } 46 | 47 | return Displayer::editConfigs($id, $cx_options, $tabs); 48 | } 49 | 50 | public function saveall($id = 0, Request $request) 51 | { 52 | $do = $request->input('do'); 53 | 54 | if ($do == 'tabs_config') { 55 | 56 | return Updater::saveTabsOptions($id, $request->values['c_tabs_options']); 57 | } 58 | 59 | if ($do == 'new_config' || $do == 'backup') { 60 | 61 | return Updater::saveConfigOptions($id, $request); 62 | } 63 | 64 | return Updater::saveConfigs($id, $request); 65 | } 66 | 67 | public function postSort(Request $request) 68 | { 69 | $__configx__ = Tool::getConfigx(); 70 | 71 | $cx_options = []; 72 | 73 | if ($__configx__ && $__configx__['description']) { 74 | 75 | $cx_options = json_decode($__configx__['description'], 1); 76 | } 77 | 78 | $data = $request->input('data'); 79 | 80 | $i = 0; 81 | foreach ($data as $s) { 82 | $i += 5; 83 | $id = $s['id']; 84 | $config = ConfigxModel::findOrFail($id); 85 | if (isset($cx_options[$config['name']])) { 86 | $cx_options[$config['name']]['order'] = $i; 87 | } else { 88 | $cx_options[$config['name']] = ['options' => [], 'element' => 'normal', 'help' => '', 'name' => '', 'order' => $i]; 89 | } 90 | } 91 | $cx_options = Tool::remove($cx_options); 92 | $__configx__['description'] = json_encode($cx_options); 93 | $__configx__->save(); 94 | return response()->json(['status' => 1, 'message' => trans('admin.update_succeeded')]); 95 | } 96 | 97 | public function delFile($id, Request $request) 98 | { 99 | $rowname = 'c_' . $id . '_'; 100 | 101 | $field = new MultipleFile($rowname, ['']); 102 | 103 | $config = ConfigxModel::findOrFail($id); 104 | 105 | $field->setOriginal([$rowname => explode(',', $config->value)]); 106 | 107 | $this->handleFileDelete($request->all()); 108 | 109 | $value = $field->destroy($request->input(Field::FILE_DELETE_FLAG)); 110 | 111 | $value = implode(',', $value); 112 | 113 | $config->value = $value; 114 | $config->update(); 115 | 116 | return response()->json(['status' => 1, 'message' => trans('admin.update_succeeded')]); 117 | } 118 | 119 | protected function handleFileDelete($input) 120 | { 121 | if (array_key_exists(Field::FILE_DELETE_FLAG, $input)) { 122 | $input[Field::FILE_DELETE_FLAG] = $input['key']; 123 | unset($input['key']); 124 | } 125 | 126 | request()->replace($input); 127 | } 128 | 129 | protected function backUp($__configx__) 130 | { 131 | if ($__configx__ && $__configx__['description']) { 132 | app('files')->put( 133 | storage_path('app/public/configx.json'), 134 | $__configx__['description'] 135 | ); 136 | 137 | admin_success(trans('admin.succeeded'), "Configx options save to : /wwwroot/storage/app/public/configx.json"); 138 | } else { 139 | admin_warning(trans('admin.failed'), "Configx options is empty!"); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /resources/views/tree.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |  {!! $saveTitle !!} 4 |
5 |
6 | @foreach($tree as $title => $fields) 7 | 15 | 16 |
17 |
    18 | @foreach($fields as $field) 19 | @if(isset($field['options']) && \Illuminate\Support\Arr::get($field['options'],'divide') =='befor') 20 |
  1. - - - - - - - - - - - - - -
  2. 21 | @endif 22 |
  3. 23 |  {!! $field['label'] !!}-[{!! $field['type_name'] !!}] 24 | 25 | 26 | @if($field['id'] != $current_id) 27 | 28 | @else 29 | 30 | @endif 31 | 32 | @if(isset($field['tds']) && count($field['tds'])) 33 |
      34 | @foreach($field['tds'] as $td) 35 |
    • {!! $td['label'] !!}-[{!! $td['type_name'] !!}] 36 | 37 |
    • 38 | @endforeach 39 |
    40 | @endif 41 |
  4. 42 | @if(isset($field['options']) && \Illuminate\Support\Arr::get($field['options'],'divide') =='after') 43 |
  5. - - - - - - - - - - - - - -
  6. 44 | @endif 45 | @endforeach 46 |
47 |
48 | @endforeach 49 |
50 |
51 | 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # laravel-admin configx 2 | 3 | ## Installation 4 | 5 | need to install laravel-admin-ext/config first, see https://github.com/laravel-admin-extensions/config 6 | 7 | Then run : 8 | 9 | ``` 10 | $ composer require ichynul/configx 11 | ``` 12 | 13 | Then run: 14 | 15 | ``` 16 | $ php artisan admin:import configx 17 | ``` 18 | 19 | Add a tabs config in `config/admin.php`: 20 | 21 | ```php 22 | 'extensions' => [ 23 | 'configx' => [ 24 | // Set to `false` if you want to disable this extension 25 | 'enable' => true, 26 | 'tabs' => [ 27 | 'base' => '基本设置', 28 | 'shop' => '店铺设置', 29 | 'uplaod' => '上传设置', 30 | 'image' => '' // if tab name is empty, get from trans : trans('admin.configx.tabs.image'); tab名称留空则从翻译中获取 31 | ], 32 | // Whether check group permissions. 33 | //if (!Admin::user()->can('confix.tab.base')) {/*hide base tab*/ } . 34 | 'check_permission' => false, 35 | 'break_when_errors' => false // do not save anything if have errors 36 | ], 37 | ], 38 | 39 | ``` 40 | 41 | ## Usage 42 | 43 | Open `http://your-host/admin/configx/edit` 44 | 45 | ## Demo 46 | 47 | You can click "+" to config tabs : 48 | 49 | add an new config key 50 | 51 | step 1 Select config type from `['base']` 52 | 53 | step 2 Select form-element type from `['normal', 'date', 'time', 'datetime', 'image', 'yes_or_no', 'number', 'rate', 'editor', 'radio_group' ,'checkbox_group', 'select']`... and so on 54 | 55 | step 3 If you selected form-element type is `['radio_group' ,'checkbox_group', 'select']` ,you need inupt `[options]` : 56 | 57 | just text: 58 | 59 | ```js 60 | text1 61 | text2 62 | ... 63 | ``` 64 | 65 | and key-text: 66 | 67 | ```js 68 | key1 : text1 69 | key2 : text2 70 | ... 71 | ``` 72 | 73 | or load from ulr: 74 | 75 | `options_url:/api/mydata` 76 | 77 | If you selected form-element type is `textarea` , you can config it `rows:3` , default is 5. 78 | 79 | If you selected form-element type is `table`, `rows / cols` is needed : 80 | 81 | `base.some_key` 82 | 83 | ``` 84 | rows: 4 85 | cols: 4 86 | ``` 87 | 88 | This wiil build a table like below : 89 | 90 | ````php 91 | /* 92 | |------------------------------------------------------------------------------------- 93 | | r_label\ c_labe | c_label1 | c_label2 | c_label3 | ⬅Col labels 94 | |------------------------------------------------------------------------------------- 95 | | r_label1 | base.some_key_1_1 | base.some_key_1_2 | base.some_key_1_3 | 96 | |------------------------------------------------------------------------------------- 97 | | r_label2 | base.some_key_2_1 | base.some_key_2_2 | base.some_key_2_3 | 98 | |------------------------------------------------------------------------------------- 99 | | r_label3 | base.some_key_3_1 | base.some_key_3_2 | base.some_key_3_3 | 100 | |------------------------------------------------------------------------------------- 101 | ↑ 102 | Row labels 103 | 104 | You can edit labels as you want. 105 | 106 | Each has a key , base.some_key_[0]_[0] to base.some_key_[rows-1]_[cols-1] . (from 0 to length -1 ) 107 | 108 | So, you can chang a label to input : 109 | 110 | |------------------------------------------------------------------------------------- 111 | | r_label\ c_labe | c_label1 | c_label2 | base.some_key_0_3 | ⬅ [c_label3] change to [base.some_key_0_3] 112 | |------------------------------------------------------------------------------------- , we can input here . 113 | | r_label1 | base.some_key_1_1 | base.some_key_1_2 | base.some_key_1_3 | (可以把label 换成输入元素) 114 | |------------------------------------------------------------------------------------- 115 | | r_label2 | base.some_key_2_1 | base.some_key_2_2 | base.some_key_2_3 | 116 | |------------------------------------------------------------------------------------- 117 | | trans.sometext | base.some_key_3_1 | hello world! | base.some_key_3_3 | 118 | |------------------------------------------------------------------------------------- 119 | ↑ ↑ 120 | ↑ [base.some_key_3_2] change to [hello world!] 121 | ↑ , we can not input here any more , 122 | ↑ it wiil just show label text 'hello world!' . 123 | ↑ (也可以把输入元素换成仅显示文字) 124 | ↑ 125 | get text from trans 126 | trans("admin.configx.base.some_key.sometext") 127 | 显示文字时可以从翻译获取文字,样式 `trans.sometext` 128 | 其中 sometext 为翻译的key 129 | 130 | 131 | 132 | note : if text = key or text = '' ,render as input form element , otherwise just show the text you leave. 133 | 134 | //if text is trans.sometext , get from trans : trans("admin.configx.{$tab}.{$tablekey}.{$sometext}") 135 | 136 | 总结 : 如果输入的字符串与td默认key一样或输入的字符串为空,这个位置将是一个可输入的表单元素,否则就显示原样你输入的字符串 . 137 | 138 | 139 | */ 140 | Add a lang config in `resources/lang/{zh-CN}/admin.php` 141 | 142 | ```php 143 | 'configx' => [ 144 | 'new_config_type' => '配置类型', 145 | 'new_config_key' => '配置key', 146 | 'new_config_name' => '配置名称', 147 | 'new_config_element' => '配置表单元素', 148 | 'new_config_help' => '配置help', 149 | 'new_config_options' => '配置扩展项', 150 | 'header' => '网站设置', 151 | 'desc' => '网站设置设置', 152 | 'backup' => '备份', 153 | 'element' => [ 154 | 'normal' => '默认', 155 | 'textarea' => '文本域', 156 | 'date' => '日期', 157 | 'time' => '时间', 158 | 'datetime' => '日期时间', 159 | 'password' => '密码', 160 | 'image' => '图片', 161 | 'multiple_image' => '多图', 162 | 'file' => '文件', 163 | 'multiple_file' => '多文件', 164 | 'yes_or_no' => '是或否', 165 | 'editor' => '编辑器', 166 | 'radio_group' => '单选框组', 167 | 'checkbox_group' => '多选框组', 168 | 'number' => '数字', 169 | 'rate' => '比例', 170 | 'select' => '下拉框', 171 | 'tags' => '标签', 172 | 'icon' => '图标', 173 | 'color' => '颜色', 174 | 'table' =>'表格', 175 | 'listbox' => '左右多选框', 176 | 'multiple_select' => '下拉多选', 177 | 'map' => '地图' 178 | ], 179 | ], 180 | 'yes' => '是', 181 | 'no' => '否' 182 | ```` 183 | 184 | if you need add a new config tab, chang it in `config/admin.php`. 185 | 186 | After add config in the panel, use `config($key)` to get value you configured. 187 | 188 | License 189 | 190 | --- 191 | 192 | Licensed under [The MIT License (MIT)](LICENSE). 193 | -------------------------------------------------------------------------------- /src/Tools/Displayer.php: -------------------------------------------------------------------------------- 1 | request('do', ''), 'tabindex' => Session::get('tabindex')]); 34 | } 35 | 36 | return static::$formWgt; 37 | } 38 | 39 | public static function newConfig($id = 0, $cx_options = [], $tabs) 40 | { 41 | if (Tool::checkPermission()) { 42 | 43 | if (!Admin::user()->can('confix.tab.' . 'new_config')) { 44 | return ''; 45 | } 46 | } 47 | 48 | Tree::getConfigTabs($tabs, $cx_options); 49 | 50 | $config = []; 51 | $tab = new Wtab(); 52 | 53 | if ($id > 0) { 54 | $config = ConfigxModel::findOrFail($id); 55 | $label = $cx_options && isset($cx_options[$config['name']]) ? Arr::get($cx_options[$config['name']], 'name') : ''; 56 | if (!$label) { 57 | $label = trans('admin.configx.' . $config['name']); 58 | } 59 | $title = trans('admin.edit') . '-' . $label; 60 | } else { 61 | $title = trans('admin.create'); 62 | } 63 | 64 | $subs = array( 65 | ['id' => 'type', 'name' => 'new_config_type', 'value' => ''], 66 | ['id' => 'key', 'name' => 'new_config_key', 'value' => ''], 67 | ['id' => 'name', 'name' => 'new_config_name', 'value' => ''], 68 | ['id' => 'element', 'name' => 'new_config_element', 'value' => ''], 69 | ['id' => 'help', 'name' => 'new_config_help', 'value' => ''], 70 | ['id' => 'options', 'name' => 'new_config_options', 'value' => ''], 71 | ); 72 | 73 | $formhtml = ''; 74 | 75 | foreach ($subs as $val) { 76 | $formhtml .= Builder::createNewConfigForm($val, $tabs, $cx_options, $config); 77 | } 78 | 79 | if ($id > 0) { 80 | $tab->add($title, '
' . $formhtml . '
' . Tree::buildTree($id, $cx_options) . '
', false); 81 | } else { 82 | $tab->add($title, '
' . $formhtml . '
' . Tree::buildTree($id, $cx_options) . '
', false); 83 | } 84 | $tab->addLink('x', admin_base_path('configx/edit/0')); 85 | 86 | return static::createform($tab, $id); 87 | } 88 | 89 | public static function tabsConfig($id = 0, $cx_options = [], $tabs) 90 | { 91 | if (Tool::checkPermission()) { 92 | 93 | if (!Admin::user()->can('confix.tab.' . 'tabs_config')) { 94 | return ''; 95 | } 96 | } 97 | 98 | $tab = new Wtab(); 99 | 100 | $title = trans('admin.edit') . '-' . trans('admin.configx.' . 'new_config_type'); 101 | 102 | $subs = array( 103 | ['id' => 'tabs_key', 'name' => 'new_config_key', 'value' => ''], 104 | ['id' => 'tabs_options', 'name' => 'new_config_options', 'value' => ''], 105 | ); 106 | 107 | $formhtml = ''; 108 | 109 | foreach ($subs as $val) { 110 | $formhtml .= Builder::createNewConfigForm($val, $tabs, $cx_options); 111 | } 112 | 113 | $tab->add($title, '
' . $formhtml . '
' . '
', true); 114 | $tab->addLink('x', admin_base_path('configx/edit/' . $id) . '?do=new_config'); 115 | 116 | return static::createform($tab, $id); 117 | } 118 | 119 | public static function editConfigs($id = 0, $cx_options = [], $tabs) 120 | { 121 | $tab = new Wtab(); 122 | 123 | if (Tool::checkPermission()) { 124 | 125 | Tool::createPermissions($tabs); 126 | 127 | Tool::createPermissions(['new_config' => trans('admin.edit') . '-config']); 128 | 129 | Tool::createPermissions(['tabs_config' => trans('admin.edit') . '-tabs']); 130 | } 131 | 132 | Tree::getConfigTabs($tabs, $cx_options); 133 | 134 | $tree = Tree::getTree(); 135 | 136 | foreach ($tree as $title => $fields) { 137 | $formhtml = ''; 138 | foreach ($fields as $field) { 139 | $formhtml .= Builder::createField($field, $cx_options); 140 | } 141 | $tab->add($title, '
' . $formhtml . '
', false); 142 | } 143 | 144 | if (!Tool::checkPermission() || Admin::user()->can('confix.tab.' . 'new_config')) { 145 | 146 | $tab->addLink('+', admin_base_path('configx/edit/0') . '?do=new_config'); 147 | } 148 | 149 | return static::createform($tab, $id); 150 | } 151 | 152 | protected static function createform($tab, $id) 153 | { 154 | $indexField = new Hidden('tabindex', 'tabindex'); 155 | $indexField->default(0); 156 | 157 | $doField = new Hidden('do', 'do'); 158 | 159 | $form = static::getFormWgt(); 160 | 161 | $html = new Outer($tab->render(), []); 162 | 163 | $form->pushField($html); 164 | $form->pushField($indexField); 165 | $form->pushField($doField); 166 | $form->action(admin_base_path('configx/saveall/' . $id)); 167 | $form->attribute('enctype', 'multipart/form-data'); 168 | 169 | $content = new Content(); 170 | 171 | return $content 172 | ->header(trans('admin.configx.header')) 173 | ->description(trans('admin.configx.desc')) 174 | ->breadcrumb( 175 | ['text' => trans('admin.configx.header'), 'url' => 'configx/edit'], 176 | ['text' => trans('admin.configx.desc')] 177 | ) 178 | ->row('
' . $form->render() . '
')->row(view( 179 | 'configx::script', 180 | [ 181 | 'call_back' => admin_base_path('configx/sort'), 182 | 'del_url' => admin_base_path('config'), 183 | 'deleteConfirm' => trans('admin.delete_confirm'), 184 | 'confirm' => trans('admin.confirm'), 185 | 'cancel' => trans('admin.cancel'), 186 | ] 187 | )); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/Tools/Tree.php: -------------------------------------------------------------------------------- 1 | &$value) { 30 | if ($check_permission && !Admin::user()->can('confix.tab.' . $key)) { 31 | continue; 32 | } 33 | $subs = ConfigxModel::group($key); 34 | if ($cx_options) { 35 | $subs = Tool::sortGroupConfigs($subs, $cx_options); 36 | } 37 | 38 | static::$tree[$value] = []; 39 | 40 | if (empty($subs)) { 41 | continue; 42 | } 43 | foreach ($subs as $val) { 44 | if (!isset($cx_options[$val['name']])) { 45 | $cx_options[$val['name']] = [ 46 | 'options' => [], 47 | 'element' => 'normal', 48 | 'help' => '', 49 | 'name' => '', 50 | 'order' => 999, 51 | ]; 52 | } 53 | if (preg_match('/\$admin\$/i', $val['name'])) { 54 | $old = $val['name']; 55 | $new = preg_replace('/\$admin\$/i', 'admin_' . Admin::user()->id, $old); 56 | $val = ConfigxModel::firstOrCreate( 57 | ['name' => $new], 58 | [ 59 | 'description' => trans('admin.configx.' . $val['name']) . ' for admin-' . Admin::user()->id, 60 | 'value' => '1', 61 | ] 62 | ); 63 | $val['name'] = $old; 64 | } 65 | if (preg_match('/admin_\d+?/i', $val['name'])) { 66 | continue; 67 | } 68 | $val['etype'] = Arr::get($cx_options[$val['name']], 'element', 'normal'); 69 | 70 | if (isset($cx_options[$val['name']]['table_field'])) { 71 | 72 | static::$tableFields[$val['name']] = $val; 73 | continue; 74 | } 75 | static::$tree[$value][] = $val; 76 | } 77 | } 78 | } 79 | 80 | public static function buildTree($id = 0, $cx_options = []) 81 | { 82 | $tree = static::$tree; 83 | $tableFields = static::$tableFields; 84 | 85 | $editTitle = trans('admin.edit'); 86 | $deleteTitle = trans('admin.delete'); 87 | $saveTitle = trans('admin.configx.backup'); 88 | $tabsEdit = !Tool::checkPermission() || Admin::user()->can('confix.tab.' . 'tabs_config') ? 89 | admin_base_path('configx/edit/' . $id) . '?do=tabs_config' : ''; 90 | 91 | foreach ($tree as &$fields) { 92 | foreach ($fields as &$field) { 93 | 94 | if (!key_exists($field['name'], $cx_options)) { 95 | $cx_options[$field['name']] = []; 96 | } 97 | 98 | $options = Arr::get($cx_options[$field['name']], 'options', []); 99 | 100 | $label = Arr::get($cx_options[$field['name']], 'name'); 101 | if (!$label) { 102 | $label = trans('admin.configx.' . $field['name']); 103 | } 104 | $field['label'] = $label; 105 | $field['options'] = $options; 106 | $field['href'] = admin_base_path('configx/edit/' . $field['id']) . '?do=new_config'; 107 | $field['type_name'] = trans('admin.configx.element.' . $field['etype']); 108 | if ($field['etype'] == 'normal' && isset($options['__element__'])) { 109 | $field['type_name'] .= '+'; 110 | } 111 | 112 | if (preg_match('/\$admin\$/i', $field['name'])) { 113 | $etype = $field['etype']; 114 | $field = ConfigxModel::firstOrCreate( 115 | ['name' => $field['name']], 116 | [ 117 | 'description' => trans('admin.configx.' . $field['name']), 118 | 'value' => 'do not delete', 119 | ] 120 | ); 121 | $field['etype'] = $etype; 122 | } 123 | if ($field['etype'] == 'table') { 124 | $tableInfo = json_decode($field['description'], 1); 125 | if ($tableInfo) { 126 | $tds = []; 127 | foreach ($tableInfo as $tk => $tv) { 128 | if (!isset($tableFields[$tk])) { 129 | continue; 130 | } 131 | $tds[$tk] = $tableFields[$tk]; 132 | $toptions = []; 133 | if (isset($cx_options[$tk])) { 134 | $toptions = Arr::get($cx_options[$tk], 'options', []); 135 | } 136 | $label = Arr::get($cx_options[$tk], 'name'); 137 | if (!$label) { 138 | $label = trans('admin.configx.' . $tk); 139 | } 140 | $tds[$tk]['label'] = $label; 141 | $tds[$tk]['href'] = admin_base_path('configx/edit/' . $tableFields[$tk]['id']) . '?do=new_config'; 142 | $tds[$tk]['options'] = $toptions; 143 | $tds[$tk]['type_name'] = trans('admin.configx.element.' . $tableFields[$tk]['etype']); 144 | if ($tableFields[$tk]['etype'] == 'normal' && isset($toptions['__element__'])) { 145 | $tds[$tk]['type_name'] .= '+'; 146 | } 147 | } 148 | $field['tds'] = $tds; 149 | } 150 | } 151 | } 152 | } 153 | 154 | return view('configx::tree', [ 155 | 'current_id' => $id, 156 | 'tree' => $tree, 157 | 'editTitle' => $editTitle, 158 | 'deleteTitle' => $deleteTitle, 159 | 'saveTitle' => $saveTitle, 160 | 'tabsEdit' => $tabsEdit, 161 | 'call_back' => admin_base_path('configx/sort'), 162 | 'del_url' => admin_base_path('config'), 163 | 'deleteConfirm' => trans('admin.delete_confirm'), 164 | 'confirm' => trans('admin.confirm'), 165 | 'cancel' => trans('admin.cancel'), 166 | ]); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/Tools/Tool.php: -------------------------------------------------------------------------------- 1 | $v) { 19 | if (preg_match('/^@\w+/', $k)) { 20 | continue; 21 | } 22 | if ($k == 'divide' && in_array($v, ['befor', 'after'])) { 23 | continue; 24 | } 25 | 26 | $_options[$k] = $v; 27 | } 28 | 29 | return $_options; 30 | } 31 | 32 | public static function callUserfunctions($field, $options) 33 | { 34 | foreach ($options as $k => $v) { 35 | if (preg_match('/^@\w+/', $k)) { 36 | $args = array_filter(explode(',', $v)); 37 | $args = collect($args)->map(function ($s) { 38 | $s = trim($s); 39 | if (preg_match('/^\d+$/', $s)) { 40 | return intval($s); 41 | } 42 | if (preg_match('/^\d+\.\d+$/', $s)) { 43 | return doubleval($s); 44 | } 45 | if (preg_match('/^(false|true)$/i', $s)) { 46 | return strtolower($s) == 'true'; 47 | } 48 | return preg_replace("/^\s*['\"]|['\"]\s*$/", '', $s); 49 | })->all(); 50 | 51 | $method = str_replace('@', '', $k); 52 | 53 | try { 54 | call_user_func_array( 55 | [$field, $method], 56 | $args 57 | ); 58 | 59 | if ($field instanceof MultipleFile && strtolower($method) == 'removable') { 60 | 61 | $id = preg_replace('/^c_(\d+)_/i', '$1', $field->column()); 62 | 63 | $field->options(['deleteUrl' => admin_base_path('configx/delfile/' . $id)]); 64 | } 65 | 66 | } catch (\Exception $e) { 67 | admin_warning('Error', "'" . $field->label() . "' call method : " . class_basename($field) . '->' . $method . "('" . implode("','", $args) . "')" . ' failed !
' . $e->getMessage()); 68 | \Log::error($e->__toString()); 69 | } 70 | } 71 | } 72 | 73 | return $field; 74 | } 75 | 76 | public static function remove($cx_options) 77 | { 78 | $forget = []; 79 | foreach (array_keys($cx_options) as $k) { 80 | if ($k == '__configx_tabs__') { 81 | continue; 82 | } 83 | $config = ConfigxModel::where('name', $k)->first(); 84 | if (!$config) { 85 | $forget[] = $k; 86 | } 87 | if (preg_match('/admin_\d+?/i', $k)) { 88 | $forget[] = $k; 89 | } 90 | } 91 | if (count($forget)) { 92 | Arr::forget($cx_options, $forget); 93 | } 94 | return $cx_options; 95 | } 96 | 97 | public static function sortGroupConfigs($configs, $cx_options = []) 98 | { 99 | $order = []; 100 | $i = 0; 101 | foreach ($configs as $conf) { 102 | $i += 1; 103 | if (isset($cx_options[$conf['name']]) && isset($cx_options[$conf['name']]['order'])) { 104 | $order[] = $cx_options[$conf['name']]['order'] ?: 999 + $i; 105 | } else { 106 | $order[] = 999 + $i; 107 | } 108 | } 109 | array_multisort($order, SORT_ASC, $configs); 110 | return $configs; 111 | } 112 | 113 | public static function sortTGroupOptions($type, $cx_options = []) 114 | { 115 | $subs = ConfigxModel::group($type); 116 | 117 | $subs = static::sortGroupConfigs($subs, $cx_options); 118 | 119 | $i = 0; 120 | foreach ($subs as $sub) { 121 | if (!isset($cx_options[$sub['name']]) || empty($cx_options[$sub['name']])) { 122 | $cx_options[$sub['name']] = [ 123 | 'options' => [], 124 | 'element' => 'normal', 125 | 'help' => '', 126 | 'name' => '', 127 | 'order' => 999, 128 | ]; 129 | } 130 | if (isset($cx_options[$sub['name']]) && isset($cx_options[$sub['name']]['table_field'])) { 131 | continue; 132 | } 133 | $i += 5; 134 | $cx_options[$sub['name']]['order'] = $i; 135 | } 136 | 137 | return $cx_options; 138 | } 139 | 140 | public static function createTableConfigs($tableInfo, $cx_options = []) 141 | { 142 | if (empty($tableInfo)) { 143 | return $cx_options; 144 | } 145 | foreach ($tableInfo as $k => $v) { 146 | if ($k == $v || '' == $v) { 147 | $conf = ConfigxModel::where('name', $k)->first(); 148 | if (!$conf) { 149 | ConfigxModel::create(['name' => $k, 'value' => '1', 'description' => 'Table field:' . $k]); 150 | } 151 | } 152 | if (!isset($cx_options[$k])) { 153 | $cx_options[$k] = ['element' => 'normal', 'options' => [], 'help' => '', 'name' => '', 'order' => 999]; 154 | } 155 | if ($k == $v || '' == $v) { 156 | $cx_options[$k]['table_field'] = 1; 157 | } else { 158 | if (isset($cx_options[$k]['table_field'])) { 159 | array_forget($cx_options[$k], 'table_field'); 160 | } 161 | } 162 | } 163 | 164 | return $cx_options; 165 | } 166 | 167 | public static function checkTableKeys($mainKey, $tableInfo) 168 | { 169 | if (empty($tableInfo)) { 170 | return []; 171 | } 172 | 173 | $_tableInfo = []; 174 | foreach ($tableInfo as $k => $v) { 175 | $newKey = $mainKey . preg_replace('/.+(_\d+_\d+)$/', '$1', $k); 176 | 177 | $_tableInfo[$newKey] = $v; 178 | } 179 | 180 | return $_tableInfo; 181 | } 182 | 183 | public static function createPermissions($tabs) 184 | { 185 | foreach ($tabs as $key => $val) { 186 | ConfigxModel::createPermission($key, $val); 187 | } 188 | } 189 | 190 | public static function checkPermission() 191 | { 192 | return Configx::config('check_permission', false); 193 | } 194 | 195 | public static function tabs($cx_options = []) 196 | { 197 | $tabs = Configx::config('tabs', ['base' => 'Base']); 198 | 199 | if (isset($cx_options['__configx_tabs__']) && isset($cx_options['__configx_tabs__']['options'])) { 200 | $tabs = !empty($cx_options['__configx_tabs__']['options']) ? $cx_options['__configx_tabs__']['options'] : $tabs; 201 | } 202 | 203 | foreach ($tabs as $key => &$value) { 204 | 205 | if (empty($value)) { 206 | $value = trans('admin.configx.tabs.' . $key); // if tab name is empty , get from trans 207 | } 208 | } 209 | 210 | return $tabs; 211 | } 212 | 213 | public static function prepareUpdate($fields, $data) 214 | { 215 | $prepared = []; 216 | 217 | foreach ($fields as $field) { 218 | $columns = $field->column(); 219 | 220 | if (!Arr::has($data, $columns)) { 221 | continue; 222 | } 223 | 224 | $value = static::getDataByColumn($data, $field->column()); 225 | 226 | $value = $field->prepare($value); 227 | 228 | if (is_array($columns)) { 229 | 230 | $key = array_values($columns)[0]; 231 | 232 | Arr::set($prepared, $key, implode(',', array_filter(array_values($value)))); 233 | } elseif (is_string($columns)) { 234 | 235 | if ($field instanceof MultipleFile) { 236 | 237 | $value = implode(',', $value); 238 | } else if ($field instanceof MultipleSelect) { 239 | 240 | $value = implode(',', $value); 241 | } 242 | 243 | Arr::set($prepared, $columns, $value); 244 | } 245 | } 246 | 247 | return $prepared; 248 | } 249 | 250 | /** 251 | * Merge validation messages from input validators. 252 | * 253 | * @param \Illuminate\Validation\Validator[] $validators 254 | * 255 | * @return MessageBag 256 | */ 257 | public static function mergeValidationMessages($validators) 258 | { 259 | $messageBag = new MessageBag(); 260 | 261 | foreach ($validators as $validator) { 262 | $messageBag = $messageBag->merge($validator->messages()); 263 | } 264 | 265 | return $messageBag; 266 | } 267 | /** 268 | * @param array $data 269 | * @param string|array $columns 270 | * 271 | * @return array|mixed 272 | */ 273 | public static function getDataByColumn($data, $columns) 274 | { 275 | 276 | if (is_string($columns)) { 277 | 278 | return Arr::get($data, $columns); 279 | } 280 | 281 | if (is_array($columns)) { 282 | $value = []; 283 | foreach ($columns as $name => $column) { 284 | if (!Arr::has($data, $column)) { 285 | continue; 286 | } 287 | $value[$name] = Arr::get($data, $column); 288 | } 289 | 290 | return $value; 291 | } 292 | } 293 | 294 | public static function getConfigx() 295 | { 296 | $__configx__ = ConfigxModel::where('name', '__configx__')->first(); 297 | 298 | if ($__configx__) { 299 | return $__configx__; 300 | } 301 | 302 | return ConfigxModel::create(['name' => '__configx__', 'description' => '', 'value' => 'do not delete']); 303 | } 304 | 305 | } 306 | -------------------------------------------------------------------------------- /resources/views/tips.blade.php: -------------------------------------------------------------------------------- 1 | Help 2 |
3 |
Use texts 4 |
  5 | text1
  6 | text2
  7 | ...
  8 | 
9 | Or key-texts 10 |
 11 | key1 : text1
 12 | key2 : text2
 13 | ...
 14 | 
15 |
16 |
17 | Or load data from url: 18 |
 19 | //Options:
 20 | options_url : /admin/api/mydata
 21 | //Or methods:
 22 | @options : /admin/api/mydata
 23 | 
24 |
25 |
26 |
 27 | //Options:
 28 | rows : 5
 29 | //Or methods:
 30 | @rows : 5
 31 | 
32 |
33 |
34 |
 35 | //Options:
 36 | max : 100
 37 | min : 1
 38 | //Or methods:
 39 | @max : 100
 40 | @min : 1
 41 | 
42 |
43 |
44 |
editor_name : editor 
45 |
46 |
47 |
 48 | //Options:
 49 | format : rgba
 50 | //color format can be : [hex, rgb, rgba]
 51 | //Or methods:
 52 | @hex
 53 | @rgb
 54 | @rgba
 55 | 
 56 | 
57 |
58 |
59 | Common options: 60 |
 61 | divide : after
 62 | //<hr> befor or after this element.
 63 | rules : required|min:3|max:12
 64 | 
65 | Common Methods: 66 |
 67 | @rules : required|min:3|max:12
 68 | @setWidth : 6, 2
 69 | //etc..
 70 | //@methodname : arg1, arg2 ...
 71 | //suported args types [string/integer/folat/boolean]
 72 | 
73 |
74 |
75 | Replace default element: 76 |
 77 | __element__ : mobile
 78 | //__element__ : ip
 79 | //__element__ : url
 80 | //__element__ : email
 81 | //__element__ : currency
 82 | //etc..
 83 | 
84 | Extend element: 85 |
 86 | __element__ : test_text
 87 | 
 88 | //Extend element in Admin/bootstrap.php :
 89 | Form::extend('new_element', App/Admin/Extensions/NewElement::class);
 90 | //Useage
 91 | __element__ : new_element
 92 | 
93 |
94 |
95 | //Image Methods: 96 |
 97 | @uniqueName
 98 | @sequenceName
 99 | @removable
100 | @move : newdir, newname
101 | @dir : newdir
102 | @name : newname
103 | @resize : 320, 240
104 | @insert : storage/public/watermark.png ,center
105 | @crop : 320, 240, 0, 0
106 | //etc..
107 | 
108 | //Some methods require intervention/image [installation] 109 |
110 | //Usage : [Intervention] 111 |
112 |
113 | //File Methods: 114 |
115 | @uniqueName
116 | @sequenceName
117 | @removable
118 | @downloadable
119 | @retainable
120 | //multiple
121 | @sortable
122 | @move : newdir, newname
123 | @dir : newdir
124 | @name : newname
125 | //etc..
126 | 
127 |
128 |
129 |
130 | //To use map ,you need to edit configs first.
131 | //map_provider in /config/admin.php
132 | //TENCENT_MAP_API_KEY or GOOGLE_API_KEY in /.env
133 | 
134 |
135 |
136 |
137 | rows : 3
138 | cols : 3
139 | 
140 | Build table 141 |
142 | 143 |
144 | 145 |
146 | 147 |
148 | 309 | -------------------------------------------------------------------------------- /src/Tools/Updater.php: -------------------------------------------------------------------------------- 1 | 0) { 31 | admin_toastr(trans('admin.update_succeeded')); 32 | return redirect()->to(admin_base_path('configx/edit/' . $id) . '?do=new_config'); 33 | } else { 34 | admin_toastr(trans('admin.save_succeeded')); 35 | return redirect()->to(admin_base_path('configx/edit/0') . '?do=new_config'); 36 | } 37 | } 38 | 39 | public static function saveTabsOptions($id = 0, $tabs_options) 40 | { 41 | $cx_options = static::cxOptions(); 42 | 43 | $c_options = explode(PHP_EOL, $tabs_options); 44 | $arr = []; 45 | foreach ($c_options as $op) { 46 | $kv = explode(":", $op); 47 | if (empty($kv[0])) { 48 | continue; 49 | } 50 | if (count($kv) > 1) { 51 | $arr[trim($kv[0])] = trim($kv[1]); 52 | } else { 53 | $arr[trim($kv[0])] = ''; 54 | } 55 | } 56 | $cx_options['__configx_tabs__']['options'] = $arr; 57 | 58 | static::cxSave($cx_options); 59 | 60 | admin_toastr(trans('admin.save_succeeded')); 61 | 62 | Session::put('tabindex', '0'); 63 | 64 | return redirect()->to(admin_base_path('configx/edit/' . $id) . '?do=tabs_config'); 65 | } 66 | 67 | public static function saveConfigs($id = 0, $request) 68 | { 69 | $cx_options = static::cxOptions(); 70 | 71 | $tabs = Tool::tabs($cx_options); 72 | 73 | Session::put('tabindex', $request->input('tabindex', 0)); 74 | 75 | Tree::getConfigTabs($tabs, $cx_options); 76 | 77 | $rootConfigs = array_values(Tree::getTree()); 78 | 79 | $data = $request->all(); 80 | 81 | $failedValidators = []; 82 | 83 | $fields = []; 84 | 85 | foreach ($rootConfigs as $root) { 86 | 87 | foreach ($root as $val) { 88 | 89 | $field = Builder::getConfigField($cx_options, $val, Builder::UPDATE); 90 | 91 | $fields[] = $field; 92 | 93 | if ($field instanceof Collect) { 94 | $subFields = $field->getFields(); 95 | 96 | foreach ($subFields as $sub) { 97 | 98 | if ($sub instanceof MultipleFile) { 99 | 100 | $sub->setOriginal([$sub->column() => explode(',', $val['value'])]); 101 | } 102 | 103 | $fields[] = $sub; 104 | } 105 | } else if ($field instanceof MultipleFile) { 106 | 107 | $field->setOriginal([$field->column() => explode(',', $val['value'])]); // fiels = old + new uploads, suport 108 | } 109 | 110 | $validator = $field->getValidator($data); 111 | 112 | if ($validator && ($validator instanceof Validator) && !$validator->passes()) { 113 | 114 | $errors[] = $val['name']; 115 | 116 | $failedValidators[] = $validator; 117 | } 118 | } 119 | } 120 | 121 | $data = static::handleFileSort($data); 122 | 123 | $prepare = Tool::prepareUpdate($fields, $data); 124 | 125 | $message = Tool::mergeValidationMessages($failedValidators); 126 | 127 | if(!(Configx::config('break_when_errors', false) && $message->any())) 128 | { 129 | admin_toastr(trans('admin.update_succeeded')); 130 | \DB::beginTransaction(); 131 | 132 | foreach ($prepare as $column => $value) { 133 | 134 | static::saveValue($column, $value); 135 | } 136 | 137 | static::cxSave($cx_options); 138 | 139 | \DB::commit(); 140 | } 141 | 142 | if ($message->any()) { 143 | 144 | static::getErrorIndex($errors[0], $failedValidators[0]->messages()->first(), $cx_options); 145 | 146 | return redirect()->to(admin_base_path('configx/edit/' . $id))->withErrors($message->getMessages())->withInput(); 147 | } 148 | 149 | return redirect()->to(admin_base_path('configx/edit/' . $id)); 150 | } 151 | 152 | /** 153 | * @param array $input 154 | * 155 | * @return array 156 | */ 157 | protected static function handleFileSort($input = []) 158 | { 159 | if (!array_key_exists(Field::FILE_SORT_FLAG, $input)) { 160 | return $input; 161 | } 162 | 163 | $sorts = array_filter($input[Field::FILE_SORT_FLAG]); 164 | 165 | if (empty($sorts)) { 166 | return $input; 167 | } 168 | 169 | foreach ($sorts as $column => $order) { 170 | $input[$column] = $order; 171 | } 172 | 173 | request()->replace($input); 174 | 175 | return $input; 176 | } 177 | 178 | protected static function getErrorIndex($first, $msg, $cx_options) 179 | { 180 | admin_warning('Error', $msg); 181 | 182 | $tabs = Tool::tabs($cx_options); 183 | $tab = explode('.', $first); 184 | if (count($tabs) && count($tab)) { 185 | $errorKey = $tab[0]; 186 | $i = 0; 187 | foreach ($tabs as $k => $v) { 188 | if ($k == $errorKey) { 189 | Session::put('tabindex', $i); 190 | break; 191 | } 192 | $i += 1; 193 | } 194 | } 195 | } 196 | 197 | protected static function saveValue($columns, $value) 198 | { 199 | if ($value == null || $value == '') { 200 | return; 201 | } 202 | 203 | $key = $columns; 204 | 205 | if (is_array($columns)) { 206 | 207 | $key = array_values($columns)[0]; 208 | } 209 | 210 | $id = preg_replace('/^c_(\d+)_/i', '$1', $key); 211 | 212 | $config = ConfigxModel::findOrFail($id); 213 | 214 | $config->value = $value; 215 | $config->update(); 216 | } 217 | 218 | public static function saveOptions($id = 0, $cx_options, Request $request) 219 | { 220 | if (empty($request->values['c_type']) || empty($request->values['c_key'])) { 221 | admin_warning('Error', trans('admin.input') . ' ' . trans('admin.configx.new_config_type') . ' / ' . trans('admin.configx.new_config_key')); 222 | return redirect()->back()->withInput(); 223 | } 224 | $config = []; 225 | $defaultVal = "1"; 226 | if ($id > 0) { 227 | $config = ConfigxModel::findOrFail($id); 228 | $new_key = $config['name']; 229 | } else { 230 | $new_key = $request->values['c_key']; 231 | if (!preg_match('/^' . $request->values['c_type'] . '\.\w{1,}/', $new_key)) { 232 | $new_key = $request->values['c_type'] . '.' . $new_key; 233 | } 234 | if (ConfigxModel::where('name', $new_key)->first()) { 235 | admin_warning('Error', "The key `{$new_key}` exists in table."); 236 | return redirect()->back()->withInput(); 237 | } 238 | if ($request->values['c_element'] == "date") { 239 | $defaultVal = "2019-01-01"; 240 | } else if ($request->values['c_element'] == "datetime") { 241 | $defaultVal = "2019-01-01 01:01:01"; 242 | } else if ($request->values['c_element'] == "icon") { 243 | $defaultVal = "fa-code"; 244 | } else if ($request->values['c_element'] == "color") { 245 | $defaultVal = "#ccc"; 246 | } else if ($request->values['c_element'] == "multiple_image") { 247 | $defaultVal = '-'; 248 | } 249 | } 250 | if (!isset($cx_options[$new_key]) || empty($cx_options[$new_key])) { 251 | $cx_options[$new_key] = [ 252 | 'options' => [], 253 | 'element' => 'normal', 254 | 'help' => '', 255 | 'name' => '', 256 | 'order' => 999, 257 | ]; 258 | } 259 | $table_field = isset($cx_options[$new_key]['table_field']); 260 | $order = Arr::get($cx_options[$new_key], 'order', 999); 261 | if (!empty($request->values['c_options'])) { 262 | $c_options = explode(PHP_EOL, $request->values['c_options']); 263 | $arr = []; 264 | foreach ($c_options as $op) { 265 | $kv = explode(":", $op); 266 | if (empty($kv[0])) { 267 | continue; 268 | } 269 | if (count($kv) == 2) { 270 | $arr[trim($kv[0])] = trim($kv[1]); 271 | } else if (count($kv) > 2) { 272 | $values = array_slice($kv, 1); 273 | $arr[trim($kv[0])] = trim(implode(':', $values)); 274 | } else { 275 | $arr[trim($kv[0])] = trim($kv[0]); 276 | } 277 | } 278 | $cx_options[$new_key] = ['options' => $arr, 'element' => $request->values['c_element'], 'help' => $request->values['c_help'], 'name' => $request->values['c_name'], 'order' => 999]; 279 | $keys = array_keys($arr); 280 | if ($request->values['c_element'] == "table") { 281 | $defaultVal = 'do not delete'; 282 | } else if (in_array( 283 | $request->values['c_element'], 284 | ['radio_group', 'checkbox_group', 'select', 'multiple_select', 'listbox'] 285 | ) && $keys) { 286 | $defaultVal = $keys[0]; 287 | } 288 | } else { 289 | if (in_array($request->values['c_element'], ['radio_group', 'checkbox_group', 'select', 'table', 'multiple_select', 'listbox'])) { 290 | admin_warning('Error', "The options is empty!"); 291 | return redirect()->back()->withInput(); 292 | } else { 293 | $cx_options[$new_key] = ['options' => [], 'element' => $request->values['c_element'], 'help' => $request->values['c_help'], 'name' => $request->values['c_name'], 'order' => 999]; 294 | } 295 | } 296 | if ($request->values['c_element'] == 'table' && empty($request->table)) { 297 | admin_warning('Error', "Build table befor save!"); 298 | return redirect()->back()->withInput(); 299 | } 300 | if ($id == 0) { 301 | if (preg_match('/\$admin\$/i', $new_key)) { 302 | $defaultVal = 'do not delete'; 303 | } 304 | $data = ['name' => $new_key, 'value' => $defaultVal, 'description' => $request->values['c_name'] ?: trans('admin.configx.' . $new_key)]; 305 | if ($request->values['c_element'] == "table") { 306 | $table = Tool::checkTableKeys($new_key, $request->table); 307 | $cx_options = Tool::createTableConfigs($table, $cx_options); 308 | $data['description'] = json_encode($table); 309 | } 310 | $config = new ConfigxModel($data); 311 | $config->save(); 312 | $c_type = $request->values['c_type']; 313 | } else { 314 | $data = ['name' => $new_key]; 315 | if ($request->values['c_element'] == "table") { 316 | $table = Tool::checkTableKeys($new_key, $request->table); 317 | $cx_options = Tool::createTableConfigs($table, $cx_options); 318 | $data['description'] = json_encode($table); 319 | } 320 | $config->update($data); 321 | $c_type = $config->getPrefix(); 322 | } 323 | $tabs = Tool::tabs($cx_options); 324 | if (count($tabs)) { 325 | $i = 0; 326 | foreach ($tabs as $k => $v) { 327 | if ($k == $c_type) { 328 | Session::put('tabindex', $i); 329 | break; 330 | } 331 | $i += 1; 332 | } 333 | } 334 | if ($table_field) { 335 | $cx_options[$new_key]['table_field'] = 1; 336 | } 337 | if ($order) { 338 | $cx_options[$new_key]['order'] = $order; 339 | } 340 | 341 | $cx_options = Tool::sortTGroupOptions($c_type, $cx_options); 342 | 343 | return $cx_options; 344 | } 345 | 346 | protected static function cxOptions() 347 | { 348 | $__configx__ = Tool::getConfigx(); 349 | 350 | $cx_options = []; 351 | 352 | if ($__configx__ && $__configx__['description']) { 353 | 354 | $cx_options = json_decode($__configx__['description'], 1); 355 | } 356 | 357 | return $cx_options; 358 | } 359 | 360 | protected static function cxSave($cx_options) 361 | { 362 | $__configx__ = Tool::getConfigx(); 363 | 364 | $cx_options = Tool::remove($cx_options); 365 | $__configx__['description'] = json_encode($cx_options); 366 | $__configx__->save(); 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /src/Tools/Builder.php: -------------------------------------------------------------------------------- 1 | [], 26 | 'element' => 'normal', 27 | 'help' => '', 28 | 'name' => '', 29 | 'order' => 999, 30 | ]; 31 | } 32 | 33 | $field = static::getConfigField($cx_options, $val, static::EDIT); 34 | 35 | if (in_array($val['id'], ['type', 'key', 'element'])) { 36 | $field->setLabelClass(['asterisk']); 37 | } 38 | 39 | if (isset($cx_options[$val['name']]['options'])) { 40 | $options = Arr::get($cx_options[$val['name']], 'options', []); 41 | if (isset($options['divide'])) { 42 | if ($options['divide'] == 'befor') { 43 | return '
' . $field->render(); 44 | } else { 45 | return $field->render() . '
'; 46 | } 47 | } 48 | } 49 | return $field->render(); 50 | } 51 | 52 | public static function createNewConfigForm($val, $tabs, $cx_options = [], $config = []) 53 | { 54 | if (!isset($cx_options[$val['name']])) { 55 | $cx_options[$val['name']] = []; 56 | } 57 | 58 | $label = trans('admin.configx.' . $val['name']); 59 | 60 | if ($config) { 61 | $editName = $config['name']; 62 | } 63 | 64 | $rowname = 'values.c_' . $val['id']; 65 | 66 | if ($val['id'] == 'type') { 67 | if ($config) { 68 | $field = new Text($rowname, [$label]); 69 | $field->readOnly(); 70 | $typekey = explode('.', $editName)[0]; 71 | $typename = Arr::get($tabs, $typekey); 72 | if (empty($typename)) { 73 | $typename = trans('admin.configx.tabs.' . $typekey); // if tab name is empty , get from trans 74 | } 75 | if (isset($cx_options[$config['name']]['table_field'])) { 76 | $tableKey = preg_replace('/^(\w+?\.\w+?)_\d+_\d+$/i', '$1', $editName); 77 | if ($cx_options && isset($cx_options[$tableKey]) && isset($cx_options[$tableKey]['name'])) { 78 | $typename .= '-' . $cx_options[$tableKey]['name']; 79 | } else { 80 | $typename .= '-' . trans('admin.configx.' . $tableKey); 81 | } 82 | } 83 | $field->value($typename); 84 | // 85 | } else { 86 | $field = new Radio($rowname, [$label]); 87 | 88 | $field->options($tabs) 89 | ->setWidth(9, 2); 90 | } 91 | } else if ($val['id'] == 'key' || $val['id'] == 'tabs_key') { 92 | $field = new Text($rowname, [$label]); 93 | 94 | if ($val['id'] == 'tabs_key') { 95 | $field->value('__configx_tabs__'); 96 | $field->readOnly(); 97 | } else { 98 | if ($config) { 99 | $field->readOnly(); 100 | $field->value($editName); 101 | } 102 | } 103 | } else if ($val['id'] == 'name') { 104 | $field = new Text($rowname, [$label]); 105 | if ($config) { 106 | if ($config && isset($cx_options[$config['name']]['name'])) { 107 | $field->value($cx_options[$config['name']]['name']); 108 | } 109 | } 110 | } else if ($val['id'] == 'element') { 111 | $field = new Radio($rowname, [$label]); 112 | $elements = [ 113 | 'normal', 'date', 'time', 'datetime', 'image', 'multiple_image', 'password', 'file', 'multiple_file', 114 | 'yes_or_no', 'rate', 'editor', 'tags', 'icon', 'color', 'number', 'table', 'textarea', 115 | 'radio_group', 'checkbox_group', 'listbox', 'select', 'multiple_select', 'map', 116 | ]; 117 | if ($config && isset($cx_options[$config['name']]['table_field'])) { 118 | array_delete($elements, 'table'); 119 | } 120 | $support = []; 121 | foreach ($elements as $el) { 122 | $support[$el] = trans('admin.configx.element.' . $el); 123 | } 124 | $field->options($support) 125 | ->default('normal') 126 | ->setWidth(9, 2); 127 | if ($config && isset($cx_options[$config['name']]['element'])) { 128 | $field->value($cx_options[$config['name']]['element']); 129 | } 130 | } else if ($val['id'] == 'help') { 131 | $field = new Text($rowname, [$label]); 132 | if ($config && isset($cx_options[$config['name']]['help'])) { 133 | $field->value($cx_options[$config['name']]['help']); 134 | } 135 | } else if ($val['id'] == 'options' || $val['id'] == 'tabs_options') { 136 | $field = new Textarea($rowname, [$label]); 137 | $table = \Request::old('table'); 138 | if ( 139 | !$table && $config && isset($cx_options[$config['name']]['element']) 140 | && $cx_options[$config['name']]['element'] == 'table' 141 | && $config['description'] 142 | ) { 143 | $table = json_decode($config['description']); 144 | } 145 | if ($val['id'] == 'options') { 146 | $field->help(view('configx::tips', ['table' => $table])); 147 | if ($config && isset($cx_options[$config['name']]['options'])) { 148 | $arr = []; 149 | foreach ($cx_options[$config['name']]['options'] as $k => $v) { 150 | if ($k == $v) { 151 | $arr[] = $k; 152 | } else { 153 | $arr[] = $k . ' : ' . $v; 154 | } 155 | } 156 | $field->value(implode(PHP_EOL, $arr)); 157 | } 158 | } else { 159 | $field->help(view('configx::tabs')); 160 | $tabs = Configx::config('tabs', ['base' => 'Base']); 161 | if (isset($cx_options['__configx_tabs__']) && isset($cx_options['__configx_tabs__']['options'])) { 162 | $tabs = !empty($cx_options['__configx_tabs__']['options']) ? $cx_options['__configx_tabs__']['options'] : $tabs; 163 | } 164 | $arr = []; 165 | foreach ($tabs as $k => $v) { 166 | $arr[] = $k . ' : ' . $v; 167 | } 168 | $field->value(implode(PHP_EOL, $arr)); 169 | } 170 | 171 | $field->rows(5); 172 | } 173 | 174 | if (in_array($val['id'], ['type', 'key', 'element'])) { 175 | $field->setLabelClass(['asterisk']); 176 | } 177 | 178 | $field->setWidgetForm(Displayer::getFormWgt()); 179 | 180 | return $field->render(); 181 | } 182 | 183 | public static function getConfigField($cx_options = [], $val, $mode) 184 | { 185 | $rowname = 'c_' . $val['id'] . '_'; 186 | 187 | if (!isset($cx_options[$val['name']]) || empty($cx_options[$val['name']])) { 188 | $cx_options[$val['name']] = [ 189 | 'options' => [], 190 | 'element' => 'normal', 191 | 'help' => '', 192 | 'name' => '', 193 | 'order' => 999, 194 | ]; 195 | } 196 | 197 | $label = Arr::get($cx_options[$val['name']], 'name', ''); 198 | if (!$label) { 199 | $label = trans('admin.configx.' . $val['name']); 200 | } 201 | 202 | if (!key_exists($val['name'], $cx_options)) { 203 | $cx_options[$val['name']] = []; 204 | } 205 | 206 | $etype = Arr::get($cx_options[$val['name']], 'element', 'normal'); 207 | $options = Arr::get($cx_options[$val['name']], 'options', []); 208 | 209 | //create field 210 | if ($etype == 'table') { 211 | if ( 212 | $val['description'] && isset($options['rows']) 213 | && isset($options['cols']) 214 | ) { 215 | $field = static::buildTable($val, $label, $options, $cx_options, $mode); 216 | } else { 217 | $field = new html('Table error cols and rows is required.', [$label]); 218 | } 219 | return $field; 220 | } 221 | 222 | $trueType = static::trueType($etype, $options); 223 | 224 | if ($etype == 'map') { 225 | $field = static::findField($trueType, $rowname . 'lat', [$rowname . 'lng', $label]); 226 | } else { 227 | $field = static::findField($trueType, $rowname, [$label]); 228 | } 229 | 230 | if (!$field) { 231 | if ($etype == 'editor') { 232 | $field = new Textarea($rowname, [$label]); 233 | } else { 234 | $field = new Text($rowname, [$label]); 235 | } 236 | 237 | $msg = ''; 238 | 239 | if ($etype == 'editor') { 240 | 241 | $msg = 'editor[' . Arr::get($options, 'editor_name', 'editor') . ']'; 242 | 243 | } else if ($etype == 'normal') { 244 | 245 | $msg = '[' . Arr::get($options, '__element__', 'text') . ']'; 246 | } else { 247 | $msg = '[' . $etype . ']'; 248 | } 249 | 250 | $field->help('The ' . $msg . ' is unuseable!'); 251 | 252 | unset($cx_options[$val['name']]['help']); 253 | 254 | $options = []; 255 | } 256 | 257 | //init field 258 | if ($etype == 'textarea') { 259 | 260 | if (isset($options['rows'])) { 261 | $field->rows($options['rows']); 262 | } 263 | } else if ($etype == 'yes_or_no') { 264 | 265 | $field->options(['1' => trans('admin.yes'), '0' => trans('admin.no')]); 266 | } else if ($etype == 'number') { 267 | 268 | if (isset($options['max'])) { 269 | $field->max($options['max']); 270 | } 271 | if (isset($options['min'])) { 272 | $field->min($options['min']); 273 | } 274 | } else if (in_array($etype, ['radio_group', 'checkbox_group', 'listbox', 'select', 'multiple_select'])) { 275 | 276 | if (in_array($etype, ['select', 'multiple_select']) && isset($options['options_url'])) { 277 | $field->options($options['options_url']); 278 | } else { 279 | $field->options(Tool::optionsFilter($options)); 280 | } 281 | } else if ($etype == 'color') { 282 | 283 | if (isset($options['format'])) { 284 | $field->options(['format' => $options['format']]); 285 | } 286 | } 287 | if (isset($options['rules'])) { 288 | $field->rules($options['rules']); 289 | } 290 | $field->setWidgetForm(Displayer::getFormWgt()); 291 | // fill value 292 | 293 | if ($mode == static::EDIT) { 294 | static::fillData($field, $etype, $rowname, $val['value']); 295 | } 296 | 297 | if (isset($cx_options[$val['name']]['help']) && !empty($cx_options[$val['name']]['help'])) { 298 | $field->help($cx_options[$val['name']]['help']); 299 | } 300 | 301 | if ($options) { 302 | 303 | $field = Tool::callUserfunctions($field, $options); 304 | 305 | if (!in_array($etype, ['image', 'file', 'multiple_image', 'multiple_file'])) { 306 | 307 | if ($mode == static::UPDATE) { 308 | $field->rules('required'); 309 | } 310 | } 311 | 312 | } else { 313 | 314 | if (in_array($etype, ['image', 'file', 'multiple_image', 'multiple_file'])) { 315 | 316 | $field->uniqueName(); 317 | } else { 318 | if ($mode == static::UPDATE) { 319 | $field->rules('required'); 320 | } 321 | } 322 | } 323 | 324 | return $field; 325 | } 326 | 327 | protected static function fillData($field, $etype, $rowname, $value) 328 | { 329 | $columns = $field->column(); 330 | 331 | if (is_array($columns)) { 332 | 333 | $values = explode(',', $value); 334 | 335 | $data = []; 336 | 337 | $i = 0; 338 | foreach ($columns as $name => $column) { 339 | $data[$column] = Arr::get($values, $i, '-'); 340 | $i += 1; 341 | } 342 | 343 | $field->fill($data); 344 | 345 | } else if (in_array($etype, ['checkbox_group', 'multiple_select', 'listbox', 'multiple_image', 'multiple_file'])) { 346 | $values = array_filter(explode(',', $value)); 347 | 348 | if ($value && count($values)) { 349 | $field->value($values); 350 | } 351 | } else { 352 | $field->fill([$rowname => $value]); 353 | } 354 | } 355 | 356 | protected static function buildTable($val, $label, $options, $cx_options, $mode) 357 | { 358 | $tableInfo = json_decode($val['description'], 1); 359 | if ($tableInfo) { 360 | Tool::createTableConfigs($tableInfo, $cx_options); 361 | } 362 | $field = new Table($label); 363 | $rows = []; 364 | for ($i = 0; $i < $options['rows']; $i += 1) { 365 | $tableRow = new TableRow(); 366 | for ($j = 0; $j < $options['cols']; $j += 1) { 367 | $fieldKey = $val['name'] . '_' . $i . '_' . $j; 368 | if (!key_exists($fieldKey, $tableInfo)) { 369 | continue; 370 | } 371 | if ($tableInfo[$fieldKey] == $fieldKey || '' == $tableInfo[$fieldKey]) { 372 | 373 | $conf = ConfigxModel::where('name', $fieldKey)->first(); 374 | if ($conf) { 375 | 376 | $etype = Arr::get($cx_options[$conf['name']], 'element', 'normal'); 377 | 378 | if ($etype == 'normal' && $options['cols'] > 8) { 379 | Arr::set($cx_options[$conf['name']], 'element', 'textSmall'); 380 | } 381 | 382 | if (!isset($cx_options[$conf['name']])) { 383 | $cx_options[$conf['name']] = [ 384 | 'options' => [], 385 | 'element' => 'normal', 386 | 'help' => '', 387 | 'name' => '', 388 | 'order' => 999, 389 | ]; 390 | } 391 | 392 | $tableField = static::getConfigField($cx_options, $conf, $mode); 393 | 394 | $tableRow->pushField($tableField, 1); 395 | } 396 | } else { 397 | $text = $tableInfo[$fieldKey]; 398 | if (preg_match('/^trans\.(\w+)/i', $text, $mchs)) { //if text is trans.sometext , get from trans : trans("admin.configx.{$tab}.{$tablekey}.{$sometext}") 399 | $text = trans("admin.configx.{$val['name']}.{$mchs[1]}"); 400 | } 401 | $tableRow->show($text)->Textalign('center'); 402 | } 403 | } 404 | $rows[] = $tableRow; 405 | } 406 | $field->setRows($rows); 407 | $field->setErrorKey($val['name']); 408 | 409 | return $field; 410 | } 411 | 412 | protected static function trueType($type, $options = []) 413 | { 414 | $type =trim($type); 415 | 416 | if(empty($type)) 417 | { 418 | $type == 'normal'; 419 | } 420 | 421 | if ($type == 'editor') { 422 | 423 | $type = Arr::get($options, 'editor_name', 'editor'); 424 | } else if ($type == 'normal') { 425 | 426 | $type = Arr::get($options, '__element__', 'text'); 427 | } else if ($type == 'radio_group' || $type == 'yes_or_no') { 428 | 429 | $type = 'radio'; 430 | } else if ($type == 'checkbox_group') { 431 | 432 | $type = 'checkbox'; 433 | } else if ($type == 'multiple_select') { 434 | 435 | $type = 'multipleSelect'; 436 | } else if ($type == 'multiple_image') { 437 | 438 | $type = 'multipleImage'; 439 | } else if ($type == 'multiple_file') { 440 | 441 | $type = 'multipleFile'; 442 | } 443 | 444 | return $type; 445 | } 446 | 447 | protected static function findField($type, $column, $args) 448 | { 449 | if ($className = Form::findFieldClass($type)) { 450 | $element = new $className($column, $args); 451 | 452 | return $element; 453 | } 454 | 455 | return null; 456 | } 457 | } 458 | --------------------------------------------------------------------------------