Base for a static organization website

ToolbarComponent.php 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. <?php
  2. /**
  3. * DebugKit DebugToolbar Component
  4. *
  5. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since DebugKit 0.1
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. App::uses('CakeLog', 'Log');
  16. App::uses('CakeLogInterface', 'Log');
  17. App::uses('DebugTimer', 'DebugKit.Lib');
  18. App::uses('DebugMemory', 'DebugKit.Lib');
  19. App::uses('HelperCollection', 'View');
  20. App::uses('CakeEventManager', 'Event');
  21. App::uses('CakeEventListener', 'Event');
  22. /**
  23. * Class ToolbarComponent
  24. *
  25. * @since DebugKit 0.1
  26. */
  27. class ToolbarComponent extends Component implements CakeEventListener {
  28. /**
  29. * Settings for the Component
  30. *
  31. * - forceEnable - Force the toolbar to display even if debug == 0. Default = false
  32. * - autoRun - Automatically display the toolbar. If set to false, toolbar display can be triggered by adding
  33. * `?debug=true` to your URL.
  34. *
  35. * @var array
  36. */
  37. public $settings = array(
  38. 'forceEnable' => false,
  39. 'autoRun' => true
  40. );
  41. /**
  42. * Controller instance reference
  43. *
  44. * @var object
  45. */
  46. public $controller;
  47. /**
  48. * Components used by DebugToolbar
  49. *
  50. * @var array
  51. */
  52. public $components = array('RequestHandler', 'Session');
  53. /**
  54. * The default panels the toolbar uses.
  55. * which panels are used can be configured when attaching the component
  56. *
  57. * @var array
  58. */
  59. protected $_defaultPanels = array(
  60. 'DebugKit.History',
  61. 'DebugKit.Session',
  62. 'DebugKit.Request',
  63. 'DebugKit.SqlLog',
  64. 'DebugKit.Timer',
  65. 'DebugKit.Log',
  66. 'DebugKit.Variables',
  67. 'DebugKit.Environment',
  68. 'DebugKit.Include'
  69. );
  70. /**
  71. * Loaded panel objects.
  72. *
  73. * @var array
  74. */
  75. public $panels = array();
  76. /**
  77. * javascript files component will be using
  78. *
  79. * @var array
  80. */
  81. public $javascript = array(
  82. 'libs' => 'DebugKit./js/js_debug_toolbar'
  83. );
  84. /**
  85. * CSS files component will be using
  86. *
  87. * @var array
  88. */
  89. public $css = array('DebugKit./css/debug_toolbar.css');
  90. /**
  91. * CacheKey used for the cache file.
  92. *
  93. * @var string
  94. */
  95. public $cacheKey = 'toolbar_cache';
  96. /**
  97. * Duration of the debug kit history cache
  98. *
  99. * @var string
  100. */
  101. public $cacheDuration = '+4 hours';
  102. /**
  103. * Status whether component is enable or disable
  104. *
  105. * @var boolean
  106. */
  107. public $enabled = true;
  108. /**
  109. * Constructor
  110. *
  111. * If debug is off the component will be disabled and not do any further time tracking
  112. * or load the toolbar helper.
  113. *
  114. * @param ComponentCollection $collection
  115. * @param array $settings
  116. * @return \ToolbarComponent
  117. */
  118. public function __construct(ComponentCollection $collection, $settings = array()) {
  119. $settings = array_merge((array)Configure::read('DebugKit'), $settings);
  120. $panels = $this->_defaultPanels;
  121. if (isset($settings['panels'])) {
  122. $panels = $this->_makePanelList($settings['panels']);
  123. unset($settings['panels']);
  124. }
  125. $this->controller = $collection->getController();
  126. parent::__construct($collection, array_merge($this->settings, (array)$settings));
  127. if (
  128. !Configure::read('debug') &&
  129. empty($this->settings['forceEnable'])
  130. ) {
  131. $this->enabled = false;
  132. return false;
  133. }
  134. if (
  135. $this->settings['autoRun'] === false &&
  136. !isset($this->controller->request->query['debug'])
  137. ) {
  138. $this->enabled = false;
  139. return false;
  140. }
  141. $this->controller->getEventManager()->attach($this);
  142. DebugMemory::record(__d('debug_kit', 'Component initialization'));
  143. $this->cacheKey .= $this->Session->read('Config.userAgent');
  144. if (
  145. in_array('DebugKit.History', $panels) ||
  146. (isset($settings['history']) && $settings['history'] !== false)
  147. ) {
  148. $this->_createCacheConfig();
  149. }
  150. $this->_loadPanels($panels, $settings);
  151. return false;
  152. }
  153. /**
  154. * Register all the timing handlers for core events.
  155. *
  156. * @return array
  157. */
  158. public function implementedEvents() {
  159. $before = function ($name) {
  160. return function () use ($name) {
  161. DebugTimer::start($name, $name);
  162. };
  163. };
  164. $after = function ($name) {
  165. return function () use ($name) {
  166. DebugTimer::stop($name);
  167. };
  168. };
  169. return array(
  170. 'Controller.initialize' => array(
  171. array('priority' => 0, 'callable' => $before('Event: Controller.initialize')),
  172. array('priority' => 999, 'callable' => $after('Event: Controller.initialize'))
  173. ),
  174. 'Controller.startup' => array(
  175. array('priority' => 0, 'callable' => $before('Event: Controller.startup')),
  176. array('priority' => 999, 'callable' => $after('Event: Controller.startup'))
  177. ),
  178. 'Controller.beforeRender' => array(
  179. array('priority' => 0, 'callable' => $before('Event: Controller.beforeRender')),
  180. array('priority' => 999, 'callable' => $after('Event: Controller.beforeRender'))
  181. ),
  182. 'Controller.shutdown' => array(
  183. array('priority' => 0, 'callable' => $before('Event: Controller.shutdown')),
  184. array('priority' => 999, 'callable' => $after('Event: Controller.shutdown'))
  185. ),
  186. 'View.beforeRender' => array(
  187. array('priority' => 0, 'callable' => $before('Event: View.beforeRender')),
  188. array('priority' => 999, 'callable' => $after('Event: View.beforeRender'))
  189. ),
  190. 'View.afterRender' => array(
  191. array('priority' => 0, 'callable' => $before('Event: View.afterRender')),
  192. array('priority' => 999, 'callable' => $after('Event: View.afterRender'))
  193. ),
  194. 'View.beforeLayout' => array(
  195. array('priority' => 0, 'callable' => $before('Event: View.beforeLayout')),
  196. array('priority' => 999, 'callable' => $after('Event: View.beforeLayout'))
  197. ),
  198. 'View.afterLayout' => array(
  199. array('priority' => 0, 'callable' => $before('Event: View.afterLayout')),
  200. array('priority' => 999, 'callable' => $after('Event: View.afterLayout'))
  201. ),
  202. );
  203. }
  204. /**
  205. * Initialize callback.
  206. * If automatically disabled, tell component collection about the state.
  207. *
  208. * @param Controller $controller
  209. * @return boolean
  210. */
  211. public function initialize(Controller $controller) {
  212. if (!$this->enabled) {
  213. $this->_Collection->disable('Toolbar');
  214. }
  215. }
  216. /**
  217. * Go through user panels and remove default panels as indicated.
  218. *
  219. * @param array $userPanels The list of panels ther user has added removed.
  220. * @return array Array of panels to use.
  221. */
  222. protected function _makePanelList($userPanels) {
  223. $panels = $this->_defaultPanels;
  224. foreach ($userPanels as $key => $value) {
  225. if (is_numeric($key)) {
  226. $panels[] = $value;
  227. }
  228. if (is_string($key) && $value === false) {
  229. $index = array_search($key, $panels);
  230. if ($index !== false) {
  231. unset($panels[$index]);
  232. }
  233. // Compatibility for when panels were not
  234. // required to have a plugin prefix.
  235. $alternate = 'DebugKit.' . ucfirst($key);
  236. $index = array_search($alternate, $panels);
  237. if ($index !== false) {
  238. unset($panels[$index]);
  239. }
  240. }
  241. }
  242. return $panels;
  243. }
  244. /**
  245. * Component Startup
  246. *
  247. * @param Controller $controller
  248. * @return boolean
  249. */
  250. public function startup(Controller $controller) {
  251. $panels = array_keys($this->panels);
  252. foreach ($panels as $panelName) {
  253. $this->panels[$panelName]->startup($controller);
  254. }
  255. DebugTimer::start(
  256. 'controllerAction',
  257. __d('debug_kit', 'Controller action')
  258. );
  259. DebugMemory::record(
  260. __d('debug_kit', 'Controller action start')
  261. );
  262. }
  263. /**
  264. * beforeRedirect callback
  265. *
  266. * @param Controller $controller
  267. * @param $url
  268. * @param null $status
  269. * @param boolean $exit
  270. * @return void
  271. */
  272. public function beforeRedirect(Controller $controller, $url, $status = null, $exit = true) {
  273. if (!class_exists('DebugTimer')) {
  274. return null;
  275. }
  276. DebugTimer::stop('controllerAction');
  277. DebugTimer::start(
  278. 'processToolbar',
  279. __d('debug_kit', 'Processing toolbar state')
  280. );
  281. $vars = $this->_gatherVars($controller);
  282. $this->_saveState($controller, $vars);
  283. DebugTimer::stop('processToolbar');
  284. }
  285. /**
  286. * beforeRender callback
  287. *
  288. * Calls beforeRender on all the panels and set the aggregate to the controller.
  289. *
  290. * @param Controller $controller
  291. * @return void
  292. */
  293. public function beforeRender(Controller $controller) {
  294. if (!class_exists('DebugTimer')) {
  295. return null;
  296. }
  297. DebugTimer::stop('controllerAction');
  298. DebugTimer::start(
  299. 'processToolbar',
  300. __d('debug_kit', 'Processing toolbar data')
  301. );
  302. $vars = $this->_gatherVars($controller);
  303. $this->_saveState($controller, $vars);
  304. $this->javascript = array_unique(array_merge($this->javascript, $vars['javascript']));
  305. $this->css = array_unique(array_merge($this->css, $vars['css']));
  306. unset($vars['javascript'], $vars['css']);
  307. $controller->set(array(
  308. 'debugToolbarPanels' => $vars,
  309. 'debugToolbarJavascript' => $this->javascript,
  310. 'debugToolbarCss' => $this->css
  311. ));
  312. $isHtml = (
  313. !isset($controller->request->params['ext']) ||
  314. $controller->request->params['ext'] === 'html'
  315. );
  316. if (!$controller->request->is('ajax') && $isHtml) {
  317. $format = 'Html';
  318. } else {
  319. $format = 'FirePhp';
  320. }
  321. $controller->helpers[] = 'DebugKit.DebugTimer';
  322. $controller->helpers['DebugKit.Toolbar'] = array(
  323. 'output' => sprintf('DebugKit.%sToolbar', $format),
  324. 'cacheKey' => $this->cacheKey,
  325. 'cacheConfig' => 'debug_kit',
  326. 'forceEnable' => $this->settings['forceEnable'],
  327. );
  328. DebugTimer::stop('processToolbar');
  329. DebugMemory::record(__d('debug_kit', 'Controller render start'));
  330. }
  331. /**
  332. * Load a toolbar state from cache
  333. *
  334. * @param integer $key
  335. * @return array
  336. */
  337. public function loadState($key) {
  338. $history = Cache::read($this->cacheKey, 'debug_kit');
  339. if (isset($history[$key])) {
  340. return $history[$key];
  341. }
  342. return array();
  343. }
  344. /**
  345. * Create the cache config for the history
  346. *
  347. * @return void
  348. */
  349. protected function _createCacheConfig() {
  350. if (Configure::read('Cache.disable') === true || Cache::config('debug_kit')) {
  351. return;
  352. }
  353. $cache = array(
  354. 'duration' => $this->cacheDuration,
  355. 'engine' => 'File',
  356. 'path' => CACHE
  357. );
  358. if (isset($this->settings['cache'])) {
  359. $cache = array_merge($cache, $this->settings['cache']);
  360. }
  361. Cache::config('debug_kit', $cache);
  362. }
  363. /**
  364. * collects the panel contents
  365. *
  366. * @param Controller $controller
  367. * @return array Array of all panel beforeRender()
  368. */
  369. protected function _gatherVars(Controller $controller) {
  370. $vars = array('javascript' => array(), 'css' => array());
  371. $panels = array_keys($this->panels);
  372. foreach ($panels as $panelName) {
  373. $panel = $this->panels[$panelName];
  374. $panelName = Inflector::underscore($panelName);
  375. $vars[$panelName]['content'] = $panel->beforeRender($controller);
  376. $elementName = Inflector::underscore($panelName) . '_panel';
  377. if (isset($panel->elementName)) {
  378. $elementName = $panel->elementName;
  379. }
  380. $vars[$panelName]['elementName'] = $elementName;
  381. $vars[$panelName]['plugin'] = $panel->plugin;
  382. $vars[$panelName]['title'] = $panel->title;
  383. $vars[$panelName]['disableTimer'] = true;
  384. if (!empty($panel->javascript)) {
  385. $vars['javascript'] = array_merge($vars['javascript'], (array)$panel->javascript);
  386. }
  387. if (!empty($panel->css)) {
  388. $vars['css'] = array_merge($vars['css'], (array)$panel->css);
  389. }
  390. }
  391. return $vars;
  392. }
  393. /**
  394. * Load Panels used in the debug toolbar
  395. *
  396. * @param $panels
  397. * @param $settings
  398. * @return void
  399. */
  400. protected function _loadPanels($panels, $settings) {
  401. foreach ($panels as $panel) {
  402. $className = ucfirst($panel) . 'Panel';
  403. list($plugin, $className) = pluginSplit($className, true);
  404. App::uses($className, $plugin . 'Panel');
  405. if (!class_exists($className)) {
  406. trigger_error(__d('debug_kit', 'Could not load DebugToolbar panel %s', $panel), E_USER_WARNING);
  407. continue;
  408. }
  409. $panelObj = new $className($settings);
  410. if ($panelObj instanceof DebugPanel) {
  411. list(, $panel) = pluginSplit($panel);
  412. $this->panels[Inflector::underscore($panel)] = $panelObj;
  413. }
  414. }
  415. }
  416. /**
  417. * Save the current state of the toolbar varibles to the cache file.
  418. *
  419. * @param \Controller|object $controller Controller instance
  420. * @param array $vars Vars to save.
  421. * @return void
  422. */
  423. protected function _saveState(Controller $controller, $vars) {
  424. $config = Cache::config('debug_kit');
  425. if (empty($config) || !isset($this->panels['history'])) {
  426. return;
  427. }
  428. $history = Cache::read($this->cacheKey, 'debug_kit');
  429. if (empty($history)) {
  430. $history = array();
  431. }
  432. if (count($history) == $this->panels['history']->history) {
  433. array_pop($history);
  434. }
  435. if (isset($vars['variables']['content'])) {
  436. // Remove unserializable native objects.
  437. array_walk_recursive($vars['variables']['content'], function (&$item) {
  438. if (
  439. $item instanceof Closure ||
  440. $item instanceof PDO ||
  441. $item instanceof SimpleXmlElement
  442. ) {
  443. $item = 'Unserializable object - ' . get_class($item);
  444. } elseif ($item instanceof Exception) {
  445. $item = sprintf(
  446. 'Unserializable object - %s. Error: %s in %s, line %s',
  447. get_class($item),
  448. $item,
  449. $item->getMessage(),
  450. $item->getFile(),
  451. $item->getLine()
  452. );
  453. }
  454. return $item;
  455. });
  456. }
  457. unset($vars['history']);
  458. array_unshift($history, $vars);
  459. Cache::write($this->cacheKey, $history, 'debug_kit');
  460. }
  461. }