ServiceProvider.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. <?php
  2. namespace Dcat\Admin\Extend;
  3. use Dcat\Admin\Admin;
  4. use Dcat\Admin\Exception\RuntimeException;
  5. use Dcat\Admin\Support\ComposerProperty;
  6. use Illuminate\Support\Arr;
  7. use Illuminate\Support\Facades\Validator;
  8. use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
  9. use Symfony\Component\Console\Output\NullOutput;
  10. abstract class ServiceProvider extends LaravelServiceProvider
  11. {
  12. /**
  13. * @var ComposerProperty
  14. */
  15. public $composerProperty;
  16. /**
  17. * @var string
  18. */
  19. protected $name;
  20. /**
  21. * @var string
  22. */
  23. protected $path;
  24. /**
  25. * @var array
  26. */
  27. protected $js = [];
  28. /**
  29. * @var array
  30. */
  31. protected $css = [];
  32. /**
  33. * @var array
  34. */
  35. protected $menu = [];
  36. /**
  37. * @var array
  38. */
  39. protected $permission = [];
  40. /**
  41. * @var array
  42. */
  43. protected $menuValidationRules = [
  44. 'title' => 'required',
  45. 'path' => 'required',
  46. 'icon' => 'required',
  47. ];
  48. /**
  49. * @var array
  50. */
  51. protected $permissionValidationRules = [
  52. 'name' => 'required',
  53. 'slug' => 'required',
  54. 'path' => 'required',
  55. ];
  56. /**
  57. * @var \Symfony\Component\Console\Output\OutputInterface
  58. */
  59. public $output;
  60. public function __construct($app)
  61. {
  62. parent::__construct($app);
  63. $this->output = new NullOutput();
  64. }
  65. /**
  66. * {@inheritdoc}
  67. */
  68. public function boot()
  69. {
  70. if ($views = $this->getViewPath()) {
  71. $this->loadViewsFrom($views, $this->getName());
  72. }
  73. if ($lang = $this->getLangPath()) {
  74. $this->loadTranslationsFrom($lang, $this->getName());
  75. }
  76. if ($routes = $this->getRoutes()) {
  77. $this->registerRoutes($routes);
  78. }
  79. $this->aliasAssets();
  80. }
  81. /**
  82. * 获取扩展名称.
  83. *
  84. * @return string
  85. */
  86. final public function getName()
  87. {
  88. return $this->name ?: ($this->name = str_replace('/', '.', $this->composerProperty->name));
  89. }
  90. /**
  91. * 获取当前已安装版本.
  92. *
  93. * @return string
  94. */
  95. final public function getVersion()
  96. {
  97. return Admin::extension()->versionManager()->getCurrentVersion($this);
  98. }
  99. /**
  100. * 获取当前最新版本.
  101. *
  102. * @return string
  103. */
  104. final public function getLatestVersion()
  105. {
  106. return Admin::extension()->versionManager()->getFileVersions($this);
  107. }
  108. /**
  109. * 获取当前本地最新版本.
  110. *
  111. * @return string
  112. */
  113. final public function getLocalLatestVersion()
  114. {
  115. return Admin::extension()->versionManager()->getFileVersions($this);
  116. }
  117. /**
  118. * 获取扩展包路径.
  119. *
  120. * @param string $path
  121. *
  122. * @return string
  123. *
  124. * @throws \ReflectionException
  125. */
  126. public function path(?string $path = null)
  127. {
  128. if (! $this->path) {
  129. $this->path = realpath(dirname((new \ReflectionClass(static::class))->getFileName()).'/..');
  130. if (! is_dir($this->path)) {
  131. throw new RuntimeException("The {$this->path} is not a directory.");
  132. }
  133. }
  134. $path = ltrim($path, '/');
  135. return $path ? $this->path.'/'.$path : $this->path;
  136. }
  137. /**
  138. * 判断扩展是否启用.
  139. *
  140. * @return bool
  141. */
  142. final public function enabled()
  143. {
  144. return Admin::extension()->enabled($this->getName());
  145. }
  146. /**
  147. * 判断扩展是否禁用.
  148. *
  149. * @return bool
  150. */
  151. final public function disabled()
  152. {
  153. return ! $this->enabled();
  154. }
  155. /**
  156. * 获取配置.
  157. *
  158. * @param string $key
  159. * @param null $default
  160. *
  161. * @return \Illuminate\Config\Repository|mixed
  162. */
  163. final public function config($key = null, $default = null)
  164. {
  165. return Admin::setting()->get($this->name);
  166. }
  167. /**
  168. * 导入扩展.
  169. */
  170. public function import()
  171. {
  172. $this->importMenus();
  173. $this->importPermissions();
  174. }
  175. /**
  176. * 卸载扩展.
  177. */
  178. public function uninstall()
  179. {
  180. }
  181. /**
  182. * 发布静态资源.
  183. */
  184. public function publishable()
  185. {
  186. if ($assets = $this->getAssetPath()) {
  187. $this->publishes([
  188. $assets => public_path(Admin::asset()->getRealPath('@extension'))
  189. ], $this->getName());
  190. }
  191. }
  192. /**
  193. * 注册路由.
  194. *
  195. * @param $callback
  196. */
  197. public function registerRoutes($callback)
  198. {
  199. Admin::app()->routes(function ($router) use ($callback) {
  200. $attributes = array_merge(
  201. [
  202. 'prefix' => config('admin.route.prefix'),
  203. 'middleware' => config('admin.route.middleware'),
  204. ]
  205. );
  206. $router->group($attributes, $callback);
  207. });
  208. }
  209. /**
  210. * 获取静态资源路径.
  211. *
  212. * @return string
  213. */
  214. final public function getAssetPath()
  215. {
  216. return $this->path('resources/assets');
  217. }
  218. /**
  219. * 获取视图路径.
  220. *
  221. * @return string
  222. */
  223. final public function getViewPath()
  224. {
  225. return $this->path('resources/views');
  226. }
  227. /**
  228. * 获取语言包路径.
  229. *
  230. * @return string
  231. */
  232. final public function getLangPath()
  233. {
  234. return $this->path('resources/lang');
  235. }
  236. /**
  237. * 获取路由地址.
  238. *
  239. * @return string
  240. *
  241. * @throws \ReflectionException
  242. */
  243. final public function getRoutes()
  244. {
  245. $path = $this->path('src/Http/routes.php');
  246. return is_file($path) ? $path : null;
  247. }
  248. /**
  249. * 获取菜单.
  250. *
  251. * @return array
  252. */
  253. protected function menu()
  254. {
  255. return $this->menu;
  256. }
  257. /**
  258. * @return array
  259. */
  260. protected function permission()
  261. {
  262. return $this->permission;
  263. }
  264. /**
  265. * @param ComposerProperty $composerProperty
  266. *
  267. * @return $this
  268. */
  269. public function withComposerProperty(ComposerProperty $composerProperty)
  270. {
  271. $this->composerProperty = $composerProperty;
  272. return $this;
  273. }
  274. /**
  275. * 导入菜单.
  276. *
  277. * @throws \Exception
  278. */
  279. protected function importMenus()
  280. {
  281. if (! ($menu = $this->menu()) || ! $this->validateMenu($menu)) {
  282. return;
  283. }
  284. extract($menu);
  285. if ($this->checkMenu($path)) {
  286. $this->output->writeln("<warn>Menu [$path] already exists!</warn>");
  287. } else {
  288. $this->createMenu($title, $path, $icon);
  289. $this->output->writeln('<info>Import extension menu succeeded!</info>');
  290. }
  291. }
  292. /**
  293. * 导入权限.
  294. *
  295. * @throws \Exception
  296. */
  297. protected function importPermissions()
  298. {
  299. if (! $this->config('admin.permission.enable')) {
  300. return;
  301. }
  302. if (! ($permission = $this->permission()) || ! $this->validatePermission($permission)) {
  303. return;
  304. }
  305. extract($permission);
  306. if ($this->checkPermission($slug)) {
  307. $this->output->writeln("<warn>Permission [$slug] already exists!</warn>");
  308. } else {
  309. $this->createPermission($name, $slug, $path);
  310. $this->output->writeln('<info>Import extension permission succeeded!</info>');
  311. }
  312. }
  313. /**
  314. * 注册别名.
  315. */
  316. protected function aliasAssets()
  317. {
  318. if ($this->js || $this->css) {
  319. Admin::asset()->alias($this->getName(), $this->js, $this->css);
  320. }
  321. }
  322. /**
  323. * 验证菜单.
  324. *
  325. * @param array $menu
  326. *
  327. * @throws \Exception
  328. *
  329. * @return bool
  330. */
  331. protected function validateMenu(array $menu)
  332. {
  333. /** @var \Illuminate\Validation\Validator $validator */
  334. $validator = Validator::make($menu, $this->menuValidationRules);
  335. if ($validator->passes()) {
  336. return true;
  337. }
  338. $message = "Invalid menu:\r\n".implode("\r\n", Arr::flatten($validator->errors()->messages()));
  339. $this->output->writeln("<error>{$message}</error>");
  340. }
  341. /**
  342. * @param $path
  343. *
  344. * @return bool
  345. */
  346. protected function checkMenu($path)
  347. {
  348. $menuModel = config('admin.database.menu_model');
  349. return $menuModel::where('uri', $path)->exists();
  350. }
  351. /**
  352. * 验证权限.
  353. *
  354. * @param array $permission
  355. *
  356. * @throws \Exception
  357. *
  358. * @return bool
  359. */
  360. protected function validatePermission(array $permission)
  361. {
  362. /** @var \Illuminate\Validation\Validator $validator */
  363. $validator = Validator::make($permission, $this->permissionValidationRules);
  364. if ($validator->passes()) {
  365. return true;
  366. }
  367. $message = "Invalid permission:\r\n".implode("\r\n", Arr::flatten($validator->errors()->messages()));
  368. $this->output->writeln("<error>{$message}</error>");
  369. }
  370. /**
  371. * 创建菜单.
  372. *
  373. * @param string $title
  374. * @param string $uri
  375. * @param string $icon
  376. * @param int $parentId
  377. */
  378. protected function createMenu($title, $uri, $icon = 'fa-bars', $parentId = 0)
  379. {
  380. $menuModel = config('admin.database.menu_model');
  381. $lastOrder = $menuModel::max('order');
  382. $menuModel::create([
  383. 'parent_id' => $parentId,
  384. 'order' => $lastOrder + 1,
  385. 'title' => $title,
  386. 'icon' => $icon,
  387. 'uri' => $uri,
  388. ]);
  389. }
  390. /**
  391. * @param $slug
  392. *
  393. * @return bool
  394. */
  395. protected function checkPermission($slug)
  396. {
  397. $permissionModel = config('admin.database.permissions_model');
  398. return $permissionModel::where('slug', $slug)->exists();
  399. }
  400. /**
  401. * 创建权限.
  402. *
  403. * @param $name
  404. * @param $slug
  405. * @param $path
  406. */
  407. protected function createPermission($name, $slug, $path)
  408. {
  409. $permissionModel = config('admin.database.permissions_model');
  410. $permissionModel::create([
  411. 'name' => $name,
  412. 'slug' => $slug,
  413. 'http_path' => trim($path, '/'),
  414. ]);
  415. }
  416. }