PageRenderTime 24ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/system/stats/stats.php

https://gitlab.com/lankerd/paGO---Testing-Site
PHP | 569 lines | 263 code | 69 blank | 237 comment | 24 complexity | 1d53982313d8ac300a528260d352def3 MD5 | raw file
  1. <?php
  2. /**
  3. * @package Joomla.Plugin
  4. * @subpackage System.stats
  5. *
  6. * @copyright Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE.txt
  8. */
  9. defined('_JEXEC') or die;
  10. /**
  11. * Statistics system plugin. This sends anonymous data back to the Joomla! Project about the
  12. * PHP, SQL, Joomla and OS versions
  13. *
  14. * @since 3.5
  15. */
  16. class PlgSystemStats extends JPlugin
  17. {
  18. /**
  19. * @const integer
  20. * @since 3.5
  21. */
  22. const MODE_ALLOW_ALWAYS = 1;
  23. /**
  24. * @const integer
  25. * @since 3.5
  26. */
  27. const MODE_ALLOW_ONCE = 2;
  28. /**
  29. * @const integer
  30. * @since 3.5
  31. */
  32. const MODE_ALLOW_NEVER = 3;
  33. /**
  34. * Application object
  35. *
  36. * @var JApplicationCms
  37. * @since 3.5
  38. */
  39. protected $app;
  40. /**
  41. * Load plugin language files automatically
  42. *
  43. * @var boolean
  44. * @since 3.5
  45. */
  46. protected $autoloadLanguage = true;
  47. /**
  48. * Database object
  49. *
  50. * @var JDatabaseDriver
  51. * @since 3.5
  52. */
  53. protected $db;
  54. /**
  55. * Url to send the statistics.
  56. *
  57. * @var string
  58. * @since 3.5
  59. */
  60. protected $serverUrl = 'https://developer.joomla.org/stats/submit';
  61. /**
  62. * Unique identifier for this site
  63. *
  64. * @var string
  65. * @since 3.5
  66. */
  67. protected $uniqueId;
  68. /**
  69. * Listener for the `onAfterInitialise` event
  70. *
  71. * @return void
  72. *
  73. * @since 3.5
  74. */
  75. public function onAfterInitialise()
  76. {
  77. if (!$this->app->isAdmin() || !$this->isAllowedUser())
  78. {
  79. return;
  80. }
  81. if (!$this->isDebugEnabled() && !$this->isUpdateRequired())
  82. {
  83. return;
  84. }
  85. if (JUri::getInstance()->getVar("tmpl") === "component")
  86. {
  87. return;
  88. }
  89. JHtml::_('jquery.framework');
  90. JHtml::script('plg_system_stats/stats.js', false, true, false);
  91. }
  92. /**
  93. * User selected to always send data
  94. *
  95. * @return void
  96. *
  97. * @since 3.5
  98. *
  99. * @throws Exception If user is not allowed.
  100. * @throws RuntimeException If there is an error saving the params or sending the data.
  101. */
  102. public function onAjaxSendAlways()
  103. {
  104. if (!$this->isAllowedUser() || !$this->isAjaxRequest())
  105. {
  106. throw new Exception(JText::_('JGLOBAL_AUTH_ACCESS_DENIED'), 403);
  107. }
  108. $this->params->set('mode', static::MODE_ALLOW_ALWAYS);
  109. if (!$this->saveParams())
  110. {
  111. throw new RuntimeException('Unable to save plugin settings', 500);
  112. }
  113. $this->sendStats();
  114. echo json_encode(array('sent' => 1));
  115. }
  116. /**
  117. * User selected to never send data.
  118. *
  119. * @return void
  120. *
  121. * @since 3.5
  122. *
  123. * @throws Exception If user is not allowed.
  124. * @throws RuntimeException If there is an error saving the params.
  125. */
  126. public function onAjaxSendNever()
  127. {
  128. if (!$this->isAllowedUser() || !$this->isAjaxRequest())
  129. {
  130. throw new Exception(JText::_('JGLOBAL_AUTH_ACCESS_DENIED'), 403);
  131. }
  132. $this->params->set('mode', static::MODE_ALLOW_NEVER);
  133. if (!$this->saveParams())
  134. {
  135. throw new RuntimeException('Unable to save plugin settings', 500);
  136. }
  137. echo json_encode(array('sent' => 0));
  138. }
  139. /**
  140. * User selected to send data once.
  141. *
  142. * @return void
  143. *
  144. * @since 3.5
  145. *
  146. * @throws Exception If user is not allowed.
  147. * @throws RuntimeException If there is an error saving the params or sending the data.
  148. */
  149. public function onAjaxSendOnce()
  150. {
  151. if (!$this->isAllowedUser() || !$this->isAjaxRequest())
  152. {
  153. throw new Exception(JText::_('JGLOBAL_AUTH_ACCESS_DENIED'), 403);
  154. }
  155. $this->params->set('mode', static::MODE_ALLOW_ONCE);
  156. if (!$this->saveParams())
  157. {
  158. throw new RuntimeException('Unable to save plugin settings', 500);
  159. }
  160. $this->sendStats();
  161. echo json_encode(array('sent' => 1));
  162. }
  163. /**
  164. * Send the stats to the server.
  165. * On first load | on demand mode it will show a message asking users to select mode.
  166. *
  167. * @return void
  168. *
  169. * @since 3.5
  170. *
  171. * @throws Exception If user is not allowed.
  172. * @throws RuntimeException If there is an error saving the params or sending the data.
  173. */
  174. public function onAjaxSendStats()
  175. {
  176. if (!$this->isAllowedUser() || !$this->isAjaxRequest())
  177. {
  178. throw new Exception(JText::_('JGLOBAL_AUTH_ACCESS_DENIED'), 403);
  179. }
  180. // User has not selected the mode. Show message.
  181. if ((int) $this->params->get('mode') !== static::MODE_ALLOW_ALWAYS)
  182. {
  183. $data = array(
  184. 'sent' => 0,
  185. 'html' => $this->getRenderer('message')->render($this->getLayoutData())
  186. );
  187. echo json_encode($data);
  188. return;
  189. }
  190. if (!$this->saveParams())
  191. {
  192. throw new RuntimeException('Unable to save plugin settings', 500);
  193. }
  194. $this->sendStats();
  195. echo json_encode(array('sent' => 1));
  196. }
  197. /**
  198. * Get the data through events
  199. *
  200. * @param string $context Context where this will be called from
  201. *
  202. * @return array
  203. *
  204. * @since 3.5
  205. */
  206. public function onGetStatsData($context)
  207. {
  208. return $this->getStatsData();
  209. }
  210. /**
  211. * Debug a layout of this plugin
  212. *
  213. * @param string $layoutId Layout identifier
  214. * @param array $data Optional data for the layout
  215. *
  216. * @return string
  217. *
  218. * @since 3.5
  219. */
  220. public function debug($layoutId, $data = array())
  221. {
  222. $data = array_merge($this->getLayoutData(), $data);
  223. return $this->getRenderer($layoutId)->debug($data);
  224. }
  225. /**
  226. * Get the data for the layout
  227. *
  228. * @return array
  229. *
  230. * @since 3.5
  231. */
  232. protected function getLayoutData()
  233. {
  234. return array(
  235. 'plugin' => $this,
  236. 'pluginParams' => $this->params,
  237. 'statsData' => $this->getStatsData()
  238. );
  239. }
  240. /**
  241. * Get the layout paths
  242. *
  243. * @return array()
  244. *
  245. * @since 3.5
  246. */
  247. protected function getLayoutPaths()
  248. {
  249. $template = JFactory::getApplication()->getTemplate();
  250. return array(
  251. JPATH_ADMINISTRATOR . '/templates/' . $template . '/html/layouts/plugins/' . $this->_type . '/' . $this->_name,
  252. __DIR__ . '/layouts',
  253. );
  254. }
  255. /**
  256. * Get the plugin renderer
  257. *
  258. * @param string $layoutId Layout identifier
  259. *
  260. * @return JLayout
  261. *
  262. * @since 3.5
  263. */
  264. protected function getRenderer($layoutId = 'default')
  265. {
  266. $renderer = new JLayoutFile($layoutId);
  267. $renderer->setIncludePaths($this->getLayoutPaths());
  268. return $renderer;
  269. }
  270. /**
  271. * Get the data that will be sent to the stats server.
  272. *
  273. * @return array.
  274. *
  275. * @since 3.5
  276. */
  277. private function getStatsData()
  278. {
  279. return array(
  280. 'unique_id' => $this->getUniqueId(),
  281. 'php_version' => PHP_VERSION,
  282. 'db_type' => $this->db->name,
  283. 'db_version' => $this->db->getVersion(),
  284. 'cms_version' => JVERSION,
  285. 'server_os' => php_uname('s') . ' ' . php_uname('r')
  286. );
  287. }
  288. /**
  289. * Get the unique id. Generates one if none is set.
  290. *
  291. * @return integer
  292. *
  293. * @since 3.5
  294. */
  295. private function getUniqueId()
  296. {
  297. if (null === $this->uniqueId)
  298. {
  299. $this->uniqueId = $this->params->get('unique_id', hash('sha1', JUserHelper::genRandomPassword(28) . time()));
  300. }
  301. return $this->uniqueId;
  302. }
  303. /**
  304. * Check if current user is allowed to send the data
  305. *
  306. * @return boolean
  307. *
  308. * @since 3.5
  309. */
  310. private function isAllowedUser()
  311. {
  312. return JFactory::getUser()->authorise('core.admin');
  313. }
  314. /**
  315. * Check if the debug is enabled
  316. *
  317. * @return boolean
  318. *
  319. * @since 3.5
  320. */
  321. private function isDebugEnabled()
  322. {
  323. return ((int) $this->params->get('debug', 0) === 1);
  324. }
  325. /**
  326. * Check if last_run + interval > now
  327. *
  328. * @return boolean
  329. *
  330. * @since 3.5
  331. */
  332. private function isUpdateRequired()
  333. {
  334. $last = (int) $this->params->get('lastrun', 0);
  335. $interval = (int) $this->params->get('interval', 12);
  336. $mode = (int) $this->params->get('mode', 0);
  337. if ($mode === static::MODE_ALLOW_NEVER)
  338. {
  339. return false;
  340. }
  341. // Never updated or debug enabled
  342. if (!$last || $this->isDebugEnabled())
  343. {
  344. return true;
  345. }
  346. return (abs(time() - $last) > $interval * 3600);
  347. }
  348. /**
  349. * Check valid AJAX request
  350. *
  351. * @return boolean
  352. *
  353. * @since 3.5
  354. */
  355. private function isAjaxRequest()
  356. {
  357. return strtolower($this->app->input->server->get('HTTP_X_REQUESTED_WITH', '')) == 'xmlhttprequest';
  358. }
  359. /**
  360. * Render a layout of this plugin
  361. *
  362. * @param string $layoutId Layout identifier
  363. * @param array $data Optional data for the layout
  364. *
  365. * @return string
  366. *
  367. * @since 3.5
  368. */
  369. public function render($layoutId, $data = array())
  370. {
  371. $data = array_merge($this->getLayoutData(), $data);
  372. return $this->getRenderer($layoutId)->render($data);
  373. }
  374. /**
  375. * Save the plugin parameters
  376. *
  377. * @return boolean
  378. *
  379. * @since 3.5
  380. */
  381. private function saveParams()
  382. {
  383. // Update params
  384. $this->params->set('lastrun', time());
  385. $this->params->set('unique_id', $this->getUniqueId());
  386. $interval = (int) $this->params->get('interval', 12);
  387. $this->params->set('interval', $interval ? $interval : 12);
  388. $query = $this->db->getQuery(true)
  389. ->update($this->db->quoteName('#__extensions'))
  390. ->set($this->db->quoteName('params') . ' = ' . $this->db->quote($this->params->toString('JSON')))
  391. ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin'))
  392. ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote('system'))
  393. ->where($this->db->quoteName('element') . ' = ' . $this->db->quote('stats'));
  394. try
  395. {
  396. // Lock the tables to prevent multiple plugin executions causing a race condition
  397. $this->db->lockTable('#__extensions');
  398. }
  399. catch (Exception $e)
  400. {
  401. // If we can't lock the tables it's too risky to continue execution
  402. return false;
  403. }
  404. try
  405. {
  406. // Update the plugin parameters
  407. $result = $this->db->setQuery($query)->execute();
  408. $this->clearCacheGroups(array('com_plugins'), array(0, 1));
  409. }
  410. catch (Exception $exc)
  411. {
  412. // If we failed to execute
  413. $this->db->unlockTables();
  414. $result = false;
  415. }
  416. try
  417. {
  418. // Unlock the tables after writing
  419. $this->db->unlockTables();
  420. }
  421. catch (Exception $e)
  422. {
  423. // If we can't lock the tables assume we have somehow failed
  424. $result = false;
  425. }
  426. return $result;
  427. }
  428. /**
  429. * Send the stats to the stats server
  430. *
  431. * @return boolean
  432. *
  433. * @since 3.5
  434. *
  435. * @throws RuntimeException If there is an error sending the data.
  436. */
  437. private function sendStats()
  438. {
  439. try
  440. {
  441. // Don't let the request take longer than 2 seconds to avoid page timeout issues
  442. $response = JHttpFactory::getHttp()->post($this->serverUrl, $this->getStatsData(), null, 2);
  443. }
  444. catch (UnexpectedValueException $e)
  445. {
  446. // There was an error sending stats. Should we do anything?
  447. throw new RuntimeException('Could not send site statistics to remote server: ' . $e->getMessage(), 500);
  448. }
  449. catch (RuntimeException $e)
  450. {
  451. // There was an error connecting to the server or in the post request
  452. throw new RuntimeException('Could not connect to statistics server: ' . $e->getMessage(), 500);
  453. }
  454. catch (Exception $e)
  455. {
  456. // An unexpected error in processing; don't let this failure kill the site
  457. throw new RuntimeException('Unexpected error connecting to statistics server: ' . $e->getMessage(), 500);
  458. }
  459. if ($response->code !== 200)
  460. {
  461. $data = json_decode($response->body);
  462. throw new RuntimeException('Could not send site statistics to remote server: ' . $data->message, $response->code);
  463. }
  464. return true;
  465. }
  466. /**
  467. * Clears cache groups. We use it to clear the plugins cache after we update the last run timestamp.
  468. *
  469. * @param array $clearGroups The cache groups to clean
  470. * @param array $cacheClients The cache clients (site, admin) to clean
  471. *
  472. * @return void
  473. *
  474. * @since 3.5
  475. */
  476. private function clearCacheGroups(array $clearGroups, array $cacheClients = array(0, 1))
  477. {
  478. $conf = JFactory::getConfig();
  479. foreach ($clearGroups as $group)
  480. {
  481. foreach ($cacheClients as $client_id)
  482. {
  483. try
  484. {
  485. $options = array(
  486. 'defaultgroup' => $group,
  487. 'cachebase' => ($client_id) ? JPATH_ADMINISTRATOR . '/cache' :
  488. $conf->get('cache_path', JPATH_SITE . '/cache')
  489. );
  490. $cache = JCache::getInstance('callback', $options);
  491. $cache->clean();
  492. }
  493. catch (Exception $e)
  494. {
  495. // Ignore it
  496. }
  497. }
  498. }
  499. }
  500. }