From 5e92c29834f3c8bdc504034c9545d6d41bbff786 Mon Sep 17 00:00:00 2001
From: Jan-Hendrik Willms <tleilax+studip@gmail.com>
Date: Thu, 26 Sep 2024 13:46:56 +0000
Subject: [PATCH] resurrect code for copying basic data, participants and
 groups of course, re #3209, re #3441, fixes #4637

Closes #4637

Merge request studip/studip!3452
---
 .../coursewizardsteps/BasicDataWizardStep.php | 178 ++++++++++++++----
 1 file changed, 145 insertions(+), 33 deletions(-)

diff --git a/lib/classes/coursewizardsteps/BasicDataWizardStep.php b/lib/classes/coursewizardsteps/BasicDataWizardStep.php
index e6f5518b6f4..bca691502e2 100644
--- a/lib/classes/coursewizardsteps/BasicDataWizardStep.php
+++ b/lib/classes/coursewizardsteps/BasicDataWizardStep.php
@@ -294,7 +294,7 @@ class BasicDataWizardStep implements CourseWizardStep
             // Add default deputies if applicable.
             if (Config::get()->DEPUTIES_ENABLE && Config::get()->DEPUTIES_DEFAULTENTRY_ENABLE) {
                 $values['deputies'] = array_merge($values['deputies'] ?: [],
-                    array_flip(array_keys(Request::option('lecturer_id'))));
+                    array_flip(array_keys(Request::optionArray('lecturer_id'))));
             }
         }
         // Remove a lecturer.
@@ -451,34 +451,31 @@ class BasicDataWizardStep implements CourseWizardStep
         }
         $institutes = Institute::findMany($institute_ids);
         $course->institutes = $institutes;
-        if ($course->isDirty()) {
-            $course->store();
-        }
+        $course->store();
+
         if (isset($values['lecturers']) && is_array($values['lecturers'])) {
-            foreach (array_keys($values['lecturers']) as $user_id) {
-                $user = User::find($user_id);
-                if (!$user) {
-                    continue;
-                }
-                try {
-                    $course->addMember($user, 'dozent');
-                } catch (\Studip\Exception $e) {
-                    //Nothing here.
-                }
-            }
+            User::findEachMany(
+                function (User $user) use ($course) {
+                    try {
+                        $course->addMember($user, 'dozent');
+                    } catch (\Studip\Exception $e) {
+                        //Nothing here.
+                    }
+                },
+                array_keys($values['lecturers'])
+            );
         }
         if (isset($values['tutors']) && is_array($values['tutors'])) {
-            foreach (array_keys($values['tutors']) as $user_id) {
-                $user = User::find($user_id);
-                if (!$user) {
-                    continue;
-                }
-                try {
-                    $course->addMember($user, 'tutor');
-                } catch (\Studip\Exception $e) {
-                    //Nothing here.
-                }
-            }
+            User::findEachMany(
+                function (User $user) use ($course) {
+                    try {
+                        $course->addMember($user, 'tutor');
+                    } catch (\Studip\Exception $e) {
+                        //Nothing here.
+                    }
+                },
+                array_keys($values['tutors'])
+            );
         }
         if (Config::get()->DEPUTIES_ENABLE && isset($values['deputies']) && is_array($values['deputies'])) {
             foreach ($values['deputies'] as $d => $assigned) {
@@ -490,6 +487,10 @@ class BasicDataWizardStep implements CourseWizardStep
             CourseSet::addCourseToSet($course_set_id, $course->id);
         }
 
+        if ($source_id  && ($copy_participants || $copy_groups || $copy_members)) {
+            self::copyParticipantsAndGroups($course, $source_id, $copy_participants, $copy_groups, $copy_members);
+        }
+
         return $course;
     }
 
@@ -498,7 +499,7 @@ class BasicDataWizardStep implements CourseWizardStep
      * to already given values. A good example are study areas which
      * are only needed for certain sem_classes.
      *
-     * @param Array $values values specified from previous steps
+     * @param array $values values specified from previous steps
      * @return bool Is the current step required for a new course?
      */
     public function isRequired($values)
@@ -509,7 +510,7 @@ class BasicDataWizardStep implements CourseWizardStep
     /**
      * Copy values for basic data wizard step from given course.
      * @param Course $course
-     * @param Array $values
+     * @param array $values
      */
     public function copy($course, $values)
     {
@@ -542,17 +543,17 @@ class BasicDataWizardStep implements CourseWizardStep
      * Fetches the default deputies for a given person if the necessary
      * config options are set.
      * @param $user_id user whose default deputies to get
-     * @return Array Default deputy user_ids.
+     * @return array Default deputy user_ids.
      */
     public function getDefaultDeputies($user_id)
     {
         if (Config::get()->DEPUTIES_ENABLE && Config::get()->DEPUTIES_DEFAULTENTRY_ENABLE) {
-            return Deputy::findDeputies($user_id)->map(function($deputy) {
+            return Deputy::findDeputies($user_id)->map(function (Deputy $deputy) {
                 return ['id' => $deputy->user_id, 'name' => $deputy->getDeputyFullname()];
             });
-        } else {
-            return [];
         }
+
+        return [];
     }
 
     public function getSearch($course_type, $institute_ids, $exclude_lecturers = [],$exclude_tutors = [])
@@ -641,10 +642,121 @@ class BasicDataWizardStep implements CourseWizardStep
 
                 }
             }
-
+        } else {
+            foreach ($indices as $index) {
+                $values[$index] = $values[$index] ?? '';
+            }
         }
 
         return $values;
     }
 
+    private function copyBasicData(
+        Course $course,
+        string $source_id
+    ): void {
+        $source = Course::find($source_id);
+        $course->setData($source->toArray('untertitel ort sonstiges art teilnehmer vorrausetzungen lernorga leistungsnachweis ects admission_turnout modules'));
+        foreach ($source->datafields as $one) {
+            $df = $one->getTypedDatafield();
+            if ($df->isEditable()) {
+                $course->datafields->findOneBy('datafield_id', $one->datafield_id)->content = $one->content;
+            }
+        }
+    }
+
+    /**
+     * Copies participants and/or groups from one course to another.
+     */
+    public static function copyParticipantsAndGroups(
+        Course $course,
+        string $source_id,
+        bool $with_participants = true,
+        bool $with_groups = true,
+        bool $with_members = true,
+        bool|array $group_ids = false
+    ): void {
+        $source = Course::find($source_id);
+
+        if (!$with_participants && !$with_groups) {
+            return;
+        }
+
+        if ($with_participants || ($with_groups && $with_members)) {
+            $member_ids = false;
+            if (!$with_participants && $with_members) {
+                $member_ids = [];
+                $source->statusgruppen->filter(function (Statusgruppen $group) use ($group_ids): bool {
+                    return $group_ids === false
+                        || in_array($group->id, $group_ids);
+                })->each(function (Statusgruppen $group) use (&$member_ids): void {
+                    $group->members->each(function (StatusgruppeUser $member) use (&$member_ids): void {
+                        if (!in_array($member->user_id, $member_ids)) {
+                            $member_ids[] = $member->user_id;
+                        }
+                    });
+                });
+            }
+
+            $source->getMembersWithStatus(['user', 'autor', 'tutor'], true)
+                ->filter(function (CourseMember $member) use ($course, $member_ids): bool {
+                    return ($member_ids === false || in_array($member->user_id, $member_ids))
+                        && !CourseMember::exists([$course->id, $member->user_id]);
+                })->each(function (CourseMember $member) use ($course): void {
+                    CourseMember::insertCourseMember(
+                        $course->id,
+                        $member->user_id,
+                        $member->status,
+                    );
+                });
+        }
+
+        if (!$with_groups) {
+            return;
+        }
+
+        $source->statusgruppen->filter(function (Statusgruppen $group) use ($group_ids): bool {
+            return $group_ids === false
+                || in_array($group->id, $group_ids);
+        })->each(function (Statusgruppen $group) use ($course, $with_members, $group_ids): void {
+            $g = Statusgruppen::findOneBySQL(
+                'range_id = ? AND name = ?',
+                [$course->id, $group->name]
+            );
+
+            if (!$g) {
+                $g = Statusgruppen::createOrUpdate(
+                    null,
+                    $group->name,
+                    $group->position,
+                    $course->id,
+                    $group->size,
+                    $group->selfassign,
+                    $group->selfassign_start,
+                    $group->selfassign_end,
+                    $group->hasFolder(),
+                    null,
+                    $group->hasBlubber()
+                );
+            }
+
+            if (!$with_members) {
+                return;
+            }
+
+            $group->members->filter(function (StatusgruppeUser $member) use ($g): bool {
+                return !StatusgruppeUser::exists([$g->id, $member->user_id]);
+            })->each(function (StatusgruppeUser $member) use ($g): void {
+                StatusgruppeUser::create([
+                    'statusgruppe_id' => $g->id,
+                    ...$member->toArray([
+                        'user_id',
+                        'position',
+                        'visible',
+                        'inherit',
+                    ])
+                ]);
+            });
+        });
+    }
 }
-- 
GitLab