modmanagercontroller.class.php 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065
  1. <?php
  2. /*
  3. * This file is part of MODX Revolution.
  4. *
  5. * Copyright (c) MODX, LLC. All Rights Reserved.
  6. *
  7. * For complete copyright and license information, see the COPYRIGHT and LICENSE
  8. * files found in the top-level directory of this distribution.
  9. */
  10. /**
  11. * Abstract class for manager controllers. Not to be initialized directly; must be extended by the implementing
  12. * controller.
  13. *
  14. * @package modx
  15. */
  16. abstract class modManagerController {
  17. /** @var modX A reference to the modX object */
  18. public $modx;
  19. /** @var array A configuration array of options related to this controller's action object. */
  20. public $config = array();
  21. /** @var bool Set to false to prevent loading of the header HTML. */
  22. public $loadHeader = true;
  23. /** @var bool Set to false to prevent loading of the footer HTML. */
  24. public $loadFooter = true;
  25. /** @var bool Set to false to prevent loading of the base MODExt JS classes. */
  26. public $loadBaseJavascript = true;
  27. /** @var array An array of possible paths to this controller's templates directory. */
  28. public $templatesPaths = array();
  29. /** @var array An array of possible paths to this controller's directory. */
  30. public $controllersPaths;
  31. /** @var modContext The current working context. */
  32. public $workingContext;
  33. /** @var modMediaSource The default media source for the user */
  34. public $defaultSource;
  35. /** @var string The current output content */
  36. public $content = '';
  37. /** @var array An array of request parameters sent to the controller */
  38. public $scriptProperties = array();
  39. /** @var array An array of css/js/html to load into the HEAD of the page */
  40. public $head = array('css' => array(),'js' => array(),'html' => array(),'lastjs' => array());
  41. /** @var array An array of placeholders that are being set to the page */
  42. public $placeholders = array();
  43. /** @var string Any Form Customization rule output that was created. */
  44. protected $ruleOutput = array();
  45. /** @var string The current manager theme. */
  46. protected $theme = 'default';
  47. /** @var string The pagetitle for this controller. */
  48. protected $title = '';
  49. /** @var bool Whether or not a failure message was sent by this controller. */
  50. protected $isFailure = false;
  51. /** @var string The failure message, if existent, for this controller. */
  52. protected $failureMessage = '';
  53. /**
  54. * The constructor for the modManagerController class.
  55. *
  56. * @param modX $modx A reference to the modX object.
  57. * @param array $config A configuration array of options related to this controller's action object.
  58. */
  59. function __construct(modX &$modx,$config = array()) {
  60. $this->modx =& $modx;
  61. $this->config = !empty($config) && is_array($config) ? $config : array();
  62. }
  63. /**
  64. * Can be used to provide custom methods prior to processing
  65. * @return void
  66. */
  67. public function initialize() {}
  68. /**
  69. * Return the proper instance of the derived class. This can be used to override how the manager loads a controller
  70. * class; for example, when handling derivative classes with class_key settings.
  71. *
  72. * @static
  73. * @param modX $modx A reference to the modX object.
  74. * @param string $className The name of the class that is being requested.
  75. * @param array $config A configuration array of options related to this controller's action object.
  76. * @return The class specified by $className
  77. */
  78. public static function getInstance(modX &$modx, $className, array $config = array()) {
  79. /** @var modManagerController $controller */
  80. $controller = new $className($modx,$config);
  81. return $controller;
  82. }
  83. /**
  84. * Sets the properties array for this controller
  85. * @param array $properties
  86. * @return void
  87. */
  88. public function setProperties(array $properties) {
  89. $this->scriptProperties = $properties;
  90. }
  91. /**
  92. * Set a property for this controller
  93. * @param string $key
  94. * @param mixed $value
  95. * @return void
  96. */
  97. public function setProperty($key,$value) {
  98. $this->scriptProperties[$key] = $value;
  99. }
  100. /**
  101. * Prepares the language placeholders
  102. */
  103. public function prepareLanguage() {
  104. $this->modx->lexicon->load('action');
  105. $languageTopics = $this->getLanguageTopics();
  106. foreach ($languageTopics as $topic) {
  107. $this->modx->lexicon->load($topic);
  108. }
  109. $this->setPlaceholder('_lang_topics',implode(',',$languageTopics));
  110. $this->setPlaceholder('_lang', $this->modx->lexicon->fetch());
  111. }
  112. /**
  113. * Render the controller.
  114. *
  115. * @return string
  116. */
  117. public function render() {
  118. if (!$this->checkPermissions()) {
  119. return $this->modx->error->failure($this->modx->lexicon('access_denied'));
  120. }
  121. $this->modx->invokeEvent('OnBeforeManagerPageInit',array(
  122. 'action' => $this->config,
  123. ));
  124. $this->theme = $this->modx->getOption('manager_theme',null,'default',true);
  125. $this->prepareLanguage();
  126. $this->setPlaceholder('_ctx',$this->modx->context->get('key'));
  127. $this->loadControllersPath();
  128. $this->loadTemplatesPath();
  129. $content = '';
  130. $this->registerBaseScripts();
  131. $this->checkFormCustomizationRules();
  132. $this->setPlaceholder('_config',$this->modx->config);
  133. $this->setCssURLPlaceholders();
  134. /* help url */
  135. $helpUrl = $this->getHelpUrl();
  136. $this->addHtml('<script type="text/javascript">MODx.helpUrl = "'.($helpUrl).'"</script>');
  137. $this->modx->invokeEvent('OnManagerPageBeforeRender',array('controller' => &$this));
  138. $placeholders = $this->process($this->scriptProperties);
  139. if (!$this->isFailure && !empty($placeholders) && is_array($placeholders)) {
  140. $this->setPlaceholders($placeholders);
  141. } elseif (!empty($placeholders)) {
  142. $content = $placeholders;
  143. }
  144. if (!$this->isFailure) {
  145. $this->loadCustomCssJs();
  146. }
  147. $this->firePreRenderEvents();
  148. /* handle FC rules */
  149. if (!empty($this->ruleOutput)) {
  150. $this->addHtml(implode("\n",$this->ruleOutput));
  151. }
  152. /* register CSS/JS */
  153. $this->registerCssJs();
  154. $this->setPlaceholder('_pagetitle',$this->getPageTitle());
  155. $this->content = '';
  156. if ($this->loadHeader) {
  157. $this->content .= $this->getHeader();
  158. }
  159. $tpl = $this->getTemplateFile();
  160. if ($this->isFailure) {
  161. $this->setPlaceholder('_e', $this->modx->error->failure($this->failureMessage));
  162. $content = $this->fetchTemplate('error.tpl');
  163. } else if (!empty($tpl)) {
  164. $content = $this->fetchTemplate($tpl);
  165. }
  166. $this->content .= $content;
  167. if ($this->loadFooter) {
  168. $this->content .= $this->getFooter();
  169. }
  170. $this->firePostRenderEvents();
  171. $this->modx->invokeEvent('OnManagerPageAfterRender',array('controller' => &$this));
  172. return $this->content;
  173. }
  174. public function getHelpUrl() {
  175. return '';
  176. }
  177. /**
  178. * @return void
  179. */
  180. protected function assignPlaceholders() {
  181. foreach ($this->placeholders as $k => $v) {
  182. $this->modx->smarty->assign($k,$v);
  183. }
  184. }
  185. /**
  186. * Set a placeholder for this controller's template
  187. *
  188. * @param string $k The key of the placeholder
  189. * @param mixed $v The value of the placeholder
  190. * @return void
  191. */
  192. public function setPlaceholder($k,$v) {
  193. $this->placeholders[$k] = $v;
  194. $this->modx->smarty->assign($k,$v);
  195. }
  196. /**
  197. * Set an array of placeholders
  198. *
  199. * @param array $keys
  200. * @return void
  201. */
  202. public function setPlaceholders($keys) {
  203. foreach ($keys as $k => $v) {
  204. $this->placeholders[$k] = $v;
  205. $this->modx->smarty->assign($k,$v);
  206. }
  207. }
  208. /**
  209. * Get all the set placeholders
  210. * @return array
  211. */
  212. public function getPlaceholders() {
  213. return $this->placeholders;
  214. }
  215. /**
  216. * Get a specific placeholder set
  217. * @param string $k
  218. * @param mixed $default
  219. * @return mixed
  220. */
  221. public function getPlaceholder($k,$default = null) {
  222. return isset($this->placeholders[$k]) ? $this->placeholders[$k] : $default;
  223. }
  224. /**
  225. * Fetch the template content
  226. * @param string $tpl The path to the template
  227. * @return string The output of the template
  228. */
  229. public function fetchTemplate($tpl) {
  230. $templatePath = '';
  231. if (is_array($this->templatesPaths)) {
  232. foreach ($this->templatesPaths as $path) {
  233. if (file_exists($path.$tpl)) {
  234. $templatePath = $path;
  235. break;
  236. }
  237. }
  238. }
  239. $this->modx->smarty->setTemplatePath($templatePath);
  240. return $this->modx->smarty->fetch($tpl);
  241. }
  242. /**
  243. * Load another manual controller file (such as header/footer)
  244. *
  245. * @param $controller
  246. * @param bool $coreOnly
  247. * @return mixed|string
  248. */
  249. public function loadController($controller,$coreOnly = false) {
  250. /** @var modX $modx */
  251. $modx =& $this->modx;
  252. $paths = $this->getControllersPaths($coreOnly);
  253. $o = '';
  254. foreach ($paths as $path) {
  255. if (file_exists($path.$controller)) {
  256. $o = include_once $path.$controller;
  257. break;
  258. }
  259. }
  260. return $o;
  261. }
  262. /**
  263. * Set a failure on this controller. This will return the error message.
  264. *
  265. * @param string $message
  266. * @return void
  267. */
  268. public function failure($message) {
  269. $this->isFailure = true;
  270. $this->failureMessage .= $message;
  271. }
  272. /**
  273. * Load the path to this controller's template's directory. Only override this if you want to override default
  274. * behavior; otherwise, overriding getTemplatesPath is preferred.
  275. *
  276. * @return string
  277. */
  278. public function loadTemplatesPath() {
  279. if (empty($this->templatesPaths)) {
  280. $templatesPaths = $this->getTemplatesPaths();
  281. if (is_string($templatesPaths)) {
  282. $templatesPaths = array($templatesPaths);
  283. }
  284. $this->setTemplatePaths($templatesPaths);
  285. }
  286. return $this->templatesPaths;
  287. }
  288. /**
  289. * Set the possible template paths for this controller
  290. *
  291. * @param array $paths
  292. * @return void
  293. */
  294. public function setTemplatePaths(array $paths) {
  295. $this->templatesPaths = $paths;
  296. }
  297. /**
  298. * Load an array of possible paths to this controller's directory. Only override this if you want to override
  299. * default behavior; otherwise, overriding getControllersPath is preferred.
  300. *
  301. * @return array
  302. */
  303. public function loadControllersPath() {
  304. if (empty($this->controllersPaths)) {
  305. $this->controllersPaths = $this->getControllersPaths();
  306. }
  307. return $this->controllersPaths;
  308. }
  309. /**
  310. * Get the path to this controller's directory. Override this to point to a custom directory.
  311. *
  312. * @param bool $coreOnly Ensure that it grabs the path from the core namespace only.
  313. * @return array
  314. */
  315. public function getControllersPaths($coreOnly = false) {
  316. if (!empty($this->config['namespace']) && $this->config['namespace'] != 'core' && !$coreOnly) { /* for non-core controllers */
  317. $managerPath = $this->modx->getOption('manager_path',null,MODX_MANAGER_PATH);
  318. $paths[] = $this->config['namespace_path'].'controllers/'.$this->theme.'/';
  319. $paths[] = $this->config['namespace_path'].'controllers/default/';
  320. $paths[] = $this->config['namespace_path'].'controllers/';
  321. $paths[] = $this->config['namespace_path'].$this->theme.'/';
  322. $paths[] = $this->config['namespace_path'].'default/';
  323. $paths[] = $this->config['namespace_path'];
  324. $paths[] = $managerPath.'controllers/'.$this->theme.'/';
  325. $paths[] = $managerPath.'controllers/default/';
  326. } else { /* for core controllers only */
  327. $managerPath = $this->modx->getOption('manager_path',null,MODX_MANAGER_PATH);
  328. $paths[] = $managerPath.'controllers/'.$this->theme.'/';
  329. $paths[] = $managerPath.'controllers/default/';
  330. }
  331. return $paths;
  332. }
  333. /**
  334. * Get an array of possible paths to this controller's template's directory.
  335. * Override this to point to a custom directory.
  336. *
  337. * @param bool $coreOnly Ensure that it grabs the path from the core namespace only.
  338. * @return array|string
  339. */
  340. public function getTemplatesPaths($coreOnly = false) {
  341. /* extras */
  342. if (!empty($this->config['namespace']) && $this->config['namespace'] != 'core' && !$coreOnly) {
  343. $namespacePath = $this->config['namespace_path'];
  344. $paths[] = $namespacePath . 'templates/'.$this->theme.'/';
  345. $paths[] = $namespacePath . 'templates/default/';
  346. $paths[] = $namespacePath . 'templates/';
  347. }
  348. $managerPath = $this->modx->getOption('manager_path',null,MODX_MANAGER_PATH);
  349. $paths[] = $managerPath . 'templates/'.$this->theme.'/';
  350. $paths[] = $managerPath . 'templates/default/';
  351. $paths = array_unique($paths);
  352. return $paths;
  353. }
  354. /**
  355. * Get an array of possible URLs to the template's directory.
  356. * Override this to point to a custom directory.
  357. *
  358. * @param bool $specificThemeOnly Return URL only to theme specified in system settings
  359. * @return array
  360. */
  361. public function getTemplatesUrls($specificThemeOnly = false) {
  362. $urls = array();
  363. $managerUrl = $this->modx->getOption('manager_url', null, MODX_MANAGER_URL);
  364. $urls[] = $managerUrl . 'templates/'.$this->theme.'/';
  365. if ($specificThemeOnly === false) {
  366. $urls[] = $managerUrl . 'templates/default/';
  367. }
  368. return $urls;
  369. }
  370. /**
  371. * Do permission checking in this method. Returning false will present a "permission denied" message.
  372. *
  373. * @abstract
  374. * @return boolean
  375. */
  376. abstract public function checkPermissions();
  377. /**
  378. * Process the controller, returning an array of placeholders to set.
  379. *
  380. * @abstract
  381. * @param array $scriptProperties A array of REQUEST parameters.
  382. * @return mixed Either an error or output string, or an array of placeholders to set.
  383. */
  384. abstract public function process(array $scriptProperties = array());
  385. /**
  386. * Return a string to set as the controller's page title.
  387. *
  388. * @abstract
  389. * @return string
  390. */
  391. abstract public function getPageTitle();
  392. /**
  393. * Register any custom CSS or JS in this method.
  394. * @abstract
  395. * @return void
  396. */
  397. abstract public function loadCustomCssJs();
  398. /**
  399. * Return the relative path to the template file to load
  400. * @abstract
  401. * @return string
  402. */
  403. abstract public function getTemplateFile();
  404. /**
  405. * Specify an array of language topics to load for this controller
  406. *
  407. * @return array
  408. */
  409. public function getLanguageTopics() {
  410. return array();
  411. }
  412. /**
  413. * Can be used to fire events after all the CSS/JS is loaded for a page
  414. * @return void
  415. */
  416. public function firePostRenderEvents() {}
  417. /**
  418. * Fire any pre-render events for the controller
  419. * @return void
  420. */
  421. public function firePreRenderEvents() {}
  422. /**
  423. * Get the page header for the controller.
  424. *
  425. * @return string
  426. */
  427. public function getHeader() {
  428. $this->loadController('header.php',true);
  429. return $this->fetchTemplate('header.tpl');
  430. }
  431. /**
  432. * Get the page footer for the controller.
  433. * @return string
  434. */
  435. public function getFooter() {
  436. $this->loadController('footer.php',true);
  437. return $this->fetchTemplate('footer.tpl');
  438. }
  439. /**
  440. * Registers the core and base JS scripts
  441. *
  442. * @access public
  443. * @return void
  444. */
  445. public function registerBaseScripts() {
  446. $managerUrl = $this->modx->getOption('manager_url');
  447. $externals = array();
  448. if ($this->loadBaseJavascript) {
  449. $compressJs = (boolean)$this->modx->getOption('compress_js',null,true);
  450. $this->modx->setOption('compress_js',$compressJs);
  451. if ($compressJs) {
  452. $externals[] = $managerUrl . 'assets/modext/modx.jsgrps-min.js';
  453. }
  454. else {
  455. $externals[] = $managerUrl . 'assets/modext/core/modx.localization.js';
  456. $externals[] = $managerUrl . 'assets/modext/util/utilities.js';
  457. $externals[] = $managerUrl . 'assets/modext/util/datetime.js';
  458. $externals[] = $managerUrl . 'assets/modext/util/uploaddialog.js';
  459. $externals[] = $managerUrl . 'assets/modext/util/fileupload.js';
  460. $externals[] = $managerUrl . 'assets/modext/util/superboxselect.js';
  461. $externals[] = $managerUrl . 'assets/modext/core/modx.component.js';
  462. $externals[] = $managerUrl . 'assets/modext/core/modx.view.js';
  463. $externals[] = $managerUrl . 'assets/modext/widgets/core/modx.button.js';
  464. $externals[] = $managerUrl . 'assets/modext/widgets/core/modx.searchbar.js';
  465. $externals[] = $managerUrl . 'assets/modext/widgets/core/modx.panel.js';
  466. $externals[] = $managerUrl . 'assets/modext/widgets/core/modx.tabs.js';
  467. $externals[] = $managerUrl . 'assets/modext/widgets/core/modx.window.js';
  468. $externals[] = $managerUrl . 'assets/modext/widgets/core/modx.combo.js';
  469. $externals[] = $managerUrl . 'assets/modext/widgets/core/modx.grid.js';
  470. $externals[] = $managerUrl . 'assets/modext/widgets/core/modx.console.js';
  471. $externals[] = $managerUrl . 'assets/modext/widgets/core/modx.portal.js';
  472. $externals[] = $managerUrl . 'assets/modext/widgets/windows.js';
  473. $externals[] = $managerUrl . 'assets/fileapi/FileAPI.js';
  474. $externals[] = $managerUrl . 'assets/modext/util/multiuploaddialog.js';
  475. $externals[] = $managerUrl . 'assets/modext/widgets/core/tree/modx.tree.js';
  476. $externals[] = $managerUrl . 'assets/modext/widgets/core/tree/modx.tree.treeloader.js';
  477. $externals[] = $managerUrl . 'assets/modext/widgets/modx.treedrop.js';
  478. $externals[] = $managerUrl . 'assets/modext/widgets/core/modx.tree.asynctreenode.js';
  479. $externals[] = $managerUrl . 'assets/modext/widgets/resource/modx.tree.resource.js';
  480. $externals[] = $managerUrl . 'assets/modext/widgets/element/modx.tree.element.js';
  481. $externals[] = $managerUrl . 'assets/modext/widgets/system/modx.tree.directory.js';
  482. $externals[] = $managerUrl . 'assets/modext/widgets/system/modx.panel.filetree.js';
  483. $externals[] = $managerUrl . 'assets/modext/widgets/media/modx.browser.js';
  484. $externals[] = $managerUrl.'assets/modext/core/modx.layout.js';
  485. }
  486. $this->loadLayout($externals);
  487. if ($this->modx->getOption('compress_css',null,true)) {
  488. $this->modx->setOption('compress_css',true);
  489. }
  490. $o = '';
  491. // Add script tags for the required javascript
  492. foreach ($externals as $js) {
  493. $o .= '<script type="text/javascript" src="'.$js.'"></script>'."\n";
  494. }
  495. // Get the state and user token for adding to the init script
  496. $state = $this->getDefaultState();
  497. if (!empty($state)) {
  498. $state = 'MODx.defaultState = '.$this->modx->toJSON($state).';';
  499. } else {
  500. $state = '';
  501. }
  502. $layout = '';
  503. if (!$this instanceof BrowserManagerController) {
  504. $siteId = $this->modx->user->getUserToken('mgr');
  505. $layout = 'MODx.load({xtype: "modx-layout",accordionPanels: MODx.accordionPanels || [],auth: "'.$siteId.'"});';
  506. }
  507. $o .= <<<HTML
  508. <script type="text/javascript">
  509. Ext.onReady(function() {
  510. {$state}
  511. {$layout}
  512. });
  513. </script>
  514. HTML;
  515. $this->modx->smarty->assign('maincssjs', $o);
  516. }
  517. }
  518. /**
  519. * Load theme specific layout.js if found, fallback to default layout
  520. *
  521. * @param array $externals An array of assets to load
  522. *
  523. * @return void
  524. */
  525. public function loadLayout(array &$externals) {
  526. $templatesUrl = $this->modx->getOption('manager_url', null, MODX_MANAGER_URL) . 'templates/';
  527. $themePath = MODX_MANAGER_PATH . "templates/{$this->theme}";
  528. $layoutFile = '/js/layout.js';
  529. if (file_exists($themePath . $layoutFile)) {
  530. // Apply to both custom themes and "default" theme
  531. $externals[] = $templatesUrl . $this->theme . $layoutFile;
  532. } elseif ($this->theme !== 'default') {
  533. // Load default layout for custom themes without a custom layout.js
  534. $externals[] = $templatesUrl . 'default' . $layoutFile;
  535. }
  536. }
  537. /**
  538. * Get the default state for the UI
  539. * @return array|mixed|string
  540. */
  541. public function getDefaultState() {
  542. /** @var modProcessorResponse $response */
  543. $response = $this->modx->runProcessor('system/registry/register/read',array(
  544. 'register' => 'state',
  545. 'topic' => '/ys/user-'.$this->modx->user->get('id').'/',
  546. 'include_keys' => true,
  547. 'poll_interval' => 1,
  548. 'poll_limit' => 1,
  549. 'remove_read' => false,
  550. 'show_filename' => false,
  551. 'time_limit' => 10,
  552. ));
  553. $obj = $response->getMessage();
  554. if (!empty($obj)) {
  555. $obj = $this->modx->fromJSON($obj);
  556. } else {
  557. $obj = array();
  558. }
  559. return $obj;
  560. }
  561. /**
  562. * Grabs a stripped version of modx to prevent caching of JS after upgrades
  563. *
  564. * @access private
  565. * @return string The parsed version string
  566. */
  567. private function _prepareVersionPostfix() {
  568. $version = $this->modx->getVersionData();
  569. return str_replace(array('.','-'),'',$version['full_version']);
  570. }
  571. /**
  572. * Appends a version postfix to a script tag
  573. *
  574. * @access private
  575. * @param string $str The script tag to append the version to
  576. * @param string $version The version to append
  577. * @return string The adjusted script tag
  578. */
  579. private function _postfixVersionToScript($str,$version) {
  580. $pos = strpos($str,'.js');
  581. $pos2 = strpos($str,'src="'); /* only apply to externals */
  582. if ($pos && $pos2) {
  583. $s = substr($str,0,strpos($str,'"></script>'));
  584. if (!empty($s) && substr($s,strlen($s)-3,strlen($s)) == '.js') {
  585. $str = $s.'?v='.$version.'"></script>';
  586. }
  587. }
  588. return $str;
  589. }
  590. /**
  591. * Registers CSS/JS to manager interface
  592. */
  593. public function registerCssJs() {
  594. $this->_prepareHead();
  595. $versionPostFix = $this->_prepareVersionPostfix();
  596. $jsToCompress = array();
  597. foreach ($this->head['js'] as $js) {
  598. $jsToCompress[] = $js;
  599. }
  600. $cssjs = array();
  601. if (!empty($jsToCompress)) {
  602. foreach ($jsToCompress as $scr) {
  603. $cssjs[] = '<script src="'.$scr.'" type="text/javascript"></script>';
  604. }
  605. }
  606. $cssToCompress = array();
  607. foreach ($this->head['css'] as $css) {
  608. $cssToCompress[] = $css;
  609. }
  610. if (!empty($cssToCompress)) {
  611. foreach ($cssToCompress as $scr) {
  612. $cssjs[] = '<link href="'.$scr.'" rel="stylesheet" type="text/css" />';
  613. }
  614. }
  615. foreach ($this->head['html'] as $html) {
  616. $cssjs[] = $html;
  617. }
  618. foreach ($this->modx->sjscripts as $scr) {
  619. $scr = $this->_postfixVersionToScript($scr,$versionPostFix);
  620. $cssjs[] = $scr;
  621. }
  622. $lastjs = array();
  623. foreach ($this->head['lastjs'] as $js) {
  624. $lastjs[] = $js;
  625. }
  626. if (!empty($lastjs)) {
  627. foreach ($lastjs as $scr) {
  628. $cssjs[] = '<script src="'.$scr.'" type="text/javascript"></script>';
  629. }
  630. }
  631. $this->modx->smarty->assign('cssjs',$cssjs);
  632. }
  633. /**
  634. * Prepare the set html/css/js to be added
  635. * @return void
  636. */
  637. private function _prepareHead() {
  638. $this->head['js'] = array_unique($this->head['js']);
  639. $this->head['html'] = array_unique($this->head['html']);
  640. $this->head['css'] = array_unique($this->head['css']);
  641. $this->head['lastjs'] = array_unique($this->head['lastjs']);
  642. }
  643. /**
  644. * Add an external Javascript file to the head of the page
  645. *
  646. * @param string $script
  647. * @return void
  648. */
  649. public function addJavascript($script) {
  650. $this->head['js'][] = $script;
  651. }
  652. /**
  653. * Add a block of HTML to the head of the page
  654. *
  655. * @param string $script
  656. * @return void
  657. */
  658. public function addHtml($script) {
  659. $this->head['html'][] = $script;
  660. }
  661. /**
  662. * Add a external CSS file to the head of the page
  663. * @param string $script
  664. * @return void
  665. */
  666. public function addCss($script) {
  667. $this->head['css'][] = $script;
  668. }
  669. /**
  670. * Add an external Javascript file to the head of the page
  671. *
  672. * @param string $script
  673. * @return void
  674. */
  675. public function addLastJavascript($script) {
  676. $this->head['lastjs'][] = $script;
  677. }
  678. /**
  679. * Checks Form Customization rules for an object.
  680. *
  681. * @param xPDOObject $obj If passed, will validate against for rules with constraints.
  682. * @param bool $forParent No longer used - filtering only happens by controller
  683. * @return array
  684. */
  685. public function checkFormCustomizationRules(&$obj = null, $forParent = false) {
  686. $overridden = array();
  687. if ($this->modx->getOption('form_customization_use_all_groups',null,false)) {
  688. $userGroups = $this->modx->user->getUserGroups();
  689. } else {
  690. $primaryGroup = $this->modx->user->getPrimaryGroup();
  691. if ($primaryGroup) {
  692. $userGroups = array($primaryGroup->get('id'));
  693. }
  694. }
  695. $c = $this->modx->newQuery('modActionDom');
  696. $c->innerJoin('modFormCustomizationSet','FCSet');
  697. $c->innerJoin('modFormCustomizationProfile','Profile','FCSet.profile = Profile.id');
  698. $c->leftJoin('modFormCustomizationProfileUserGroup','ProfileUserGroup','Profile.id = ProfileUserGroup.profile');
  699. $c->leftJoin('modFormCustomizationProfile','UGProfile','UGProfile.id = ProfileUserGroup.profile');
  700. // Filter on the controller (action).
  701. $controller = array_key_exists('controller', $this->config) ? $this->config['controller'] : '';
  702. if (strpos($controller, '/') !== false) {
  703. // For multi-level controllers (e.g. resource/create), we get the last part
  704. // of the controller name to also search for a wildcard (e.g. resource/*)
  705. $wildController = substr($controller, 0, strrpos($controller, '/')) . '/*';
  706. $c->where(array(
  707. 'modActionDom.action:IN' => array($controller, $wildController),
  708. ));
  709. }
  710. else {
  711. $c->where(array(
  712. 'modActionDom.action' => array_key_exists('controller',$this->config) ? $this->config['controller'] : '',
  713. ));
  714. }
  715. $c->where(array(
  716. 'FCSet.active' => true,
  717. 'Profile.active' => true,
  718. ));
  719. if (!empty($userGroups)) {
  720. $c->where(array(
  721. array(
  722. 'ProfileUserGroup.usergroup:IN' => $userGroups,
  723. array(
  724. 'OR:ProfileUserGroup.usergroup:IS' => null,
  725. 'AND:UGProfile.active:=' => true,
  726. ),
  727. ),
  728. 'OR:ProfileUserGroup.usergroup:=' => null,
  729. ),xPDOQuery::SQL_AND,null,2);
  730. }
  731. $c->select($this->modx->getSelectColumns('modActionDom', 'modActionDom'));
  732. $c->select($this->modx->getSelectColumns('modFormCustomizationSet', 'FCSet', '', array(
  733. 'constraint_class',
  734. 'constraint_field',
  735. 'constraint',
  736. 'template'
  737. )));
  738. $c->sortby('modActionDom.rank','ASC');
  739. $domRules = $this->modx->getCollection('modActionDom',$c);
  740. $rules = array();
  741. /** @var modActionDom $rule */
  742. foreach ($domRules as $rule) {
  743. $template = $rule->get('template');
  744. if (!empty($template) && $obj) {
  745. if ($template != $obj->get('template')) continue;
  746. }
  747. $constraintClass = $rule->get('constraint_class');
  748. if (!empty($constraintClass)) {
  749. if (empty($obj) || !($obj instanceof $constraintClass)) continue;
  750. $constraintField = $rule->get('constraint_field');
  751. $constraint = $rule->get('constraint');
  752. $constraintList = explode(',', $constraint);
  753. $constraintList = array_map('trim', $constraintList);
  754. if (($obj->get($constraintField) != $constraint) && (!in_array($obj->get($constraintField), $constraintList))) {
  755. continue;
  756. }
  757. }
  758. if ($rule->get('rule') == 'fieldDefault') {
  759. $field = $rule->get('name');
  760. if ($field == 'modx-resource-content') $field = 'content';
  761. $overridden[$field] = $rule->get('value');
  762. if ($field == 'parent-cmb') {
  763. $overridden['parent'] = (int)$rule->get('value');
  764. $overridden['parent-cmb'] = (int)$rule->get('value');
  765. }
  766. }
  767. $r = $rule->apply();
  768. if (!empty($r)) $rules[] = $r;
  769. }
  770. if (!empty($rules)) {
  771. $this->ruleOutput[] = '<script type="text/javascript">Ext.onReady(function() {'.implode("\n",$rules).'});</script>';
  772. }
  773. return $overridden;
  774. }
  775. /**
  776. * Load the working context for this controller.
  777. *
  778. * @return modContext|string
  779. */
  780. public function loadWorkingContext() {
  781. $wctx = !empty($_GET['wctx']) ? $_GET['wctx'] : $this->modx->context->get('key');
  782. if (!empty($wctx)) {
  783. $this->workingContext = $this->modx->getContext($wctx);
  784. if (!$this->workingContext) {
  785. $this->failure($this->modx->lexicon('permission_denied'));
  786. }
  787. } else {
  788. $this->workingContext =& $this->modx->context;
  789. }
  790. return $this->workingContext;
  791. }
  792. /**
  793. * Adds a topic to the JS language array
  794. * @param string $topic
  795. * @return string
  796. */
  797. public function addLexiconTopic($topic) {
  798. $this->modx->lexicon->load($topic);
  799. $langTopics = $this->getPlaceholder('_lang_topics');
  800. $langTopics = explode(',',$langTopics);
  801. $langTopics[] = $topic;
  802. $langTopics = implode(',',$langTopics);
  803. $this->setPlaceholder('_lang_topics',$langTopics);
  804. return $langTopics;
  805. }
  806. public function setCssURLPlaceholders()
  807. {
  808. $managerUrl = $this->modx->getOption('manager_url', null, MODX_MANAGER_URL);
  809. $managerPath = $this->modx->getOption('manager_path',null,MODX_MANAGER_PATH);
  810. $index = false;
  811. $login = false;
  812. if ($this->theme != 'default') {
  813. if (file_exists($managerPath . 'templates/' . $this->theme . '/css/index.css')) {
  814. $this->setPlaceholder('indexCss', $managerUrl . 'templates/' . $this->theme . '/css/index.css');
  815. $index = true;
  816. }
  817. if (file_exists($managerPath . 'templates/' . $this->theme . '/css/login.css')) {
  818. $this->setPlaceholder('loginCss', $managerUrl . 'templates/' . $this->theme . '/css/login.css');
  819. $login = true;
  820. }
  821. }
  822. $versionToken = hash('adler32', $this->modx->getOption('settings_version') . $this->modx->uuid );
  823. $this->setPlaceholder('versionToken', $versionToken);
  824. if (!$index) {
  825. $this->setPlaceholder('indexCss', $managerUrl . 'templates/default/css/index.css');
  826. }
  827. if (!$login) {
  828. $this->setPlaceholder('loginCss', $managerUrl . 'templates/default/css/login.css');
  829. }
  830. }
  831. }
  832. /**
  833. * Utility abstract class for usage by Extras that has a subrequest handler that does auto-routing by the &action
  834. * REQUEST parameter. You must extend this class in your Extra to use it.
  835. *
  836. * @abstract
  837. * @package modx
  838. */
  839. abstract class modExtraManagerController extends modManagerController {
  840. /**
  841. * Define the default controller action for this namespace
  842. * @static
  843. * @return string A default controller action
  844. */
  845. public static function getDefaultController() { return 'index'; }
  846. /**
  847. * Get an instance of this extra controller
  848. * @static
  849. * @param modX $modx A reference to the modX object
  850. * @param string $className The string className that is being requested to load
  851. * @param array $config An array of configuration options built from the modAction object
  852. * @return modManagerController A newly created modManagerController instance
  853. */
  854. public static function getInstanceDeprecated(modX &$modx, $className, array $config = array()) {
  855. $action = call_user_func(array($className,'getDefaultController'));
  856. if (isset($_REQUEST['action'])) {
  857. $action = str_replace(array('../','./','.','-','@'),'',$_REQUEST['action']);
  858. }
  859. $className = self::getControllerClassName($action,$config['namespace']);
  860. $classPath = $config['namespace_path'].'controllers/'.$action.'.class.php';
  861. require_once $classPath;
  862. /** @var modManagerController $controller */
  863. $controller = new $className($modx,$config);
  864. return $controller;
  865. }
  866. /**
  867. * Return the class name of a controller given the action
  868. * @static
  869. * @param string $action The action name, eg: "home" or "create"
  870. * @param string $namespace The namespace of the Exra
  871. * @param string $postFix The string to postfix to the class name
  872. * @return string A full class name of the controller class
  873. */
  874. public static function getControllerClassName($action,$namespace = '',$postFix = 'ManagerController') {
  875. $className = explode('/',$action);
  876. $o = array();
  877. foreach ($className as $k) {
  878. $o[] = ucfirst(str_replace(array('.','_','-'),'',$k));
  879. }
  880. return ucfirst($namespace).implode('',$o).$postFix;
  881. }
  882. /**
  883. * Do any page-specific logic and/or processing here
  884. * @param array $scriptProperties
  885. * @return void
  886. */
  887. public function process(array $scriptProperties = array()) {}
  888. /**
  889. * The page title for this controller
  890. * @return string The string title of the page
  891. */
  892. public function getPageTitle() { return ''; }
  893. /**
  894. * Loads any page-specific CSS/JS for the controller
  895. * @return void
  896. */
  897. public function loadCustomCssJs() {}
  898. /**
  899. * Specify the location of the template file
  900. * @return string The absolute path to the template file
  901. */
  902. public function getTemplateFile() { return ''; }
  903. /**
  904. * Check whether the active user has access to view this page
  905. * @return bool True if the user passes permission checks
  906. */
  907. public function checkPermissions() { return true;}
  908. }
  909. /**
  910. * A base manager controller to implement, which makes use of the regular parser
  911. */
  912. abstract class modParsedManagerController extends modExtraManagerController {
  913. /**
  914. * The request HTTP method
  915. *
  916. * @var string
  917. */
  918. protected $method = 'GET';
  919. public function initialize() {
  920. parent::initialize();
  921. // Let's check the HTTP method to display a different content based on it (ie. when we submit a form)
  922. $this->method = filter_input(INPUT_SERVER, 'REQUEST_METHOD');
  923. // Some hack to put the HTML content in its right place so ExtJS can resize the main content "area" properly
  924. $this->addHtml(<<<HTML
  925. <script>
  926. Ext.onReady(function() {
  927. var node = document.getElementById('modx-panel-holder').nextElementSibling
  928. ,content = node.innerHTML;
  929. node.parentNode.removeChild(node);
  930. MODx.add({
  931. xtype: 'box'
  932. ,html: content
  933. ,cls: node.className || ''
  934. ,id: node.id || Ext.id()
  935. });
  936. });
  937. </script>
  938. HTML
  939. );
  940. }
  941. /**
  942. * @inheritDoc
  943. */
  944. public function render() {
  945. $html = parent::render();
  946. // Make controller placeholders available as modx placeholders
  947. $this->modx->setPlaceholders($this->placeholders, 'ph.');
  948. // Make script properties available as placeholders too
  949. $this->modx->setPlaceholders($this->scriptProperties, 'prop.');
  950. // Make modx parses tags
  951. $this->modx->getParser()->processElementTags('', $html, true, true);
  952. return $html;
  953. }
  954. }