first commit
This commit is contained in:
55
plugins/system/stats/layouts/field/data.php
Normal file
55
plugins/system/stats/layouts/field/data.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage System.stats
|
||||
*
|
||||
* @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Language\Text;
|
||||
|
||||
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
|
||||
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
|
||||
$wa->registerAndUseScript('plg_system_stats.stats', 'plg_system_stats/stats.js', [], ['defer' => true], ['core']);
|
||||
|
||||
extract($displayData);
|
||||
|
||||
/**
|
||||
* Layout variables
|
||||
* -----------------
|
||||
* @var string $autocomplete Autocomplete attribute for the field.
|
||||
* @var boolean $autofocus Is autofocus enabled?
|
||||
* @var string $class Classes for the input.
|
||||
* @var string $description Description of the field.
|
||||
* @var boolean $disabled Is this field disabled?
|
||||
* @var string $group Group the field belongs to. <fields> section in form XML.
|
||||
* @var boolean $hidden Is this field hidden in the form?
|
||||
* @var string $hint Placeholder for the field.
|
||||
* @var string $id DOM id of the field.
|
||||
* @var string $label Label of the field.
|
||||
* @var string $labelclass Classes to apply to the label.
|
||||
* @var boolean $multiple Does this field support multiple values?
|
||||
* @var string $name Name of the input field.
|
||||
* @var string $onchange Onchange attribute for the field.
|
||||
* @var string $onclick Onclick attribute for the field.
|
||||
* @var string $pattern Pattern (Reg Ex) of value of the form field.
|
||||
* @var boolean $readonly Is this field read only?
|
||||
* @var boolean $repeat Allows extensions to duplicate elements.
|
||||
* @var boolean $required Is this field required?
|
||||
* @var integer $size Size attribute of the input.
|
||||
* @var boolean $spellcheck Spellcheck state for the form field.
|
||||
* @var string $validate Validation rules to apply.
|
||||
* @var string $value Value attribute of the field.
|
||||
* @var array $options Options available for this field.
|
||||
* @var array $statsData Statistics that will be sent to the stats server
|
||||
*/
|
||||
?>
|
||||
<?php if (count($statsData)) : ?>
|
||||
<a href="#" id="js-pstats-data-details-toggler"><?php echo Text::_('PLG_SYSTEM_STATS_MSG_WHAT_DATA_WILL_BE_SENT'); ?></a>
|
||||
<?php echo $field->render('stats', compact('statsData')); ?>
|
||||
<?php endif; ?>
|
||||
49
plugins/system/stats/layouts/field/uniqueid.php
Normal file
49
plugins/system/stats/layouts/field/uniqueid.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage System.stats
|
||||
*
|
||||
* @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Language\Text;
|
||||
|
||||
extract($displayData);
|
||||
|
||||
/**
|
||||
* Layout variables
|
||||
* -----------------
|
||||
* @var string $autocomplete Autocomplete attribute for the field.
|
||||
* @var boolean $autofocus Is autofocus enabled?
|
||||
* @var string $class Classes for the input.
|
||||
* @var string $description Description of the field.
|
||||
* @var boolean $disabled Is this field disabled?
|
||||
* @var string $group Group the field belongs to. <fields> section in form XML.
|
||||
* @var boolean $hidden Is this field hidden in the form?
|
||||
* @var string $hint Placeholder for the field.
|
||||
* @var string $id DOM id of the field.
|
||||
* @var string $label Label of the field.
|
||||
* @var string $labelclass Classes to apply to the label.
|
||||
* @var boolean $multiple Does this field support multiple values?
|
||||
* @var string $name Name of the input field.
|
||||
* @var string $onchange Onchange attribute for the field.
|
||||
* @var string $onclick Onclick attribute for the field.
|
||||
* @var string $pattern Pattern (Reg Ex) of value of the form field.
|
||||
* @var boolean $readonly Is this field read only?
|
||||
* @var boolean $repeat Allows extensions to duplicate elements.
|
||||
* @var boolean $required Is this field required?
|
||||
* @var integer $size Size attribute of the input.
|
||||
* @var boolean $spellcheck Spellcheck state for the form field.
|
||||
* @var string $validate Validation rules to apply.
|
||||
* @var string $value Value attribute of the field.
|
||||
* @var array $options Options available for this field.
|
||||
*/
|
||||
?>
|
||||
<input type="hidden" name="<?php echo $name; ?>" id="<?php echo $id; ?>" value="<?php echo htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<button class="btn btn-secondary" type="button" id="js-pstats-reset-uid">
|
||||
<span class="icon-sync"></span> <?php echo Text::_('PLG_SYSTEM_STATS_RESET_UNIQUE_ID'); ?>
|
||||
</button>
|
||||
47
plugins/system/stats/layouts/message.php
Normal file
47
plugins/system/stats/layouts/message.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage System.stats
|
||||
*
|
||||
* @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
extract($displayData);
|
||||
|
||||
/**
|
||||
* Layout variables
|
||||
* -----------------
|
||||
* @var PlgSystemStats $plugin Plugin rendering this layout
|
||||
* @var Registry $pluginParams Plugin parameters
|
||||
* @var array $statsData Array containing the data that will be sent to the stats server
|
||||
*/
|
||||
?>
|
||||
|
||||
<joomla-alert type="info" dismiss class="js-pstats-alert hidden" role="alertdialog" close-text="<?php echo Text::_('JCLOSE'); ?>" aria-labelledby="alert-stats-heading">
|
||||
<div class="alert-heading" id="alert-stats-heading"><?php echo Text::_('PLG_SYSTEM_STATS_LABEL_MESSAGE_TITLE'); ?></div>
|
||||
<div>
|
||||
<div class="alert-message">
|
||||
<p>
|
||||
<?php echo Text::_('PLG_SYSTEM_STATS_MSG_JOOMLA_WANTS_TO_SEND_DATA'); ?>
|
||||
</p>
|
||||
<p>
|
||||
<a href="#" class="js-pstats-btn-details alert-link"><?php echo Text::_('PLG_SYSTEM_STATS_MSG_WHAT_DATA_WILL_BE_SENT'); ?></a>
|
||||
</p>
|
||||
<?php
|
||||
echo $plugin->render('stats', compact('statsData'));
|
||||
?>
|
||||
<p class="fw-bold"><?php echo Text::_('PLG_SYSTEM_STATS_MSG_ALLOW_SENDING_DATA'); ?></p>
|
||||
<p class="actions">
|
||||
<button type="button" class="btn btn-primary js-pstats-btn-allow-never"><?php echo Text::_('PLG_SYSTEM_STATS_BTN_NEVER_SEND'); ?></button>
|
||||
<button type="button" class="btn btn-primary js-pstats-btn-allow-always"><?php echo Text::_('PLG_SYSTEM_STATS_BTN_SEND_ALWAYS'); ?></button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</joomla-alert>
|
||||
47
plugins/system/stats/layouts/stats.php
Normal file
47
plugins/system/stats/layouts/stats.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage System.stats
|
||||
*
|
||||
* @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Language\Text;
|
||||
|
||||
extract($displayData);
|
||||
|
||||
/**
|
||||
* Layout variables
|
||||
* -----------------
|
||||
* @var array $statsData Array containing the data that will be sent to the stats server
|
||||
*/
|
||||
|
||||
$versionFields = ['php_version', 'db_version', 'cms_version'];
|
||||
?>
|
||||
<table class="table mb-3 d-none" id="js-pstats-data-details">
|
||||
<caption class="visually-hidden">
|
||||
<?php echo Text::_('PLG_SYSTEM_STATS_STATISTICS'); ?>
|
||||
</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-15">
|
||||
<?php echo Text::_('PLG_SYSTEM_STATS_SETTING'); ?>
|
||||
</th>
|
||||
<th scope="col">
|
||||
<?php echo Text::_('PLG_SYSTEM_STATS_VALUE'); ?>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($statsData as $key => $value) : ?>
|
||||
<tr>
|
||||
<th scope="row"><?php echo Text::_('PLG_SYSTEM_STATS_LABEL_' . strtoupper($key)); ?></th>
|
||||
<td><?php echo in_array($key, $versionFields) ? (preg_match('/\d+(?:\.\d+)+/', $value, $matches) ? $matches[0] : $value) : $value; ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
48
plugins/system/stats/services/provider.php
Normal file
48
plugins/system/stats/services/provider.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage System.stats
|
||||
*
|
||||
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
\defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Extension\PluginInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\Database\DatabaseInterface;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Plugin\System\Stats\Extension\Stats;
|
||||
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
public function register(Container $container): void
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$plugin = new Stats(
|
||||
$container->get(DispatcherInterface::class),
|
||||
(array) PluginHelper::getPlugin('system', 'stats')
|
||||
);
|
||||
$plugin->setApplication(Factory::getApplication());
|
||||
$plugin->setDatabase($container->get(DatabaseInterface::class));
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
620
plugins/system/stats/src/Extension/Stats.php
Normal file
620
plugins/system/stats/src/Extension/Stats.php
Normal file
@ -0,0 +1,620 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage System.stats
|
||||
*
|
||||
* @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Plugin\System\Stats\Extension;
|
||||
|
||||
use Joomla\CMS\Cache\Cache;
|
||||
use Joomla\CMS\Http\HttpFactory;
|
||||
use Joomla\CMS\Layout\FileLayout;
|
||||
use Joomla\CMS\Log\Log;
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
use Joomla\CMS\User\UserHelper;
|
||||
use Joomla\Database\DatabaseAwareTrait;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
// Uncomment the following line to enable debug mode for testing purposes. Note: statistics will be sent on every page load
|
||||
// define('PLG_SYSTEM_STATS_DEBUG', 1);
|
||||
|
||||
/**
|
||||
* Statistics system plugin. This sends anonymous data back to the Joomla! Project about the
|
||||
* PHP, SQL, Joomla and OS versions
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
final class Stats extends CMSPlugin
|
||||
{
|
||||
use DatabaseAwareTrait;
|
||||
|
||||
/**
|
||||
* Indicates sending statistics is always allowed.
|
||||
*
|
||||
* @var integer
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
public const MODE_ALLOW_ALWAYS = 1;
|
||||
|
||||
/**
|
||||
* Indicates sending statistics is never allowed.
|
||||
*
|
||||
* @var integer
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
public const MODE_ALLOW_NEVER = 3;
|
||||
|
||||
/**
|
||||
* URL to send the statistics.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
protected $serverUrl = 'https://developer.joomla.org/stats/submit';
|
||||
|
||||
/**
|
||||
* Unique identifier for this site
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
protected $uniqueId;
|
||||
|
||||
/**
|
||||
* Listener for the `onAfterInitialise` event
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
public function onAfterInitialise()
|
||||
{
|
||||
if (!$this->getApplication()->isClient('administrator') || !$this->isAllowedUser()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isCaptiveMFA()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->isDebugEnabled() && !$this->isUpdateRequired()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->getApplication()->getInput()->getVar('tmpl') === 'component') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load plugin language files only when needed (ex: they are not needed in site client).
|
||||
$this->loadLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for the `onAfterDispatch` event
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function onAfterDispatch()
|
||||
{
|
||||
if (!$this->getApplication()->isClient('administrator') || !$this->isAllowedUser()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isCaptiveMFA()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->isDebugEnabled() && !$this->isUpdateRequired()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->getApplication()->getInput()->getVar('tmpl') === 'component') {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->getApplication()->getDocument()->getType() !== 'html') {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->getApplication()->getDocument()->getWebAssetManager()
|
||||
->registerAndUseScript('plg_system_stats.message', 'plg_system_stats/stats-message.js', [], ['defer' => true], ['core']);
|
||||
}
|
||||
|
||||
/**
|
||||
* User selected to always send data
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @throws \Exception If user is not allowed.
|
||||
* @throws \RuntimeException If there is an error saving the params or sending the data.
|
||||
*/
|
||||
public function onAjaxSendAlways()
|
||||
{
|
||||
if (!$this->isAllowedUser() || !$this->isAjaxRequest()) {
|
||||
throw new \Exception($this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_ACCESS_DENIED'), 403);
|
||||
}
|
||||
|
||||
$this->params->set('mode', static::MODE_ALLOW_ALWAYS);
|
||||
|
||||
if (!$this->saveParams()) {
|
||||
throw new \RuntimeException('Unable to save plugin settings', 500);
|
||||
}
|
||||
|
||||
echo json_encode(['sent' => (int) $this->sendStats()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* User selected to never send data.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @throws \Exception If user is not allowed.
|
||||
* @throws \RuntimeException If there is an error saving the params.
|
||||
*/
|
||||
public function onAjaxSendNever()
|
||||
{
|
||||
if (!$this->isAllowedUser() || !$this->isAjaxRequest()) {
|
||||
throw new \Exception($this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_ACCESS_DENIED'), 403);
|
||||
}
|
||||
|
||||
$this->params->set('mode', static::MODE_ALLOW_NEVER);
|
||||
|
||||
if (!$this->saveParams()) {
|
||||
throw new \RuntimeException('Unable to save plugin settings', 500);
|
||||
}
|
||||
|
||||
if (!$this->disablePlugin()) {
|
||||
throw new \RuntimeException('Unable to disable the statistics plugin', 500);
|
||||
}
|
||||
|
||||
echo json_encode(['sent' => 0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the stats to the server.
|
||||
* On first load | on demand mode it will show a message asking users to select mode.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @throws \Exception If user is not allowed.
|
||||
* @throws \RuntimeException If there is an error saving the params, disabling the plugin or sending the data.
|
||||
*/
|
||||
public function onAjaxSendStats()
|
||||
{
|
||||
if (!$this->isAllowedUser() || !$this->isAjaxRequest()) {
|
||||
throw new \Exception($this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_ACCESS_DENIED'), 403);
|
||||
}
|
||||
|
||||
// User has not selected the mode. Show message.
|
||||
if ((int) $this->params->get('mode') !== static::MODE_ALLOW_ALWAYS) {
|
||||
$data = [
|
||||
'sent' => 0,
|
||||
'html' => $this->getRenderer('message')->render($this->getLayoutData()),
|
||||
];
|
||||
|
||||
echo json_encode($data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->saveParams()) {
|
||||
throw new \RuntimeException('Unable to save plugin settings', 500);
|
||||
}
|
||||
|
||||
echo json_encode(['sent' => (int) $this->sendStats()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data through events
|
||||
*
|
||||
* @param string $context Context where this will be called from
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
public function onGetStatsData($context)
|
||||
{
|
||||
return $this->getStatsData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug a layout of this plugin
|
||||
*
|
||||
* @param string $layoutId Layout identifier
|
||||
* @param array $data Optional data for the layout
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
public function debug($layoutId, $data = [])
|
||||
{
|
||||
$data = array_merge($this->getLayoutData(), $data);
|
||||
|
||||
return $this->getRenderer($layoutId)->debug($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data for the layout
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
private function getLayoutData()
|
||||
{
|
||||
return [
|
||||
'plugin' => $this,
|
||||
'pluginParams' => $this->params,
|
||||
'statsData' => $this->getStatsData(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the layout paths
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
private function getLayoutPaths()
|
||||
{
|
||||
$template = $this->getApplication()->getTemplate();
|
||||
|
||||
return [
|
||||
JPATH_ADMINISTRATOR . '/templates/' . $template . '/html/layouts/plugins/' . $this->_type . '/' . $this->_name,
|
||||
JPATH_PLUGINS . '/' . $this->_type . '/' . $this->_name . '/layouts',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin renderer
|
||||
*
|
||||
* @param string $layoutId Layout identifier
|
||||
*
|
||||
* @return \Joomla\CMS\Layout\LayoutInterface
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
private function getRenderer($layoutId = 'default')
|
||||
{
|
||||
$renderer = new FileLayout($layoutId);
|
||||
|
||||
$renderer->setIncludePaths($this->getLayoutPaths());
|
||||
|
||||
return $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data that will be sent to the stats server.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
private function getStatsData()
|
||||
{
|
||||
$data = [
|
||||
'unique_id' => $this->getUniqueId(),
|
||||
'php_version' => PHP_VERSION,
|
||||
'db_type' => $this->getDatabase()->name,
|
||||
'db_version' => $this->getDatabase()->getVersion(),
|
||||
'cms_version' => JVERSION,
|
||||
'server_os' => php_uname('s') . ' ' . php_uname('r'),
|
||||
];
|
||||
|
||||
// Check if we have a MariaDB version string and extract the proper version from it
|
||||
if (preg_match('/^(?:5\.5\.5-)?(mariadb-)?(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)/i', $data['db_version'], $versionParts)) {
|
||||
$data['db_version'] = $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unique id. Generates one if none is set.
|
||||
*
|
||||
* @return integer
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
private function getUniqueId()
|
||||
{
|
||||
if (null === $this->uniqueId) {
|
||||
$this->uniqueId = $this->params->get('unique_id', hash('sha1', UserHelper::genRandomPassword(28) . time()));
|
||||
}
|
||||
|
||||
return $this->uniqueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current user is allowed to send the data
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
private function isAllowedUser()
|
||||
{
|
||||
return $this->getApplication()->getIdentity() && $this->getApplication()->getIdentity()->authorise('core.admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the debug is enabled
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
private function isDebugEnabled()
|
||||
{
|
||||
return \defined('PLG_SYSTEM_STATS_DEBUG');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if last_run + interval > now
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
private function isUpdateRequired()
|
||||
{
|
||||
$last = (int) $this->params->get('lastrun', 0);
|
||||
$interval = (int) $this->params->get('interval', 12);
|
||||
$mode = (int) $this->params->get('mode', 0);
|
||||
|
||||
if ($mode === static::MODE_ALLOW_NEVER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Never updated or debug enabled
|
||||
if (!$last || $this->isDebugEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return abs(time() - $last) > $interval * 3600;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check valid AJAX request
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
private function isAjaxRequest()
|
||||
{
|
||||
return strtolower($this->getApplication()->getInput()->server->get('HTTP_X_REQUESTED_WITH', '')) === 'xmlhttprequest';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a layout of this plugin
|
||||
*
|
||||
* @param string $layoutId Layout identifier
|
||||
* @param array $data Optional data for the layout
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
public function render($layoutId, $data = [])
|
||||
{
|
||||
$data = array_merge($this->getLayoutData(), $data);
|
||||
|
||||
return $this->getRenderer($layoutId)->render($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the plugin parameters
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
private function saveParams()
|
||||
{
|
||||
// Update params
|
||||
$this->params->set('lastrun', time());
|
||||
$this->params->set('unique_id', $this->getUniqueId());
|
||||
$interval = (int) $this->params->get('interval', 12);
|
||||
$this->params->set('interval', $interval ?: 12);
|
||||
|
||||
$paramsJson = $this->params->toString('JSON');
|
||||
$db = $this->getDatabase();
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__extensions'))
|
||||
->set($db->quoteName('params') . ' = :params')
|
||||
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
|
||||
->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('stats'))
|
||||
->bind(':params', $paramsJson);
|
||||
|
||||
try {
|
||||
// Lock the tables to prevent multiple plugin executions causing a race condition
|
||||
$db->lockTable('#__extensions');
|
||||
} catch (\Exception $e) {
|
||||
// If we can't lock the tables it's too risky to continue execution
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Update the plugin parameters
|
||||
$result = $db->setQuery($query)->execute();
|
||||
|
||||
$this->clearCacheGroups(['com_plugins']);
|
||||
} catch (\Exception $exc) {
|
||||
// If we failed to execute
|
||||
$db->unlockTables();
|
||||
$result = false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Unlock the tables after writing
|
||||
$db->unlockTables();
|
||||
} catch (\Exception $e) {
|
||||
// If we can't lock the tables assume we have somehow failed
|
||||
$result = false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the stats to the stats server
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @throws \RuntimeException If there is an error sending the data and debug mode enabled.
|
||||
*/
|
||||
private function sendStats()
|
||||
{
|
||||
$error = false;
|
||||
|
||||
try {
|
||||
// Don't let the request take longer than 2 seconds to avoid page timeout issues
|
||||
$response = HttpFactory::getHttp()->post($this->serverUrl, $this->getStatsData(), [], 2);
|
||||
|
||||
if (!$response) {
|
||||
$error = 'Could not send site statistics to remote server: No response';
|
||||
} elseif ($response->code !== 200) {
|
||||
$data = json_decode($response->body);
|
||||
|
||||
$error = 'Could not send site statistics to remote server: ' . $data->message;
|
||||
}
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
// There was an error sending stats. Should we do anything?
|
||||
$error = 'Could not send site statistics to remote server: ' . $e->getMessage();
|
||||
} catch (\RuntimeException $e) {
|
||||
// There was an error connecting to the server or in the post request
|
||||
$error = 'Could not connect to statistics server: ' . $e->getMessage();
|
||||
} catch (\Exception $e) {
|
||||
// An unexpected error in processing; don't let this failure kill the site
|
||||
$error = 'Unexpected error connecting to statistics server: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
if ($error !== false) {
|
||||
// Log any errors if logging enabled.
|
||||
Log::add($error, Log::WARNING, 'jerror');
|
||||
|
||||
// If Stats debug mode enabled, or Global Debug mode enabled, show error to the user.
|
||||
if ($this->isDebugEnabled() || $this->getApplication()->get('debug')) {
|
||||
throw new \RuntimeException($error, 500);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears cache groups. We use it to clear the plugins cache after we update the last run timestamp.
|
||||
*
|
||||
* @param array $clearGroups The cache groups to clean
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
private function clearCacheGroups(array $clearGroups)
|
||||
{
|
||||
foreach ($clearGroups as $group) {
|
||||
try {
|
||||
$options = [
|
||||
'defaultgroup' => $group,
|
||||
'cachebase' => $this->getApplication()->get('cache_path', JPATH_CACHE),
|
||||
];
|
||||
|
||||
$cache = Cache::getInstance('callback', $options);
|
||||
$cache->clean();
|
||||
} catch (\Exception $e) {
|
||||
// Ignore it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable this plugin, if user selects once or never, to stop Joomla loading the plugin on every page load and
|
||||
* therefore regaining a tiny bit of performance
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function disablePlugin()
|
||||
{
|
||||
$db = $this->getDatabase();
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__extensions'))
|
||||
->set($db->quoteName('enabled') . ' = 0')
|
||||
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
|
||||
->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('stats'));
|
||||
|
||||
try {
|
||||
// Lock the tables to prevent multiple plugin executions causing a race condition
|
||||
$db->lockTable('#__extensions');
|
||||
} catch (\Exception $e) {
|
||||
// If we can't lock the tables it's too risky to continue execution
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Update the plugin parameters
|
||||
$result = $db->setQuery($query)->execute();
|
||||
|
||||
$this->clearCacheGroups(['com_plugins']);
|
||||
} catch (\Exception $exc) {
|
||||
// If we failed to execute
|
||||
$db->unlockTables();
|
||||
$result = false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Unlock the tables after writing
|
||||
$db->unlockTables();
|
||||
} catch (\Exception $e) {
|
||||
// If we can't lock the tables assume we have somehow failed
|
||||
$result = false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we in a Multi-factor Authentication page?
|
||||
*
|
||||
* @return bool
|
||||
* @since 4.2.1
|
||||
*/
|
||||
private function isCaptiveMFA(): bool
|
||||
{
|
||||
return method_exists($this->getApplication(), 'isMultiFactorAuthenticationPage')
|
||||
&& $this->getApplication()->isMultiFactorAuthenticationPage(true);
|
||||
}
|
||||
}
|
||||
44
plugins/system/stats/src/Field/AbstractStatsField.php
Normal file
44
plugins/system/stats/src/Field/AbstractStatsField.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage System.stats
|
||||
*
|
||||
* @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Plugin\System\Stats\Field;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Form\FormField;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Base field for the Stats Plugin.
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
abstract class AbstractStatsField extends FormField
|
||||
{
|
||||
/**
|
||||
* Get the layouts paths
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
protected function getLayoutPaths()
|
||||
{
|
||||
$template = Factory::getApplication()->getTemplate();
|
||||
|
||||
return [
|
||||
JPATH_ADMINISTRATOR . '/templates/' . $template . '/html/layouts/plugins/system/stats',
|
||||
JPATH_PLUGINS . '/system/stats/layouts',
|
||||
JPATH_SITE . '/layouts',
|
||||
];
|
||||
}
|
||||
}
|
||||
62
plugins/system/stats/src/Field/DataField.php
Normal file
62
plugins/system/stats/src/Field/DataField.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage System.stats
|
||||
*
|
||||
* @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Plugin\System\Stats\Field;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Unique ID Field class for the Stats Plugin.
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
class DataField extends AbstractStatsField
|
||||
{
|
||||
/**
|
||||
* The form field type.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.5
|
||||
*/
|
||||
protected $type = 'Data';
|
||||
|
||||
/**
|
||||
* Name of the layout being used to render the field
|
||||
*
|
||||
* @var string
|
||||
* @since 3.5
|
||||
*/
|
||||
protected $layout = 'field.data';
|
||||
|
||||
/**
|
||||
* Method to get the data to be passed to the layout for rendering.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
protected function getLayoutData()
|
||||
{
|
||||
$data = parent::getLayoutData();
|
||||
|
||||
PluginHelper::importPlugin('system', 'stats');
|
||||
|
||||
$result = Factory::getApplication()->triggerEvent('onGetStatsData', ['stats.field.data']);
|
||||
|
||||
$data['statsData'] = $result ? reset($result) : [];
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
39
plugins/system/stats/src/Field/UniqueidField.php
Normal file
39
plugins/system/stats/src/Field/UniqueidField.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage System.stats
|
||||
*
|
||||
* @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Plugin\System\Stats\Field;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Unique ID Field class for the Stats Plugin.
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
class UniqueidField extends AbstractStatsField
|
||||
{
|
||||
/**
|
||||
* The form field type.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.5
|
||||
*/
|
||||
protected $type = 'Uniqueid';
|
||||
|
||||
/**
|
||||
* Name of the layout being used to render the field
|
||||
*
|
||||
* @var string
|
||||
* @since 3.5
|
||||
*/
|
||||
protected $layout = 'field.uniqueid';
|
||||
}
|
||||
65
plugins/system/stats/stats.xml
Normal file
65
plugins/system/stats/stats.xml
Normal file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="plugin" group="system" method="upgrade">
|
||||
<name>plg_system_stats</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2013-11</creationDate>
|
||||
<copyright>(C) 2013 Open Source Matters, Inc.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<authorEmail>admin@joomla.org</authorEmail>
|
||||
<authorUrl>www.joomla.org</authorUrl>
|
||||
<version>3.5.0</version>
|
||||
<description>PLG_SYSTEM_STATS_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Plugin\System\Stats</namespace>
|
||||
<files>
|
||||
<folder>layouts</folder>
|
||||
<folder plugin="stats">services</folder>
|
||||
<folder>src</folder>
|
||||
</files>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_system_stats.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_system_stats.sys.ini</language>
|
||||
</languages>
|
||||
<config>
|
||||
<fields name="params">
|
||||
<fieldset name="basic" addfieldprefix="Joomla\Plugin\System\Stats\Field">
|
||||
<field
|
||||
name="data"
|
||||
type="data"
|
||||
label=""
|
||||
/>
|
||||
|
||||
<field
|
||||
name="unique_id"
|
||||
type="uniqueid"
|
||||
label="PLG_SYSTEM_STATS_UNIQUE_ID_LABEL"
|
||||
/>
|
||||
|
||||
<field
|
||||
name="interval"
|
||||
type="number"
|
||||
label="PLG_SYSTEM_STATS_INTERVAL_LABEL"
|
||||
filter="integer"
|
||||
default="12"
|
||||
/>
|
||||
|
||||
<field
|
||||
name="mode"
|
||||
type="list"
|
||||
label="PLG_SYSTEM_STATS_MODE_LABEL"
|
||||
default="1"
|
||||
validate="options"
|
||||
>
|
||||
<option value="2">PLG_SYSTEM_STATS_MODE_OPTION_ON_DEMAND</option>
|
||||
<option value="1">PLG_SYSTEM_STATS_MODE_OPTION_ALWAYS_SEND</option>
|
||||
<option value="3">PLG_SYSTEM_STATS_MODE_OPTION_NEVER_SEND</option>
|
||||
</field>
|
||||
|
||||
<field
|
||||
name="lastrun"
|
||||
type="hidden"
|
||||
default="0"
|
||||
/>
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
Reference in New Issue
Block a user