aboutsummaryrefslogtreecommitdiff
path: root/lib/classes/StudipLog.php
diff options
context:
space:
mode:
authorPhilipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de>2024-09-24 10:53:31 +0200
committerPhilipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de>2024-09-24 10:53:31 +0200
commit4459dd7917f4d1c34f40bb68f0e991e9c3d53e4c (patch)
tree5c07151ae61276d334e88f6309c30d439a85c12e /lib/classes/StudipLog.php
parentda0022e5c1abbf9825ae76debaabdff7e8623bb4 (diff)
parent97a188592c679890a25c37ab78463add76a52ff7 (diff)
Merge branch 'main' into issue-3911issue-3911
Diffstat (limited to 'lib/classes/StudipLog.php')
-rw-r--r--lib/classes/StudipLog.php380
1 files changed, 380 insertions, 0 deletions
diff --git a/lib/classes/StudipLog.php b/lib/classes/StudipLog.php
new file mode 100644
index 0000000..7a520c9
--- /dev/null
+++ b/lib/classes/StudipLog.php
@@ -0,0 +1,380 @@
+<?php
+/**
+ * StudipLog
+ * Internal API for the Stud.IP logging functions.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * @author Peter Thienel <thienel@data-quest.de>
+ * @copyright 2013 Stud.IP Core-Group
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ * @since 3.0
+ */
+
+class StudipLog
+{
+ /**
+ * Magic log, intercepts all undefined static method calls
+ * called method name must be the name of a log action
+ *
+ * @param string $name
+ * @param array $arguments
+ * @return boolean True if event was written or false if logging is disabled
+ */
+ public static function __callStatic($name, $arguments)
+ {
+ $log_action_name = mb_strtoupper($name);
+ $log_action = LogAction::findByName($log_action_name);
+ if ($log_action) {
+ return call_user_func_array('StudipLog::log', array_merge([$log_action_name], $arguments));
+ }
+ throw new BadMethodCallException('Unknown method called: '
+ . $log_action_name);
+ }
+
+ /**
+ * Logs an event to the database after a certain action took place along with
+ * the ids of the range object the action possibly affected. You can provide
+ * additional info as well as debug information.
+ *
+ * @param String $action_name Name of the action that took place
+ * @param mixed $affected Range id that was affected by the action, if any
+ * @param mixed $coaffected Range id that was possibly affected as well
+ * @param mixed $info Information to add to the event
+ * @param mixed $dbg_info Debug information to add to the event
+ * @param mixed $user_id Provide null for the current user id
+ **/
+ public static function log(
+ $action_name,
+ $affected = null,
+ $coaffected = null,
+ $info = null,
+ $dbg_info = null,
+ $user_id = null
+ ) {
+ if (!Config::get()->LOG_ENABLE) {
+ return false;
+ }
+
+ // automagically set current user as agent
+ if (!$user_id) {
+ $user_id = $GLOBALS['user']->id;
+ }
+
+ $log_action = LogAction::findOneByName($action_name);
+ if (!$log_action) {
+ // Action doesn't exist -> LOG_ERROR
+ $debug = sprintf(
+ 'StudipLog::log(%s,%s,%s,%s,%s) for user %s',
+ $action_name,
+ $affected,
+ $coaffected,
+ $info,
+ $dbg_info,
+ $user_id
+ );
+ self::log('LOG_ERROR', null, null, null, $debug);
+ return false;
+ }
+
+ if (!$log_action->isActive()) {
+ return false;
+ }
+
+ $log_event = new LogEvent();
+ $log_event->user_id = $user_id;
+ $log_event->action_id = $log_action->getId();
+ $log_event->affected_range_id = $affected;
+ $log_event->coaffected_range_id = $coaffected;
+ $log_event->info = $info;
+ $log_event->dbg_info = $dbg_info;
+ $log_event->store();
+ return true;
+ }
+
+ /**
+ * Registers a new log action in database.
+ * Use this function to register log actions for Stud.IP core objects.
+ *
+ * @param string $name The name of the action.
+ * @param string $description The action's description.
+ * @param string $info_template The template
+ * @param string $class Name of the core class.
+ */
+ public static function registerAction($name, $description, $info_template,
+ $class)
+ {
+ $action = new LogAction();
+ $action->name = $name;
+ $action->description = $description;
+ $action->info_template = $info_template;
+ $action->class = $class;
+ $action->type = 'core';
+ $action->store();
+ }
+
+ /**
+ * Registers a new log action in database.
+ * Use this function to register log actions for plugin classes.
+ *
+ * @param string $name The name of the action.
+ * @param string $description The action's description.
+ * @param string $info_template The template
+ * @param string $plugin_class_name Name of the plugin class.
+ */
+ public static function registerActionPlugin($name, $description,
+ $info_template, $plugin_class_name)
+ {
+ $action = new LogAction();
+ $action->name = $name;
+ $action->description = $description;
+ $action->info_template = $info_template;
+ $action->class = $plugin_class_name;
+ $action->type = 'plugin';
+ $action->store();
+ }
+
+ /**
+ * Registers a new log action in database.
+ * Use this function to register log actions for arbitrary objects.
+ *
+ * @param string $name The name of the action.
+ * @param string $description The action's description.
+ * @param string $info_template The template
+ * @param string $filename Path to the file with the class (relative
+ * to Stud.IP root).
+ * @param string $class Name of class to be logged.
+ */
+ public static function registerActionFile($name, $description,
+ $info_template, $filename, $class)
+ {
+ $path_file = $GLOBALS['STUDIP_BASE_PATH'] . '/' . $filename;
+ if (!file_exists($path_file)) {
+ $message = sprintf('Task class file "%s" does not exist.',
+ $path_file);
+ throw new InvalidArgumentException($message);
+ }
+ $action = new LogAction();
+ $action->name = $name;
+ $action->description = $description;
+ $action->info_template = $info_template;
+ $action->filename = $path_file;
+ $action->class = $class;
+ $action->type = 'file';
+ $action->store();
+ }
+
+ /**
+ * Removes the action from database.
+ * Deletes all related log events also.
+ *
+ * @param string $name The name of the log action.
+ * @return mixed Number of deleted objects or false if action is unknown.
+ */
+ public static function unregisterAction($name)
+ {
+ $action = LogAction::findOneByName($name);
+ if ($action) {
+ return $action->delete();
+ }
+ return false;
+ }
+
+ /**
+ * Finds all seminars by given search string. Searches for the name of
+ * existing or already deleted seminars.
+ *
+ * @param string $needle The needle to search for.
+ * @return array
+ */
+ public static function searchSeminar($needle)
+ {
+ $result = [];
+
+ // search for active seminars
+ $courses = Course::findBySQL("VeranstaltungsNummer LIKE CONCAT('%', :needle, '%')
+ OR seminare.Name LIKE CONCAT('%', :needle, '%') ORDER BY start_time DESC",
+ [':needle' => $needle]);
+
+ foreach ($courses as $course) {
+ $title = sprintf('%s %s (%s)',
+ $course->VeranstaltungsNummer,
+ my_substr($course->name, 0, 40),
+ $course->start_semester->name);
+ $result[] = [$course->getId(), $title];
+ }
+
+ // search deleted seminars
+ // SemName and Number is part of info field, old id (still in DB) is in affected column
+ $log_action_ids_archived_seminar = SimpleORMapCollection::createFromArray(
+ LogAction::findBySQL(
+ "name IN ('SEM_ARCHIVE', 'SEM_DELETE_FROM_ARCHIVE')"))
+ ->pluck('action_id');
+ $log_events_archived_seminar = LogEvent::findBySQL("info LIKE CONCAT('%', ?, '%')
+ AND action_id IN (?) ",
+ [$needle, $log_action_ids_archived_seminar]);
+ foreach ($log_events_archived_seminar as $log_event) {
+ $title = sprintf('%s (%s)', my_substr($log_event->info, 0, 40), _('gelöscht'));
+ $result[] = [$log_event->affected_range_id, $title];
+ }
+
+ return $result;
+ }
+
+ /**
+ * Finds all institutes by given search string. Searches for the name of
+ * existing or already deleted institutes.
+ *
+ * @param type $needle The needle to search for.
+ * @return array
+ */
+ public static function searchInstitute($needle)
+ {
+ $result = [];
+
+ $institutes = Institute::findBySQL(
+ "name LIKE CONCAT('%', ?, '%') ORDER BY name", [$needle]);
+ foreach ($institutes as $institute) {
+ $result[] = [$institute->getId(), my_substr($institute->name, 0, 28)];
+ }
+
+ // search for deleted institutes
+ // Name of deleted institute is part of info field,
+ // old id (still in DB) is in affected column
+ $log_action_delete_institute = LogAction::findOneByName('INST_DEL');
+ $log_events_delete_institute = LogEvent::findBySQL(
+ "action_id = ? AND info LIKE CONCAT('%', ?, '%')",
+ [$log_action_delete_institute->getId(), $needle]);
+ foreach ($log_events_delete_institute as $log_event) {
+ $title = sprintf('%s (%s)', $log_event->info, _('gelöscht'));
+ $result[] = [$log_event->affected_range_id, $title];
+ }
+
+ return $result;
+ }
+
+ /**
+ * Finds all users by given search string. Searches for the users id,
+ * part of the name or the username.
+ *
+ * @param type $needle The needle to search for.
+ * @return array
+ */
+ public static function searchUser($needle)
+ {
+ $users = User::findBySQL(
+ "Nachname LIKE CONCAT('%', :needle, '%')
+ OR Vorname LIKE CONCAT('%', :needle, '%')
+ OR CONCAT(Nachname, ', ', Vorname) LIKE CONCAT('%', :needle, '%')
+ OR CONCAT(Vorname, ' ', Nachname) LIKE CONCAT('%', :needle, '%')
+ OR username LIKE CONCAT('%', :needle, '%')
+ ORDER BY Nachname DESC, Vorname DESC",
+ [':needle' => $needle]
+ );
+
+ $result = [];
+ foreach ($users as $user) {
+ $name = sprintf(
+ '%s (%s)',
+ my_substr($user->getFullName(), 0, 20),
+ $user->username
+ );
+ $result[] = [$user->getId(), $name];
+ }
+
+ // search for deleted users
+ //
+ // The name of the user is part of info field,
+ // old id (still in DB) is in affected column.
+ //
+ // The log action "USER_DEL" was removed from the list of initially
+ // registered log actions in the past.
+ // Search for the user if it is still in database. If not, the search
+ // for deleted users is not possible.
+ $log_action_deleted_user = LogAction::findOneByName('USER_DEL');
+ if ($log_action_deleted_user) {
+ $log_events_deleted_user = LogEvent::findBySQL(
+ "action_id = ? AND info LIKE CONCAT('%', ?, '%')",
+ [$log_action_deleted_user->getId(), $needle]
+ );
+ foreach ($log_events_deleted_user as $log_event) {
+ $name = sprintf('%s (%s)', $log_event->info, _('gelöscht'));
+ $result[] = [$log_event->affected_range_id, $name];
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Finds all resources by given search string. The search string can be
+ * either a resource id or part of the name.
+ *
+ * @param string $needle The needle to search for.
+ * @return array
+ */
+ public static function searchResource($needle)
+ {
+ $result = [];
+
+ $query = "SELECT id, name FROM resources WHERE name LIKE CONCAT('%', ?, '%') ORDER by name";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$needle]);
+
+ while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
+ $result[] = [$row['id'], my_substr($row['name'], 0, 30)];
+ }
+
+ return $result;
+ }
+
+ /**
+ * Finds all objects related to the given action by search string.
+ * The search string can be either a part of the name or the id
+ * of the object.
+ *
+ * Calls the method Loggable::logSearch() to retrieve the result.
+ *
+ * @param string $needle
+ * @param type $action_id
+ * @return type
+ */
+ public static function searchObjectByAction($needle, $action_id)
+ {
+ $action = LogAction::find($action_id);
+
+ if ($action) {
+ switch ($action->type) {
+ case 'plugin':
+ $plugin_manager = PluginManager::getInstance();
+ $plugin_info = $plugin_manager->getPluginInfo($action->class);
+ $class_name = $plugin_info['class'];
+ $plugin = $plugin_manager->getPlugin($class_name);
+ if ($plugin instanceof Loggable) {
+ return $class_name::logSearch($needle, $action->name);
+ }
+ break;
+ case 'file':
+ if (!file_exists($action->filename)) {
+ require_once($action->filename);
+ $class_name = $action->class;
+ if ($class_name instanceof Loggable) {
+ return $class_name::logSearch($needle, $action->name);
+ }
+ }
+ break;
+ case 'core':
+ $class_name = $action->class;
+ $interfaces = class_implements($class_name);
+ if (isset($interfaces['Loggable'])) {
+ return $class_name::logSearch($needle, $action->name);
+ }
+ }
+ }
+ return [];
+ }
+}