first commit
This commit is contained in:
29
administrator/components/com_actionlogs/actionlogs.xml
Normal file
29
administrator/components/com_actionlogs/actionlogs.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="component" method="upgrade">
|
||||
<name>com_actionlogs</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2018-05</creationDate>
|
||||
<copyright>(C) 2018 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.9.0</version>
|
||||
<description>COM_ACTIONLOGS_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Component\Actionlogs</namespace>
|
||||
<administration>
|
||||
<menu>COM_ACTIONLOGS</menu>
|
||||
<files folder="admin">
|
||||
<file>actionlogs.xml</file>
|
||||
<file>config.xml</file>
|
||||
<folder>forms</folder>
|
||||
<folder>layouts</folder>
|
||||
<folder>services</folder>
|
||||
<folder>src</folder>
|
||||
<folder>tmpl</folder>
|
||||
</files>
|
||||
<languages folder="admin">
|
||||
<language tag="en-GB">language/en-GB/com_actionlogs.ini</language>
|
||||
<language tag="en-GB">language/en-GB/com_actionlogs.sys.ini</language>
|
||||
</languages>
|
||||
</administration>
|
||||
</extension>
|
||||
74
administrator/components/com_actionlogs/config.xml
Normal file
74
administrator/components/com_actionlogs/config.xml
Normal file
@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config addfieldprefix="Joomla\Component\Actionlogs\Administrator\Field">
|
||||
<help key="User_Actions_Log:_Options"/>
|
||||
<inlinehelp button="show"/>
|
||||
<fieldset name="actionlogs" label="COM_ACTIONLOGS_OPTIONS">
|
||||
<field
|
||||
name="ip_logging"
|
||||
type="radio"
|
||||
label="COM_ACTIONLOGS_IP_LOGGING_LABEL"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
default="0"
|
||||
filter="integer"
|
||||
>
|
||||
<option value="0">JNO</option>
|
||||
<option value="1">JYES</option>
|
||||
</field>
|
||||
<field
|
||||
name="csv_delimiter"
|
||||
type="list"
|
||||
label="COM_ACTIONLOGS_CSV_DELIMITER_LABEL"
|
||||
default=","
|
||||
validate="options"
|
||||
>
|
||||
<option value=",">COM_ACTIONLOGS_COMMA</option>
|
||||
<option value=";">COM_ACTIONLOGS_SEMICOLON</option>
|
||||
</field>
|
||||
<field
|
||||
name="loggable_extensions"
|
||||
type="logtype"
|
||||
label="COM_ACTIONLOGS_LOG_EXTENSIONS_LABEL"
|
||||
multiple="true"
|
||||
layout="joomla.form.field.list-fancy-select"
|
||||
default="com_banners,com_cache,com_categories,com_checkin,com_config,com_contact,com_content,com_fields,com_installer,com_media,com_menus,com_messages,com_modules,com_newsfeeds,com_plugins,com_redirect,com_scheduler,com_tags,com_templates,com_users"
|
||||
/>
|
||||
<field
|
||||
name="loggable_api"
|
||||
type="radio"
|
||||
label="COM_ACTIONLOGS_API_LOGGING_LABEL"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
default="0"
|
||||
filter="integer"
|
||||
>
|
||||
<option value="0">JNO</option>
|
||||
<option value="1">JYES</option>
|
||||
</field>
|
||||
<field
|
||||
name="loggable_verbs"
|
||||
type="list"
|
||||
label="COM_ACTIONLOGS_LOG_VERBS_LABEL"
|
||||
multiple="true"
|
||||
showon="loggable_api:1"
|
||||
default="GET"
|
||||
validate="options"
|
||||
layout="joomla.form.field.list-fancy-select"
|
||||
>
|
||||
<option value="GET">COM_ACTIONLOGS_FIELD_VALUE_GET</option>
|
||||
<option value="POST">COM_ACTIONLOGS_FIELD_VALUE_POST</option>
|
||||
<option value="DELETE">COM_ACTIONLOGS_FIELD_VALUE_DELETE</option>
|
||||
<option value="PUT">COM_ACTIONLOGS_FIELD_VALUE_PUT</option>
|
||||
<option value="PATCH">COM_ACTIONLOGS_FIELD_VALUE_PATCH</option>
|
||||
</field>
|
||||
<field
|
||||
name="date_relative"
|
||||
type="radio"
|
||||
label="COM_ACTIONLOGS_DATE_RELATIVE_LABEL"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
default="1"
|
||||
filter="integer"
|
||||
>
|
||||
<option value="0">JHIDE</option>
|
||||
<option value="1">JSHOW</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</config>
|
||||
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form addfieldprefix="Joomla\Component\Actionlogs\Administrator\Field">
|
||||
<fields name="filter">
|
||||
<field
|
||||
name="search"
|
||||
type="text"
|
||||
inputmode="search"
|
||||
label="COM_ACTIONLOGS_FILTER_SEARCH_LABEL"
|
||||
description="COM_ACTIONLOGS_FILTER_SEARCH_DESC"
|
||||
hint="JSEARCH_FILTER"
|
||||
/>
|
||||
<field
|
||||
name="extension"
|
||||
type="extension"
|
||||
label="COM_ACTIONLOGS_EXTENSION"
|
||||
class="js-select-submit-on-change"
|
||||
>
|
||||
<option value="">COM_ACTIONLOGS_SELECT_EXTENSION</option>
|
||||
</field>
|
||||
<field
|
||||
name="dateRange"
|
||||
type="logsdaterange"
|
||||
label="COM_ACTIONLOGS_DATE"
|
||||
class="js-select-submit-on-change"
|
||||
>
|
||||
<option value="">COM_ACTIONLOGS_OPTION_FILTER_DATE</option>
|
||||
</field>
|
||||
<field
|
||||
name="user"
|
||||
type="logcreator"
|
||||
label="COM_ACTIONLOGS_NAME"
|
||||
class="js-select-submit-on-change"
|
||||
>
|
||||
<option value="">COM_ACTIONLOGS_SELECT_USER</option>
|
||||
</field>
|
||||
</fields>
|
||||
<fields name="list">
|
||||
<field
|
||||
name="fullordering"
|
||||
type="list"
|
||||
label="JGLOBAL_SORT_BY"
|
||||
class="js-select-submit-on-change"
|
||||
default="a.id DESC"
|
||||
validate="options"
|
||||
>
|
||||
<option value="">JGLOBAL_SORT_BY</option>
|
||||
<option value="a.message ASC">COM_ACTIONLOGS_ACTION_ASC</option>
|
||||
<option value="a.message DESC">COM_ACTIONLOGS_ACTION_DESC</option>
|
||||
<option value="a.extension ASC">COM_ACTIONLOGS_EXTENSION_ASC</option>
|
||||
<option value="a.extension DESC">COM_ACTIONLOGS_EXTENSION_DESC</option>
|
||||
<option value="a.log_date ASC">JDATE_ASC</option>
|
||||
<option value="a.log_date DESC">JDATE_DESC</option>
|
||||
<option value="a.user_id ASC">JGLOBAL_NAME_ASC</option>
|
||||
<option value="a.user_id DESC">JGLOBAL_NAME_DESC</option>
|
||||
<option value="a.id ASC">JGRID_HEADING_ID_ASC</option>
|
||||
<option value="a.id DESC">JGRID_HEADING_ID_DESC</option>
|
||||
</field>
|
||||
</fields>
|
||||
<fields name="list">
|
||||
<field
|
||||
name="limit"
|
||||
type="limitbox"
|
||||
label="JGLOBAL_LIST_LIMIT"
|
||||
class="input-mini js-select-submit-on-change"
|
||||
default="25"
|
||||
/>
|
||||
</fields>
|
||||
</form>
|
||||
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2019 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\Dispatcher\ComponentDispatcherFactoryInterface;
|
||||
use Joomla\CMS\Extension\ComponentInterface;
|
||||
use Joomla\CMS\Extension\MVCComponent;
|
||||
use Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory;
|
||||
use Joomla\CMS\Extension\Service\Provider\MVCFactory;
|
||||
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
|
||||
/**
|
||||
* The actionlogs service provider.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container->registerServiceProvider(new MVCFactory('\\Joomla\\Component\\Actionlogs'));
|
||||
$container->registerServiceProvider(new ComponentDispatcherFactory('\\Joomla\\Component\\Actionlogs'));
|
||||
|
||||
$container->set(
|
||||
ComponentInterface::class,
|
||||
function (Container $container) {
|
||||
$component = new MVCComponent($container->get(ComponentDispatcherFactoryInterface::class));
|
||||
|
||||
$component->setMVCFactory($container->get(MVCFactoryInterface::class));
|
||||
|
||||
return $component;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Actionlogs\Administrator\Controller;
|
||||
|
||||
use Joomla\CMS\Application\CMSApplication;
|
||||
use Joomla\CMS\Component\ComponentHelper;
|
||||
use Joomla\CMS\Date\Date;
|
||||
use Joomla\CMS\Event\ActionLog\AfterLogExportEvent;
|
||||
use Joomla\CMS\Input\Input;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\Controller\AdminController;
|
||||
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\Component\Actionlogs\Administrator\Helper\ActionlogsHelper;
|
||||
use Joomla\Component\Actionlogs\Administrator\Model\ActionlogsModel;
|
||||
use Joomla\Utilities\ArrayHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Actionlogs list controller class.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
class ActionlogsController extends AdminController
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $config An optional associative array of configuration settings.
|
||||
* Recognized key values include 'name', 'default_task', 'model_path', and
|
||||
* 'view_path' (this list is not meant to be comprehensive).
|
||||
* @param MVCFactoryInterface $factory The factory.
|
||||
* @param CMSApplication $app The Application for the dispatcher
|
||||
* @param Input $input Input
|
||||
*
|
||||
* @since 3.9.0
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($config = [], MVCFactoryInterface $factory = null, $app = null, $input = null)
|
||||
{
|
||||
parent::__construct($config, $factory, $app, $input);
|
||||
|
||||
$this->registerTask('exportSelectedLogs', 'exportLogs');
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to export logs
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.9.0
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function exportLogs()
|
||||
{
|
||||
// Check for request forgeries.
|
||||
$this->checkToken();
|
||||
|
||||
$task = $this->getTask();
|
||||
|
||||
$pks = [];
|
||||
|
||||
if ($task == 'exportSelectedLogs') {
|
||||
// Get selected logs
|
||||
$pks = ArrayHelper::toInteger(explode(',', $this->input->post->getString('cids')));
|
||||
}
|
||||
|
||||
/** @var ActionlogsModel $model */
|
||||
$model = $this->getModel();
|
||||
|
||||
// Get the logs data
|
||||
$data = $model->getLogDataAsIterator($pks);
|
||||
|
||||
if (\count($data)) {
|
||||
try {
|
||||
$rows = ActionlogsHelper::getCsvData($data);
|
||||
} catch (\InvalidArgumentException $exception) {
|
||||
$this->setMessage(Text::_('COM_ACTIONLOGS_ERROR_COULD_NOT_EXPORT_DATA'), 'error');
|
||||
$this->setRedirect(Route::_('index.php?option=com_actionlogs&view=actionlogs', false));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Destroy the iterator now
|
||||
unset($data);
|
||||
|
||||
$date = new Date('now', new \DateTimeZone('UTC'));
|
||||
$filename = 'logs_' . $date->format('Y-m-d_His_T');
|
||||
|
||||
$csvDelimiter = ComponentHelper::getComponent('com_actionlogs')->getParams()->get('csv_delimiter', ',');
|
||||
|
||||
$this->app->setHeader('Content-Type', 'application/csv', true)
|
||||
->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '.csv"', true)
|
||||
->setHeader('Cache-Control', 'must-revalidate', true)
|
||||
->sendHeaders();
|
||||
|
||||
$output = fopen("php://output", "w");
|
||||
|
||||
foreach ($rows as $row) {
|
||||
fputcsv($output, $row, $csvDelimiter);
|
||||
}
|
||||
|
||||
fclose($output);
|
||||
$this->getDispatcher()->dispatch('onAfterLogExport', new AfterLogExportEvent('onAfterLogExport'));
|
||||
$this->app->close();
|
||||
} else {
|
||||
$this->setMessage(Text::_('COM_ACTIONLOGS_NO_LOGS_TO_EXPORT'));
|
||||
$this->setRedirect(Route::_('index.php?option=com_actionlogs&view=actionlogs', false));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get a model object, loading it if required.
|
||||
*
|
||||
* @param string $name The model name. Optional.
|
||||
* @param string $prefix The class prefix. Optional.
|
||||
* @param array $config Configuration array for model. Optional.
|
||||
*
|
||||
* @return object The model.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public function getModel($name = 'Actionlogs', $prefix = 'Administrator', $config = ['ignore_request' => true])
|
||||
{
|
||||
// Return the model
|
||||
return parent::getModel($name, $prefix, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean out the logs
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public function purge()
|
||||
{
|
||||
// Check for request forgeries.
|
||||
$this->checkToken();
|
||||
|
||||
$model = $this->getModel();
|
||||
|
||||
if ($model->purge()) {
|
||||
$message = Text::_('COM_ACTIONLOGS_PURGE_SUCCESS');
|
||||
} else {
|
||||
$message = Text::_('COM_ACTIONLOGS_PURGE_FAIL');
|
||||
}
|
||||
|
||||
$this->setRedirect(Route::_('index.php?option=com_actionlogs&view=actionlogs', false), $message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Actionlogs\Administrator\Controller;
|
||||
|
||||
use Joomla\CMS\MVC\Controller\BaseController;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Plugins display controller.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class DisplayController extends BaseController
|
||||
{
|
||||
/**
|
||||
* The default view.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.6
|
||||
*/
|
||||
protected $default_view = 'actionlogs';
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Actionlogs\Administrator\Dispatcher;
|
||||
|
||||
use Joomla\CMS\Access\Exception\NotAllowed;
|
||||
use Joomla\CMS\Dispatcher\ComponentDispatcher;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* ComponentDispatcher class for com_actionlogs
|
||||
*
|
||||
* @since 4.2.7
|
||||
*/
|
||||
class Dispatcher extends ComponentDispatcher
|
||||
{
|
||||
/**
|
||||
* Method to check component access permission
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.2.7
|
||||
*/
|
||||
protected function checkAccess()
|
||||
{
|
||||
$user = $this->app->getIdentity();
|
||||
|
||||
// Access check
|
||||
if (!$user->authorise('core.admin')) {
|
||||
throw new NotAllowed($this->app->getLanguage()->_('JERROR_ALERTNOAUTHOR'), 403);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Actionlogs\Administrator\Field;
|
||||
|
||||
use Joomla\CMS\Form\Field\ListField;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\Component\Actionlogs\Administrator\Helper\ActionlogsHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Field to load a list of all extensions that have logged actions
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
class ExtensionField extends ListField
|
||||
{
|
||||
/**
|
||||
* The form field type.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected $type = 'extension';
|
||||
|
||||
/**
|
||||
* Method to get the options to populate list
|
||||
*
|
||||
* @return array The field option objects.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
$db = $this->getDatabase();
|
||||
$query = $db->getQuery(true)
|
||||
->select('DISTINCT ' . $db->quoteName('extension'))
|
||||
->from($db->quoteName('#__action_logs'))
|
||||
->order($db->quoteName('extension'));
|
||||
|
||||
$db->setQuery($query);
|
||||
$context = $db->loadColumn();
|
||||
|
||||
$options = [];
|
||||
|
||||
if (\count($context) > 0) {
|
||||
foreach ($context as $item) {
|
||||
$extensions[] = strtok($item, '.');
|
||||
}
|
||||
|
||||
$extensions = array_unique($extensions);
|
||||
|
||||
foreach ($extensions as $extension) {
|
||||
ActionlogsHelper::loadTranslationFiles($extension);
|
||||
$options[] = HTMLHelper::_('select.option', $extension, Text::_($extension));
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge(parent::getOptions(), $options);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Actionlogs\Administrator\Field;
|
||||
|
||||
use Joomla\CMS\Form\Field\ListField;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Field to load a list of all users that have logged actions
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
class LogcreatorField extends ListField
|
||||
{
|
||||
/**
|
||||
* Cached array of the category items.
|
||||
*
|
||||
* @var array
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected static $options = [];
|
||||
|
||||
/**
|
||||
* The form field type.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected $type = 'LogCreator';
|
||||
|
||||
/**
|
||||
* Method to get the options to populate list
|
||||
*
|
||||
* @return array The field option objects.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
// Accepted modifiers
|
||||
$hash = md5($this->element);
|
||||
|
||||
if (!isset(static::$options[$hash])) {
|
||||
static::$options[$hash] = parent::getOptions();
|
||||
|
||||
$db = $this->getDatabase();
|
||||
|
||||
// Construct the query
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName('u.id', 'value'))
|
||||
->select($db->quoteName('u.username', 'text'))
|
||||
->from($db->quoteName('#__users', 'u'))
|
||||
->join('INNER', $db->quoteName('#__action_logs', 'c') . ' ON ' . $db->quoteName('c.user_id') . ' = ' . $db->quoteName('u.id'))
|
||||
->group($db->quoteName('u.id'))
|
||||
->group($db->quoteName('u.username'))
|
||||
->order($db->quoteName('u.username'));
|
||||
|
||||
// Setup the query
|
||||
$db->setQuery($query);
|
||||
|
||||
// Return the result
|
||||
if ($options = $db->loadObjectList()) {
|
||||
static::$options[$hash] = array_merge(static::$options[$hash], $options);
|
||||
}
|
||||
}
|
||||
|
||||
return static::$options[$hash];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Actionlogs\Administrator\Field;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Form\Field\PredefinedlistField;
|
||||
use Joomla\CMS\Form\Form;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Field to show a list of range dates to sort with
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
class LogsdaterangeField extends PredefinedlistField
|
||||
{
|
||||
/**
|
||||
* The form field type.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected $type = 'logsdaterange';
|
||||
|
||||
/**
|
||||
* Available options
|
||||
*
|
||||
* @var array
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected $predefinedOptions = [
|
||||
'today' => 'COM_ACTIONLOGS_OPTION_RANGE_TODAY',
|
||||
'past_week' => 'COM_ACTIONLOGS_OPTION_RANGE_PAST_WEEK',
|
||||
'past_1month' => 'COM_ACTIONLOGS_OPTION_RANGE_PAST_1MONTH',
|
||||
'past_3month' => 'COM_ACTIONLOGS_OPTION_RANGE_PAST_3MONTH',
|
||||
'past_6month' => 'COM_ACTIONLOGS_OPTION_RANGE_PAST_6MONTH',
|
||||
'past_year' => 'COM_ACTIONLOGS_OPTION_RANGE_PAST_YEAR',
|
||||
];
|
||||
|
||||
/**
|
||||
* Method to instantiate the form field object.
|
||||
*
|
||||
* @param Form $form The form to attach to the form field object.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public function __construct($form = null)
|
||||
{
|
||||
parent::__construct($form);
|
||||
|
||||
// Load the required language
|
||||
$lang = Factory::getLanguage();
|
||||
$lang->load('com_actionlogs', JPATH_ADMINISTRATOR);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Actionlogs\Administrator\Field;
|
||||
|
||||
use Joomla\CMS\Application\ApplicationHelper;
|
||||
use Joomla\CMS\Form\Field\ListField;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\Component\Actionlogs\Administrator\Helper\ActionlogsHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Field to load a list of all extensions that have logged actions
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
class LogtypeField extends ListField
|
||||
{
|
||||
/**
|
||||
* The form field type.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected $type = 'LogType';
|
||||
|
||||
/**
|
||||
* Method to get the field options.
|
||||
*
|
||||
* @return array The field option objects.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
$db = $this->getDatabase();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName('extension'))
|
||||
->from($db->quoteName('#__action_logs_extensions'));
|
||||
|
||||
$extensions = $db->setQuery($query)->loadColumn();
|
||||
|
||||
$options = [];
|
||||
|
||||
foreach ($extensions as $extension) {
|
||||
ActionlogsHelper::loadTranslationFiles($extension);
|
||||
$extensionName = Text::_($extension);
|
||||
$options[ApplicationHelper::stringURLSafe($extensionName) . '_' . $extension] = HTMLHelper::_('select.option', $extension, $extensionName);
|
||||
}
|
||||
|
||||
ksort($options);
|
||||
|
||||
return array_merge(parent::getOptions(), array_values($options));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Actionlogs\Administrator\Field;
|
||||
|
||||
use Joomla\CMS\Form\FormField;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Router\Route;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Information field.
|
||||
*
|
||||
* @since 3.9.2
|
||||
*/
|
||||
class PlugininfoField extends FormField
|
||||
{
|
||||
/**
|
||||
* The form field type.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.9.2
|
||||
*/
|
||||
protected $type = 'PluginInfo';
|
||||
|
||||
/**
|
||||
* Method to get the field input markup.
|
||||
*
|
||||
* @return string The field input markup.
|
||||
*
|
||||
* @since 3.9.2
|
||||
*/
|
||||
protected function getInput()
|
||||
{
|
||||
$db = $this->getDatabase();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName('extension_id'))
|
||||
->from($db->quoteName('#__extensions'))
|
||||
->where($db->quoteName('folder') . ' = ' . $db->quote('actionlog'))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('joomla'));
|
||||
$db->setQuery($query);
|
||||
|
||||
$result = (int) $db->loadResult();
|
||||
|
||||
$link = HTMLHelper::_(
|
||||
'link',
|
||||
Route::_('index.php?option=com_plugins&task=plugin.edit&extension_id=' . $result),
|
||||
Text::_('PLG_SYSTEM_ACTIONLOGS_JOOMLA_ACTIONLOG_DISABLED'),
|
||||
['class' => 'alert-link']
|
||||
);
|
||||
|
||||
return '<div class="alert alert-info">'
|
||||
. '<span class="icon-info-circle" aria-hidden="true"></span><span class="visually-hidden">'
|
||||
. Text::_('INFO')
|
||||
. '</span>'
|
||||
. Text::sprintf('PLG_SYSTEM_ACTIONLOGS_JOOMLA_ACTIONLOG_DISABLED_REDIRECT', $link)
|
||||
. '</div>';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,352 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Actionlogs\Administrator\Helper;
|
||||
|
||||
use Joomla\CMS\Date\Date;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Object\CMSObject;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\Filesystem\Path;
|
||||
use Joomla\String\StringHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Actionlogs component helper.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
class ActionlogsHelper
|
||||
{
|
||||
/**
|
||||
* Array of characters starting a formula
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @since 3.9.7
|
||||
*/
|
||||
private static $characters = ['=', '+', '-', '@'];
|
||||
|
||||
/**
|
||||
* Method to convert logs objects array to an iterable type for use with a CSV export
|
||||
*
|
||||
* @param array|\Traversable $data The logs data objects to be exported
|
||||
*
|
||||
* @return \Generator
|
||||
*
|
||||
* @since 3.9.0
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function getCsvData($data): \Generator
|
||||
{
|
||||
if (!is_iterable($data)) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf(
|
||||
'%s() requires an array or object implementing the Traversable interface, a %s was given.',
|
||||
__METHOD__,
|
||||
\is_object($data) ? \get_class($data) : \gettype($data)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$disabledText = Text::_('COM_ACTIONLOGS_DISABLED');
|
||||
|
||||
// Header row
|
||||
yield ['Id', 'Action', 'Extension', 'Date', 'Name', 'IP Address'];
|
||||
|
||||
foreach ($data as $log) {
|
||||
$extension = strtok($log->extension, '.');
|
||||
|
||||
static::loadTranslationFiles($extension);
|
||||
|
||||
yield [
|
||||
'id' => $log->id,
|
||||
'message' => self::escapeCsvFormula(strip_tags(static::getHumanReadableLogMessage($log, false))),
|
||||
'extension' => self::escapeCsvFormula(Text::_($extension)),
|
||||
'date' => (new Date($log->log_date, new \DateTimeZone('UTC')))->format('Y-m-d H:i:s T'),
|
||||
'name' => self::escapeCsvFormula($log->name),
|
||||
'ip_address' => self::escapeCsvFormula($log->ip_address === 'COM_ACTIONLOGS_DISABLED' ? $disabledText : $log->ip_address),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the translation files for an extension
|
||||
*
|
||||
* @param string $extension Extension name
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public static function loadTranslationFiles($extension)
|
||||
{
|
||||
static $cache = [];
|
||||
$extension = strtolower($extension);
|
||||
|
||||
if (isset($cache[$extension])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$lang = Factory::getLanguage();
|
||||
$source = '';
|
||||
|
||||
switch (substr($extension, 0, 3)) {
|
||||
case 'com':
|
||||
default:
|
||||
$source = JPATH_ADMINISTRATOR . '/components/' . $extension;
|
||||
break;
|
||||
|
||||
case 'lib':
|
||||
$source = JPATH_LIBRARIES . '/' . substr($extension, 4);
|
||||
break;
|
||||
|
||||
case 'mod':
|
||||
$source = JPATH_SITE . '/modules/' . $extension;
|
||||
break;
|
||||
|
||||
case 'plg':
|
||||
$parts = explode('_', $extension, 3);
|
||||
|
||||
if (\count($parts) > 2) {
|
||||
$source = JPATH_PLUGINS . '/' . $parts[1] . '/' . $parts[2];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'pkg':
|
||||
$source = JPATH_SITE;
|
||||
break;
|
||||
|
||||
case 'tpl':
|
||||
$source = JPATH_BASE . '/templates/' . substr($extension, 4);
|
||||
break;
|
||||
}
|
||||
|
||||
$lang->load($extension, JPATH_ADMINISTRATOR)
|
||||
|| $lang->load($extension, $source);
|
||||
|
||||
if (!$lang->hasKey(strtoupper($extension))) {
|
||||
$lang->load($extension . '.sys', JPATH_ADMINISTRATOR)
|
||||
|| $lang->load($extension . '.sys', $source);
|
||||
}
|
||||
|
||||
$cache[$extension] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parameters to be
|
||||
*
|
||||
* @param string $context The context of the content
|
||||
*
|
||||
* @return mixed An object contains content type parameters, or null if not found
|
||||
*
|
||||
* @since 3.9.0
|
||||
*
|
||||
* @deprecated 4.3 will be removed in 6.0
|
||||
* Use the action log config model instead
|
||||
* Example: Factory::getApplication()->bootComponent('actionlogs')->getMVCFactory()
|
||||
* ->createModel('ActionlogConfig', 'Administrator')->getLogContentTypeParams($context);
|
||||
*/
|
||||
public static function getLogContentTypeParams($context)
|
||||
{
|
||||
return Factory::getApplication()->bootComponent('actionlogs')->getMVCFactory()
|
||||
->createModel('ActionlogConfig', 'Administrator')->getLogContentTypeParams($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get human readable log message for a User Action Log
|
||||
*
|
||||
* @param \stdClass $log A User Action log message record
|
||||
* @param boolean $generateLinks Flag to disable link generation when creating a message
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public static function getHumanReadableLogMessage($log, $generateLinks = true)
|
||||
{
|
||||
static $links = [];
|
||||
|
||||
$message = Text::_($log->message_language_key);
|
||||
$messageData = json_decode($log->message, true);
|
||||
|
||||
// Special handling for translation extension name
|
||||
if (isset($messageData['extension_name'])) {
|
||||
static::loadTranslationFiles($messageData['extension_name']);
|
||||
$messageData['extension_name'] = Text::_($messageData['extension_name']);
|
||||
}
|
||||
|
||||
// Translating application
|
||||
if (isset($messageData['app'])) {
|
||||
$messageData['app'] = Text::_($messageData['app']);
|
||||
}
|
||||
|
||||
// Translating type
|
||||
if (isset($messageData['type'])) {
|
||||
$messageData['type'] = Text::_($messageData['type']);
|
||||
}
|
||||
|
||||
$linkMode = Factory::getApplication()->get('force_ssl', 0) >= 1 ? Route::TLS_FORCE : Route::TLS_IGNORE;
|
||||
|
||||
foreach ($messageData as $key => $value) {
|
||||
// Escape any markup in the values to prevent XSS attacks
|
||||
$value = $value !== null ? htmlspecialchars($value, ENT_QUOTES, 'UTF-8') : '';
|
||||
|
||||
// Convert relative url to absolute url so that it is clickable in action logs notification email
|
||||
if ($generateLinks && StringHelper::strpos($value, 'index.php?') === 0) {
|
||||
if (!isset($links[$value])) {
|
||||
$links[$value] = Route::link('administrator', $value, false, $linkMode, true);
|
||||
}
|
||||
|
||||
$value = $links[$value];
|
||||
}
|
||||
|
||||
$message = str_replace('{' . $key . '}', $value, $message);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get link to an item of given content type
|
||||
*
|
||||
* @param string $component
|
||||
* @param string $contentType
|
||||
* @param integer $id
|
||||
* @param string $urlVar
|
||||
* @param CMSObject $object
|
||||
*
|
||||
* @return string Link to the content item
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public static function getContentTypeLink($component, $contentType, $id, $urlVar = 'id', $object = null)
|
||||
{
|
||||
// Try to find the component helper.
|
||||
$eName = str_replace('com_', '', $component);
|
||||
$file = Path::clean(JPATH_ADMINISTRATOR . '/components/' . $component . '/helpers/' . $eName . '.php');
|
||||
|
||||
if (file_exists($file)) {
|
||||
$prefix = ucfirst(str_replace('com_', '', $component));
|
||||
$cName = $prefix . 'Helper';
|
||||
|
||||
\JLoader::register($cName, $file);
|
||||
|
||||
if (class_exists($cName) && \is_callable([$cName, 'getContentTypeLink'])) {
|
||||
return $cName::getContentTypeLink($contentType, $id, $object);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($urlVar)) {
|
||||
$urlVar = 'id';
|
||||
}
|
||||
|
||||
// Return default link to avoid having to implement getContentTypeLink in most of our components
|
||||
return 'index.php?option=' . $component . '&task=' . $contentType . '.edit&' . $urlVar . '=' . $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load both enabled and disabled actionlog plugins language file.
|
||||
*
|
||||
* It is used to make sure actions log is displayed properly instead of only language items displayed when a plugin is disabled.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public static function loadActionLogPluginsLanguage()
|
||||
{
|
||||
$lang = Factory::getLanguage();
|
||||
$db = Factory::getDbo();
|
||||
|
||||
// Get all (both enabled and disabled) actionlog plugins
|
||||
$query = $db->getQuery(true)
|
||||
->select(
|
||||
$db->quoteName(
|
||||
[
|
||||
'folder',
|
||||
'element',
|
||||
'params',
|
||||
'extension_id',
|
||||
],
|
||||
[
|
||||
'type',
|
||||
'name',
|
||||
'params',
|
||||
'id',
|
||||
]
|
||||
)
|
||||
)
|
||||
->from($db->quoteName('#__extensions'))
|
||||
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
|
||||
->where($db->quoteName('folder') . ' = ' . $db->quote('actionlog'))
|
||||
->whereIn($db->quoteName('state'), [0, 1])
|
||||
->order($db->quoteName('ordering'));
|
||||
$db->setQuery($query);
|
||||
|
||||
try {
|
||||
$rows = $db->loadObjectList();
|
||||
} catch (\RuntimeException $e) {
|
||||
$rows = [];
|
||||
}
|
||||
|
||||
if (empty($rows)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$name = $row->name;
|
||||
$type = $row->type;
|
||||
$extension = 'Plg_' . $type . '_' . $name;
|
||||
$extension = strtolower($extension);
|
||||
|
||||
// If language already loaded, don't load it again.
|
||||
if ($lang->getPaths($extension)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lang->load($extension, JPATH_ADMINISTRATOR)
|
||||
|| $lang->load($extension, JPATH_PLUGINS . '/' . $type . '/' . $name);
|
||||
}
|
||||
|
||||
// Load plg_system_actionlogs too
|
||||
$lang->load('plg_system_actionlogs', JPATH_ADMINISTRATOR);
|
||||
|
||||
// Load com_privacy too.
|
||||
$lang->load('com_privacy', JPATH_ADMINISTRATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes potential characters that start a formula in a CSV value to prevent injection attacks
|
||||
*
|
||||
* @param mixed $value csv field value
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @since 3.9.7
|
||||
*/
|
||||
protected static function escapeCsvFormula($value)
|
||||
{
|
||||
if ($value == '') {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (\in_array($value[0], self::$characters, true)) {
|
||||
$value = ' ' . $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Actionlogs\Administrator\Model;
|
||||
|
||||
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Model to interact with the action log configuration.
|
||||
*
|
||||
* @since 4.2.0
|
||||
*/
|
||||
class ActionlogConfigModel extends BaseDatabaseModel
|
||||
{
|
||||
/**
|
||||
* Returns the action logs config for the given context.
|
||||
*
|
||||
* @param string $context The context of the content
|
||||
*
|
||||
* @return \stdClass|null An object contains content type parameters, or null if not found
|
||||
*
|
||||
* @since 4.2.0
|
||||
*/
|
||||
public function getLogContentTypeParams(string $context): ?\stdClass
|
||||
{
|
||||
$db = $this->getDatabase();
|
||||
$query = $db->getQuery(true)
|
||||
->select('a.*')
|
||||
->from($db->quoteName('#__action_log_config', 'a'))
|
||||
->where($db->quoteName('a.type_alias') . ' = :context')
|
||||
->bind(':context', $context);
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return $db->loadObject();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Actionlogs\Administrator\Model;
|
||||
|
||||
use Joomla\CMS\Component\ComponentHelper;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Mail\Exception\MailDisabledException;
|
||||
use Joomla\CMS\Mail\MailTemplate;
|
||||
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
|
||||
use Joomla\CMS\User\UserFactoryAwareInterface;
|
||||
use Joomla\CMS\User\UserFactoryAwareTrait;
|
||||
use Joomla\Component\Actionlogs\Administrator\Helper\ActionlogsHelper;
|
||||
use Joomla\Utilities\IpHelper;
|
||||
use PHPMailer\PHPMailer\Exception as phpMailerException;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Methods supporting a list of Actionlog records.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
class ActionlogModel extends BaseDatabaseModel implements UserFactoryAwareInterface
|
||||
{
|
||||
use UserFactoryAwareTrait;
|
||||
|
||||
/**
|
||||
* Function to add logs to the database
|
||||
* This method adds a record to #__action_logs contains (message_language_key, message, date, context, user)
|
||||
*
|
||||
* @param array $messages The contents of the messages to be logged
|
||||
* @param string $messageLanguageKey The language key of the message
|
||||
* @param string $context The context of the content passed to the plugin
|
||||
* @param integer $userId ID of user perform the action, usually ID of current logged in user
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public function addLog($messages, $messageLanguageKey, $context, $userId = 0)
|
||||
{
|
||||
if (!is_numeric($userId)) {
|
||||
@trigger_error(sprintf('User ID must be an integer in %s.', __METHOD__), E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
$user = $userId ? $this->getUserFactory()->loadUserById($userId) : $this->getCurrentUser();
|
||||
$db = $this->getDatabase();
|
||||
$date = Factory::getDate();
|
||||
$params = ComponentHelper::getComponent('com_actionlogs')->getParams();
|
||||
|
||||
if ($params->get('ip_logging', 0)) {
|
||||
$ip = IpHelper::getIp();
|
||||
|
||||
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||
$ip = 'COM_ACTIONLOGS_IP_INVALID';
|
||||
}
|
||||
} else {
|
||||
$ip = 'COM_ACTIONLOGS_DISABLED';
|
||||
}
|
||||
|
||||
$loggedMessages = [];
|
||||
|
||||
foreach ($messages as $message) {
|
||||
$logMessage = new \stdClass();
|
||||
$logMessage->message_language_key = $messageLanguageKey;
|
||||
$logMessage->message = json_encode($message);
|
||||
$logMessage->log_date = (string) $date;
|
||||
$logMessage->extension = $context;
|
||||
$logMessage->user_id = $user->id;
|
||||
$logMessage->ip_address = $ip;
|
||||
$logMessage->item_id = isset($message['id']) ? (int) $message['id'] : 0;
|
||||
|
||||
try {
|
||||
$db->insertObject('#__action_logs', $logMessage);
|
||||
$loggedMessages[] = $logMessage;
|
||||
} catch (\RuntimeException $e) {
|
||||
// Ignore it
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Send notification email to users who choose to be notified about the action logs
|
||||
$this->sendNotificationEmails($loggedMessages, $user->name, $context);
|
||||
} catch (MailDisabledException | phpMailerException $e) {
|
||||
// Ignore it
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification emails about the action log
|
||||
*
|
||||
* @param array $messages The logged messages
|
||||
* @param string $username The username
|
||||
* @param string $context The Context
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.9.0
|
||||
*
|
||||
* @throws MailDisabledException if mail is disabled
|
||||
* @throws phpmailerException if sending mail failed
|
||||
*/
|
||||
protected function sendNotificationEmails($messages, $username, $context)
|
||||
{
|
||||
$app = Factory::getApplication();
|
||||
$lang = $app->getLanguage();
|
||||
$db = $this->getDatabase();
|
||||
$query = $db->getQuery(true);
|
||||
|
||||
$query
|
||||
->select($db->quoteName(['u.email', 'l.extensions']))
|
||||
->from($db->quoteName('#__users', 'u'))
|
||||
->where($db->quoteName('u.block') . ' = 0')
|
||||
->join(
|
||||
'INNER',
|
||||
$db->quoteName('#__action_logs_users', 'l') . ' ON ( ' . $db->quoteName('l.notify') . ' = 1 AND '
|
||||
. $db->quoteName('l.user_id') . ' = ' . $db->quoteName('u.id') . ')'
|
||||
);
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
$users = $db->loadObjectList();
|
||||
|
||||
$recipients = [];
|
||||
|
||||
foreach ($users as $user) {
|
||||
$extensions = json_decode($user->extensions, true);
|
||||
|
||||
if ($extensions && \in_array(strtok($context, '.'), $extensions)) {
|
||||
$recipients[] = $user->email;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($recipients)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$extension = strtok($context, '.');
|
||||
$lang->load('com_actionlogs', JPATH_ADMINISTRATOR);
|
||||
ActionlogsHelper::loadTranslationFiles($extension);
|
||||
$temp = [];
|
||||
|
||||
foreach ($messages as $message) {
|
||||
$m = [];
|
||||
$m['extension'] = Text::_($extension);
|
||||
$m['message'] = ActionlogsHelper::getHumanReadableLogMessage($message);
|
||||
$m['date'] = HTMLHelper::_('date', $message->log_date, 'Y-m-d H:i:s T', 'UTC');
|
||||
$m['username'] = $username;
|
||||
$temp[] = $m;
|
||||
}
|
||||
|
||||
$templateData = [
|
||||
'messages' => $temp,
|
||||
];
|
||||
|
||||
$mailer = new MailTemplate('com_actionlogs.notification', $app->getLanguage()->getTag());
|
||||
$mailer->addTemplateData($templateData);
|
||||
|
||||
foreach ($recipients as $recipient) {
|
||||
$mailer->addRecipient($recipient);
|
||||
}
|
||||
|
||||
$mailer->send();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,399 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Actionlogs\Administrator\Model;
|
||||
|
||||
use Joomla\CMS\Component\ComponentHelper;
|
||||
use Joomla\CMS\Date\Date;
|
||||
use Joomla\CMS\Event\ActionLog\AfterLogPurgeEvent;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Form\Form;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\Model\ListModel;
|
||||
use Joomla\Database\DatabaseIterator;
|
||||
use Joomla\Database\DatabaseQuery;
|
||||
use Joomla\Database\ParameterType;
|
||||
use Joomla\Utilities\ArrayHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Methods supporting a list of article records.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
class ActionlogsModel extends ListModel
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $config An optional associative array of configuration settings.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($config = [])
|
||||
{
|
||||
if (empty($config['filter_fields'])) {
|
||||
$config['filter_fields'] = [
|
||||
'a.id', 'id',
|
||||
'a.extension', 'extension',
|
||||
'a.user_id', 'user',
|
||||
'a.message', 'message',
|
||||
'a.log_date', 'log_date',
|
||||
'a.ip_address', 'ip_address',
|
||||
'dateRange',
|
||||
];
|
||||
}
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to auto-populate the model state.
|
||||
*
|
||||
* @param string $ordering An optional ordering field.
|
||||
* @param string $direction An optional direction (asc|desc).
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.9.0
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function populateState($ordering = 'a.id', $direction = 'desc')
|
||||
{
|
||||
parent::populateState($ordering, $direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an SQL query to load the list data.
|
||||
*
|
||||
* @return DatabaseQuery
|
||||
*
|
||||
* @since 3.9.0
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getListQuery()
|
||||
{
|
||||
$db = $this->getDatabase();
|
||||
$query = $db->getQuery(true)
|
||||
->select('a.*')
|
||||
->select($db->quoteName('u.name'))
|
||||
->from($db->quoteName('#__action_logs', 'a'))
|
||||
->join('LEFT', $db->quoteName('#__users', 'u') . ' ON ' . $db->quoteName('a.user_id') . ' = ' . $db->quoteName('u.id'));
|
||||
|
||||
// Get ordering
|
||||
$fullorderCol = $this->state->get('list.fullordering', 'a.id DESC');
|
||||
|
||||
// Apply ordering
|
||||
if (!empty($fullorderCol)) {
|
||||
$query->order($db->escape($fullorderCol));
|
||||
}
|
||||
|
||||
// Get filter by user
|
||||
$user = $this->getState('filter.user');
|
||||
|
||||
// Apply filter by user
|
||||
if (!empty($user)) {
|
||||
$user = (int) $user;
|
||||
$query->where($db->quoteName('a.user_id') . ' = :userid')
|
||||
->bind(':userid', $user, ParameterType::INTEGER);
|
||||
}
|
||||
|
||||
// Get filter by extension
|
||||
$extension = $this->getState('filter.extension');
|
||||
|
||||
// Apply filter by extension
|
||||
if (!empty($extension)) {
|
||||
$extension .= '%';
|
||||
$query->where($db->quoteName('a.extension') . ' LIKE :extension')
|
||||
->bind(':extension', $extension);
|
||||
}
|
||||
|
||||
// Get filter by date range
|
||||
$dateRange = $this->getState('filter.dateRange');
|
||||
|
||||
// Apply filter by date range
|
||||
if (!empty($dateRange)) {
|
||||
$date = $this->buildDateRange($dateRange);
|
||||
|
||||
// If the chosen range is not more than a year ago
|
||||
if ($date['dNow'] !== false && $date['dStart'] !== false) {
|
||||
$dStart = $date['dStart']->format('Y-m-d H:i:s');
|
||||
$dNow = $date['dNow']->format('Y-m-d H:i:s');
|
||||
$query->where(
|
||||
$db->quoteName('a.log_date') . ' BETWEEN :dstart AND :dnow'
|
||||
);
|
||||
$query->bind(':dstart', $dStart);
|
||||
$query->bind(':dnow', $dNow);
|
||||
}
|
||||
}
|
||||
|
||||
// Filter the items over the search string if set.
|
||||
$search = $this->getState('filter.search');
|
||||
|
||||
if (!empty($search)) {
|
||||
if (stripos($search, 'id:') === 0) {
|
||||
$ids = (int) substr($search, 3);
|
||||
$query->where($db->quoteName('a.id') . ' = :id')
|
||||
->bind(':id', $ids, ParameterType::INTEGER);
|
||||
} elseif (stripos($search, 'item_id:') === 0) {
|
||||
$ids = (int) substr($search, 8);
|
||||
$query->where($db->quoteName('a.item_id') . ' = :itemid')
|
||||
->bind(':itemid', $ids, ParameterType::INTEGER);
|
||||
} else {
|
||||
$search = '%' . $search . '%';
|
||||
$query->where($db->quoteName('a.message') . ' LIKE :message')
|
||||
->bind(':message', $search);
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the date range to filter on.
|
||||
*
|
||||
* @param string $range The textual range to construct the filter for.
|
||||
*
|
||||
* @return array The date range to filter on.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function buildDateRange($range)
|
||||
{
|
||||
// Get UTC for now.
|
||||
$dNow = new Date();
|
||||
$dStart = clone $dNow;
|
||||
|
||||
switch ($range) {
|
||||
case 'past_week':
|
||||
$dStart->modify('-7 day');
|
||||
break;
|
||||
|
||||
case 'past_1month':
|
||||
$dStart->modify('-1 month');
|
||||
break;
|
||||
|
||||
case 'past_3month':
|
||||
$dStart->modify('-3 month');
|
||||
break;
|
||||
|
||||
case 'past_6month':
|
||||
$dStart->modify('-6 month');
|
||||
break;
|
||||
|
||||
case 'past_year':
|
||||
$dStart->modify('-1 year');
|
||||
break;
|
||||
|
||||
case 'today':
|
||||
// Ranges that need to align with local 'days' need special treatment.
|
||||
$offset = Factory::getApplication()->get('offset');
|
||||
|
||||
// Reset the start time to be the beginning of today, local time.
|
||||
$dStart = new Date('now', $offset);
|
||||
$dStart->setTime(0, 0, 0);
|
||||
|
||||
// Now change the timezone back to UTC.
|
||||
$tz = new \DateTimeZone('GMT');
|
||||
$dStart->setTimezone($tz);
|
||||
break;
|
||||
}
|
||||
|
||||
return ['dNow' => $dNow, 'dStart' => $dStart];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all log entries for an item
|
||||
*
|
||||
* @param string $extension The extension the item belongs to
|
||||
* @param integer $itemId The item ID
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public function getLogsForItem($extension, $itemId)
|
||||
{
|
||||
$itemId = (int) $itemId;
|
||||
$db = $this->getDatabase();
|
||||
$query = $db->getQuery(true)
|
||||
->select('a.*')
|
||||
->select($db->quoteName('u.name'))
|
||||
->from($db->quoteName('#__action_logs', 'a'))
|
||||
->join('INNER', $db->quoteName('#__users', 'u') . ' ON ' . $db->quoteName('a.user_id') . ' = ' . $db->quoteName('u.id'))
|
||||
->where($db->quoteName('a.extension') . ' = :extension')
|
||||
->where($db->quoteName('a.item_id') . ' = :itemid')
|
||||
->bind(':extension', $extension)
|
||||
->bind(':itemid', $itemId, ParameterType::INTEGER);
|
||||
|
||||
// Get ordering
|
||||
$fullorderCol = $this->getState('list.fullordering', 'a.id DESC');
|
||||
|
||||
// Apply ordering
|
||||
if (!empty($fullorderCol)) {
|
||||
$query->order($db->escape($fullorderCol));
|
||||
}
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return $db->loadObjectList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get logs data into Table object
|
||||
*
|
||||
* @param integer[]|null $pks An optional array of log record IDs to load
|
||||
*
|
||||
* @return array All logs in the table
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public function getLogsData($pks = null)
|
||||
{
|
||||
$db = $this->getDatabase();
|
||||
$query = $this->getLogDataQuery($pks);
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return $db->loadObjectList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get logs data as a database iterator
|
||||
*
|
||||
* @param integer[]|null $pks An optional array of log record IDs to load
|
||||
*
|
||||
* @return DatabaseIterator
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public function getLogDataAsIterator($pks = null)
|
||||
{
|
||||
$db = $this->getDatabase();
|
||||
$query = $this->getLogDataQuery($pks);
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return $db->getIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query for loading logs data
|
||||
*
|
||||
* @param integer[]|null $pks An optional array of log record IDs to load
|
||||
*
|
||||
* @return DatabaseQuery
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
private function getLogDataQuery($pks = null)
|
||||
{
|
||||
$db = $this->getDatabase();
|
||||
$query = $db->getQuery(true)
|
||||
->select('a.*')
|
||||
->select($db->quoteName('u.name'))
|
||||
->from($db->quoteName('#__action_logs', 'a'))
|
||||
->join('INNER', $db->quoteName('#__users', 'u') . ' ON ' . $db->quoteName('a.user_id') . ' = ' . $db->quoteName('u.id'));
|
||||
|
||||
if (\is_array($pks) && \count($pks) > 0) {
|
||||
$pks = ArrayHelper::toInteger($pks);
|
||||
$query->whereIn($db->quoteName('a.id'), $pks);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete logs
|
||||
*
|
||||
* @param array $pks Primary keys of logs
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public function delete(&$pks)
|
||||
{
|
||||
$keys = ArrayHelper::toInteger($pks);
|
||||
$db = $this->getDatabase();
|
||||
$query = $db->getQuery(true)
|
||||
->delete($db->quoteName('#__action_logs'))
|
||||
->whereIn($db->quoteName('id'), $keys);
|
||||
$db->setQuery($query);
|
||||
|
||||
try {
|
||||
$db->execute();
|
||||
} catch (\RuntimeException $e) {
|
||||
$this->setError($e->getMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->getDispatcher()->dispatch('onAfterLogPurge', new AfterLogPurgeEvent('onAfterLogPurge'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all of logs from the table.
|
||||
*
|
||||
* @return boolean result of operation
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public function purge()
|
||||
{
|
||||
try {
|
||||
$this->getDatabase()->truncateTable('#__action_logs');
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->getDispatcher()->dispatch('onAfterLogPurge', new AfterLogPurgeEvent('onAfterLogPurge'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filter form
|
||||
*
|
||||
* @param array $data data
|
||||
* @param boolean $loadData load current data
|
||||
*
|
||||
* @return Form|boolean The Form object or false on error
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public function getFilterForm($data = [], $loadData = true)
|
||||
{
|
||||
$form = parent::getFilterForm($data, $loadData);
|
||||
$params = ComponentHelper::getParams('com_actionlogs');
|
||||
$ipLogging = (bool) $params->get('ip_logging', 0);
|
||||
|
||||
// Add ip sort options to sort dropdown
|
||||
if ($form && $ipLogging) {
|
||||
/* @var \Joomla\CMS\Form\Field\ListField $field */
|
||||
$field = $form->getField('fullordering', 'list');
|
||||
$field->addOption(Text::_('COM_ACTIONLOGS_IP_ADDRESS_ASC'), ['value' => 'a.ip_address ASC']);
|
||||
$field->addOption(Text::_('COM_ACTIONLOGS_IP_ADDRESS_DESC'), ['value' => 'a.ip_address DESC']);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Actionlogs\Administrator\Plugin;
|
||||
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Abstract Action Log Plugin
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
abstract class ActionLogPlugin extends CMSPlugin
|
||||
{
|
||||
/**
|
||||
* Application object.
|
||||
*
|
||||
* @var \Joomla\CMS\Application\CMSApplication
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* Database object.
|
||||
*
|
||||
* @var \Joomla\Database\DatabaseDriver
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* Load plugin language file automatically so that it can be used inside component
|
||||
*
|
||||
* @var boolean
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected $autoloadLanguage = true;
|
||||
|
||||
/**
|
||||
* Proxy for ActionlogsModelUserlog addLog method
|
||||
*
|
||||
* This method adds a record to #__action_logs contains (message_language_key, message, date, context, user)
|
||||
*
|
||||
* @param array $messages The contents of the messages to be logged
|
||||
* @param string $messageLanguageKey The language key of the message
|
||||
* @param string $context The context of the content passed to the plugin
|
||||
* @param int $userId ID of user perform the action, usually ID of current logged in user
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected function addLog($messages, $messageLanguageKey, $context, $userId = null)
|
||||
{
|
||||
$user = $this->app->getIdentity();
|
||||
|
||||
foreach ($messages as $index => $message) {
|
||||
if (!\array_key_exists('userid', $message)) {
|
||||
$message['userid'] = $user->id;
|
||||
}
|
||||
|
||||
if (!\array_key_exists('username', $message)) {
|
||||
$message['username'] = $user->username;
|
||||
}
|
||||
|
||||
if (!\array_key_exists('accountlink', $message)) {
|
||||
$message['accountlink'] = 'index.php?option=com_users&task=user.edit&id=' . $user->id;
|
||||
}
|
||||
|
||||
if (\array_key_exists('type', $message)) {
|
||||
$message['type'] = strtoupper($message['type']);
|
||||
}
|
||||
|
||||
if (\array_key_exists('app', $message)) {
|
||||
$message['app'] = strtoupper($message['app']);
|
||||
}
|
||||
|
||||
$messages[$index] = $message;
|
||||
}
|
||||
|
||||
/** @var \Joomla\Component\Actionlogs\Administrator\Model\ActionlogModel $model */
|
||||
$model = $this->app->bootComponent('com_actionlogs')
|
||||
->getMVCFactory()->createModel('Actionlog', 'Administrator', ['ignore_request' => true]);
|
||||
|
||||
$model->addLog($messages, strtoupper($messageLanguageKey), $context, $userId);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Actionlogs\Administrator\View\Actionlogs;
|
||||
|
||||
use Joomla\CMS\Component\ComponentHelper;
|
||||
use Joomla\CMS\Form\Form;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\View\GenericDataException;
|
||||
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
|
||||
use Joomla\CMS\Pagination\Pagination;
|
||||
use Joomla\CMS\Toolbar\Toolbar;
|
||||
use Joomla\CMS\Toolbar\ToolbarHelper;
|
||||
use Joomla\Component\Actionlogs\Administrator\Helper\ActionlogsHelper;
|
||||
use Joomla\Component\Actionlogs\Administrator\Model\ActionlogsModel;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* View class for a list of logs.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
class HtmlView extends BaseHtmlView
|
||||
{
|
||||
/**
|
||||
* An array of items.
|
||||
*
|
||||
* @var array
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected $items;
|
||||
|
||||
/**
|
||||
* The model state
|
||||
*
|
||||
* @var array
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* The pagination object
|
||||
*
|
||||
* @var Pagination
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected $pagination;
|
||||
|
||||
/**
|
||||
* Form object for search filters
|
||||
*
|
||||
* @var Form
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public $filterForm;
|
||||
|
||||
/**
|
||||
* The active search filters
|
||||
*
|
||||
* @var array
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public $activeFilters;
|
||||
|
||||
/**
|
||||
* Setting if the IP column should be shown
|
||||
*
|
||||
* @var boolean
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected $showIpColumn = false;
|
||||
|
||||
/**
|
||||
* Setting if the date should be displayed relative to the current date.
|
||||
*
|
||||
* @var boolean
|
||||
* @since 4.1.0
|
||||
*/
|
||||
protected $dateRelative = false;
|
||||
|
||||
/**
|
||||
* Method to display the view.
|
||||
*
|
||||
* @param string $tpl A template file to load. [optional]
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.9.0
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function display($tpl = null)
|
||||
{
|
||||
/** @var ActionlogsModel $model */
|
||||
$model = $this->getModel();
|
||||
$this->items = $model->getItems();
|
||||
$this->state = $model->getState();
|
||||
$this->pagination = $model->getPagination();
|
||||
$this->filterForm = $model->getFilterForm();
|
||||
$this->activeFilters = $model->getActiveFilters();
|
||||
$params = ComponentHelper::getParams('com_actionlogs');
|
||||
$this->showIpColumn = (bool) $params->get('ip_logging', 0);
|
||||
$this->dateRelative = (bool) $params->get('date_relative', 1);
|
||||
|
||||
if (\count($errors = $model->getErrors())) {
|
||||
throw new GenericDataException(implode("\n", $errors), 500);
|
||||
}
|
||||
|
||||
$this->addToolbar();
|
||||
|
||||
// Load all actionlog plugins language files
|
||||
ActionlogsHelper::loadActionLogPluginsLanguage();
|
||||
|
||||
parent::display($tpl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the page title and toolbar.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
protected function addToolbar()
|
||||
{
|
||||
ToolbarHelper::title(Text::_('COM_ACTIONLOGS_MANAGER_USERLOGS'), 'icon-list-2');
|
||||
$toolbar = Toolbar::getInstance();
|
||||
|
||||
$toolbar->standardButton('download', 'COM_ACTIONLOGS_EXPORT_CSV', 'actionlogs.exportSelectedLogs')
|
||||
->icon('icon-download')
|
||||
->listCheck(true);
|
||||
|
||||
$toolbar->standardButton('download', 'COM_ACTIONLOGS_EXPORT_ALL_CSV', 'actionlogs.exportLogs')
|
||||
->icon('icon-download')
|
||||
->listCheck(false);
|
||||
|
||||
$toolbar->delete('actionlogs.delete')
|
||||
->message('JGLOBAL_CONFIRM_DELETE');
|
||||
|
||||
$toolbar->confirmButton('delete', 'COM_ACTIONLOGS_TOOLBAR_PURGE', 'actionlogs.purge')
|
||||
->message('COM_ACTIONLOGS_PURGE_CONFIRM')
|
||||
->listCheck(false);
|
||||
|
||||
$toolbar->preferences('com_actionlogs');
|
||||
$toolbar->help('User_Actions_Log');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage com_actionlogs
|
||||
*
|
||||
* @copyright (C) 2018 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\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Layout\LayoutHelper;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\Component\Actionlogs\Administrator\Helper\ActionlogsHelper;
|
||||
use Joomla\Component\Actionlogs\Administrator\View\Actionlogs\HtmlView;
|
||||
|
||||
/** @var HtmlView $this */
|
||||
|
||||
$listOrder = $this->escape($this->state->get('list.ordering'));
|
||||
$listDirn = $this->escape($this->state->get('list.direction'));
|
||||
|
||||
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
|
||||
$wa = $this->document->getWebAssetManager();
|
||||
$wa->useScript('keepalive')
|
||||
->useScript('table.columns')
|
||||
->useScript('multiselect')
|
||||
->useScript('com_actionlogs.admin-actionlogs');
|
||||
|
||||
?>
|
||||
|
||||
<form action="<?php echo Route::_('index.php?option=com_actionlogs&view=actionlogs'); ?>" method="post" name="adminForm" id="adminForm">
|
||||
<div id="j-main-container" class="j-main-container">
|
||||
<?php // Search tools bar ?>
|
||||
<?php echo LayoutHelper::render('joomla.searchtools.default', ['view' => $this]); ?>
|
||||
<?php if (empty($this->items)) : ?>
|
||||
<div class="alert alert-info">
|
||||
<span class="icon-info-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('INFO'); ?></span>
|
||||
<?php echo Text::_('JGLOBAL_NO_MATCHING_RESULTS'); ?>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<table class="table" id="logsList">
|
||||
<caption class="visually-hidden">
|
||||
<?php echo Text::_('COM_ACTIONLOGS_TABLE_CAPTION'); ?>,
|
||||
<span id="orderedBy"><?php echo Text::_('JGLOBAL_SORTED_BY'); ?> </span>,
|
||||
<span id="filteredBy"><?php echo Text::_('JGLOBAL_FILTERED_BY'); ?></span>
|
||||
</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="w-1 text-center">
|
||||
<?php echo HTMLHelper::_('grid.checkall'); ?>
|
||||
</td>
|
||||
<th scope="col" class="d-md-table-cell">
|
||||
<?php echo HTMLHelper::_('searchtools.sort', 'COM_ACTIONLOGS_ACTION', 'a.message', $listDirn, $listOrder); ?>
|
||||
</th>
|
||||
<th scope="col" class="w-15 d-none d-md-table-cell">
|
||||
<?php echo HTMLHelper::_('searchtools.sort', 'COM_ACTIONLOGS_EXTENSION', 'a.extension', $listDirn, $listOrder); ?>
|
||||
</th>
|
||||
<th scope="col" class="w-15 d-none d-md-table-cell">
|
||||
<?php echo HTMLHelper::_('searchtools.sort', 'COM_ACTIONLOGS_DATE', 'a.log_date', $listDirn, $listOrder); ?>
|
||||
</th>
|
||||
<th scope="col" class="w-10 d-md-table-cell">
|
||||
<?php echo HTMLHelper::_('searchtools.sort', 'COM_ACTIONLOGS_NAME', 'a.user_id', $listDirn, $listOrder); ?>
|
||||
</th>
|
||||
<?php if ($this->showIpColumn) : ?>
|
||||
<th scope="col" class="w-10 d-none d-md-table-cell">
|
||||
<?php echo HTMLHelper::_('searchtools.sort', 'COM_ACTIONLOGS_IP_ADDRESS', 'a.ip_address', $listDirn, $listOrder); ?>
|
||||
</th>
|
||||
<?php endif; ?>
|
||||
<th scope="col" class="w-1 d-none d-md-table-cell">
|
||||
<?php echo HTMLHelper::_('searchtools.sort', 'JGRID_HEADING_ID', 'a.id', $listDirn, $listOrder); ?>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($this->items as $i => $item) :
|
||||
$extension = strtok($item->extension, '.');
|
||||
ActionlogsHelper::loadTranslationFiles($extension); ?>
|
||||
<tr class="row<?php echo $i % 2; ?>">
|
||||
<td class="text-center">
|
||||
<?php echo HTMLHelper::_('grid.id', $i, $item->id); ?>
|
||||
</td>
|
||||
<th scope="row" class="d-md-table-cell">
|
||||
<?php echo ActionlogsHelper::getHumanReadableLogMessage($item); ?>
|
||||
</th>
|
||||
<td class="d-none d-md-table-cell">
|
||||
<?php echo $this->escape(Text::_($extension)); ?>
|
||||
</td>
|
||||
<td class="d-none d-md-table-cell">
|
||||
<?php if ($this->dateRelative) : ?>
|
||||
<?php echo HTMLHelper::_('date.relative', $item->log_date); ?>
|
||||
<div class="small">
|
||||
<?php endif; ?>
|
||||
<?php echo HTMLHelper::_('date', $item->log_date, Text::_('DATE_FORMAT_LC6')); ?>
|
||||
<?php if ($this->dateRelative) : ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="d-md-table-cell">
|
||||
<?php echo $this->escape($item->name); ?>
|
||||
</td>
|
||||
<?php if ($this->showIpColumn) : ?>
|
||||
<td class="d-none d-md-table-cell">
|
||||
<?php echo Text::_($this->escape($item->ip_address)); ?>
|
||||
</td>
|
||||
<?php endif;?>
|
||||
<td class="d-none d-md-table-cell">
|
||||
<?php echo (int) $item->id; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php // Load the pagination. ?>
|
||||
<?php echo $this->pagination->getListFooter(); ?>
|
||||
|
||||
<?php endif;?>
|
||||
<input type="hidden" name="task" value="" />
|
||||
<input type="hidden" name="boxchecked" value="0" />
|
||||
<?php echo HTMLHelper::_('form.token'); ?>
|
||||
</div>
|
||||
</form>
|
||||
<form action="<?php echo Route::_('index.php?option=com_actionlogs&view=actionlogs'); ?>" method="post" name="exportForm" id="exportForm">
|
||||
<input type="hidden" name="task" value="" />
|
||||
<input type="hidden" name="cids" value="" />
|
||||
<?php echo HTMLHelper::_('form.token'); ?>
|
||||
</form>
|
||||
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata>
|
||||
<layout title="COM_ACTIONLOGS_VIEW_DEFAULT_TITLE">
|
||||
<message>
|
||||
<![CDATA[COM_ACTIONLOGS_VIEW_DEFAULT_DESC]]>
|
||||
</message>
|
||||
</layout>
|
||||
</metadata>
|
||||
|
||||
Reference in New Issue
Block a user