[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * @package Joomla.Plugins 4 * @subpackage System.actionlogs 5 * 6 * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10 defined('_JEXEC') or die; 11 12 use Joomla\CMS\Cache\Cache; 13 use Joomla\CMS\Factory; 14 use Joomla\CMS\Form\Form; 15 use Joomla\CMS\HTML\HTMLHelper; 16 use Joomla\CMS\Language\Text; 17 use Joomla\CMS\Plugin\CMSPlugin; 18 use Joomla\CMS\Plugin\PluginHelper; 19 use Joomla\CMS\User\User; 20 use Joomla\Component\Actionlogs\Administrator\Helper\ActionlogsHelper; 21 use Joomla\Database\Exception\ExecutionFailureException; 22 use Joomla\Database\ParameterType; 23 24 /** 25 * Joomla! Users Actions Logging Plugin. 26 * 27 * @since 3.9.0 28 */ 29 class PlgSystemActionLogs extends CMSPlugin 30 { 31 /** 32 * @var \Joomla\CMS\Application\CMSApplication 33 * 34 * @since 3.9.0 35 */ 36 protected $app; 37 38 /** 39 * @var \Joomla\Database\DatabaseDriver 40 * 41 * @since 3.9.0 42 */ 43 protected $db; 44 45 /** 46 * Constructor. 47 * 48 * @param object $subject The object to observe. 49 * @param array $config An optional associative array of configuration settings. 50 * 51 * @since 3.9.0 52 */ 53 public function __construct(&$subject, $config) 54 { 55 parent::__construct($subject, $config); 56 57 // Import actionlog plugin group so that these plugins will be triggered for events 58 PluginHelper::importPlugin('actionlog'); 59 } 60 61 /** 62 * Listener for the `onAfterInitialise` event 63 * 64 * @return void 65 * 66 * @since 4.0.0 67 */ 68 public function onAfterInitialise() 69 { 70 // Load plugin language files. 71 $this->loadLanguage(); 72 } 73 74 /** 75 * Adds additional fields to the user editing form for logs e-mail notifications 76 * 77 * @param Form $form The form to be altered. 78 * @param mixed $data The associated data for the form. 79 * 80 * @return boolean 81 * 82 * @since 3.9.0 83 * 84 * @throws Exception 85 */ 86 public function onContentPrepareForm(Form $form, $data) 87 { 88 $formName = $form->getName(); 89 90 $allowedFormNames = [ 91 'com_users.profile', 92 'com_users.user', 93 ]; 94 95 if (!in_array($formName, $allowedFormNames, true)) 96 { 97 return true; 98 } 99 100 /** 101 * We only allow users who have Super User permission to change this setting for themselves or for other 102 * users who have the same Super User permission 103 */ 104 105 $user = Factory::getUser(); 106 107 if (!$user->authorise('core.admin')) 108 { 109 return true; 110 } 111 112 // If we are on the save command, no data is passed to $data variable, we need to get it directly from request 113 $jformData = $this->app->input->get('jform', [], 'array'); 114 115 if ($jformData && !$data) 116 { 117 $data = $jformData; 118 } 119 120 if (is_array($data)) 121 { 122 $data = (object) $data; 123 } 124 125 if (empty($data->id) || !User::getInstance($data->id)->authorise('core.admin')) 126 { 127 return true; 128 } 129 130 Form::addFormPath(__DIR__ . '/forms'); 131 132 if ((!PluginHelper::isEnabled('actionlog', 'joomla')) && (Factory::getApplication()->isClient('administrator'))) 133 { 134 $form->loadFile('information', false); 135 136 return true; 137 } 138 139 if (!PluginHelper::isEnabled('actionlog', 'joomla')) 140 { 141 return true; 142 } 143 144 $form->loadFile('actionlogs', false); 145 146 return true; 147 } 148 149 /** 150 * Runs on content preparation 151 * 152 * @param string $context The context for the data 153 * @param object $data An object containing the data for the form. 154 * 155 * @return boolean 156 * 157 * @since 3.9.0 158 */ 159 public function onContentPrepareData($context, $data) 160 { 161 if (!in_array($context, ['com_users.profile', 'com_users.user'])) 162 { 163 return true; 164 } 165 166 if (is_array($data)) 167 { 168 $data = (object) $data; 169 } 170 171 if (!User::getInstance($data->id)->authorise('core.admin')) 172 { 173 return true; 174 } 175 176 $db = $this->db; 177 $id = (int) $data->id; 178 179 $query = $db->getQuery(true) 180 ->select($db->quoteName(['notify', 'extensions'])) 181 ->from($db->quoteName('#__action_logs_users')) 182 ->where($db->quoteName('user_id') . ' = :userid') 183 ->bind(':userid', $id, ParameterType::INTEGER); 184 185 try 186 { 187 $values = $db->setQuery($query)->loadObject(); 188 } 189 catch (ExecutionFailureException $e) 190 { 191 return false; 192 } 193 194 if (!$values) 195 { 196 return true; 197 } 198 199 $data->actionlogs = new stdClass; 200 $data->actionlogs->actionlogsNotify = $values->notify; 201 $data->actionlogs->actionlogsExtensions = $values->extensions; 202 203 if (!HTMLHelper::isRegistered('users.actionlogsNotify')) 204 { 205 HTMLHelper::register('users.actionlogsNotify', array(__CLASS__, 'renderActionlogsNotify')); 206 } 207 208 if (!HTMLHelper::isRegistered('users.actionlogsExtensions')) 209 { 210 HTMLHelper::register('users.actionlogsExtensions', array(__CLASS__, 'renderActionlogsExtensions')); 211 } 212 213 return true; 214 } 215 216 /** 217 * Runs after the HTTP response has been sent to the client and delete log records older than certain days 218 * 219 * @return void 220 * 221 * @since 3.9.0 222 */ 223 public function onAfterRespond() 224 { 225 $daysToDeleteAfter = (int) $this->params->get('logDeletePeriod', 0); 226 227 if ($daysToDeleteAfter <= 0) 228 { 229 return; 230 } 231 232 // The delete frequency will be once per day 233 $deleteFrequency = 3600 * 24; 234 235 // Do we need to run? Compare the last run timestamp stored in the plugin's options with the current 236 // timestamp. If the difference is greater than the cache timeout we shall not execute again. 237 $now = time(); 238 $last = (int) $this->params->get('lastrun', 0); 239 240 if (abs($now - $last) < $deleteFrequency) 241 { 242 return; 243 } 244 245 // Update last run status 246 $this->params->set('lastrun', $now); 247 248 $db = $this->db; 249 $params = $this->params->toString('JSON'); 250 $query = $db->getQuery(true) 251 ->update($db->quoteName('#__extensions')) 252 ->set($db->quoteName('params') . ' = :params') 253 ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) 254 ->where($db->quoteName('folder') . ' = ' . $db->quote('system')) 255 ->where($db->quoteName('element') . ' = ' . $db->quote('actionlogs')) 256 ->bind(':params', $params); 257 258 try 259 { 260 // Lock the tables to prevent multiple plugin executions causing a race condition 261 $db->lockTable('#__extensions'); 262 } 263 catch (Exception $e) 264 { 265 // If we can't lock the tables it's too risky to continue execution 266 return; 267 } 268 269 try 270 { 271 // Update the plugin parameters 272 $result = $db->setQuery($query)->execute(); 273 274 $this->clearCacheGroups(['com_plugins'], [0, 1]); 275 } 276 catch (Exception $exc) 277 { 278 // If we failed to execute 279 $db->unlockTables(); 280 $result = false; 281 } 282 283 try 284 { 285 // Unlock the tables after writing 286 $db->unlockTables(); 287 } 288 catch (Exception $e) 289 { 290 // If we can't lock the tables assume we have somehow failed 291 $result = false; 292 } 293 294 // Abort on failure 295 if (!$result) 296 { 297 return; 298 } 299 300 $daysToDeleteAfter = (int) $this->params->get('logDeletePeriod', 0); 301 $now = Factory::getDate()->toSql(); 302 303 if ($daysToDeleteAfter > 0) 304 { 305 $days = -1 * $daysToDeleteAfter; 306 307 $query->clear() 308 ->delete($db->quoteName('#__action_logs')) 309 ->where($db->quoteName('log_date') . ' < ' . $query->dateAdd($db->quote($now), $days, 'DAY')); 310 311 $db->setQuery($query); 312 313 try 314 { 315 $db->execute(); 316 } 317 catch (RuntimeException $e) 318 { 319 // Ignore it 320 return; 321 } 322 } 323 } 324 325 /** 326 * Clears cache groups. We use it to clear the plugins cache after we update the last run timestamp. 327 * 328 * @param array $clearGroups The cache groups to clean 329 * @param array $cacheClients The cache clients (site, admin) to clean 330 * 331 * @return void 332 * 333 * @since 3.9.0 334 */ 335 private function clearCacheGroups(array $clearGroups, array $cacheClients = [0, 1]) 336 { 337 foreach ($clearGroups as $group) 338 { 339 foreach ($cacheClients as $clientId) 340 { 341 try 342 { 343 $options = [ 344 'defaultgroup' => $group, 345 'cachebase' => $clientId ? JPATH_ADMINISTRATOR . '/cache' : 346 Factory::getApplication()->get('cache_path', JPATH_SITE . '/cache'), 347 ]; 348 349 $cache = Cache::getInstance('callback', $options); 350 $cache->clean(); 351 } 352 catch (Exception $e) 353 { 354 // Ignore it 355 } 356 } 357 } 358 } 359 360 /** 361 * Utility method to act on a user after it has been saved. 362 * 363 * @param array $user Holds the new user data. 364 * @param boolean $isNew True if a new user is stored. 365 * @param boolean $success True if user was successfully stored in the database. 366 * @param string $msg Message. 367 * 368 * @return void 369 * 370 * @since 3.9.0 371 */ 372 public function onUserAfterSave($user, $isNew, $success, $msg): void 373 { 374 if (!$success) 375 { 376 return; 377 } 378 379 // Clear access rights in case user groups were changed. 380 $userObject = new User($user['id']); 381 $userObject->clearAccessRights(); 382 383 $authorised = $userObject->authorise('core.admin'); 384 $userid = (int) $user['id']; 385 $db = $this->db; 386 387 $query = $db->getQuery(true) 388 ->select('COUNT(*)') 389 ->from($db->quoteName('#__action_logs_users')) 390 ->where($db->quoteName('user_id') . ' = :userid') 391 ->bind(':userid', $userid, ParameterType::INTEGER); 392 393 try 394 { 395 $exists = (bool) $db->setQuery($query)->loadResult(); 396 } 397 catch (ExecutionFailureException $e) 398 { 399 return; 400 } 401 402 $query->clear(); 403 404 // If preferences don't exist, insert. 405 if (!$exists && $authorised && isset($user['actionlogs'])) 406 { 407 $notify = (int) $user['actionlogs']['actionlogsNotify']; 408 $values = [':userid', ':notify']; 409 $bind = [$userid, $notify]; 410 $columns = ['user_id', 'notify']; 411 412 $query->bind($values, $bind, ParameterType::INTEGER); 413 414 if (isset($user['actionlogs']['actionlogsExtensions'])) 415 { 416 $values[] = ':extension'; 417 $columns[] = 'extensions'; 418 $extension = json_encode($user['actionlogs']['actionlogsExtensions']); 419 $query->bind(':extension', $extension); 420 } 421 422 $query->insert($db->quoteName('#__action_logs_users')) 423 ->columns($db->quoteName($columns)) 424 ->values(implode(',', $values)); 425 } 426 elseif ($exists && $authorised && isset($user['actionlogs'])) 427 { 428 // Update preferences. 429 $notify = (int) $user['actionlogs']['actionlogsNotify']; 430 $values = [$db->quoteName('notify') . ' = :notify']; 431 432 $query->bind(':notify', $notify, ParameterType::INTEGER); 433 434 if (isset($user['actionlogs']['actionlogsExtensions'])) 435 { 436 $values[] = $db->quoteName('extensions') . ' = :extension'; 437 $extension = json_encode($user['actionlogs']['actionlogsExtensions']); 438 $query->bind(':extension', $extension); 439 } 440 441 $query->update($db->quoteName('#__action_logs_users')) 442 ->set($values) 443 ->where($db->quoteName('user_id') . ' = :userid') 444 ->bind(':userid', $userid, ParameterType::INTEGER); 445 } 446 elseif ($exists && !$authorised) 447 { 448 // Remove preferences if user is not authorised. 449 $query->delete($db->quoteName('#__action_logs_users')) 450 ->where($db->quoteName('user_id') . ' = :userid') 451 ->bind(':userid', $userid, ParameterType::INTEGER); 452 } 453 else 454 { 455 return; 456 } 457 458 try 459 { 460 $db->setQuery($query)->execute(); 461 } 462 catch (ExecutionFailureException $e) 463 { 464 // Do nothing. 465 } 466 } 467 468 /** 469 * Removes user preferences 470 * 471 * Method is called after user data is deleted from the database 472 * 473 * @param array $user Holds the user data 474 * @param boolean $success True if user was successfully stored in the database 475 * @param string $msg Message 476 * 477 * @return void 478 * 479 * @since 3.9.0 480 */ 481 public function onUserAfterDelete($user, $success, $msg): void 482 { 483 if (!$success) 484 { 485 return; 486 } 487 488 $db = $this->db; 489 $userid = (int) $user['id']; 490 491 $query = $db->getQuery(true) 492 ->delete($db->quoteName('#__action_logs_users')) 493 ->where($db->quoteName('user_id') . ' = :userid') 494 ->bind(':userid', $userid, ParameterType::INTEGER); 495 496 try 497 { 498 $db->setQuery($query)->execute(); 499 } 500 catch (ExecutionFailureException $e) 501 { 502 // Do nothing. 503 } 504 } 505 506 /** 507 * Method to render a value. 508 * 509 * @param integer|string $value The value (0 or 1). 510 * 511 * @return string The rendered value. 512 * 513 * @since 3.9.16 514 */ 515 public static function renderActionlogsNotify($value) 516 { 517 return Text::_($value ? 'JYES' : 'JNO'); 518 } 519 520 /** 521 * Method to render a list of extensions. 522 * 523 * @param array|string $extensions Array of extensions or an empty string if none selected. 524 * 525 * @return string The rendered value. 526 * 527 * @since 3.9.16 528 */ 529 public static function renderActionlogsExtensions($extensions) 530 { 531 // No extensions selected. 532 if (!$extensions) 533 { 534 return Text::_('JNONE'); 535 } 536 537 // Load the helper. 538 JLoader::register('ActionlogsHelper', JPATH_ADMINISTRATOR . '/components/com_actionlogs/helpers/actionlogs.php'); 539 540 foreach ($extensions as &$extension) 541 { 542 // Load extension language files and translate extension name. 543 ActionlogsHelper::loadTranslationFiles($extension); 544 $extension = Text::_($extension); 545 } 546 547 return implode(', ', $extensions); 548 } 549 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |