From 1e7019538a8ee3985bfc0a19960dca9737688a26 Mon Sep 17 00:00:00 2001
From: Peter Thienel <thienel@data-quest.de>
Date: Wed, 3 Jan 2024 15:08:46 +0000
Subject: [PATCH] Resolve "MVV: Logging von Personen und Dateizuordnungen"

Closes #3384

Merge request studip/studip!2302
---
 .../5.5.21_tic_3384_add_mvv_log_actions.php   | 154 +++++++++
 lib/classes/MVV.class.php                     | 317 +++++++++++++++---
 lib/models/MvvContact.php                     |  22 ++
 lib/models/MvvContactRange.php                |  28 +-
 lib/models/MvvExternContact.php               |  26 ++
 lib/models/MvvFile.php                        |  28 +-
 lib/models/MvvFileFileref.php                 |  23 ++
 lib/models/MvvFileRange.php                   |  24 ++
 8 files changed, 574 insertions(+), 48 deletions(-)
 create mode 100644 db/migrations/5.5.21_tic_3384_add_mvv_log_actions.php

diff --git a/db/migrations/5.5.21_tic_3384_add_mvv_log_actions.php b/db/migrations/5.5.21_tic_3384_add_mvv_log_actions.php
new file mode 100644
index 00000000000..127d0692bc2
--- /dev/null
+++ b/db/migrations/5.5.21_tic_3384_add_mvv_log_actions.php
@@ -0,0 +1,154 @@
+<?php
+
+class Tic3384AddMvvLogActions extends Migration
+{
+
+    public function description()
+    {
+        return 'Adds new log actions to mvv for contacts and files.';
+    }
+
+    public function up() {
+        StudipLog::registerAction(
+            'MVV_CONTACT_NEW',
+            'MVV: Kontaktperson erstellen',
+            '%user erstellt neue Kontaktperson %contact(%affected).',
+            'MVV'
+        );
+        StudipLog::registerAction(
+            'MVV_CONTACT_UPDATE',
+            'MVV: Kontaktperson ändern',
+            '%user ändert Kontaktperson %contact(%affected).',
+            'MVV'
+        );
+        StudipLog::registerAction(
+            'MVV_CONTACT_DELETE',
+            'MVV: Kontaktperson löschen',
+            '%user löscht Kontaktperson %contact(%affected).',
+            'MVV'
+        );
+
+        StudipLog::registerAction(
+            'MVV_EXTERN_CONTACT_NEW',
+            'MVV: Externe Kontaktperson erstellen',
+            '%user erstellt neue externe Kontaktperson %contact(%affected).',
+            'MVV'
+        );
+
+        StudipLog::registerAction(
+            'MVV_EXTERN_CONTACT_UPDATE',
+            'MVV: Externe Kontaktperson ändern',
+            '%user ändert externe Kontaktperson %contact(%affected).',
+            'MVV'
+        );
+        StudipLog::registerAction(
+            'MVV_EXTERN_CONTACT_DELETE',
+            'MVV: Externe Kontaktperson löschen',
+            '%user löscht externe Kontaktperson %contact(%affected).',
+            'MVV'
+        );
+
+        StudipLog::registerAction(
+            'MVV_CONTACT_RANGE_NEW',
+            'MVV: Kontaktperson zuordnen',
+            '%user ordnet die Kontaktperson %contact(%affected) dem Bereich %range(%coaffected) zu.',
+            'MVV'
+        );
+        StudipLog::registerAction(
+            'MVV_CONTACT_RANGE_DELETE',
+            'MVV: Zuordnung der Kontaktperson löschen',
+            '%user löscht die Zuordnung der Kontaktperson %contact(%affected) zum Bereich %range(%coaffected).',
+            'MVV'
+        );
+        StudipLog::registerAction(
+            'MVV_CONTACT_RANGE_UPDATE',
+            'MVV: Zuordnung der Kontaktperson ändern',
+            '%user ändert die Zuordnung der Kontaktperson %contact(%affected) zum Bereich %range(%coaffected).',
+            'MVV'
+        );
+
+        StudipLog::registerAction(
+            'MVV_FILE_NEW',
+            'MVV: Material/Dokument erstellen',
+            '%user erstellt neues Material/Dokument %file(%affected).',
+            'MVV'
+        );
+        StudipLog::registerAction(
+            'MVV_FILE_UPDATE',
+            'MVV: Material/Dokument ändern',
+            '%user ändert Material/Dokument %file(%affected).',
+            'MVV'
+        );
+        StudipLog::registerAction(
+            'MVV_FILE_DELETE',
+            'MVV: Material/Dokument löschen',
+            '%user löscht Material/Dokument %file(%affected).',
+            'MVV'
+        );
+
+        StudipLog::registerAction(
+            'MVV_FILE_FILEREF_NEW',
+            'MVV: Datei erstellen',
+            '%user erstellt neue Datei %fileref(%affected).',
+            'MVV'
+        );
+        StudipLog::registerAction(
+            'MVV_FILE_FILEREF_UPDATE',
+            'MVV: Datei ändern',
+            '%user ändert Datei %fileref(%affected).',
+            'MVV'
+        );
+        StudipLog::registerAction(
+            'MVV_FILE_FILEREF_DELETE',
+            'MVV: Datei löschen',
+            '%user löscht Datei %fileref(%affected).',
+            'MVV'
+        );
+
+        StudipLog::registerAction(
+            'MVV_FILE_RANGE_NEW',
+            'MVV: Material/Dokument zuordnen',
+            '%user ordnet Material/Dokument %fileref(%affected) zum Bereich %range(%coaffected) zu.',
+            'MVV'
+        );
+        StudipLog::registerAction(
+            'MVV_FILE_RANGE_UPDATE',
+            'MVV: Zuordnung von Material/Dokument zu Bereich ändern.',
+            '%user ändert Zuordnung von Material/Dokument %fileref(%affected) zu Bereich %range(%coaffected).',
+            'MVV'
+        );
+        StudipLog::registerAction(
+            'MVV_FILE_RANGE_DELETE',
+            'MVV: Zuordnung von Material/Dokument zu Bereich löschen',
+            '%user löscht Zuordnung von Material/Dokument %fileref(%affected) von Bereich %range/%coaffected).',
+            'MVV'
+        );
+    }
+
+    public function down() {
+        StudipLog::unregisterAction('MVV_CONTACT_NEW');
+        StudipLog::unregisterAction('MVV_CONTACT_UPDATE');
+        StudipLog::unregisterAction('MVV_CONTACT_DELETE');
+
+        StudipLog::unregisterAction('MVV_EXTERN_CONTACT_NEW');
+        StudipLog::unregisterAction('MVV_EXTERN_CONTACT_UPDATE');
+        StudipLog::unregisterAction('MVV_EXTERN_CONTACT_DELETE');
+
+        StudipLog::unregisterAction('MVV_CONTACT_RANGE_NEW');
+        StudipLog::unregisterAction('MVV_CONTACT_RANGE_UPDATE');
+        StudipLog::unregisterAction('MVV_CONTACT_RANGE_DELETE');
+
+        StudipLog::unregisterAction('MVV_FILE_NEW');
+        StudipLog::unregisterAction('MVV_FILE_UPDATE');
+        StudipLog::unregisterAction('MVV_FILE_DELETE');
+
+        StudipLog::unregisterAction('MVV_FILE_FILEREF_NEW');
+        StudipLog::unregisterAction('MVV_FILE_FILEREF_UPDATE');
+        StudipLog::unregisterAction('MVV_FILE_FILEREF_DELETE');
+
+        StudipLog::unregisterAction('MVV_FILE_RANGE_NEW');
+        StudipLog::unregisterAction('MVV_FILE_RANGE_UPDATE');
+        StudipLog::unregisterAction('MVV_FILE_RANGE_DELETE');
+    }
+
+}
diff --git a/lib/classes/MVV.class.php b/lib/classes/MVV.class.php
index 6c577093779..a9274986659 100644
--- a/lib/classes/MVV.class.php
+++ b/lib/classes/MVV.class.php
@@ -86,7 +86,6 @@ class MVV implements Loggable {
         $templ = $event->action->info_template;
 
         $table = explode('.', $event->info);
-
         switch ($table[0]) {
 
             case 'abschluss':
@@ -118,22 +117,6 @@ class MVV implements Loggable {
                 }
                 break;
 
-            case 'mvv_files':
-                $dokument = MvvFile::find($event->affected_range_id);
-                if ($dokument) {
-                    $url = URLHelper::getURL('dispatch.php/materialien/dokumente/details/' . $dokument->getId(), [], true);
-                    $templ = str_replace('%dokument(%affected)', '<a href="' . $url . '">' . htmlReady($dokument->getDisplayName()) . '</a>', $templ);
-                    if ($event->coaffected_range_id) {
-                        $mmv_object = call_user_func([$event->dbg_info, 'find'], $event->coaffected_range_id);
-                        if ($mmv_object) {
-                            $templ = str_replace('%object_type(%coaffected)', 'in ' . htmlReady($mmv_object->getDisplayName()), $templ);
-                        } else {
-                            $templ = str_replace('%object_type(%coaffected)', '', $templ);
-                        }
-                    }
-                }
-                break;
-
             case 'fach':
             case 'mvv_fach_inst':
                 $fach = Fach::find($event->affected_range_id);
@@ -312,6 +295,148 @@ class MVV implements Loggable {
                 }
                 break;
 
+            case 'mvv_contacts':
+                $contact = MvvContact::find($event->affected_range_id);
+                if ($contact) {
+                    $url = URLHelper::getLink('dispatch.php/shared/contacts/details/index/' . $contact->id);
+                    $templ = str_replace(
+                        '%contact(%affected)',
+                        '<a href="' . $url . '">' . htmlReady($contact->getDisplayName()) . '</a>',
+                        $templ
+                    );
+                }
+                break;
+
+            case 'mvv_contacts_ranges':
+                $contact_range = MvvContactRange::find($event->dbg_info);
+                if ($contact_range) {
+                    if ($contact_range->contact) {
+                        $url_affected = URLHelper::getLink(
+                            'dispatch.php/shared/contacts/details/index/' . $contact_range->contact->id);
+                        $templ = str_replace(
+                            '%contact(%affected)',
+                            '<a href="' . $url_affected . '">' . htmlReady($contact_range->contact->getDisplayName()) . '</a>',
+                            $templ
+                        );
+                    }
+                    $range = null;
+                    switch ($contact_range->range_type) {
+                        case 'Modul':
+                            $range = Modul::find($event->coaffected_range_id);
+                            $url_coaffected = URLHelper::getLink(
+                                'dispatch.php/module/module/details/' . $range->id,
+                                [],
+                                true
+                            );
+                            break;
+                        case 'Studiengang':
+                            $range = Studiengang::find($event->coaffected_range_id);
+                            $url_coaffected = URLHelper::getLink(
+                                'dispatch.php/studiengaenge/studiengaenge/details_studiengang/' . $range->id,
+                                [],
+                                true
+                            );
+                            break;
+                        case 'StudiengangTeil':
+                            $range = StudiengangTeil::find($event->coaffected_range_id);
+                            $url_coaffected = URLHelper::getLink(
+                                'dispatch.php/studiengaenge/studiengangteile/details_versionen/' . $range->id,
+                                [],
+                                true
+                            );
+
+                    }
+                    if ($range) {
+                        $templ = str_replace(
+                            '%range(%coaffected)',
+                            '<a href="' . $url_coaffected . '">' . htmlReady($range->getDisplayName()) . '</a>',
+                            $templ
+                        );
+                    }
+                }
+                break;
+
+            case 'mvv_extern_contacts':
+                $extern_contact = MvvExternContact::find($event->affected_range_id);
+                if ($extern_contact) {
+                    $url = URLHelper::getLink('dispatch.php/shared/contacts/details/index/' . $extern_contact->id);
+                    $templ = str_replace(
+                        '%contact(%affected)',
+                        '<a href="' . $url . '">' . htmlReady($extern_contact->getDisplayName()) . '</a>',
+                        $templ
+                    );
+                }
+                break;
+
+            case 'mvv_files':
+                $file = MvvFile::find($event->affected_range_id);
+                if ($file) {
+                    $url = URLHelper::getLink('dispatch.php/materialien/files/index/' . $file->id);
+                    $templ = str_replace(
+                        '%file(%affected)',
+                        '<a href="' . $url . '">' . htmlReady($file->getDisplayName()) . '</a>',
+                        $templ
+                    );
+                }
+                break;
+
+            case 'mvv_files_filerefs':
+                $fileref = MvvFileFileref::find($event->affected_range_id);
+                if ($fileref) {
+                    $url = URLHelper::getLink('dispatch.php/materialien/index/' . $fileref->mvvfile_id);
+                    $templ = str_replace(
+                        '%fileref(%affected)',
+                        '<a href="' . $url . '">' . htmlReady($fileref->getDisplayName()) . '</a>',
+                        $templ
+                    );
+                }
+                break;
+
+            case 'mvv_files_ranges':
+                $file_range = MvvFileRange::find([
+                    $event->affected_range_id,
+                    $event->coaffected_range_id
+                ]);
+                if ($file_range) {
+                    if ($file_range->mvv_file) {
+                        $url_affected = URLHelper::getLink(
+                            'dispatch.php/materialien/index/' . $file_range->mvvfile_id
+                        );
+                        $templ = str_replace(
+                            '%fileref(%affected)',
+                            '<a href="' . $url_affected . '">' . htmlReady($file_range->mvv_file->getDisplayName()) . '</a>',
+                            $templ
+                        );
+                    }
+                    $range = null;
+                    switch ($file_range->range_type) {
+                        case 'Studiengang':
+                            $range = Studiengang::find($event->coaffected_range_id);
+                            $url_coaffected = URLHelper::getLink(
+                                'dispatch.php/studiengaenge/studiengaenge/details_studiengang/' . $range->id,
+                                [],
+                                true
+                            );
+                            break;
+                        case 'StgteilVersion':
+                            $range = StgteilVersion::find($event->coaffected_range_id);
+                            $url_coaffected = URLHelper::getLink(
+                                'dispatch.php/studiengaenge/studiengangteile/version/' . $range->id,
+                                [],
+                                true
+                            );
+
+                    }
+                    if ($range) {
+                        $templ = str_replace(
+                            '%range(%coaffected)',
+                            '<a href="' . $url_coaffected . '">' . htmlReady($range->getDisplayName()) . '</a>',
+                            $templ
+                        );
+                    }
+                }
+                break;
+
             default:
                 break;
         }
@@ -320,7 +445,6 @@ class MVV implements Loggable {
         // Benutzergruppen im Modul z.B. Modulverantwortlicher
         $templ = str_replace('%gruppe', $event->dbg_info, $templ);
         // Objekt konnte nicht eingesetzt werden da es vermutlich nicht mehr existiert, beim delete landet der alte Bezeichner im debug
-        $templ = str_replace('%dokument(%affected)', $event->dbg_info, $templ);
         $templ = str_replace('%modul(%affected)', $event->dbg_info, $templ);
         $templ = str_replace('%modulteil(%affected)', $event->dbg_info, $templ);
         $templ = str_replace('%modulteildesk(%affected)', $event->dbg_info, $templ);
@@ -341,11 +465,11 @@ class MVV implements Loggable {
         $templ = str_replace('%language(%coaffected)', $event->dbg_info, $templ);
         $templ = str_replace('%stgteilabs(%coaffected)', $event->dbg_info, $templ);
         $templ = str_replace('%stgteil(%coaffected)', $event->dbg_info, $templ);
+        $templ = str_replace('%contact', $event->dbg_info, $templ);
 
         return $templ;
     }
 
-
     /**
      * This method searches the log-entries for log-actions of the mvv classes.
      * Used by search function on log page.
@@ -358,7 +482,6 @@ class MVV implements Loggable {
     public static function logSearch($needle, $action_name = null)
     {
         $result = [];
-        $sql_needle = DBManager::get()->quote($needle);
 
         $modul_actions = [
             'MVV_MODUL_NEW',
@@ -385,8 +508,14 @@ class MVV implements Loggable {
         ];
 
         if (in_array($action_name, $modul_actions)) {
-            $module = Modul::findBySQL("code LIKE CONCAT('%', " . $sql_needle . ", '%') OR modul_id = " . $sql_needle);
-            $deskriptoren = ModulDeskriptor::findBySql("bezeichnung LIKE CONCAT('%', " . $sql_needle . ", '%') OR deskriptor_id = " . $sql_needle);
+            $module = Modul::findBySQL(
+                "code LIKE CONCAT('%', :needle, '%') OR modul_id = :needle"
+            );
+            $deskriptoren = ModulDeskriptor::findBySql(
+                "bezeichnung LIKE CONCAT('%', :needle, '%')
+                 OR deskriptor_id = :needle",
+                [':needle' => $needle]
+            );
             foreach ($module as $modul) {
                 $result[] = [
                     $modul->getId(),
@@ -418,7 +547,11 @@ class MVV implements Loggable {
         ];
 
         if (in_array($action_name, $modulteile_actions)) {
-            $deskriptoren = ModulDeskriptor::findBySql("bezeichnung LIKE CONCAT('%', " . $sql_needle . ", '%') OR deskriptor_id = " . $sql_needle);
+            $deskriptoren = ModulDeskriptor::findBySql(
+                "bezeichnung LIKE CONCAT('%', :needle, '%')
+                 OR deskriptor_id = :needle",
+                [':needle' => $needle]
+            );
             foreach ($deskriptoren as $desk) {
                 $modulteil = Modulteil::find($desk->modulteil_id);
                 $result[] = [
@@ -436,7 +569,11 @@ class MVV implements Loggable {
             'MVV_STG_STGTEIL_DEL',
             'MVV_STG_STGTEIL_UPDATE'
         ])) {
-            $stg = Studiengang::findBySQL("name LIKE CONCAT('%', " . $sql_needle . ", '%') OR studiengang_id = " . $sql_needle);
+            $stg = Studiengang::findBySQL(
+                "name LIKE CONCAT('%', :needle, '%')
+                 OR studiengang_id = :needle",
+                [':needle' => $needle]
+            );
             foreach ($stg as $studiengang) {
                 $result[] = [
                     $studiengang->getId(),
@@ -453,7 +590,12 @@ class MVV implements Loggable {
             'MVV_FACHBERATER_UPDATE',
             'MVV_FACHBERATER_DEL'
         ])) {
-            $stgteile = StudiengangTeil::findBySQL("zusatz LIKE CONCAT('%', " . $sql_needle . ", '%') OR zusatz_en LIKE CONCAT('%', " . $sql_needle . ", '%') OR stgteil_id = " . $sql_needle);
+            $stgteile = StudiengangTeil::findBySQL(
+                "zusatz LIKE CONCAT('%', :needle, '%')
+                 OR zusatz_en LIKE CONCAT('%', :needle, '%')
+                 OR stgteil_id = :needle",
+                [':needle' => $needle]
+            );
             foreach ($stgteile as $stgteil) {
                 $result[] = [
                     $stgteil->getId(),
@@ -467,7 +609,12 @@ class MVV implements Loggable {
             'MVV_STGTEILVERSION_UPDATE',
             'MVV_STGTEILVERSION_DEL'
         ])) {
-            $versionen = StgteilVersion::findBySQL("code LIKE CONCAT('%', " . $sql_needle . ", '%') OR version_id = " . $sql_needle . " OR stgteil_id = " . $sql_needle);
+            $versionen = StgteilVersion::findBySQL(
+                "code LIKE CONCAT('%', :needle, '%')
+                 OR version_id = :needle
+                 OR stgteil_id = :needle",
+                [':needle' => $needle]
+            );
             foreach ($versionen as $version) {
                 $result[] = [
                     $version->getId(),
@@ -481,7 +628,12 @@ class MVV implements Loggable {
             'MVV_STGTEILBEZ_UPDATE',
             'MVV_STGTEILBEZ_DEL'
         ])) {
-            $stgbez = StgteilBezeichnung::findBySQL("name LIKE CONCAT('%', " . $sql_needle . ", '%') OR name_en LIKE CONCAT('%', " . $sql_needle . ", '%') OR stgteil_bez_id = " . $sql_needle);
+            $stgbez = StgteilBezeichnung::findBySQL(
+                "name LIKE CONCAT('%', :needle, '%')
+                 OR name_en LIKE CONCAT('%', :needle, '%')
+                 OR stgteil_bez_id = :needle",
+                [':needle' => $needle]
+            );
             foreach ($stgbez as $bez) {
                 $result[] = [
                     $bez->getId(),
@@ -506,7 +658,12 @@ class MVV implements Loggable {
         ];
 
         if (in_array($action_name, $stgteil_actions)) {
-            $stgteilabs = Lvgruppe::findBySQL("name LIKE CONCAT('%', " . $sql_needle . ", '%') OR name_en LIKE CONCAT('%', " . $sql_needle . ", '%') OR abschnitt_id = " . $sql_needle);
+            $stgteilabs = Lvgruppe::findBySQL(
+                "name LIKE CONCAT('%', :needle, '%')
+                 OR name_en LIKE CONCAT('%', :needle, '%')
+                 OR abschnitt_id = :needle",
+                [':needle' => $needle]
+            );
             foreach ($stgteilabs as $abschnitt) {
                 $result[] = [
                     $abschnitt->getId(),
@@ -526,7 +683,12 @@ class MVV implements Loggable {
             'MVV_LVSEMINAR_DEL',
             'MVV_LVSEMINAR_UPDATE'
         ])) {
-            $lvgruppen = Lvgruppe::findBySQL("name LIKE CONCAT('%', " . $sql_needle . ", '%') OR name_en LIKE CONCAT('%', " . $sql_needle . ", '%') OR lvgruppe_id = " . $sql_needle);
+            $lvgruppen = Lvgruppe::findBySQL(
+                "name LIKE CONCAT('%', :needle, '%')
+                 OR name_en LIKE CONCAT('%', :needle, '%')
+                 OR lvgruppe_id = :needle",
+                [':needle' => $needle]
+            );
             foreach ($lvgruppen as $lvgruppe) {
                 $result[] = [
                     $lvgruppe->getId(),
@@ -543,7 +705,12 @@ class MVV implements Loggable {
             'MVV_FACHINST_DEL',
             'MVV_FACHINST_UPDATE'
         ])) {
-            $faecher = Fach::findBySQL("name LIKE CONCAT('%', " . $sql_needle . ", '%') OR name_en LIKE CONCAT('%', " . $sql_needle . ", '%') OR fach_id = " . $sql_needle);
+            $faecher = Fach::findBySQL(
+                "name LIKE CONCAT('%', :needle, '%')
+                 OR name_en LIKE CONCAT('%', :needle, '%')
+                 OR fach_id = :needle",
+                [':needle' => $needle]
+            );
             foreach ($faecher as $fach) {
                 $result[] = [
                     $fach->getId(),
@@ -560,7 +727,12 @@ class MVV implements Loggable {
             'MVV_ABS_ZUORD_DEL',
             'MVV_ABS_ZUORD_UPDATE'
         ])) {
-            $abschluesse = Abschluss::findBySQL("name LIKE CONCAT('%', " . $sql_needle . ", '%') OR name_en LIKE CONCAT('%', " . $sql_needle . ", '%') OR abschluss_id = " . $sql_needle);
+            $abschluesse = Abschluss::findBySQL(
+                "name LIKE CONCAT('%', :needle, '%')
+                 OR name_en LIKE CONCAT('%', :needle, '%')
+                 OR abschluss_id = :needle",
+                [':needle' => $needle]
+            );
             foreach ($abschluesse as $abschluss) {
                 $result[] = [
                     $abschluss->getId(),
@@ -577,7 +749,12 @@ class MVV implements Loggable {
             'MVV_ABS_ZUORD_DEL',
             'MVV_ABS_ZUORD_UPDATE'
         ])) {
-            $abskategorien = AbschlussKategorie::findBySQL("name LIKE CONCAT('%', " . $sql_needle . ", '%') OR name_en LIKE CONCAT('%', " . $sql_needle . ", '%') OR kategorie_id = " . $sql_needle);
+            $abskategorien = AbschlussKategorie::findBySQL(
+                "name LIKE CONCAT('%', :needle, '%')
+                 OR name_en LIKE CONCAT('%', :needle, '%')
+                 OR kategorie_id = :needle",
+                [':needle' => $needle]
+            );
             foreach ($abskategorien as $abskategorie) {
                 $result[] = [
                     $abskategorie->getId(),
@@ -587,22 +764,70 @@ class MVV implements Loggable {
         }
 
         if (in_array($action_name, [
-            'MVV_DOKUMENT_NEW',
-            'MVV_DOKUMENT_UPDATE',
-            'MVV_DOKUMENT_DEL',
-            'MVV_DOK_ZUORD_NEW',
-            'MVV_DOK_ZUORD_DEL',
-            'MVV_DOK_ZUORD_UPDATE'
+            'MVV_CONTACT_NEW',
+            'MVV_CONTACT_UPDATE',
+            'MVV_CONTACT_DELETE',
+            'MVV_CONTACT_RANGE_NEW',
+            'MVV_CONTACT_RANGE_UPDATE',
+            'MVV_CONTACT_RANGE_DELETE',
+            'MVV_CONTACT_EXTERN_NEW',
+            'MVV_CONTACT_EXTERN_UPDATE',
+            'MVV_CONTACT_EXTERN_DELETE'
         ])) {
-            MvvFile::findEachBySQL(
-                function (MvvFile $file) use (&$result) {
-                    $result[] = [
-                        $file->id,
-                        $file->getDisplayName(),
-                    ];
-                },
-                "name LIKE CONCAT('%', " . $sql_needle . ", '%') OR mvvfile_id = " . $sql_needle
+            $contacts_intern = MvvContact::findBySQL(
+                "LEFT JOIN `auth_user_md5`
+                   ON `mvv_contacts`.`contact_id` = `auth_user_md5`.`user_id`
+                 WHERE `auth_user_md5`.`username` LIKE CONCAT('%', :needle, '%')
+                    OR `auth_user_md5`.`nachname` LIKE CONCAT('%', :needle, '%')
+                    OR `auth_user_md5`.`email` = :needle",
+                [':needle' => $needle]
             );
+            $contacts_extern = MvvContact::findBySQL(
+                "LEFT JOIN `mvv_extern_contacts`
+                   ON `mvv_contacts`.`contact_id` = `mvv_extern_contacts`.`extern_contact_id`
+                 WHERE `mvv_extern_contacts`.`name` = :needle
+                    OR `mvv_extern_contacts`.`mail` = :needle",
+                [':needle' => $needle]
+            );
+            $contacts = array_merge($contacts_intern, $contacts_extern);
+            foreach ($contacts as $contact) {
+                $result[] = [
+                    $contact->id,
+                    $contact->getDisplayName()
+                ];
+            }
+        }
+
+        if (in_array($action_name, [
+            'MVV_FILE_NEW',
+            'MVV_FILE_UPDATE',
+            'MVV_FILE_DELETE',
+            'MVV_FILE_RANGE_NEW',
+            'MVV_FILE_RANGE_UPDATE',
+            'MVV_FILE_RANGE_DELETE',
+            'MVV_FILE_FILEREF_NEW',
+            'MVV_FILE_FILEREF_UPDATE',
+            'MVV_FILE_FILEREF_DEL'
+        ])) {
+            $files = MvvFile::findBySQL(
+                "LEFT JOIN `mvv_files_filerefs` USING (`mvvfile_id`)
+                 LEFT JOIN `file_refs`
+                   ON `mvv_files_filerefs`.`fileref_id` = `file_refs`.`id`
+                 LEFT JOIN `files`
+                   ON `file_refs`.`file_id` = `files`.`id`
+                 WHERE `mvv_files`.`tags` LIKE CONCAT('%', :needle, '%')
+                    OR `mvv_files_filerefs`.`name` LIKE CONCAT('%', :needle, '%')
+                    OR `file_refs`.`name` LIKE CONCAT('%', :needle, '%')
+                    OR `files`.`name` = :needle
+                    OR `files`.`author_name` = :needle",
+                [':needle' => $needle]
+            );
+            foreach ($files as $file) {
+                $result[$file->id] = [
+                    $file->id,
+                    $file->getDisplayName()
+                ];
+            }
         }
 
         return $result;
diff --git a/lib/models/MvvContact.php b/lib/models/MvvContact.php
index a174bd68c63..075acf9dd80 100644
--- a/lib/models/MvvContact.php
+++ b/lib/models/MvvContact.php
@@ -452,4 +452,26 @@ class MvvContact extends ModuleManagementModel
         ];
     }
 
+    protected function logChanges($action = null)
+    {
+        $log_action = 'MVV_CONTACT_' . mb_strtoupper($action);
+        $affected = $this->id;
+        $info = ['mvv_contacts.*'];
+        $debug_info = $this->getDisplayName();
+        if ($action === 'update') {
+            if ($this->isFieldDirty('contact_status')) {
+                $info[] = 'contatct_status: '
+                    . ($this->contact_status ?? '-')
+                    . ' (' . ($this->getPristineValue('contact_status') ?? '-')
+                    . ')';
+            }
+            if ($this->isFieldDirty('alt_mail')) {
+                $info[] = 'alt_mail: '
+                    . ($this->alt_mail ?? '-')
+                    . ' (' . ($this->getPristineValue('alt_mail') ?? '-')
+                    . ')';
+            }
+        }
+        StudipLog::log($log_action, $affected, '', implode(' | ', $info), $debug_info);
+    }
 }
diff --git a/lib/models/MvvContactRange.php b/lib/models/MvvContactRange.php
index 4b3bcfcc267..72930b49935 100644
--- a/lib/models/MvvContactRange.php
+++ b/lib/models/MvvContactRange.php
@@ -182,7 +182,7 @@ class MvvContactRange extends ModuleManagementModel
     /**
      * Returns the 'PERSONEN_GRUPPEN' from mvv config of given range type.
      *
-     * @param sting $range_type type of the mvv object.
+     * @param string $range_type type of the mvv object.
      * @return array PERSONEN_GRUPPEN
      */
     public static function getCategoriesByRangetype($range_type)
@@ -214,4 +214,30 @@ class MvvContactRange extends ModuleManagementModel
         return $cats[$this->category]['name'];
     }
 
+    protected function logChanges($action = null)
+    {
+        $log_action = 'MVV_CONTACT_RANGE_' . mb_strtoupper($action);
+        $affected = $this->contact_id;
+        $co_affected = $this->range_id;
+        $info = ['mvv_contacts_ranges.*'];
+        $debug_info = $this->id;
+        if ($action === 'update') {
+            $logged_fields = [
+                'range_type',
+                'type',
+                'category',
+                'position',
+            ];
+            foreach ($logged_fields as $logged_field) {
+                if ($this->isFieldDirty($logged_field)) {
+                    $info[] = $logged_field
+                        . ': ' . ($this->getValue($logged_field) ?? '-')
+                        . ' (' . ($this->getPristineValue($logged_field) ?? '-')
+                        . ')';
+                }
+            }
+        }
+        StudipLog::log($log_action, $affected, $co_affected, implode(' | ', $info), $debug_info);
+    }
+
 }
diff --git a/lib/models/MvvExternContact.php b/lib/models/MvvExternContact.php
index 504de65d994..4e13ad461ea 100644
--- a/lib/models/MvvExternContact.php
+++ b/lib/models/MvvExternContact.php
@@ -43,4 +43,30 @@ class MvvExternContact extends ModuleManagementModel
 
         parent::configure($config);
     }
+
+    protected function logChanges($action = null)
+    {
+        $log_action = 'MVV_EXTERN_CONTACT_' . mb_strtoupper($action);
+        $affected = $this->id;
+        $info = ['mvv_extern_contacts.*'];
+        $debug_info = $this->getDisplayName();
+        if ($action === 'update') {
+            $logged_fields = [
+                'name',
+                'vorname',
+                'homepage',
+                'mail',
+                'tel',
+            ];
+            foreach ($logged_fields as $logged_field) {
+                if ($this->isFieldDirty($logged_field)) {
+                    $info[] = $logged_field
+                        . ': ' . ($this->getValue($logged_field) ?? '-')
+                        . ' (' . ($this->getPristineValue($logged_field) ?? '-')
+                        . ')';
+                }
+            }
+        }
+        StudipLog::log($log_action, $affected, null, implode(' | ', $info), $debug_info);
+    }
 }
diff --git a/lib/models/MvvFile.php b/lib/models/MvvFile.php
index f4f4324c2e5..44d908e349c 100644
--- a/lib/models/MvvFile.php
+++ b/lib/models/MvvFile.php
@@ -369,7 +369,7 @@ class MvvFile extends ModuleManagementModel
     /**
      * Adds this mvvfile to given range.
      *
-     * @param sting $range_id Id of the mvv object.
+     * @param string $range_id Id of the mvv object.
      */
     public function addToRange($range_id, $range_type)
     {
@@ -489,4 +489,30 @@ class MvvFile extends ModuleManagementModel
         return $stm->fetchAll(PDO::FETCH_COLUMN, 0);
     }
 
+    protected function logChanges($action = null)
+    {
+        $log_action = 'MVV_FILE_' . mb_strtoupper($action);
+        $affected = $this->id;
+        $info = ['mvv_files.*'];
+        $debug_info = $this->getDisplayName();
+        if ($action === 'update') {
+            $logged_fields = [
+                'year',
+                'type',
+                'category',
+                'tags',
+                'extern_visible',
+            ];
+            foreach ($logged_fields as $logged_field) {
+                if ($this->isFieldDirty($logged_field)) {
+                    $info[] = $logged_field
+                        . ': ' . ($this->getValue($logged_field) ?? '-')
+                        . ' (' . ($this->getPristineValue($logged_field) ?? '-')
+                        . ')';
+                }
+            }
+        }
+        StudipLog::log($log_action, $affected, null, implode(' | ', $info), $debug_info);
+    }
+
 }
diff --git a/lib/models/MvvFileFileref.php b/lib/models/MvvFileFileref.php
index fe2e9e62472..98cda759394 100644
--- a/lib/models/MvvFileFileref.php
+++ b/lib/models/MvvFileFileref.php
@@ -111,4 +111,27 @@ class MvvFileFileref extends ModuleManagementModel
 
         return false;
     }
+
+    protected function logChanges($action = null)
+    {
+        $log_action = 'MVV_FILEREF_' . mb_strtoupper($action);
+        $affected = $this->id;
+        $info = ['mvv_files_filerefs.*'];
+        $debug_info = $this->getDisplayName();
+        if ($action === 'update') {
+            $logged_fields = [
+                'file_language',
+                'name',
+            ];
+            foreach ($logged_fields as $logged_field) {
+                if ($this->isFieldDirty($logged_field)) {
+                    $info[] = $logged_field
+                        . ': ' . ($this->getValue($logged_field) ?? '-')
+                        . ' (' . ($this->getPristineValue($logged_field) ?? '-')
+                        . ')';
+                }
+            }
+        }
+        StudipLog::log($log_action, $affected, null, implode(' | ', $info), $debug_info);
+    }
 }
diff --git a/lib/models/MvvFileRange.php b/lib/models/MvvFileRange.php
index 9f0dc8d7bf8..9155c1fd909 100644
--- a/lib/models/MvvFileRange.php
+++ b/lib/models/MvvFileRange.php
@@ -55,4 +55,28 @@ class MvvFileRange extends ModuleManagementModel
         return $this->range_type;
     }
 
+    protected function logChanges($action = null)
+    {
+        $log_action = 'MVV_FILE_RANGE_' . mb_strtoupper($action);
+        $affected = $this->mvvfile_id;
+        $co_affected = $this->range_id;
+        $info = ['mvv_files_ranges.*'];
+        $debug_info = $this->id;
+        if ($action === 'update') {
+            $logged_fields = [
+                'range_type',
+                'position',
+            ];
+            foreach ($logged_fields as $logged_field) {
+                if ($this->isFieldDirty($logged_field)) {
+                    $info[] = $logged_field
+                        . ': ' . ($this->getValue($logged_field) ?? '-')
+                        . ' (' . ($this->getPristineValue($logged_field) ?? '-')
+                        . ')';
+                }
+            }
+        }
+        StudipLog::log($log_action, $affected, $co_affected, implode(' | ', $info), $debug_info);
+    }
+
 }
-- 
GitLab