123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- <?php
- namespace app\admin\command;
- use think\addons\AddonException;
- use think\addons\Service;
- use think\Config;
- use think\console\Command;
- use think\console\Input;
- use think\console\input\Option;
- use think\console\Output;
- use think\Db;
- use think\Exception;
- use think\exception\PDOException;
- class Addon extends Command
- {
- protected function configure()
- {
- $this
- ->setName('addon')
- ->addOption('name', 'a', Option::VALUE_REQUIRED, 'addon name', null)
- ->addOption('action', 'c', Option::VALUE_REQUIRED, 'action(create/enable/disable/uninstall/refresh/package/move)', 'create')
- ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', null)
- ->addOption('release', 'r', Option::VALUE_OPTIONAL, 'addon release version', null)
- ->addOption('uid', 'u', Option::VALUE_OPTIONAL, 'fastadmin uid', null)
- ->addOption('token', 't', Option::VALUE_OPTIONAL, 'fastadmin token', null)
- ->addOption('domain', 'd', Option::VALUE_OPTIONAL, 'domain', null)
- ->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local package', null)
- ->setDescription('Addon manager');
- }
- protected function execute(Input $input, Output $output)
- {
- \think\Config::load(dirname(dirname(__FILE__)) . DS . 'config.php');
- $name = $input->getOption('name') ?: '';
- $action = $input->getOption('action') ?: '';
- if (stripos($name, 'addons' . DS) !== false) {
- $name = explode(DS, $name)[1];
- }
- //强制覆盖
- $force = $input->getOption('force');
- //版本
- $release = $input->getOption('release') ?: '';
- //uid
- $uid = $input->getOption('uid') ?: '';
- //token
- $token = $input->getOption('token') ?: '';
- include dirname(__DIR__) . DS . 'common.php';
- if (!$name && !in_array($action, ['refresh'])) {
- throw new Exception('Addon name could not be empty');
- }
- if (!$action || !in_array($action, ['create', 'disable', 'enable', 'install', 'uninstall', 'refresh', 'upgrade', 'package', 'move'])) {
- throw new Exception('Please input correct action name');
- }
- // 查询一次SQL,判断连接是否正常
- Db::execute("SELECT 1");
- $addonDir = ADDON_PATH . $name . DS;
- switch ($action) {
- case 'create':
- //非覆盖模式时如果存在则报错
- if (is_dir($addonDir) && !$force) {
- throw new Exception("addon already exists!\nIf you need to create again, use the parameter --force=true ");
- }
- //如果存在先移除
- if (is_dir($addonDir)) {
- rmdirs($addonDir);
- }
- mkdir($addonDir, 0755, true);
- mkdir($addonDir . DS . 'controller', 0755, true);
- $menuList = \app\common\library\Menu::export($name);
- $createMenu = $this->getCreateMenu($menuList);
- $prefix = Config::get('database.prefix');
- $createTableSql = '';
- try {
- $result = Db::query("SHOW CREATE TABLE `" . $prefix . $name . "`;");
- if (isset($result[0]) && isset($result[0]['Create Table'])) {
- $createTableSql = $result[0]['Create Table'];
- }
- } catch (PDOException $e) {
- }
- $data = [
- 'name' => $name,
- 'addon' => $name,
- 'addonClassName' => ucfirst($name),
- 'addonInstallMenu' => $createMenu ? "\$menu = " . var_export_short($createMenu) . ";\n\tMenu::create(\$menu);" : '',
- 'addonUninstallMenu' => $menuList ? 'Menu::delete("' . $name . '");' : '',
- 'addonEnableMenu' => $menuList ? 'Menu::enable("' . $name . '");' : '',
- 'addonDisableMenu' => $menuList ? 'Menu::disable("' . $name . '");' : '',
- ];
- $this->writeToFile("addon", $data, $addonDir . ucfirst($name) . '.php');
- $this->writeToFile("config", $data, $addonDir . 'config.php');
- $this->writeToFile("info", $data, $addonDir . 'info.ini');
- $this->writeToFile("controller", $data, $addonDir . 'controller' . DS . 'Index.php');
- if ($createTableSql) {
- $createTableSql = str_replace("`" . $prefix, '`__PREFIX__', $createTableSql);
- file_put_contents($addonDir . 'install.sql', $createTableSql);
- }
- $output->info("Create Successed!");
- break;
- case 'disable':
- case 'enable':
- try {
- //调用启用、禁用的方法
- Service::$action($name, 0);
- } catch (AddonException $e) {
- if ($e->getCode() != -3) {
- throw new Exception($e->getMessage());
- }
- if (!$force) {
- //如果有冲突文件则提醒
- $data = $e->getData();
- foreach ($data['conflictlist'] as $k => $v) {
- $output->warning($v);
- }
- $output->info("Are you sure you want to " . ($action == 'enable' ? 'override' : 'delete') . " all those files? Type 'yes' to continue: ");
- $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r'));
- if (trim($line) != 'yes') {
- throw new Exception("Operation is aborted!");
- }
- }
- //调用启用、禁用的方法
- Service::$action($name, 1);
- } catch (Exception $e) {
- throw new Exception($e->getMessage());
- }
- $output->info(ucfirst($action) . " Successed!");
- break;
- case 'uninstall':
- //非覆盖模式时如果存在则报错
- if (!$force) {
- throw new Exception("If you need to uninstall addon, use the parameter --force=true ");
- }
- try {
- Service::uninstall($name, 0);
- } catch (AddonException $e) {
- if ($e->getCode() != -3) {
- throw new Exception($e->getMessage());
- }
- if (!$force) {
- //如果有冲突文件则提醒
- $data = $e->getData();
- foreach ($data['conflictlist'] as $k => $v) {
- $output->warning($v);
- }
- $output->info("Are you sure you want to delete all those files? Type 'yes' to continue: ");
- $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r'));
- if (trim($line) != 'yes') {
- throw new Exception("Operation is aborted!");
- }
- }
- Service::uninstall($name, 1);
- } catch (Exception $e) {
- throw new Exception($e->getMessage());
- }
- $output->info("Uninstall Successed!");
- break;
- case 'refresh':
- Service::refresh();
- $output->info("Refresh Successed!");
- break;
- case 'package':
- $infoFile = $addonDir . 'info.ini';
- if (!is_file($infoFile)) {
- throw new Exception(__('Addon info file was not found'));
- }
- $info = get_addon_info($name);
- if (!$info) {
- throw new Exception(__('Addon info file data incorrect'));
- }
- $infoname = $info['name'] ?? '';
- if (!$infoname || !preg_match("/^[a-z]+$/i", $infoname) || $infoname != $name) {
- throw new Exception(__('Addon info name incorrect'));
- }
- $infoversion = $info['version'] ?? '';
- if (!$infoversion || !preg_match("/^\d+\.\d+\.\d+$/i", $infoversion)) {
- throw new Exception(__('Addon info version incorrect'));
- }
- $addonTmpDir = RUNTIME_PATH . 'addons' . DS;
- if (!is_dir($addonTmpDir)) {
- @mkdir($addonTmpDir, 0755, true);
- }
- $addonFile = $addonTmpDir . $infoname . '-' . $infoversion . '.zip';
- if (!class_exists('ZipArchive')) {
- throw new Exception(__('ZinArchive not install'));
- }
- $zip = new \ZipArchive;
- $zip->open($addonFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
- $files = new \RecursiveIteratorIterator(
- new \RecursiveDirectoryIterator($addonDir), \RecursiveIteratorIterator::LEAVES_ONLY
- );
- foreach ($files as $name => $file) {
- if (!$file->isDir()) {
- $filePath = $file->getRealPath();
- $relativePath = str_replace(DS, '/', substr($filePath, strlen($addonDir)));
- if (!in_array($file->getFilename(), ['.git', '.DS_Store', 'Thumbs.db'])) {
- $zip->addFile($filePath, $relativePath);
- }
- }
- }
- $zip->close();
- $output->info("Package Successed!");
- break;
- case 'move':
- $movePath = [
- 'adminOnlySelfDir' => ['admin/behavior', 'admin/controller', 'admin/library', 'admin/model', 'admin/validate', 'admin/view'],
- 'adminAllSubDir' => ['admin/lang'],
- 'publicDir' => ['public/assets/addons', 'public/assets/js/backend']
- ];
- $paths = [];
- $appPath = str_replace('/', DS, APP_PATH);
- $rootPath = str_replace('/', DS, ROOT_PATH);
- foreach ($movePath as $k => $items) {
- switch ($k) {
- case 'adminOnlySelfDir':
- foreach ($items as $v) {
- $v = str_replace('/', DS, $v);
- $oldPath = $appPath . $v . DS . $name;
- $newPath = $rootPath . "addons" . DS . $name . DS . "application" . DS . $v . DS . $name;
- $paths[$oldPath] = $newPath;
- }
- break;
- case 'adminAllSubDir':
- foreach ($items as $v) {
- $v = str_replace('/', DS, $v);
- $vPath = $appPath . $v;
- $list = scandir($vPath);
- foreach ($list as $_v) {
- if (!in_array($_v, ['.', '..']) && is_dir($vPath . DS . $_v)) {
- $oldPath = $appPath . $v . DS . $_v . DS . $name;
- $newPath = $rootPath . "addons" . DS . $name . DS . "application" . DS . $v . DS . $_v . DS . $name;
- $paths[$oldPath] = $newPath;
- }
- }
- }
- break;
- case 'publicDir':
- foreach ($items as $v) {
- $v = str_replace('/', DS, $v);
- $oldPath = $rootPath . $v . DS . $name;
- $newPath = $rootPath . 'addons' . DS . $name . DS . $v . DS . $name;
- $paths[$oldPath] = $newPath;
- }
- break;
- }
- }
- foreach ($paths as $oldPath => $newPath) {
- if (is_dir($oldPath)) {
- if ($force) {
- if (is_dir($newPath)) {
- $list = scandir($newPath);
- foreach ($list as $_v) {
- if (!in_array($_v, ['.', '..'])) {
- $file = $newPath . DS . $_v;
- @chmod($file, 0777);
- @unlink($file);
- }
- }
- @rmdir($newPath);
- }
- }
- copydirs($oldPath, $newPath);
- }
- }
- break;
- default:
- break;
- }
- }
- /**
- * 获取创建菜单的数组
- * @param array $menu
- * @return array
- */
- protected function getCreateMenu($menu)
- {
- $result = [];
- foreach ($menu as $k => & $v) {
- $arr = [
- 'name' => $v['name'],
- 'title' => $v['title'],
- ];
- if ($v['icon'] != 'fa fa-circle-o') {
- $arr['icon'] = $v['icon'];
- }
- if ($v['ismenu']) {
- $arr['ismenu'] = $v['ismenu'];
- }
- if (isset($v['childlist']) && $v['childlist']) {
- $arr['sublist'] = $this->getCreateMenu($v['childlist']);
- }
- $result[] = $arr;
- }
- return $result;
- }
- /**
- * 写入到文件
- * @param string $name
- * @param array $data
- * @param string $pathname
- * @return mixed
- */
- protected function writeToFile($name, $data, $pathname)
- {
- $search = $replace = [];
- foreach ($data as $k => $v) {
- $search[] = "{%{$k}%}";
- $replace[] = $v;
- }
- $stub = file_get_contents($this->getStub($name));
- $content = str_replace($search, $replace, $stub);
- if (!is_dir(dirname($pathname))) {
- mkdir(strtolower(dirname($pathname)), 0755, true);
- }
- return file_put_contents($pathname, $content);
- }
- /**
- * 获取基础模板
- * @param string $name
- * @return string
- */
- protected function getStub($name)
- {
- return __DIR__ . '/Addon/stubs/' . $name . '.stub';
- }
- }
|