mirror of
https://gitee.com/zoujingli/ThinkAdmin.git
synced 2025-04-05 19:41:44 +08:00
ComposerUpdate
This commit is contained in:
parent
f28ff2e8c6
commit
89ab78ec92
6
app/admin/module/version.json
Normal file
6
app/admin/module/version.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "admin",
|
||||
"author": "Anyon",
|
||||
"version": "2020.08.03.01",
|
||||
"content": "ThinkAdmin 系统基础模块"
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2020 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 代码仓库:https://gitee.com/zoujingli/ThinkAdmin
|
||||
// | github 代码仓库:https://github.com/zoujingli/ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
'name' => 'admin',
|
||||
'author' => 'Anyon',
|
||||
'version' => '2020.08.03.01',
|
||||
'content' => 'ThinkAdmin 系统基础模块',
|
||||
];
|
6
app/wechat/module/version.json
Normal file
6
app/wechat/module/version.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "wechat",
|
||||
"author": "Anyon",
|
||||
"version": "2020.08.03.01",
|
||||
"content": "ThinkAdmin 微信基础模块"
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2020 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 代码仓库:https://gitee.com/zoujingli/ThinkAdmin
|
||||
// | github 代码仓库:https://github.com/zoujingli/ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
'name' => 'wechat',
|
||||
'author' => 'Anyon',
|
||||
'version' => '2020.08.03.01',
|
||||
'content' => 'ThinkAdmin 微信基础模块',
|
||||
];
|
18
composer.lock
generated
18
composer.lock
generated
@ -74,16 +74,16 @@
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/flysystem.git",
|
||||
"reference": "63cd8c14708b9544d3f61d3c15b747fda1c95c6e"
|
||||
"reference": "9be3b16c877d477357c015cec057548cf9b2a14a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/63cd8c14708b9544d3f61d3c15b747fda1c95c6e",
|
||||
"reference": "63cd8c14708b9544d3f61d3c15b747fda1c95c6e",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/9be3b16c877d477357c015cec057548cf9b2a14a",
|
||||
"reference": "9be3b16c877d477357c015cec057548cf9b2a14a",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
@ -167,7 +167,7 @@
|
||||
"type": "other"
|
||||
}
|
||||
],
|
||||
"time": "2020-08-18T10:57:55+00:00"
|
||||
"time": "2020-08-23T07:39:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem-cached-adapter",
|
||||
@ -937,12 +937,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zoujingli/ThinkLibrary.git",
|
||||
"reference": "1076fed8234de42aba3f3c6a51d55fd96f996272"
|
||||
"reference": "08c1ba46f3fca22959fd5797f8a04521f1b48667"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/zoujingli/ThinkLibrary/zipball/1076fed8234de42aba3f3c6a51d55fd96f996272",
|
||||
"reference": "1076fed8234de42aba3f3c6a51d55fd96f996272",
|
||||
"url": "https://api.github.com/repos/zoujingli/ThinkLibrary/zipball/08c1ba46f3fca22959fd5797f8a04521f1b48667",
|
||||
"reference": "08c1ba46f3fca22959fd5797f8a04521f1b48667",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
@ -987,7 +987,7 @@
|
||||
],
|
||||
"description": "ThinkPHP v6.0 Development Library",
|
||||
"homepage": "http://thinkadmin.top",
|
||||
"time": "2020-08-21T09:27:14+00:00"
|
||||
"time": "2020-08-24T03:54:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "zoujingli/wechat-developer",
|
||||
|
@ -41,5 +41,5 @@ return [
|
||||
// 显示错误信息
|
||||
'show_error_msg' => app()->isDebug(),
|
||||
// 当前应用版本号
|
||||
'thinkadmin_ver' => 'v6',
|
||||
'thinkadmin_ver' => 'v6.0.0',
|
||||
];
|
2
vendor/composer/autoload_classmap.php
vendored
2
vendor/composer/autoload_classmap.php
vendored
@ -300,11 +300,11 @@ return array(
|
||||
'think\\admin\\service\\CaptchaService' => $vendorDir . '/zoujingli/think-library/src/service/CaptchaService.php',
|
||||
'think\\admin\\service\\ExpressService' => $vendorDir . '/zoujingli/think-library/src/service/ExpressService.php',
|
||||
'think\\admin\\service\\InstallService' => $vendorDir . '/zoujingli/think-library/src/service/InstallService.php',
|
||||
'think\\admin\\service\\InterfaceService' => $vendorDir . '/zoujingli/think-library/src/service/InterfaceService.php',
|
||||
'think\\admin\\service\\MenuService' => $vendorDir . '/zoujingli/think-library/src/service/MenuService.php',
|
||||
'think\\admin\\service\\MessageService' => $vendorDir . '/zoujingli/think-library/src/service/MessageService.php',
|
||||
'think\\admin\\service\\ModuleService' => $vendorDir . '/zoujingli/think-library/src/service/ModuleService.php',
|
||||
'think\\admin\\service\\NodeService' => $vendorDir . '/zoujingli/think-library/src/service/NodeService.php',
|
||||
'think\\admin\\service\\OpenService' => $vendorDir . '/zoujingli/think-library/src/service/OpenService.php',
|
||||
'think\\admin\\service\\ProcessService' => $vendorDir . '/zoujingli/think-library/src/service/ProcessService.php',
|
||||
'think\\admin\\service\\QueueService' => $vendorDir . '/zoujingli/think-library/src/service/QueueService.php',
|
||||
'think\\admin\\service\\SystemService' => $vendorDir . '/zoujingli/think-library/src/service/SystemService.php',
|
||||
|
2
vendor/composer/autoload_static.php
vendored
2
vendor/composer/autoload_static.php
vendored
@ -433,11 +433,11 @@ class ComposerStaticInitb911c14a0826c73d9f097343fd33a252
|
||||
'think\\admin\\service\\CaptchaService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/CaptchaService.php',
|
||||
'think\\admin\\service\\ExpressService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/ExpressService.php',
|
||||
'think\\admin\\service\\InstallService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/InstallService.php',
|
||||
'think\\admin\\service\\InterfaceService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/InterfaceService.php',
|
||||
'think\\admin\\service\\MenuService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/MenuService.php',
|
||||
'think\\admin\\service\\MessageService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/MessageService.php',
|
||||
'think\\admin\\service\\ModuleService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/ModuleService.php',
|
||||
'think\\admin\\service\\NodeService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/NodeService.php',
|
||||
'think\\admin\\service\\OpenService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/OpenService.php',
|
||||
'think\\admin\\service\\ProcessService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/ProcessService.php',
|
||||
'think\\admin\\service\\QueueService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/QueueService.php',
|
||||
'think\\admin\\service\\SystemService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/SystemService.php',
|
||||
|
20
vendor/composer/installed.json
vendored
20
vendor/composer/installed.json
vendored
@ -69,17 +69,17 @@
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem",
|
||||
"version": "1.1.2",
|
||||
"version_normalized": "1.1.2.0",
|
||||
"version": "1.1.3",
|
||||
"version_normalized": "1.1.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/flysystem.git",
|
||||
"reference": "63cd8c14708b9544d3f61d3c15b747fda1c95c6e"
|
||||
"reference": "9be3b16c877d477357c015cec057548cf9b2a14a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/63cd8c14708b9544d3f61d3c15b747fda1c95c6e",
|
||||
"reference": "63cd8c14708b9544d3f61d3c15b747fda1c95c6e",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/9be3b16c877d477357c015cec057548cf9b2a14a",
|
||||
"reference": "9be3b16c877d477357c015cec057548cf9b2a14a",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
@ -116,7 +116,7 @@
|
||||
"spatie/flysystem-dropbox": "Allows you to use Dropbox storage",
|
||||
"srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications"
|
||||
},
|
||||
"time": "2020-08-18T10:57:55+00:00",
|
||||
"time": "2020-08-23T07:39:11+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
@ -963,12 +963,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zoujingli/ThinkLibrary.git",
|
||||
"reference": "1076fed8234de42aba3f3c6a51d55fd96f996272"
|
||||
"reference": "08c1ba46f3fca22959fd5797f8a04521f1b48667"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/zoujingli/ThinkLibrary/zipball/1076fed8234de42aba3f3c6a51d55fd96f996272",
|
||||
"reference": "1076fed8234de42aba3f3c6a51d55fd96f996272",
|
||||
"url": "https://api.github.com/repos/zoujingli/ThinkLibrary/zipball/08c1ba46f3fca22959fd5797f8a04521f1b48667",
|
||||
"reference": "08c1ba46f3fca22959fd5797f8a04521f1b48667",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
@ -985,7 +985,7 @@
|
||||
"ext-mbstring": "*",
|
||||
"topthink/framework": "^6.0"
|
||||
},
|
||||
"time": "2020-08-21T09:27:14+00:00",
|
||||
"time": "2020-08-24T03:54:43+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"think": {
|
||||
|
@ -645,10 +645,7 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
$tries = 0;
|
||||
|
||||
while ( ! $this->isConnected() && $tries < 3) {
|
||||
$tries++;
|
||||
if ( ! $this->isConnected()) {
|
||||
$this->disconnect();
|
||||
$this->connect();
|
||||
}
|
||||
@ -700,4 +697,9 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function isConnected();
|
||||
|
||||
protected function escapePath($path)
|
||||
{
|
||||
return str_replace(['*', '[', ']'], ['\\*', '\\[', '\\]'], $path);
|
||||
}
|
||||
}
|
||||
|
13
vendor/league/flysystem/src/Adapter/Ftp.php
vendored
13
vendor/league/flysystem/src/Adapter/Ftp.php
vendored
@ -2,7 +2,6 @@
|
||||
|
||||
namespace League\Flysystem\Adapter;
|
||||
|
||||
use ErrorException;
|
||||
use League\Flysystem\Adapter\Polyfill\StreamedCopyTrait;
|
||||
use League\Flysystem\AdapterInterface;
|
||||
use League\Flysystem\Config;
|
||||
@ -130,6 +129,9 @@ class Ftp extends AbstractFtpAdapter
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
$tries = 3;
|
||||
start_connecting:
|
||||
|
||||
if ($this->ssl) {
|
||||
$this->connection = @ftp_ssl_connect($this->getHost(), $this->getPort(), $this->getTimeout());
|
||||
} else {
|
||||
@ -137,6 +139,10 @@ class Ftp extends AbstractFtpAdapter
|
||||
}
|
||||
|
||||
if ( ! $this->connection) {
|
||||
$tries--;
|
||||
|
||||
if ($tries > 0) goto start_connecting;
|
||||
|
||||
throw new ConnectionRuntimeException('Could not connect to host: ' . $this->getHost() . ', port:' . $this->getPort());
|
||||
}
|
||||
|
||||
@ -394,7 +400,7 @@ class Ftp extends AbstractFtpAdapter
|
||||
return ['type' => 'dir', 'path' => $path];
|
||||
}
|
||||
|
||||
$listing = $this->ftpRawlist('-A', str_replace('*', '\\*', $path));
|
||||
$listing = $this->ftpRawlist('-A', $path);
|
||||
|
||||
if (empty($listing) || in_array('total 0', $listing, true)) {
|
||||
return false;
|
||||
@ -490,8 +496,6 @@ class Ftp extends AbstractFtpAdapter
|
||||
*/
|
||||
protected function listDirectoryContents($directory, $recursive = true)
|
||||
{
|
||||
$directory = str_replace('*', '\\*', $directory);
|
||||
|
||||
if ($recursive && $this->recurseManually) {
|
||||
return $this->listDirectoryContentsRecursive($directory);
|
||||
}
|
||||
@ -560,6 +564,7 @@ class Ftp extends AbstractFtpAdapter
|
||||
|
||||
if ($this->isPureFtpd) {
|
||||
$path = str_replace(' ', '\ ', $path);
|
||||
$this->escapePath($path);
|
||||
}
|
||||
|
||||
return ftp_rawlist($connection, $options . ' ' . $path);
|
||||
|
7
vendor/league/flysystem/src/Adapter/Ftpd.php
vendored
7
vendor/league/flysystem/src/Adapter/Ftpd.php
vendored
@ -12,13 +12,16 @@ class Ftpd extends Ftp
|
||||
if ($path === '') {
|
||||
return ['type' => 'dir', 'path' => ''];
|
||||
}
|
||||
|
||||
if (@ftp_chdir($this->getConnection(), $path) === true) {
|
||||
$this->setConnectionRoot();
|
||||
|
||||
return ['type' => 'dir', 'path' => $path];
|
||||
}
|
||||
|
||||
if ( ! ($object = ftp_raw($this->getConnection(), 'STAT ' . $path)) || count($object) < 3) {
|
||||
$object = ftp_raw($this->getConnection(), 'STAT ' . $this->escapePath($path));
|
||||
|
||||
if ( ! $object || count($object) < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -34,7 +37,7 @@ class Ftpd extends Ftp
|
||||
*/
|
||||
protected function listDirectoryContents($directory, $recursive = true)
|
||||
{
|
||||
$listing = ftp_rawlist($this->getConnection(), $directory, $recursive);
|
||||
$listing = ftp_rawlist($this->getConnection(), $this->escapePath($directory), $recursive);
|
||||
|
||||
if ($listing === false || ( ! empty($listing) && substr($listing[0], 0, 5) === "ftpd:")) {
|
||||
return [];
|
||||
|
6
vendor/league/flysystem/src/Filesystem.php
vendored
6
vendor/league/flysystem/src/Filesystem.php
vendored
@ -74,7 +74,7 @@ class Filesystem implements FilesystemInterface
|
||||
*/
|
||||
public function writeStream($path, $resource, array $config = [])
|
||||
{
|
||||
if ( ! is_resource($resource)) {
|
||||
if ( ! is_resource($resource) || get_resource_type($resource) !== 'stream') {
|
||||
throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.');
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ class Filesystem implements FilesystemInterface
|
||||
*/
|
||||
public function putStream($path, $resource, array $config = [])
|
||||
{
|
||||
if ( ! is_resource($resource)) {
|
||||
if ( ! is_resource($resource) || get_resource_type($resource) !== 'stream') {
|
||||
throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.');
|
||||
}
|
||||
|
||||
@ -158,7 +158,7 @@ class Filesystem implements FilesystemInterface
|
||||
*/
|
||||
public function updateStream($path, $resource, array $config = [])
|
||||
{
|
||||
if ( ! is_resource($resource)) {
|
||||
if ( ! is_resource($resource) || get_resource_type($resource) !== 'stream') {
|
||||
throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.');
|
||||
}
|
||||
|
||||
|
2
vendor/services.php
vendored
2
vendor/services.php
vendored
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// This file is automatically generated at:2020-08-22 13:00:57
|
||||
// This file is automatically generated at:2020-08-24 11:57:42
|
||||
declare (strict_types = 1);
|
||||
return array (
|
||||
0 => 'think\\admin\\Library',
|
||||
|
@ -120,7 +120,7 @@ abstract class Storage
|
||||
public static function name($url, $ext = '', $pre = '', $fun = 'md5')
|
||||
{
|
||||
if (empty($ext)) $ext = pathinfo($url, 4);
|
||||
list($xmd, $ext) = [$fun($url), trim($ext, '.\\/')];
|
||||
[$xmd, $ext] = [$fun($url), trim($ext, '.\\/')];
|
||||
$attr = [trim($pre, '.\\/'), substr($xmd, 0, 2), substr($xmd, 2, 30)];
|
||||
return trim(join('/', $attr), '/') . '.' . strtolower($ext ? $ext : 'tmp');
|
||||
}
|
||||
@ -188,7 +188,7 @@ abstract class Storage
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
list($content) = [curl_exec($ch), curl_close($ch)];
|
||||
[$content] = [curl_exec($ch), curl_close($ch)];
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ if (!function_exists('encode')) {
|
||||
*/
|
||||
function encode($content)
|
||||
{
|
||||
list($chars, $length) = ['', strlen($string = iconv('UTF-8', 'GBK//TRANSLIT', $content))];
|
||||
[$chars, $length] = ['', strlen($string = iconv('UTF-8', 'GBK//TRANSLIT', $content))];
|
||||
for ($i = 0; $i < $length; $i++) $chars .= str_pad(base_convert(ord($string[$i]), 10, 36), 2, 0, 0);
|
||||
return $chars;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class DataExtend
|
||||
*/
|
||||
public static function arr2tree($list, $cid = 'id', $pid = 'pid', $sub = 'sub')
|
||||
{
|
||||
list($tree, $tmp) = [[], array_combine(array_column($list, $cid), array_values($list))];
|
||||
[$tree, $tmp] = [[], array_combine(array_column($list, $cid), array_values($list))];
|
||||
foreach ($list as $vo) isset($vo[$pid]) && isset($tmp[$vo[$pid]]) ? $tmp[$vo[$pid]][$sub][] = &$tmp[$vo[$cid]] : $tree[] = &$tmp[$vo[$cid]];
|
||||
unset($tmp, $list);
|
||||
return $tree;
|
||||
|
@ -64,7 +64,7 @@ class ExcelExtend
|
||||
*/
|
||||
public static function parseKeyDotValue(array $data, $rule)
|
||||
{
|
||||
list($temp, $attr) = [$data, explode('.', trim($rule, '.'))];
|
||||
[$temp, $attr] = [$data, explode('.', trim($rule, '.'))];
|
||||
while ($key = array_shift($attr)) $temp = $temp[$key] ?? $temp;
|
||||
return (is_string($temp) || is_numeric($temp)) ? @iconv('utf-8', 'gbk//TRANSLIT', "{$temp}") : '';
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ class HttpExtend
|
||||
*/
|
||||
public static function submit($url, array $data = [], array $file = [], array $header = [], $method = 'POST', $returnHeader = true)
|
||||
{
|
||||
list($line, $boundary) = [[], CodeExtend::random(18)];
|
||||
[$line, $boundary] = [[], CodeExtend::random(18)];
|
||||
foreach ($data as $key => $value) {
|
||||
$line[] = "--{$boundary}";
|
||||
$line[] = "Content-Disposition: form-data; name=\"{$key}\"";
|
||||
|
@ -80,8 +80,8 @@ class JsonRpcServer
|
||||
$error = ['code' => '-32601', 'message' => '找不到方法', 'meaning' => '该方法不存在或无效'];
|
||||
$response = ['jsonrpc' => '2.0', 'id' => $request['id'], 'result' => null, 'error' => $error];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$error = ['code' => $e->getCode(), 'message' => $e->getMessage()];
|
||||
} catch (\Exception $exception) {
|
||||
$error = ['code' => $exception->getCode(), 'message' => $exception->getMessage()];
|
||||
$response = ['jsonrpc' => '2.0', 'id' => $request['id'], 'result' => null, 'error' => $error];
|
||||
}
|
||||
// Output the response
|
||||
|
@ -70,7 +70,7 @@ class FormHelper extends Helper
|
||||
public function init($dbQuery, $template = '', $field = '', $where = [], $data = [])
|
||||
{
|
||||
$this->query = $this->buildQuery($dbQuery);
|
||||
list($this->template, $this->where, $this->data) = [$template, $where, $data];
|
||||
[$this->template, $this->where, $this->data] = [$template, $where, $data];
|
||||
$this->field = $field ?: ($this->query->getPk() ?: 'id');
|
||||
$this->value = input($this->field, $data[$this->field] ?? null);
|
||||
// GET请求, 获取数据并显示表单页面
|
||||
|
@ -73,9 +73,9 @@ class QueryHelper extends Helper
|
||||
{
|
||||
$data = $this->app->request->$input();
|
||||
foreach (is_array($fields) ? $fields : explode(',', $fields) as $field) {
|
||||
list($dk, $qk) = [$field, $field];
|
||||
[$dk, $qk] = [$field, $field];
|
||||
if (stripos($field, $alias) !== false) {
|
||||
list($dk, $qk) = explode($alias, $field);
|
||||
[$dk, $qk] = explode($alias, $field);
|
||||
}
|
||||
if (isset($data[$qk]) && $data[$qk] !== '') {
|
||||
$this->query->whereLike($dk, "%{$data[$qk]}%");
|
||||
@ -95,9 +95,9 @@ class QueryHelper extends Helper
|
||||
{
|
||||
$data = $this->app->request->$input();
|
||||
foreach (is_array($fields) ? $fields : explode(',', $fields) as $field) {
|
||||
list($dk, $qk) = [$field, $field];
|
||||
[$dk, $qk] = [$field, $field];
|
||||
if (stripos($field, $alias) !== false) {
|
||||
list($dk, $qk) = explode($alias, $field);
|
||||
[$dk, $qk] = explode($alias, $field);
|
||||
}
|
||||
if (isset($data[$qk]) && $data[$qk] !== '') {
|
||||
$this->query->where($dk, "{$data[$qk]}");
|
||||
@ -118,9 +118,9 @@ class QueryHelper extends Helper
|
||||
{
|
||||
$data = $this->app->request->$input();
|
||||
foreach (is_array($fields) ? $fields : explode(',', $fields) as $field) {
|
||||
list($dk, $qk) = [$field, $field];
|
||||
[$dk, $qk] = [$field, $field];
|
||||
if (stripos($field, $alias) !== false) {
|
||||
list($dk, $qk) = explode($alias, $field);
|
||||
[$dk, $qk] = explode($alias, $field);
|
||||
}
|
||||
if (isset($data[$qk]) && $data[$qk] !== '') {
|
||||
$this->query->whereIn($dk, explode($split, $data[$qk]));
|
||||
@ -185,12 +185,12 @@ class QueryHelper extends Helper
|
||||
{
|
||||
$data = $this->app->request->$input();
|
||||
foreach (is_array($fields) ? $fields : explode(',', $fields) as $field) {
|
||||
list($dk, $qk) = [$field, $field];
|
||||
[$dk, $qk] = [$field, $field];
|
||||
if (stripos($field, $alias) !== false) {
|
||||
list($dk, $qk) = explode($alias, $field);
|
||||
[$dk, $qk] = explode($alias, $field);
|
||||
}
|
||||
if (isset($data[$qk]) && $data[$qk] !== '') {
|
||||
list($begin, $after) = explode($split, $data[$qk]);
|
||||
[$begin, $after] = explode($split, $data[$qk]);
|
||||
if (is_callable($callback)) {
|
||||
$after = call_user_func($callback, $after, 'after');
|
||||
$begin = call_user_func($callback, $begin, 'begin');
|
||||
|
@ -29,7 +29,8 @@ class ValidateHelper extends Helper
|
||||
* 快捷输入并验证( 支持 规则 # 别名 )
|
||||
* @param array $rules 验证规则( 验证信息数组 )
|
||||
* @param string|array $input 输入内容 ( post. 或 get. )
|
||||
* @return array
|
||||
* @param boolean $callable 异常处理操作
|
||||
* @return array|void
|
||||
* age.require => message // 最大值限定
|
||||
* age.between:1,120 => message // 范围限定
|
||||
* name.require => message // 必填内容
|
||||
@ -37,7 +38,7 @@ class ValidateHelper extends Helper
|
||||
* region.value => value // 固定字段数值内容
|
||||
* 更多规则参照 ThinkPHP 官方的验证类
|
||||
*/
|
||||
public function init(array $rules, $input = ''): array
|
||||
public function init(array $rules, $input = '', $callable = null): array
|
||||
{
|
||||
if (is_string($input)) {
|
||||
$input = trim($input, '.') ?: 'request';
|
||||
@ -63,8 +64,10 @@ class ValidateHelper extends Helper
|
||||
$validate = new Validate();
|
||||
if ($validate->rule($rule)->message($info)->check($data)) {
|
||||
return $data;
|
||||
} elseif (is_callable($callable)) {
|
||||
return call_user_func($callable, $validate->getError());
|
||||
} else {
|
||||
$this->controller->error($validate->getError());
|
||||
return $this->controller->error($validate->getError());
|
||||
}
|
||||
}
|
||||
}
|
@ -14,17 +14,23 @@
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
'think_library_not_auth' => 'Sorry, no permission to access the operation.',
|
||||
'think_library_not_login' => 'Sorry, requiring login to obtain view permission.',
|
||||
'think_library_delete_success' => 'Data deletion completed.',
|
||||
'think_library_delete_error' => 'Sorry, data deletion failed, please try again later.',
|
||||
'think_library_form_success' => 'Data saving completed.',
|
||||
'think_library_form_error' => 'Sorry, data saving failed, please try again later.',
|
||||
'think_library_save_success' => 'Data update completed.',
|
||||
'think_library_save_error' => 'Sorry, data update failed, please try again later.',
|
||||
'think_library_sort_success' => 'Modification of list sort completed.',
|
||||
'think_library_sort_error' => 'Sorry, modification of list sort failed, please try again later.',
|
||||
'think_library_page_html' => 'Total %s records, display %s per page, total %s page current display %s page.',
|
||||
'think_library_csrf_error' => 'Form token validation failed, please refresh and try again later.',
|
||||
'think_library_queue_exist' => 'Task has been created, please wait for processing to complete.',
|
||||
'think_library_not_auth' => 'Sorry, no permission to access the operation.',
|
||||
'think_library_not_login' => 'Sorry, requiring login to obtain view permission.',
|
||||
'think_library_delete_success' => 'Data deletion completed.',
|
||||
'think_library_delete_error' => 'Sorry, data deletion failed, please try again later.',
|
||||
'think_library_form_success' => 'Data saving completed.',
|
||||
'think_library_form_error' => 'Sorry, data saving failed, please try again later.',
|
||||
'think_library_save_success' => 'Data update completed.',
|
||||
'think_library_save_error' => 'Sorry, data update failed, please try again later.',
|
||||
'think_library_sort_success' => 'Modification of list sort completed.',
|
||||
'think_library_sort_error' => 'Sorry, modification of list sort failed, please try again later.',
|
||||
'think_library_page_html' => 'Total %s records, display %s per page, total %s page current display %s page.',
|
||||
'think_library_csrf_error' => 'Form token validation failed, please refresh and try again later.',
|
||||
'think_library_queue_exist' => 'Task has been created, please wait for processing to complete.',
|
||||
'think_library_response_failed' => 'Server interface response exception.',
|
||||
'think_library_response_success' => 'Server interface response succeeded.',
|
||||
'think_library_params_failed_empty' => 'Interface request parameter %s cannot be empty.',
|
||||
'think_library_params_failed_auth' => 'Interface account authentication failed.',
|
||||
'think_library_params_failed_time' => 'Interface request time difference is too large.',
|
||||
'think_library_params_failed_sign' => 'Interface request signature verification failed.',
|
||||
];
|
@ -14,17 +14,23 @@
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
'think_library_not_auth' => '抱歉,没有访问该操作的权限!',
|
||||
'think_library_not_login' => '抱歉,需要登录获取访问权限!',
|
||||
'think_library_delete_success' => '恭喜, 数据删除成功!',
|
||||
'think_library_delete_error' => '抱歉,数据删除失败, 请稍候再试!',
|
||||
'think_library_form_success' => '恭喜, 数据保存成功!',
|
||||
'think_library_form_error' => '抱歉,数据保存失败, 请稍候再试!',
|
||||
'think_library_save_success' => '恭喜,数据更新成功!',
|
||||
'think_library_save_error' => '抱歉,数据更新失败, 请稍候再试!',
|
||||
'think_library_sort_success' => '恭喜,列表排序成功!',
|
||||
'think_library_sort_error' => '抱歉,列表排序失败,请稍候再试!',
|
||||
'think_library_page_html' => '共 %s 条记录,每页显示 %s 条,共 %s 页当前显示第 %s 页。',
|
||||
'think_library_csrf_error' => '表单令牌验证失败,请刷新页面再试!',
|
||||
'think_library_queue_exist' => '任务已创建,请等待处理完成!',
|
||||
'think_library_not_auth' => '抱歉,没有访问该操作的权限!',
|
||||
'think_library_not_login' => '抱歉,需要登录获取访问权限!',
|
||||
'think_library_delete_success' => '恭喜, 数据删除成功!',
|
||||
'think_library_delete_error' => '抱歉,数据删除失败, 请稍候再试!',
|
||||
'think_library_form_success' => '恭喜, 数据保存成功!',
|
||||
'think_library_form_error' => '抱歉,数据保存失败, 请稍候再试!',
|
||||
'think_library_save_success' => '恭喜,数据更新成功!',
|
||||
'think_library_save_error' => '抱歉,数据更新失败, 请稍候再试!',
|
||||
'think_library_sort_success' => '恭喜,列表排序成功!',
|
||||
'think_library_sort_error' => '抱歉,列表排序失败,请稍候再试!',
|
||||
'think_library_page_html' => '共 %s 条记录,每页显示 %s 条,共 %s 页当前显示第 %s 页。',
|
||||
'think_library_csrf_error' => '表单令牌验证失败,请刷新页面再试!',
|
||||
'think_library_queue_exist' => '任务已创建,请等待处理完成!',
|
||||
'think_library_response_failed' => '服务端请求响应异常!',
|
||||
'think_library_response_success' => '服务端请求响应成功!',
|
||||
'think_library_params_failed_empty' => '接口请求参数%s不能为空!',
|
||||
'think_library_params_failed_auth' => '接口账号认证失败!',
|
||||
'think_library_params_failed_time' => '接口请求时差过大!',
|
||||
'think_library_params_failed_sign' => '接口请求签名验证失败!',
|
||||
];
|
@ -106,7 +106,7 @@ class AdminService extends Service
|
||||
$nodes[$node] = ['node' => $node, 'title' => $method['title'], 'pnode' => $pnode, 'checked' => in_array($node, $checkeds)];
|
||||
}
|
||||
}
|
||||
foreach (array_keys($nodes) as $key) foreach ($methods as $node => $method) if (stripos($key, "{$node}/") !== false) {
|
||||
foreach (array_keys($nodes) as $key) foreach ($methods as $node => $method) if (stripos($key, $node . '/') !== false) {
|
||||
$pnode = substr($node, 0, strripos($node, '/'));
|
||||
$nodes[$node] = ['node' => $node, 'title' => $method['title'], 'pnode' => $pnode, 'checked' => in_array($node, $checkeds)];
|
||||
$nodes[$pnode] = ['node' => $pnode, 'title' => ucfirst($pnode), 'pnode' => '', 'checked' => in_array($pnode, $checkeds)];
|
||||
|
@ -29,61 +29,66 @@ class InstallService extends Service
|
||||
* 项目根目录
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
private $root;
|
||||
|
||||
/**
|
||||
* 线上服务器地址
|
||||
* @var string
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* 当前大版本号
|
||||
* @var string
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
/**
|
||||
* 更新规则
|
||||
* @var array
|
||||
*/
|
||||
protected $rules = [];
|
||||
|
||||
/**
|
||||
* 忽略规则
|
||||
* @var array
|
||||
*/
|
||||
protected $ignore = [];
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* 初始化服务
|
||||
*/
|
||||
protected function initialize()
|
||||
{
|
||||
// 应用框架版本
|
||||
$this->version = $this->app->config->get('app.thinkadmin_ver') ?: 'v4';
|
||||
// 线上应用代码
|
||||
$this->server = "https://{$this->version}.thinkadmin.top";
|
||||
// 应用根目录
|
||||
$this->root = strtr($this->app->getRootPath(), '\\', '/');
|
||||
$this->server = ModuleService::instance()->getServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取线上接口
|
||||
* @return string
|
||||
* 获取文件信息列表
|
||||
* @param array $rules 文件规则
|
||||
* @param array $ignore 忽略规则
|
||||
* @param array $data 扫描结果列表
|
||||
* @return array
|
||||
*/
|
||||
public function getServer()
|
||||
public function getList(array $rules, array $ignore = [], array $data = []): array
|
||||
{
|
||||
return $this->server;
|
||||
// 扫描规则文件
|
||||
foreach ($rules as $key => $rule) {
|
||||
$name = strtr(trim($rule, '\\/'), '\\', '/');
|
||||
$data = array_merge($data, $this->_scanList("{$this->root}{$name}"));
|
||||
}
|
||||
// 清除忽略文件
|
||||
foreach ($data as $key => $item) foreach ($ignore as $ign) {
|
||||
if (stripos($item['name'], $ign) === 0) unset($data[$key]);
|
||||
}
|
||||
// 返回文件数据
|
||||
return ['rules' => $rules, 'ignore' => $ignore, 'list' => $data];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前版本
|
||||
* @return string
|
||||
* 获取文件差异数据
|
||||
* @param array $rules 文件规则
|
||||
* @param array $ignore 忽略规则
|
||||
* @return array
|
||||
*/
|
||||
public function getVersion()
|
||||
public function grenerateDifference(array $rules = [], array $ignore = []): array
|
||||
{
|
||||
return $this->version;
|
||||
[$rules1, $ignore1, $data] = [$rules, $ignore, []];
|
||||
$result = json_decode(HttpExtend::post("{$this->server}/admin/api.update/node", [
|
||||
'rules' => json_encode($rules1), 'ignore' => json_encode($ignore1),
|
||||
]), true);
|
||||
if (!empty($result['code'])) {
|
||||
$new = $this->getList($result['data']['rules'], $result['data']['ignore']);
|
||||
foreach ($this->_grenerateDifferenceContrast($result['data']['list'], $new['list']) as $file) {
|
||||
if (in_array($file['type'], ['add', 'del', 'mod'])) foreach ($rules1 as $rule) {
|
||||
if (stripos($file['name'], $rule) === 0) $data[] = $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,7 +99,7 @@ class InstallService extends Service
|
||||
public function updateFileByDownload(array $file): array
|
||||
{
|
||||
if (in_array($file['type'], ['add', 'mod'])) {
|
||||
if ($this->downloadFile(encode($file['name']))) {
|
||||
if ($this->_downloadFile(encode($file['name']))) {
|
||||
return [true, $file['type'], $file['name']];
|
||||
} else {
|
||||
return [false, $file['type'], $file['name']];
|
||||
@ -102,7 +107,7 @@ class InstallService extends Service
|
||||
} elseif (in_array($file['type'], ['del'])) {
|
||||
$real = $this->root . $file['name'];
|
||||
if (is_file($real) && unlink($real)) {
|
||||
$this->removeEmptyDirectory(dirname($real));
|
||||
$this->_removeEmptyDirectory(dirname($real));
|
||||
return [true, $file['type'], $file['name']];
|
||||
} else {
|
||||
return [false, $file['type'], $file['name']];
|
||||
@ -115,7 +120,7 @@ class InstallService extends Service
|
||||
* @param string $encode
|
||||
* @return boolean|integer
|
||||
*/
|
||||
private function downloadFile($encode)
|
||||
private function _downloadFile($encode)
|
||||
{
|
||||
$source = "{$this->server}/admin/api.update/get?encode={$encode}";
|
||||
$result = json_decode(HttpExtend::get($source), true);
|
||||
@ -129,43 +134,20 @@ class InstallService extends Service
|
||||
* 清理空目录
|
||||
* @param string $path
|
||||
*/
|
||||
private function removeEmptyDirectory($path)
|
||||
private function _removeEmptyDirectory($path)
|
||||
{
|
||||
if (is_dir($path) && count(scandir($path)) === 2 && rmdir($path)) {
|
||||
$this->removeEmptyDirectory(dirname($path));
|
||||
$this->_removeEmptyDirectory(dirname($path));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件差异数据
|
||||
* @param array $rules 文件规则
|
||||
* @param array $ignore 忽略规则
|
||||
* @return array
|
||||
*/
|
||||
public function grenerateDifference(array $rules = [], array $ignore = []): array
|
||||
{
|
||||
[$this->rules, $this->ignore, $data] = [$rules, $ignore, []];
|
||||
$result = json_decode(HttpExtend::post("{$this->server}/admin/api.update/node", [
|
||||
'rules' => json_encode($this->rules), 'ignore' => json_encode($this->ignore),
|
||||
]), true);
|
||||
if (!empty($result['code'])) {
|
||||
$new = $this->getList($result['data']['rules'], $result['data']['ignore']);
|
||||
foreach ($this->grenerateDifferenceContrast($result['data']['list'], $new['list']) as $file) {
|
||||
if (in_array($file['type'], ['add', 'del', 'mod'])) foreach ($this->rules as $rule) {
|
||||
if (stripos($file['name'], $rule) === 0) $data[] = $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 两二维数组对比
|
||||
* @param array $serve 线上文件列表信息
|
||||
* @param array $local 本地文件列表信息
|
||||
* @return array
|
||||
*/
|
||||
private function grenerateDifferenceContrast(array $serve = [], array $local = []): array
|
||||
private function _grenerateDifferenceContrast(array $serve = [], array $local = []): array
|
||||
{
|
||||
// 数据扁平化
|
||||
[$_serve, $_local, $_diffy] = [[], [], []];
|
||||
@ -186,57 +168,29 @@ class InstallService extends Service
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件信息列表
|
||||
* @param array $rules 文件规则
|
||||
* @param array $ignore 忽略规则
|
||||
* @param array $data 扫描结果列表
|
||||
* 获取指定文件信息
|
||||
* @param string $path 文件路径
|
||||
* @return array
|
||||
*/
|
||||
public function getList(array $rules, array $ignore = [], array $data = []): array
|
||||
private function _getInfo($path): array
|
||||
{
|
||||
// 扫描规则文件
|
||||
foreach ($rules as $key => $rule) {
|
||||
$name = strtr(trim($rule, '\\/'), '\\', '/');
|
||||
$data = array_merge($data, $this->scanList("{$this->root}{$name}"));
|
||||
}
|
||||
// 清除忽略文件
|
||||
foreach ($data as $key => $item) foreach ($ignore as $igr) {
|
||||
if (stripos($item['name'], $igr) === 0) unset($data[$key]);
|
||||
}
|
||||
// 返回文件数据
|
||||
return ['rules' => $rules, 'ignore' => $ignore, 'list' => $data];
|
||||
return [
|
||||
'name' => str_replace($this->root, '', $path),
|
||||
'hash' => md5(preg_replace('/\s+/', '', file_get_contents($path))),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取目录文件列表
|
||||
* @param string $path 待扫描的目录
|
||||
* @param string $path 待扫描目录
|
||||
* @param array $data 扫描结果
|
||||
* @return array
|
||||
*/
|
||||
private function scanList($path, $data = []): array
|
||||
private function _scanList($path, $data = []): array
|
||||
{
|
||||
if (file_exists($path)) if (is_dir($path)) foreach (scandir($path) as $sub) {
|
||||
if (strpos($sub, '.') !== 0) if (is_dir($temp = "{$path}/{$sub}")) {
|
||||
$data = array_merge($data, $this->scanList($temp));
|
||||
} else {
|
||||
array_push($data, $this->getInfo($temp));
|
||||
}
|
||||
} else {
|
||||
return [$this->getInfo($path)];
|
||||
foreach (NodeService::instance()->scanDirectory($path) as $file) {
|
||||
$data[] = $this->_getInfo($file);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定文件信息
|
||||
* @param string $realname 文件路径
|
||||
* @return array
|
||||
*/
|
||||
private function getInfo($realname): array
|
||||
{
|
||||
return [
|
||||
'name' => str_replace($this->root, '', $realname),
|
||||
'hash' => md5(preg_replace('/\s+/', '', file_get_contents($realname))),
|
||||
];
|
||||
}
|
||||
}
|
205
vendor/zoujingli/think-library/src/service/InterfaceService.php
vendored
Normal file
205
vendor/zoujingli/think-library/src/service/InterfaceService.php
vendored
Normal file
@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2020 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://gitee.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 代码仓库:https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 代码仓库:https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use think\admin\extend\HttpExtend;
|
||||
use think\admin\helper\ValidateHelper;
|
||||
use think\admin\Service;
|
||||
use think\App;
|
||||
use think\exception\HttpResponseException;
|
||||
|
||||
/**
|
||||
* 系统接口基础服务
|
||||
* Class InterfaceService
|
||||
* @package think\admin\service
|
||||
*/
|
||||
class InterfaceService extends Service
|
||||
{
|
||||
/**
|
||||
* 接口认证账号
|
||||
* @var string
|
||||
*/
|
||||
public $appid;
|
||||
|
||||
/**
|
||||
* 接口认证密钥
|
||||
* @var string
|
||||
*/
|
||||
public $appkey;
|
||||
|
||||
/**
|
||||
* 接口请求地址
|
||||
* @var string
|
||||
*/
|
||||
public $baseapi;
|
||||
|
||||
/**
|
||||
* 接口服务初始化
|
||||
* OpenService constructor.
|
||||
* @param App $app
|
||||
* @param string $appid 接口账号
|
||||
* @param string $appkey 接口密钥
|
||||
* @param string $baseapi 接口地址
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function __construct(App $app, $appid = '', $appkey = '', $baseapi = '')
|
||||
{
|
||||
parent::__construct($app);
|
||||
$this->appid = $appid ?: sysconf('data.interface_appid');
|
||||
$this->appkey = $appkey ?: sysconf('data.interface_appkey');
|
||||
$this->baseapi = $baseapi ?: sysconf('data.interface_baseapi');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取接口账号
|
||||
* @return string
|
||||
*/
|
||||
public function getAppid()
|
||||
{
|
||||
return $this->appid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求数据
|
||||
* @return array
|
||||
*/
|
||||
public function get(): array
|
||||
{
|
||||
// 基础参数获取
|
||||
$input = ValidateHelper::instance()->init([
|
||||
'appid.require' => lang('think_library_params_failed_empty', ['appid']),
|
||||
'nostr.require' => lang('think_library_params_failed_empty', ['nostr']),
|
||||
'time.require' => lang('think_library_params_failed_empty', ['time']),
|
||||
'sign.require' => lang('think_library_params_failed_empty', ['sign']),
|
||||
'data.require' => lang('think_library_params_failed_empty', ['data']),
|
||||
], 'post', [$this, 'baseError']);
|
||||
// 接口参数处理
|
||||
if ($input['appid'] !== $this->appid) {
|
||||
$this->baseError(lang('think_library_params_failed_auth'));
|
||||
}
|
||||
// 请求时间检查
|
||||
if (abs($input['time'] - time()) > 30) {
|
||||
$this->baseError(lang('think_library_params_failed_time'));
|
||||
}
|
||||
// 请求签名验证
|
||||
if (!$this->_checkSign($input)) {
|
||||
$this->baseError(lang('think_library_params_failed_sign'));
|
||||
}
|
||||
// 解析请求数据
|
||||
return json_decode($input['data'], true) ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复业务处理失败的消息
|
||||
* @param mixed $info 消息内容
|
||||
* @param mixed $data 返回数据
|
||||
* @param mixed $code 返回状态码
|
||||
*/
|
||||
public function error($info, $data = '{-null-}', $code = 0)
|
||||
{
|
||||
if ($data === '{-null-}') $data = new \stdClass();
|
||||
$this->baseResponse(lang('think_library_response_success'), ['code' => $code, 'info' => $info, 'data' => $data], 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复业务处理成功的消息
|
||||
* @param mixed $info 消息内容
|
||||
* @param mixed $data 返回数据
|
||||
* @param mixed $code 返回状态码
|
||||
*/
|
||||
public function success($info, $data = '{-null-}', $code = 1)
|
||||
{
|
||||
if ($data === '{-null-}') $data = new \stdClass();
|
||||
$this->baseResponse(lang('think_library_response_success'), ['code' => $code, 'info' => $info, 'data' => $data], 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复根失败消息
|
||||
* @param mixed $info 消息内容
|
||||
* @param mixed $data 返回数据
|
||||
* @param mixed $code 根状态码
|
||||
*/
|
||||
public function baseError($info, $data = [], $code = 0)
|
||||
{
|
||||
$this->baseResponse($info, $data, $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复根成功消息
|
||||
* @param mixed $info 消息内容
|
||||
* @param mixed $data 返回数据
|
||||
* @param mixed $code 根状态码
|
||||
*/
|
||||
public function baseSuccess($info, $data = [], $code = 1)
|
||||
{
|
||||
$this->baseResponse($info, $data, $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复根签名消息
|
||||
* @param mixed $info 消息内容
|
||||
* @param mixed $data 返回数据
|
||||
* @param mixed $code 根状态码
|
||||
*/
|
||||
public function baseResponse($info, $data = [], $code = 1)
|
||||
{
|
||||
$extend = ['code' => $code, 'info' => $info, 'data' => $data, 'appid' => $data['appid']];
|
||||
throw new HttpResponseException(json(array_merge($this->_buildSign($data), $extend)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口数据请求
|
||||
* @param string $uri 接口地址
|
||||
* @param array $data 请求数据
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function doRequest(string $uri, array $data = []): array
|
||||
{
|
||||
$result = json_decode(HttpExtend::post($this->baseapi . $uri, $this->_buildSign($data)), true);
|
||||
if (empty($result)) throw new \think\admin\Exception(lang('think_library_response_failed'));
|
||||
if (empty($result['code'])) throw new \think\admin\Exception($result['info']);
|
||||
return $result['data'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求数据签名验证
|
||||
* @param array $data 待检查数据
|
||||
* @return boolean
|
||||
*/
|
||||
private function _checkSign(array $data): bool
|
||||
{
|
||||
if (isset($data['sign']) && isset($data['appid']) && isset($data['data']) && isset($data['time']) && isset($data['nostr'])) {
|
||||
return md5("{$data['appid']}#{$data['data']}#{$data['time']}#{$this->appkey}#{$data['nostr']}") === $data['sign'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口数据签名
|
||||
* @param array $data ['appid','nostr','time','sign','data']
|
||||
* @return array
|
||||
*/
|
||||
private function _buildSign(array $data): array
|
||||
{
|
||||
[$time, $nostr, $json] = [time(), uniqid(), json_encode($data, 256)];
|
||||
$sign = md5("{$this->appid}#{$json}#{$time}#{$this->appkey}#{$nostr}");
|
||||
return ['appid' => $this->appid, 'nostr' => $nostr, 'time' => $time, 'sign' => $sign, 'data' => $json];
|
||||
}
|
||||
}
|
@ -52,7 +52,7 @@ class MenuService extends Service
|
||||
public function getTree()
|
||||
{
|
||||
$result = $this->app->db->name('SystemMenu')->where(['status' => '1'])->order('sort desc,id asc')->select();
|
||||
return $this->buildData(DataExtend::arr2tree($result->toArray()), NodeService::instance()->getMethods());
|
||||
return $this->_buildData(DataExtend::arr2tree($result->toArray()), NodeService::instance()->getMethods());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -62,11 +62,11 @@ class MenuService extends Service
|
||||
* @return array
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
private function buildData($menus, $nodes)
|
||||
private function _buildData($menus, $nodes)
|
||||
{
|
||||
foreach ($menus as $key => &$menu) {
|
||||
if (!empty($menu['sub'])) {
|
||||
$menu['sub'] = $this->buildData($menu['sub'], $nodes);
|
||||
$menu['sub'] = $this->_buildData($menu['sub'], $nodes);
|
||||
}
|
||||
if (!empty($menu['sub'])) $menu['url'] = '#';
|
||||
elseif ($menu['url'] === '#') unset($menus[$key]);
|
||||
|
@ -26,6 +26,47 @@ use think\admin\Service;
|
||||
*/
|
||||
class ModuleService extends Service
|
||||
{
|
||||
/**
|
||||
* 官方应用地址
|
||||
* @var string
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* 官方应用版本
|
||||
* @var string
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
/**
|
||||
* 服务初始化
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
$full = $this->app->config->get('app.thinkadmin_ver', 'v4.0.0');
|
||||
$this->version = trim($full, 'v');
|
||||
$version = strstr($full . '.', '.', true);
|
||||
$this->server = "https://{$version}.thinkadmin.top";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务端地址
|
||||
* @return string
|
||||
*/
|
||||
public function getServer()
|
||||
{
|
||||
return $this->server;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本号信息
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模块变更
|
||||
* @return array
|
||||
@ -58,10 +99,9 @@ class ModuleService extends Service
|
||||
{
|
||||
$data = $this->app->cache->get('moduleOnlineData', []);
|
||||
if (!empty($data)) return $data;
|
||||
$server = InstallService::instance()->getServer();
|
||||
$result = json_decode(HttpExtend::get("{$server}/admin/api.update/version"), true);
|
||||
$result = json_decode(HttpExtend::get("{$this->server}/admin/api.update/version"), true);
|
||||
if (isset($result['code']) && $result['code'] > 0 && isset($result['data']) && is_array($result['data'])) {
|
||||
$this->app->cache->set('moduleOnlineData', $result['data'], 1800);
|
||||
$this->app->cache->set('moduleOnlineData', $result['data'], 30);
|
||||
return $result['data'];
|
||||
} else {
|
||||
return [];
|
||||
@ -76,7 +116,7 @@ class ModuleService extends Service
|
||||
public function install($name): array
|
||||
{
|
||||
$this->app->cache->set('moduleOnlineData', []);
|
||||
$data = InstallService::instance()->grenerateDifference(["app/{$name}"]);
|
||||
$data = InstallService::instance()->grenerateDifference(['app' . '/' . $name]);
|
||||
if (empty($data)) {
|
||||
return [0, '没有需要安装的文件', []];
|
||||
} else {
|
||||
@ -104,13 +144,13 @@ class ModuleService extends Service
|
||||
*/
|
||||
public function getModules(array $data = []): array
|
||||
{
|
||||
foreach (NodeService::instance()->getModules() as $name) {
|
||||
if (is_array($vars = $this->getModuleVersion($name)) && isset($vars['version'])) {
|
||||
if (preg_match('|^\d{4}\.\d{2}\.\d{2}\.\d{2}$|', $vars['version'])) {
|
||||
$data[$name] = $vars;
|
||||
foreach (glob("{$this->app->getBasePath()}{$name}/module/change/*.md") as $file) {
|
||||
$data[$name]['change'][pathinfo($file, PATHINFO_FILENAME)] = Parsedown::instance()->parse(file_get_contents($file));
|
||||
}
|
||||
$service = NodeService::instance();
|
||||
foreach ($service->getModules() as $name) {
|
||||
$vars = $this->_getModuleVersion($name);
|
||||
if (is_array($vars) && isset($vars['version']) && preg_match('|^\d{4}\.\d{2}\.\d{2}\.\d{2}$|', $vars['version'])) {
|
||||
$data[$name] = $vars;
|
||||
foreach ($service->scanDirectory($this->app->getBasePath() . $name . '/module/change/', [], '.md') as $file) {
|
||||
$data[$name]['change'][pathinfo($file, PATHINFO_FILENAME)] = Parsedown::instance()->parse(file_get_contents($file));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -155,10 +195,12 @@ class ModuleService extends Service
|
||||
* @param string $name 模块名称
|
||||
* @return bool|array|null
|
||||
*/
|
||||
private function getModuleVersion($name)
|
||||
private function _getModuleVersion($name)
|
||||
{
|
||||
$file = "{$this->app->getBasePath()}{$name}/module/version.php";
|
||||
if (file_exists($file) && is_file($file) && is_array($vars = @include $file)) {
|
||||
$appdir = $this->app->getBasePath() . $name;
|
||||
$filename = $appdir . DIRECTORY_SEPARATOR . 'module' . DIRECTORY_SEPARATOR . 'version.json';
|
||||
if (file_exists($filename) && is_file($filename) && is_readable($filename)) {
|
||||
$vars = json_decode(file_get_contents($filename), true);
|
||||
return isset($vars['name']) && isset($vars['version']) ? $vars : null;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -33,7 +33,7 @@ class NodeService extends Service
|
||||
{
|
||||
$dots = [];
|
||||
foreach (explode('.', strtr($name, '/', '.')) as $dot) {
|
||||
$dots[] = trim(preg_replace("/[A-Z]/", "_\\0", $dot), "_");
|
||||
$dots[] = trim(preg_replace("/[A-Z]/", "_\\0", $dot), '_');
|
||||
}
|
||||
return strtolower(join('.', $dots));
|
||||
}
|
||||
@ -60,7 +60,7 @@ class NodeService extends Service
|
||||
{
|
||||
if (empty($node)) return $this->getCurrent();
|
||||
if (count($attrs = explode('/', $node)) === 1) {
|
||||
return $this->getCurrent('controller') . "/{$node}";
|
||||
return $this->getCurrent('controller') . '/' . $node;
|
||||
} else {
|
||||
$attrs[1] = $this->nameTolower($attrs[1]);
|
||||
return join('/', $attrs);
|
||||
@ -74,11 +74,9 @@ class NodeService extends Service
|
||||
*/
|
||||
public function getModules($data = [])
|
||||
{
|
||||
if ($handle = opendir($this->app->getBasePath())) {
|
||||
while (false !== ($file = readdir($handle))) if ($file !== "." && $file !== "..") {
|
||||
if (is_dir($this->app->getBasePath() . $file)) $data[] = $file;
|
||||
}
|
||||
closedir($handle);
|
||||
$path = $this->app->getBasePath();
|
||||
foreach (scandir($path) as $item) if ($item[0] !== '.') {
|
||||
if (is_dir(realpath($path . $item))) $data[] = $item;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
@ -100,7 +98,7 @@ class NodeService extends Service
|
||||
$data = [];
|
||||
}
|
||||
$ignores = get_class_methods('\think\admin\Controller');
|
||||
foreach ($this->_scanDirectory($this->app->getBasePath()) as $file) {
|
||||
foreach ($this->scanDirectory($this->app->getBasePath()) as $file) {
|
||||
if (preg_match("|/(\w+)/(\w+)/controller/(.+)\.php$|i", $file, $matches)) {
|
||||
[, $namespace, $appname, $classname] = $matches;
|
||||
$prefix = strtr("{$appname}/{$this->nameTolower($classname)}", '\\', '/');
|
||||
@ -127,9 +125,7 @@ class NodeService extends Service
|
||||
{
|
||||
$text = strtr($comment, "\n", ' ');
|
||||
$title = preg_replace('/^\/\*\s*\*\s*\*\s*(.*?)\s*\*.*?$/', '$1', $text);
|
||||
foreach (['@auth', '@menu', '@login'] as $find) if (stripos($title, $find) === 0) {
|
||||
$title = $default;
|
||||
}
|
||||
if (in_array(substr($title, 0, 5), ['@auth', '@menu', '@logi'])) $title = $default;
|
||||
return [
|
||||
'title' => $title ?: $default,
|
||||
'isauth' => intval(preg_match('/@auth\s*true/i', $text)),
|
||||
@ -142,16 +138,17 @@ class NodeService extends Service
|
||||
* 获取所有PHP文件列表
|
||||
* @param string $path 扫描目录
|
||||
* @param array $data 额外数据
|
||||
* @param string $ext 有文件后缀
|
||||
* @param string $ext 文件后缀
|
||||
* @return array
|
||||
*/
|
||||
private function _scanDirectory($path, $data = [], $ext = 'php')
|
||||
public function scanDirectory($path, $data = [], $ext = '.php')
|
||||
{
|
||||
foreach (glob("{$path}*") as $item) {
|
||||
if (is_dir($item)) {
|
||||
$data = array_merge($data, $this->_scanDirectory("{$item}" . DIRECTORY_SEPARATOR));
|
||||
} elseif (is_file($item) && pathinfo($item, PATHINFO_EXTENSION) === $ext) {
|
||||
$data[] = strtr($item, '\\', '/');
|
||||
foreach (scandir($path) as $item) if ($item[0] !== '.') {
|
||||
$realpath = rtrim($path, '\\/') . DIRECTORY_SEPARATOR . $item;
|
||||
if (is_readable($realpath)) if (is_dir($realpath)) {
|
||||
$data = $this->scanDirectory($realpath, $data, $ext);
|
||||
} elseif (is_file($realpath) && (is_null($ext) || strstr($realpath, '.') === $ext)) {
|
||||
$data[] = $realpath;
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
|
@ -1,85 +0,0 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2020 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://gitee.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 代码仓库:https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 代码仓库:https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use think\admin\extend\HttpExtend;
|
||||
use think\admin\Service;
|
||||
use think\App;
|
||||
|
||||
/**
|
||||
* 楚才开放平台服务
|
||||
* Class OpenService
|
||||
* @package think\admin\service
|
||||
*/
|
||||
class OpenService extends Service
|
||||
{
|
||||
/**
|
||||
* 接口账号
|
||||
* @var string
|
||||
*/
|
||||
protected $appid;
|
||||
|
||||
/**
|
||||
* 接口密钥
|
||||
* @var string
|
||||
*/
|
||||
protected $appkey;
|
||||
|
||||
/**
|
||||
* 楚才开放平台初始化
|
||||
* OpenService constructor.
|
||||
* @param App $app
|
||||
* @param string $appid 接口账号
|
||||
* @param string $appkey 接口密钥
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function __construct(App $app, $appid = '', $appkey = '')
|
||||
{
|
||||
parent::__construct($app);
|
||||
$this->appid = $appid ?: sysconf('data.cuci_open_appid');
|
||||
$this->appkey = $appkey ?: sysconf('data.cuci_open_appkey');
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口数据签名
|
||||
* @param array $data [time, nostr, json, sign]
|
||||
* @return array
|
||||
*/
|
||||
public function signData(array $data): array
|
||||
{
|
||||
[$time, $nostr, $json] = [time(), uniqid(), json_encode($data, JSON_UNESCAPED_UNICODE)];
|
||||
return [$time, $nostr, $json, md5("{$this->appid}#{$json}#{$time}#{$this->appkey}#{$nostr}")];
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口数据请求
|
||||
* @param string $uri 接口地址
|
||||
* @param array $data 请求数据
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function doRequest(string $uri, array $data = []): array
|
||||
{
|
||||
[$time, $nostr, $json, $sign] = $this->signData($data);
|
||||
$post = ['appid' => $this->appid, 'time' => $time, 'nostr' => $nostr, 'sign' => $sign, 'data' => $json];
|
||||
$result = json_decode(HttpExtend::post("https://open.cuci.cc/{$uri}", $post), true);
|
||||
if (empty($result)) throw new \think\admin\Exception('服务端接口响应异常');
|
||||
if (empty($result['code'])) throw new \think\admin\Exception($result['info']);
|
||||
return $result['data'] ?? [];
|
||||
}
|
||||
}
|
@ -42,7 +42,7 @@ class ProcessService extends Service
|
||||
*/
|
||||
public function version()
|
||||
{
|
||||
return $this->app->config->get('app.thinkadmin_ver', 'v4');
|
||||
return ModuleService::instance()->getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -154,7 +154,7 @@ class QueueService extends Service
|
||||
*/
|
||||
public function progress($status = null, $message = null, $progress = null)
|
||||
{
|
||||
$cachefile = "queue_{$this->code}_progress";
|
||||
$ckey = "queue_{$this->code}_progress";
|
||||
if (is_numeric($status) && intval($status) === 3) {
|
||||
if (!is_numeric($progress)) $progress = '100.00';
|
||||
if (is_null($message)) $message = '>>> 任务已经完成 <<<';
|
||||
@ -164,7 +164,7 @@ class QueueService extends Service
|
||||
if (is_null($message)) $message = '>>> 任务执行失败 <<<';
|
||||
}
|
||||
try {
|
||||
$data = $this->app->cache->get($cachefile, [
|
||||
$data = $this->app->cache->get($ckey, [
|
||||
'code' => $this->code, 'status' => $status, 'message' => $message, 'progress' => $progress, 'history' => [],
|
||||
]);
|
||||
} catch (\Exception|\Error $exception) {
|
||||
@ -187,7 +187,7 @@ class QueueService extends Service
|
||||
if (count($data['history']) > 10) {
|
||||
$data['history'] = array_slice($data['history'], -10);
|
||||
}
|
||||
$this->app->cache->set($cachefile, $data, 86400);
|
||||
$this->app->cache->set($ckey, $data, 86400);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
@ -43,18 +43,27 @@ class TokenService extends Service
|
||||
private $cachedata = [];
|
||||
|
||||
/**
|
||||
* 表单令牌服务初始化
|
||||
* 令牌服务初始化
|
||||
*/
|
||||
protected function initialize()
|
||||
{
|
||||
$user = AdminService::instance()->getUserName();
|
||||
$this->cachename = 'systoken_' . ($user ?: 'default');
|
||||
$this->cachename = $this->getCacheName();
|
||||
$this->cachedata = $this->_getCacheList(true);
|
||||
$this->app->event->listen('HttpEnd', function () {
|
||||
TokenService::instance()->saveCacheData();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存名称
|
||||
* @return string
|
||||
*/
|
||||
public function getCacheName()
|
||||
{
|
||||
$sid = $this->app->session->getId();
|
||||
return 'systoken_' . ($sid ?: 'default');
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存缓存到文件
|
||||
*/
|
||||
|
@ -126,9 +126,8 @@ class ZtSmsService extends Service
|
||||
if (strpos($sign, '】') === false) $signs[$key] = $sign . '】';
|
||||
if (strpos($sign, '【') === false) $signs[$key] = '【' . $sign;
|
||||
}
|
||||
return $this->doRequest('https://api.mix2.zthysms.com/sms/v1/sign', [
|
||||
'sign' => $signs, 'remark' => $remark,
|
||||
]);
|
||||
$data = ['sign' => $signs, 'remark' => $remark];
|
||||
return $this->doRequest('https://api.mix2.zthysms.com/sms/v1/sign', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,7 +133,7 @@ class AliossStorage extends Storage
|
||||
*/
|
||||
public function del($name, $safe = false)
|
||||
{
|
||||
list($file) = explode('?', $name);
|
||||
[$file] = explode('?', $name);
|
||||
$result = HttpExtend::request('DELETE', "http://{$this->bucket}.{$this->point}/{$file}", [
|
||||
'returnHeader' => true, 'headers' => $this->headerSign('DELETE', $file),
|
||||
]);
|
||||
|
@ -108,7 +108,7 @@ class QiniuStorage extends Storage
|
||||
*/
|
||||
public function del($name, $safe = false)
|
||||
{
|
||||
list($EncodedEntryURI, $AccessToken) = $this->getAccessToken($name, 'delete');
|
||||
[$EncodedEntryURI, $AccessToken] = $this->getAccessToken($name, 'delete');
|
||||
$data = json_decode(HttpExtend::post("http://rs.qiniu.com/delete/{$EncodedEntryURI}", [], [
|
||||
'headers' => ["Authorization:QBox {$AccessToken}"],
|
||||
]), true);
|
||||
@ -158,7 +158,7 @@ class QiniuStorage extends Storage
|
||||
*/
|
||||
public function info($name, $safe = false, $attname = null)
|
||||
{
|
||||
list($entry, $token) = $this->getAccessToken($name);
|
||||
[$entry, $token] = $this->getAccessToken($name);
|
||||
$data = json_decode(HttpExtend::get("http://rs.qiniu.com/stat/{$entry}", [], ['headers' => ["Authorization: QBox {$token}"]]), true);
|
||||
return isset($data['md5']) ? ['file' => $name, 'url' => $this->url($name, $safe, $attname), 'key' => $name] : [];
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user