abstract forms for options

This commit is contained in:
printempw 2016-11-12 23:50:41 +08:00
parent fdf2a82e2e
commit be4af844fe
13 changed files with 425 additions and 216 deletions

View File

@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use View;
use Utils;
use Option;
use App\Models\User;
use App\Models\Player;
use App\Models\Texture;
@ -31,7 +32,53 @@ class AdminController extends Controller
public function options()
{
return view('admin.options');
$general = Option::form('general', '常规选项', function($form)
{
$form->text('site_name', '站点标题');
$form->text('site_description', '站点描述');
$form->text('site_url', '站点地址URL')->hint('以 http(s):// 开头,不要以 / 结尾');
$form->checkbox('user_can_register', '开放注册', '任何人都可以注册');
$form->text('regs_per_ip', '每个 IP 限制注册数');
$form->group('max_upload_file_size', '最大允许上传大小', function($group) {
// main textbox
$group->text('max_upload_file_size', option('max_upload_file_size'));
$group->addon('KB');
})->hint('PHP 限制:'.ini_get('post_max_size').',定义在 php.ini 中。');
$form->checkbox('allow_chinese_playername', '角色名', '允许中文角色名');
$form->select('api_type', '首选 JSON API', function($options) {
$options->add('0', 'CustomSkinLoader API');
$options->add('1', 'UniversalSkinAPI');
$options->setSelected(option('api_type'));
});
$form->checkbox('auto_del_invalid_texture', '失效材质', '自动删除失效材质')->hint('自动从皮肤库中删除文件不存在的材质记录');
$form->textarea('comment_script', '评论代码', option('comment_script'), function($textarea) {
$textarea->setRows(6);
$textarea->setDescription('评论代码内可使用占位符,<code>{tid}</code> 将会被自动替换为材质的 id<code>{name}</code> 会被替换为材质名称,<code>{url}</code> 会被替换为当前页面地址。');
});
})->handle(function() {
if (substr($_POST['site_url'], -1) == "/")
$_POST['site_url'] = substr($_POST['site_url'], 0, -1);
});
$cache = Option::form('cache', '缓存相关配置', function($form)
{
$form->checkbox('avatar_query_string', '头像缓存', '为头像添加 Query String');
$form->checkbox('auto_detect_asset_url', '资源地址', '自动判断资源文件地址')->hint('根据当前 URL 自动加载资源文件。如果出现 CDN 回源问题请关闭');
$form->checkbox('return_200_when_notfound', 'HTTP 响应码', '请求不存在的角色时返回 200 而不是 404');
$form->text('cache_expire_time', '缓存失效时间')->hint('秒数86400 = 一天31536000 = 一年');
})->hint('如果启用了 CDN 缓存请适当修改这些配置')->handle();
return view('admin.options')->with('forms', compact('general', 'cache'));
}
/**

View File

@ -2,7 +2,8 @@
namespace App\Services\Facades;
use \Illuminate\Support\Facades\Facade;
use App\Services\OptionForm;
use Illuminate\Support\Facades\Facade;
class Option extends Facade
{
@ -15,4 +16,13 @@ class Option extends Facade
{
return 'option';
}
public static function form($id, $title, $callback)
{
$form = new OptionForm($id, $title);
call_user_func($callback, $form);
return $form;
}
}

304
app/Services/OptionForm.php Normal file
View File

@ -0,0 +1,304 @@
<?php
namespace App\Services;
use Option;
use Illuminate\Support\Arr;
class OptionForm
{
public $id;
public $title;
protected $hint;
protected $items;
protected $success = false;
public function __construct($id, $title)
{
$this->id = $id;
$this->title = $title;
}
public function text($id, $name, $value = null)
{
return $this->addItem('text', $id, $name);
}
public function checkbox($id, $name, $label, $checked = null)
{
$checkbox = $this->addItem('checkbox', $id, $name);
$checkbox->set('label', $label);
return $checkbox;
}
public function select($id, $name, $callback)
{
$item = $this->addItem('select', $id, $name);
$select = new OptionFormSelect($id);
call_user_func($callback, $select);
$item->set('view', $select->render());
return $select;
}
public function textarea($id, $name, $value, $callback)
{
$item = $this->addItem('textarea', $id, $name);
$textarea = new OptionFormTextarea($id, $value);
call_user_func($callback, $textarea);
$item->set('view', $textarea->render());
return $textarea;
}
public function group($id, $name, $callback)
{
$item = $this->addItem('group', $id, $name);
$group = new OptionFormGroup($id);
call_user_func($callback, $group);
$item->set('view', $group->render());
return $item;
}
public function addItem($type, $id, $name)
{
$item = new OptionFormItem($id, $name, $type);
$this->items[] = $item;
return $item;
}
public function hint($hint_content)
{
$this->hint = view('vendor.option-form.hint')->with('hint', $hint_content)->render();
return $this;
}
public function handle($callback = null)
{
if (Arr::get($_POST, 'option') == $this->id) {
if (!is_null($callback)) {
call_user_func($callback);
}
foreach ($this->items as $item) {
if ($item->type == "checkbox" && !isset($_POST[$item->id])) {
$_POST[$item->id] = "0";
}
if ($_POST[$item->id] != option($item->id)) {
Option::set($item->id, $_POST[$item->id]);
}
}
$this->success = true;
}
return $this;
}
public function render()
{
foreach ($this->items as $item) {
$id = $item->id;
$value = Option::get($item->id);
switch ($item->type) {
case 'text':
$view = view('vendor.option-form.text')->with(compact('id', 'value'));
break;
case 'checkbox':
$view = view('vendor.option-form.checkbox')->with([
'id' => $id,
'label' => $item->get('label'),
'checked' => (bool) $value
]);
break;
case 'select':
case 'textarea':
case 'group':
$view = $item->get('view');
break;
}
$item->setContent($view->render());
}
return view('vendor.option-form.main')->with([
'title' => $this->title,
'id' => $this->id,
'hint' => $this->hint,
'items' => $this->items,
'success' => $this->success
])->render();
}
}
class OptionFormItem
{
public $id;
public $type;
public $title;
protected $data;
protected $hint;
protected $content;
public function __construct($id, $title, $type = "")
{
$this->id = $id;
$this->type = $type;
$this->title = $title;
}
public function hint($hint_content)
{
$this->hint = view('vendor.option-form.hint')->with('hint', $hint_content)->render();
return $this;
}
public function setContent($content)
{
$this->content = $content;
return $this;
}
public function set($key, $value)
{
$this->data[$key] = $value;
}
public function get($key)
{
return Arr::get($this->data, $key);
}
public function render()
{
return view('vendor.option-form.item')->with([
'title' => $this->title,
'content' => $this->content,
'hint' => $this->hint
]);
}
}
class OptionFormSelect
{
protected $id;
protected $items;
protected $selected;
public function __construct($id)
{
$this->id = $id;
}
public function add($id, $name)
{
$this->items[] = [$id, $name];
}
public function setSelected($id)
{
$this->selected = $id;
}
public function render()
{
return view('vendor.option-form.select')->with([
'id' => $this->id,
'items' => $this->items,
'selected' => $this->selected
]);
}
}
class OptionFormTextarea
{
protected $id;
protected $value;
protected $rows = 3;
protected $description = "";
public function __construct($id, $value)
{
$this->id = $id;
$this->value = $value;
}
public function setRows($rows)
{
$this->rows = $rows;
}
public function setDescription($description)
{
$this->description = $description;
}
public function render()
{
return view('vendor.option-form.textarea')->with([
'rows' => $this->rows,
'id' => $this->id,
'value' => $this->value,
'description' => $this->description
]);
}
}
class OptionFormGroup
{
protected $id;
protected $items = [];
public function __construct($id)
{
$this->id = $id;
}
public function text($id, $value)
{
$this->items[] = view('vendor.option-form.text')->withId($id)->withValue($value);
}
public function addon($value)
{
$this->items[] = view('vendor.option-form.addon')->withValue($value);
}
public function render()
{
return view('vendor.option-form.group')->with('items', $this->items);
}
}

View File

@ -2,13 +2,6 @@
@section('title', trans('general.options'))
@section('style')
<style type="text/css">
.box-body > textarea { height: 200px; }
.description { margin: 7px 0 0 0; color: #555; }
</style>
@endsection
@section('content')
<!-- Content Wrapper. Contains page content -->
@ -26,134 +19,7 @@
<div class="row">
<div class="col-md-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">常规选项</h3>
</div><!-- /.box-header -->
<form method="post">
<input type="hidden" name="option" value="general">
<div class="box-body">
<?php
if (isset($_POST['option']) && ($_POST['option'] == "general")) {
// pre-set some options because they will not be posted if not checked
$presets = [
'user_can_register',
'allow_chinese_playername',
'auto_del_invalid_texture'
];
foreach ($presets as $key) {
$_POST[$key] = isset($_POST[$key]) ? $_POST[$key] : "0";
}
foreach ($_POST as $key => $value) {
// remove slash if site_url is ended with slash
if ($key == "site_url" && substr($value, -1) == "/")
$value = substr($value, 0, -1);
if ($key != "option" && $key != "submit")
Option::set($key, $value);
}
echo '<div class="callout callout-success">设置已保存。</div>';
} ?>
<table class="table">
<tbody>
<tr>
<td class="key">站点标题</td>
<td class="value">
<input type="text" class="form-control" name="site_name" value="{{ option('site_name') }}">
</td>
</tr>
<tr>
<td class="key">站点描述</td>
<td class="value">
<input type="text" class="form-control" name="site_description" value="{{ option('site_description') }}">
</td>
</tr>
<tr title="以 http(s):// 开头,不要以 / 结尾" data-toggle="tooltip" data-placement="top">
<td class="key">站点地址URL</td>
<td class="value">
<input type="text" class="form-control" name="site_url" value="{{ option('site_url') }}">
</td>
</tr>
<tr>
<td class="key">开放注册</td>
<td class="value">
<label for="user_can_register">
<input {{ option('user_can_register') ? 'checked="true"' : '' }} type="checkbox" id="user_can_register" name="user_can_register" value="1"> 任何人都可以注册
</label>
</td>
</tr>
<tr>
<td class="key">每个 IP 限制注册数</td>
<td class="value">
<input type="text" class="form-control" name="regs_per_ip" value="{{ option('regs_per_ip') }}">
</td>
</tr>
<tr>
<td class="key">最大允许上传大小
<i class="fa fa-question-circle" title="PHP 限制:{{ ini_get('post_max_size') }},定义在 php.ini 中。" data-toggle="tooltip" data-placement="top"></i>
</td>
<td class="value">
<div class="input-group">
<input type="text" class="form-control" name="max_upload_file_size" value="{{ option('max_upload_file_size') }}">
<span class="input-group-addon">KB</span>
</div>
</td>
</tr>
<tr>
<td class="key">角色名</td>
<td class="value">
<label for="allow_chinese_playername">
<input {{ option('allow_chinese_playername') ? 'checked="true"' : '' }} type="checkbox" id="allow_chinese_playername" name="allow_chinese_playername" value="1"> 允许中文角色名
</label>
</td>
</tr>
<tr>
<td class="key">首选 JSON API</td>
<td class="value">
<select class="form-control" name="api_type">
<option {{ (option('api_type') == '0') ? 'selected="selected"' : '' }} value="0">CustomSkinLoader API</option>
<option {{ (option('api_type') == '1') ? 'selected="selected"' : '' }} value="1">UniversalSkinAPI</option>
</select>
</td>
</tr>
<tr>
<td class="key">失效材质
<i class="fa fa-question-circle" title="自动从皮肤库中删除文件不存在的材质记录" data-toggle="tooltip" data-placement="top"></i>
</td>
<td class="value">
<label for="auto_del_invalid_texture">
<input {{ option('auto_del_invalid_texture') ? 'checked="true"' : '' }} type="checkbox" id="auto_del_invalid_texture" name="auto_del_invalid_texture" value="1"> 自动删除失效材质
</label>
</td>
</tr>
<tr>
<td class="key">评论代码
<i class="fa fa-question-circle" title="就是 Disqus多说畅言等评论服务提供的代码。留空以停用评论功能" data-toggle="tooltip" data-placement="top"></i>
</td>
<td class="value">
<textarea class="form-control" rows="6" name="comment_script">{{ option('comment_script') }}</textarea>
<p class="description">评论代码内可使用占位符,<code>{tid}</code> 将会被自动替换为材质的 id<code>{name}</code> 会被替换为材质名称,<code>{url}</code> 会被替换为当前页面地址。</p>
</td>
</tr>
</tbody>
</table>
</div><!-- /.box-body -->
<div class="box-footer">
<button type="submit" name="submit" class="btn btn-primary">提交</button>
</div>
</form>
</div>
{!! $forms['general']->render() !!}
</div>
<div class="col-md-6">
@ -180,85 +46,7 @@
</form>
</div>
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">缓存相关配置
<i class="fa fa-question-circle" title="如果启用了 CDN 缓存请适当修改这些配置" data-toggle="tooltip" data-placement="top"></i>
</h3>
</div><!-- /.box-header -->
<form method="post">
<input type="hidden" name="option" value="cache">
<div class="box-body">
<?php
if (isset($_POST['option']) && ($_POST['option'] == "cache")) {
// pre-set some options because they will not be posted if not checked
$presets = [
'avatar_query_string',
'return_200_when_notfound',
'auto_detect_asset_url'
];
foreach ($presets as $key) {
$_POST[$key] = isset($_POST[$key]) ? $_POST[$key] : "0";
}
foreach ($_POST as $key => $value) {
// remove slash if site_url is ended with slash
if ($key == "site_url" && substr($value, -1) == "/")
$value = substr($value, 0, -1);
if ($key != "option" && $key != "submit")
Option::set($key, $value);
}
echo '<div class="callout callout-success">设置已保存。</div>';
} ?>
<table class="table">
<tbody>
<tr>
<td class="key">头像缓存</td>
<td class="value">
<label for="avatar_query_string">
<input {{ option('avatar_query_string') ? 'checked="true"' : '' }} type="checkbox" id="avatar_query_string" name="avatar_query_string" value="1"> 为头像添加 Query String
</label>
</td>
</tr>
<tr>
<td class="key">资源地址
<i class="fa fa-question-circle" title="根据当前 URL 自动加载资源文件。如果出现 CDN 回源问题请关闭" data-toggle="tooltip" data-placement="top"></i>
</td>
<td class="value">
<label for="auto_detect_asset_url">
<input {{ option('auto_detect_asset_url') ? 'checked="true"' : '' }} type="checkbox" id="auto_detect_asset_url" name="auto_detect_asset_url" value="1"> 自动判断资源文件地址
</label>
</td>
</tr>
<tr>
<td class="key">HTTP 响应码</td>
<td class="value">
<label for="return_200_when_notfound">
<input {{ option('return_200_when_notfound') ? 'checked="true"' : '' }} type="checkbox" id="return_200_when_notfound" name="return_200_when_notfound" value="1"> 请求不存在的角色时返回 200 而不是 404
</label>
</td>
</tr>
<tr>
<td class="key">缓存失效时间
<i class="fa fa-question-circle" title="秒数86400 = 一天31536000 = 一年" data-toggle="tooltip" data-placement="top"></i>
</td>
<td class="value">
<input type="text" class="form-control" name="cache_expire_time" value="{{ option('cache_expire_time') }}">
</td>
</tr>
</tbody>
</table>
</div><!-- /.box-body -->
<div class="box-footer">
<button type="submit" name="submit" class="btn btn-primary">提交</button>
</div>
</form>
</div>
{!! $forms['cache']->render() !!}
</div>
</div>
@ -267,3 +55,10 @@
</div><!-- /.content-wrapper -->
@endsection
@section('style')
<style type="text/css">
.box-body > textarea { height: 200px; }
.description { margin: 7px 0 0 0; color: #555; }
</style>
@endsection

View File

@ -0,0 +1 @@
<span class="input-group-addon">{{ $value }}</span>

View File

@ -0,0 +1,3 @@
<label for="{{ $id }}">
<input {!! $checked ? 'checked="true"' : '' !!} type="checkbox" id="{{ $id }}" name="{{ $id }}" value="1"> {{ $label }}
</label>

View File

@ -0,0 +1,5 @@
<div class="input-group">
@foreach($items as $item)
{!! $item->render() !!}
@endforeach
</div>

View File

@ -0,0 +1 @@
<i class="fa fa-question-circle" title="{!! $hint !!}" data-toggle="tooltip" data-placement="top"></i>

View File

@ -0,0 +1,6 @@
<tr>
<td class="key">{{ $title }} {!! $hint or '' !!}</td>
<td class="value">
{!! $content !!}
</td>
</tr>

View File

@ -0,0 +1,23 @@
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">{{ $title }} {!! $hint or '' !!}</h3>
</div><!-- /.box-header -->
<form method="post">
<input type="hidden" name="option" value="{{ $id }}">
<div class="box-body">
@if ($success)
<div class="callout callout-success">设置已保存。</div>
@endif
<table class="table">
<tbody>
@foreach($items as $item)
{!! $item->render() !!}
@endforeach
</tbody>
</table>
</div><!-- /.box-body -->
<div class="box-footer">
<button type="submit" name="submit" class="btn btn-primary">{{ trans('general.submit') }}</button>
</div>
</form>
</div>

View File

@ -0,0 +1,8 @@
<select class="form-control" name="{{ $id }}">
@foreach ((array) $items as $item)
<?php list($id, $name) = $item; ?>
<option {!! $selected == $id ? 'selected="selected"' : '' !!} value="{{ $id }}">{{ $name }}</option>";
@endforeach
</select>

View File

@ -0,0 +1 @@
<input type="text" class="form-control" name="{{ $id }}" value="{{ $value }}">

View File

@ -0,0 +1,5 @@
<textarea class="form-control" rows="{{ $rows }}" name="{{ $id }}">{{ $value }}</textarea>
@if ($description != "")
<p class="description">{!! $description !!}</p>
@endif