BaseModule.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. <?php
  2. namespace rss;
  3. use function GuzzleHttp\Psr7\str;
  4. use yii\base\BootstrapInterface;
  5. use Yii;
  6. use yii\base\Module;
  7. /**
  8. * Base module definition class
  9. */
  10. class BaseModule extends Module implements BootstrapInterface
  11. {
  12. /**
  13. * @var string the prefix for routing of module
  14. */
  15. public $routePrefix = "admin";
  16. /**
  17. * @var string, the name of module
  18. */
  19. public $name = "Base module";
  20. /**
  21. * @var string, the description of module
  22. */
  23. public $description = "Base module interface";
  24. /**
  25. * @var string the vendor name of module
  26. */
  27. private $vendor = "";
  28. /**
  29. * @var string the alias of module base path
  30. */
  31. private $alias;
  32. /**
  33. * @var array the eta data of current module
  34. */
  35. private $meta;
  36. /**
  37. * @var string the module version
  38. */
  39. private $version = "0";
  40. /**
  41. * @var integer, priority of initialization
  42. */
  43. private $priority = 10;
  44. /**
  45. * @var array of strings missing translations
  46. */
  47. public $missingTranslation;
  48. /**
  49. * Private properties of child modules
  50. * @var array
  51. */
  52. private $privateProperties = [
  53. 'name', 'description', 'controllerNamespace', 'defaultRoute', 'routePrefix', 'vendor', 'controllerMap'
  54. ];
  55. /**
  56. * {@inheritdoc}
  57. */
  58. public function init()
  59. {
  60. parent::init();
  61. // Set controller namespace for console commands
  62. if (Yii::$app instanceof \yii\console\Application) {
  63. $this->controllerNamespace = 'rss\\' . $this->id . '\commands';
  64. $this->defaultRoute = 'init';
  65. }
  66. // Set alias of module base path
  67. $this->alias = '@'.$this->vendor.'/'.$this->id;
  68. $this->setAliases([
  69. $this->alias => $this->basePath
  70. ]);
  71. // Set version of current module
  72. $this->setVersion($this->version);
  73. // Set priority of current module
  74. $this->setPriority($this->priority);
  75. // Register translations
  76. $this->registerTranslations();
  77. // Normalize route prefix
  78. $this->routePrefixNormalize();
  79. // Set meta data of current module
  80. $this->setMetaData();
  81. }
  82. /**
  83. * Get module vendor
  84. * @return string of current module vendor
  85. */
  86. public function getVendor() {
  87. return $this->vendor;
  88. }
  89. /**
  90. * Get module version
  91. * @return string of current module version
  92. */
  93. public function getVersion() {
  94. $this->version = parent::getVersion();
  95. return $this->version;
  96. }
  97. /**
  98. * Set current module version
  99. * @param $version string
  100. */
  101. public function setVersion($version)
  102. {
  103. parent::setVersion($version);
  104. $this->version = $version;
  105. }
  106. /**
  107. * Get module priority
  108. * @return integer of current module priority
  109. */
  110. public function getPriority() {
  111. return $this->priority;
  112. }
  113. /**
  114. * Set current module priority
  115. * @param $priority integer
  116. */
  117. public function setPriority($priority) {
  118. $this->priority = $priority;
  119. }
  120. /**
  121. * Get alias
  122. * @return string of current module alias
  123. */
  124. public function getBaseAlias() {
  125. return $this->alias;
  126. }
  127. /**
  128. * Set meta data
  129. * @return array of current module meta data
  130. */
  131. public function setMetaData() {
  132. $data = [];
  133. $module = $this;
  134. if (isset($module->id)) {
  135. $data['id'] = $module->id;
  136. $data['uniqueId'] = $module->getUniqueId();
  137. $data['name'] = str_replace(Yii::getAlias('@vendor').'/',"", $module->getBasePath());
  138. $data['label'] = (isset($module->name)) ? $module->name : null;
  139. $data['version'] = $module->getVersion();
  140. $data['vendor'] = (isset($module->vendor)) ? $module->vendor : null;
  141. $data['alias'] = $module->getBaseAlias();
  142. $data['paths']['basePath'] = $module->getBasePath();
  143. $data['paths']['controllerPath'] = $module->getControllerPath();
  144. $data['paths']['layoutPath'] = $module->getLayoutPath();
  145. $data['paths']['viewPath'] = $module->getViewPath();
  146. $data['components'] = $module->getComponents();
  147. $data['parent']['id'] = (isset($module->module->id)) ? $module->module->id : null;
  148. $data['parent']['uniqueId'] = $module->module->getUniqueId();
  149. $data['parent']['version'] = $module->module->getVersion();
  150. $data['parent']['paths']['basePath'] = $module->module->getBasePath();
  151. $data['parent']['paths']['controllerPath'] = $module->module->getControllerPath();
  152. $data['parent']['paths']['layoutPath'] = $module->module->getLayoutPath();
  153. $data['parent']['paths']['viewPath'] = $module->module->getViewPath();
  154. $data['extensions'] = Yii::$app->extensions;
  155. }
  156. $this->meta = $data;
  157. }
  158. /**
  159. * Get meta data
  160. * @return array of current module meta data
  161. */
  162. public function getMetaData() {
  163. return $this->meta;
  164. }
  165. /**
  166. * Get option from DB, params or module public properties
  167. * @return mixed of params or current module properties
  168. */
  169. public function getOption($option) {
  170. $value = null;
  171. if (isset(Yii::$app->options)) {
  172. if (!$value = Yii::$app->options->get($option)) {
  173. if (preg_match('/\./', $option)) {
  174. $split = explode('.', $option, 2);
  175. if (count($split) > 1) {
  176. if (!empty($split[0]) && !empty($split[1])) {
  177. $option = $split[1];
  178. }
  179. }
  180. }
  181. if (isset(Yii::$app->params[$option]))
  182. $value = Yii::$app->params[$option];
  183. elseif (isset($this->$option))
  184. $value = $this->$option;
  185. }
  186. } else {
  187. if (preg_match('/\./', $option)) {
  188. $split = explode('.', $option, 2);
  189. if (count($split) > 1) {
  190. if (!empty($split[0]) && !empty($split[1])) {
  191. $section = $split[0];
  192. $option = $split[1];
  193. }
  194. }
  195. }
  196. if (isset(Yii::$app->params[$option]))
  197. $value = Yii::$app->params[$option];
  198. elseif (isset($this->$option))
  199. $value = $this->$option;
  200. }
  201. if (YII_ENV_DEV) {
  202. if (isset(Yii::$app->options))
  203. Yii::debug('Option from options`'.$option.'` is ' . gettype(Yii::$app->options->get('admin.checkForUpdates')) .' and value: '. var_export(Yii::$app->options->get('admin.checkForUpdates'), true));
  204. if (isset(Yii::$app->params['admin.checkForUpdates']))
  205. Yii::debug('Option from params`'.$option.'` is ' . gettype(Yii::$app->params['admin.checkForUpdates']) .' and value: '. var_export(Yii::$app->params['admin.checkForUpdates'], true));
  206. Yii::debug('`'.$option.'` is ' . gettype($value) .' and value: '. var_export($value, true));
  207. }
  208. return $value;
  209. }
  210. /**
  211. * {@inheritdoc}
  212. */
  213. public function afterAction($action, $result)
  214. {
  215. // Log missing translations
  216. if (is_array($this->missingTranslation) && YII_ENV == 'dev')
  217. Yii::warning('Missing translations: ' . var_export($this->missingTranslation, true), 'i18n');
  218. $result = parent::afterAction($action, $result);
  219. return $result;
  220. }
  221. /**
  222. * Registers translations for module
  223. */
  224. public function registerTranslations()
  225. {
  226. $moduleId = 'base';
  227. if (!is_null($this->id))
  228. $moduleId = $this->id;
  229. Yii::$app->i18n->translations['app/modules/' . $moduleId] = [
  230. 'class' => 'yii\i18n\PhpMessageSource',
  231. 'sourceLanguage' => 'en-US',
  232. 'basePath' => '@rss/messages',
  233. 'on missingTranslation' => function ($event) {
  234. if (YII_ENV == 'dev')
  235. $this->missingTranslation[] = $event->message;
  236. }
  237. ];
  238. // Name and description translation of module
  239. // $this->name = Yii::t('app/modules/' . $moduleId, $this->name);
  240. // $this->description = Yii::t('app/modules/' . $moduleId, $this->description);
  241. }
  242. /**
  243. * Public translation function, Module::t('app/modules/admin', 'Dashboard');
  244. * @return string of current message translation
  245. */
  246. public static function t($category, $message, $params = [], $language = null)
  247. {
  248. return Yii::t('app/modules/' . self::id .'/'. $category, $message, $params, $language);
  249. }
  250. /**
  251. * Normalize route
  252. * @return string of current route
  253. */
  254. public function normalizeRoute($route)
  255. {
  256. $route = ltrim($route, '/');
  257. $route = rtrim($route, '/');
  258. $route = '/'.$route;
  259. $route = str_replace('//', '/', $route);
  260. return $route;
  261. }
  262. /**
  263. * Normalize route prefix
  264. * @return string of current route prefix
  265. */
  266. public function routePrefixNormalize()
  267. {
  268. if(!empty($this->routePrefix))
  269. $this->routePrefix = self::normalizeRoute($this->routePrefix);
  270. return $this->routePrefix;
  271. }
  272. /**
  273. * Build dashboard navigation items for NavBar
  274. * @param $options array, if you need to add a custom menu routes, like create, update, etc. item
  275. * @return array of current module nav items
  276. */
  277. public function dashboardNavItems($options = false)
  278. {
  279. $items = [
  280. 'label' => $this->name,
  281. 'url' => [$this->routePrefix . '/'. $this->id . ((isset($this->defaultRoute)) ? '/' . $this->defaultRoute : '')],
  282. 'active' => (\Yii::$app->controller->module->id == $this->id) ? true : false
  283. ];
  284. if (is_array($options)) {
  285. foreach ($options as $route => $option) {
  286. if (is_string($route)) {
  287. $items['items'] = [
  288. [
  289. 'label' => (isset($option['label'])) ? $option['label'] : $route,
  290. 'icon' => (isset($option['icon'])) ? $option['icon'] : null,
  291. 'url' => [$this->routePrefix . '/' . $this->id . '/' . ltrim($route, '/')],
  292. 'active' => ((\Yii::$app->controller->module->id == $this->id) && (\Yii::$app->controller->action->id == ltrim($route, '/'))) ? true : false
  293. ],
  294. ];
  295. }
  296. }
  297. }
  298. return $items;
  299. }
  300. /**
  301. * Check if module exist
  302. *
  303. * @param $id
  304. * @param bool $returnId
  305. * @return string
  306. */
  307. public function moduleExist($id, $returnId = false)
  308. {
  309. // If module configured without parent module, like `admin/...`
  310. if (!(preg_match('/\/[^\s]+/', $id))) {
  311. $parent = $this->module->id;
  312. if ($parent)
  313. $id = $parent . '/' . $id;
  314. }
  315. $isExist = (Yii::$app->hasModule($id));
  316. if ($returnId && $isExist)
  317. return $id;
  318. return $isExist;
  319. }
  320. /**
  321. * Check if module exist and return instance
  322. *
  323. * @param $id string, the module name (if the module name does not contain the parent module,
  324. * it will be assigned from the launch module)
  325. * @param $returnInstance boolean, the return instance of module
  326. * @return boolean, null or intance
  327. */
  328. public function moduleLoaded($id, $returnInstance = false, $autoLoad = false)
  329. {
  330. // If module configured without parent module, like `admin/...`
  331. if (!(preg_match('/\/[^\s]+/', $id))) {
  332. $parent = $this->module->id;
  333. if ($parent)
  334. $id = $parent . '/' . $id;
  335. }
  336. if ($id = $this->moduleExist($id, true)) {
  337. if ($returnInstance)
  338. return Yii::$app->getModule($id, $autoLoad);
  339. else
  340. return true;
  341. } else {
  342. return false;
  343. }
  344. return null;
  345. }
  346. /**
  347. * Returns a value indicating whether the current request is made via command line (console).
  348. *
  349. * @return bool
  350. */
  351. public function isConsole()
  352. {
  353. return Yii::$app->request->isConsoleRequest;
  354. }
  355. /**
  356. * Returns a value indicating whether the current request made for admin dashboard.
  357. *
  358. * @return bool
  359. * @throws \yii\base\InvalidConfigException
  360. */
  361. public function isBackend($onlyAuth = true)
  362. {
  363. if ($this->isConsole())
  364. return true;
  365. $isBackend = false;
  366. if (substr(Yii::$app->request->getUrl(), 0, 6) == '/admin')
  367. return true;
  368. if ($onlyAuth && Yii::$app->getUser()->getIsGuest())
  369. return false;
  370. return false;
  371. }
  372. public function isRestAPI()
  373. {
  374. if ($this->isConsole())
  375. return false;
  376. if (Yii::$app->controller instanceof \yii\rest\Controller)
  377. return true;
  378. return false;
  379. }
  380. /**
  381. * Bootstrap interface, to be called during application loaded module.
  382. * @param $app object, the application currently running
  383. */
  384. public function bootstrap($app)
  385. {
  386. // Get URL path prefix if exist
  387. if (isset($this->routePrefix))
  388. $prefix = $this->routePrefix . '/';
  389. else
  390. $prefix = '';
  391. // Add module URL rules
  392. if ($urlManager = $app->getUrlManager()) {
  393. $urlManager->addRules([
  394. $prefix . '<module:' . $this->id . '>/<controller:\w+>/<action:\w+>/<id:\d+>' => '<module>/<controller>/<action>',
  395. $prefix . '<module:' . $this->id . '>/<controller:\w+>/<action:\w+>' => '<module>/<controller>/<action>',
  396. $prefix . '<module:' . $this->id . '>/<controller:\w+>' => '<module>/<controller>/index',
  397. ], true);
  398. }
  399. // Add short module alias, like `@sitemap` instead of long `@wdmg/sitemap`
  400. if ($this->id !== 'base') {
  401. Yii::setAlias('@' . $this->id, Yii::getAlias($this->getBaseAlias()));
  402. }
  403. // Get missing translations
  404. $missingTranslation = $this->missingTranslation;
  405. // Log missing translations
  406. if (!($app instanceof \yii\console\Application) && $this->module) {
  407. \yii\base\Event::on(\yii\base\Controller::class, \yii\base\Controller::EVENT_AFTER_ACTION, function ($event) use ($missingTranslation) {
  408. // Log missing translations
  409. if (is_array($missingTranslation) && YII_ENV == 'dev')
  410. Yii::warning('Missing translations: ' . var_export($missingTranslation, true), 'i18n');
  411. });
  412. }
  413. }
  414. public function install() {
  415. if (!($options = Yii::$app->getModule('admin/options')))
  416. $options = Yii::$app->getModule('options');
  417. if (!is_null($options) && isset(Yii::$app->options)) {
  418. $props = get_class_vars(get_class($this));
  419. foreach ($props as $prop => $value) {
  420. // Skip private properties of modules
  421. if (in_array($prop, $this->privateProperties))
  422. continue;
  423. if (is_array($value))
  424. Yii::$app->options->set($this->id .'.'. $prop, $value, 'array', null, true, false);
  425. elseif (is_object($value))
  426. Yii::$app->options->set($this->id .'.'. $prop, $value, 'object', null, true, false);
  427. elseif (is_bool($value))
  428. Yii::$app->options->set($this->id .'.'. $prop, ($value) ? 1 : 0, 'boolean', null, true, false);
  429. else
  430. Yii::$app->options->set($this->id .'.'. $prop, $value, null, null, true, false);
  431. }
  432. return true;
  433. }
  434. return false;
  435. }
  436. /**
  437. * Main method of uninstallation module
  438. * @return boolean, false if uninstall failure
  439. */
  440. public function uninstall() {
  441. return true;
  442. }
  443. /**
  444. * Runs command in console (CLI) from web-request
  445. *
  446. * @param string $command, cli command, like `php yii hello/index`
  447. * @param bool $await, flag if need wait the return execution output
  448. * @param bool $cli, flag if need execute system comands
  449. * @param bool $escape, flag if need to escape shell metacharacters
  450. * @return null|array
  451. */
  452. public function runConsole($command, $await = true, $cli = false, $escape = true)
  453. {
  454. ignore_user_abort(true);
  455. set_time_limit(0);
  456. $status = null;
  457. $output = null;
  458. $cmd = "";
  459. if (!$cli) {
  460. $script = Yii::getAlias('@app/yii');
  461. $cmd = "php " . $script . " " . $command;
  462. } else {
  463. $cmd = $command;
  464. }
  465. if (!$await) {
  466. header('Connection: close');
  467. @ob_end_flush();
  468. @ob_flush();
  469. @flush();
  470. if (session_id())
  471. session_write_close();
  472. }
  473. if ($escape)
  474. $cmd = escapeshellcmd($cmd);
  475. if (mb_strtolower(mb_substr(php_uname(), 0, 7)) == "windows" || $cli) {
  476. $status = popen($cmd . ' 2>&1', 'r');
  477. $output = '';
  478. while (!feof($status)) {
  479. $output .= fgets($status);
  480. }
  481. return [pclose($status), trim($output)];
  482. } else {
  483. $output = exec($cmd . " > /dev/null 2>&1 &", $status);
  484. return [$status, trim($output)];
  485. }
  486. }
  487. }