Forked from
Stud.IP / Stud.IP
922 commits behind the upstream repository.
-
Closes #3441 Merge request studip/studip!2873
Closes #3441 Merge request studip/studip!2873
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
BasicDataWizardStep.php 30.28 KiB
<?php
/**
* BasicDataWizardStep.php
* Course wizard step for getting the basic course data.
*
* 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 Thomas Hackl <thomas.hackl@uni-passau.de>
* @copyright 2015 Stud.IP Core-Group
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
* @category Stud.IP
*/
class BasicDataWizardStep implements CourseWizardStep
{
/**
* Returns the Flexi template for entering the necessary values
* for this step.
*
* @param Array $values Pre-set values
* @param int $stepnumber which number has the current step in the wizard?
* @param String $temp_id temporary ID for wizard workflow
* @return String a Flexi template for getting needed data.
*/
public function getStepTemplate($values, $stepnumber, $temp_id)
{
// Load template from step template directory.
$factory = new Flexi\Factory($GLOBALS['STUDIP_BASE_PATH'] . '/app/views/course/wizard/steps');
if (!empty($values[__CLASS__]['studygroup'])) {
$tpl = $factory->open('basicdata/index_studygroup');
$values[__CLASS__]['lecturers'][$GLOBALS['user']->id] = 1;
} else {
$tpl = $factory->open('basicdata/index');
}
if ($this->setupTemplateAttributes($tpl, $values, $stepnumber, $temp_id)) {
return $tpl->render();
}
return '';
}
protected function setupTemplateAttributes($tpl, $values, $stepnumber, $temp_id)
{
// We only need our own stored values here.
$values = $values[__CLASS__] ?? [];
// Get all available course types and their categories.
$typestruct = [];
foreach (SemType::getTypes() as $type) {
$class = $type->getClass();
// Creates a studygroup.
if (!empty($values['studygroup'])) {
// Get all studygroup types.
if ($class['studygroup_mode']) {
$typestruct[$class['name']][] = $type;
}
// Pre-set institute for studygroup assignment.
$values['institute'] = Config::get()->STUDYGROUP_DEFAULT_INST;
// Normal course.
} else {
if (!$class['course_creation_forbidden'] && !$class['studygroup_mode']) {
$typestruct[$class['name']][] = $type;
}
}
}
$tpl->set_attribute('types', $typestruct);
// Select a default type if none is given.
if (empty($values['coursetype'])) {
if ($GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER && Request::isXhr()) {
$values['coursetype'] = $GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER;
} else {
$values['coursetype'] = 1;
}
}
// Semester selection.
$semesters = [];
$now = time();
// Allow only current or future semesters for selection.
foreach (Semester::getAll() as $s) {
if ($s->ende >= $now) {
if ($GLOBALS['perm']->have_perm("admin")) {
if (
$s->id == $GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE
&& empty($values['start_time'])
&& Request::isXhr()
) {
$values['start_time'] = $s->beginn;
}
}
$semesters[] = $s;
}
}
if (empty($values['start_time'])) {
$values['start_time'] = Semester::findDefault()->beginn;
}
if (!empty($values['studygroup']) && (!count($typestruct) || empty($values['institute'])) ) {
$message = sprintf(_('Die Konfiguration der Studiengruppen ist unvollständig. ' .
'Bitte wenden Sie sich an [die Stud.IP-Administration]%s .'),
URLHelper::getLink('dispatch.php/siteinfo/show')
);
PageLayout::postError(formatReady($message));
return false;
}
if (count($semesters) > 0) {
$tpl->set_attribute('semesters', array_reverse($semesters));
// If no semester is set, use current as selected default.
if (empty($values['start_time'])) {
$values['start_time'] = Semester::findCurrent()->beginn;
}
} else {
$message = sprintf(_('Veranstaltungen können nur ' .
'im aktuellen oder in zukünftigen Semestern angelegt werden. ' .
'Leider wurde kein passendes Semester gefunden. Bitte wenden ' .
'Sie sich an [die Stud.IP-Administration]%s .'),
URLHelper::getLink('dispatch.php/siteinfo/show')
);
PageLayout::postError(formatReady($message));
return false;
}
// Create a I18NString for course name and description.
$values = $this->makeI18N($values, ['name', 'description']);
// Get all allowed home institutes (my own).
$institutes = Institute::getMyInstitutes();
if (!empty($values['studygroup']) || count($institutes) > 0) {
$tpl->set_attribute('institutes', $institutes);
if (empty($values['institute'])) {
if ($GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT && Request::isXhr()) {
$values['institute'] = $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT;
} else {
$values['institute'] = InstituteMember::getDefaultInstituteIdForUser($GLOBALS['user']->id);
// if for some reason no default institute is set, use the first one listed
if (!$values['institute']) {
$values['institute'] = $institutes[0]['Institut_id'];
}
}
}
} else {
$message = sprintf(_('Um Veranstaltungen ' .
'anlegen zu können, muss Ihr Account der Einrichtung, ' .
'für die Sie eine Veranstaltung anlegen möchten, zugeordnet ' .
'werden. Bitte wenden Sie sich an [die ' .
'Stud.IP-Administration]%s .'),
URLHelper::getLink('dispatch.php/siteinfo/show')
);
PageLayout::postError(formatReady($message));
return false;
}
// QuickSearch for participating institutes.
// No JS: Keep search value and results for displaying in search select box.
if (!empty($values['part_inst_id'])) {
Request::getInstance()->offsetSet('part_inst_id', $values['part_inst_id']);
}
if (!empty($values['part_inst_id_parameter'])) {
Request::getInstance()->offsetSet('part_inst_id_parameter', $values['part_inst_id_parameter']);
}
$instsearch = new StandardSearch('Institut_id',
_('Beteiligte Einrichtung hinzufügen'),
'part_inst_id'
);
$tpl->set_attribute('instsearch', QuickSearch::get('part_inst_id', $instsearch)
->withButton(['search_button_name' => 'search_part_inst', 'reset_button_name' => 'reset_instsearch'])
->fireJSFunctionOnSelect('STUDIP.CourseWizard.addParticipatingInst')
->render());
if (empty($values['participating'])) {
$values['participating'] = [];
}
// Quicksearch for lecturers.
// No JS: Keep search value and results for displaying in search select box.
if (!empty($values['lecturer_id'])) {
Request::getInstance()->offsetSet('lecturer_id', $values['lecturer_id']);
}
if (!empty($values['lecturer_id_parameter'])) {
Request::getInstance()->offsetSet('lecturer_id_parameter', $values['lecturer_id_parameter']);
}
// Check for deputies.
$deputies = Config::get()->DEPUTIES_ENABLE;
/*
* No lecturers set, add yourself so that at least one lecturer is
* present. But this can only be done if your own permission level
* is 'dozent'.
*/
if (
empty($values['lecturers'])
&& $GLOBALS['perm']->have_perm('dozent')
&& !$GLOBALS['perm']->have_perm('admin')
) {
$values['lecturers'] = [$GLOBALS['user']->id => true];
// Remove from deputies if set.
if ($deputies && isset($values['deputies'][$GLOBALS['user']->id])) {
unset($values['deputies'][$GLOBALS['user']->id]);
}
// Add your own default deputies if applicable.
if ($deputies && Config::get()->DEPUTIES_DEFAULTENTRY_ENABLE) {
$values['deputies'] = array_merge(
$values['deputies'] ?? [],
array_flip(Deputy::findDeputies($GLOBALS['user']->id)->pluck('user_id'))
);
}
}
// Add lecturer from my courses filter.
if ($GLOBALS['user']->cfg->ADMIN_COURSES_TEACHERFILTER && empty($values['lecturers']) && Request::isXhr()) {
$values['lecturers'] = [$GLOBALS['user']->cfg->ADMIN_COURSES_TEACHERFILTER => true];
// Add this lecturer's default deputies if applicable.
if ($deputies && Config::get()->DEPUTIES_DEFAULTENTRY_ENABLE) {
$values['deputies'] = array_merge(
$values['deputies'] ?? [],
array_flip(Deputy::findDeputies($GLOBALS['user']->cfg->ADMIN_COURSES_TEACHERFILTER)->pluck('user_id'))
);
}
}
if (empty($values['lecturers'])) {
$values['lecturers'] = [];
}
if ($deputies && empty($values['deputies'])) {
$values['deputies'] = [];
}
// Quicksearch for deputies if applicable.
if ($deputies) {
// No JS: Keep search value and results for displaying in search select box.
if (!empty($values['deputy_id'])) {
Request::getInstance()->offsetSet('deputy_id', $values['deputy_id']);
}
if (!empty($values['deputy_id_parameter'])) {
Request::getInstance()->offsetSet('deputy_id_parameter', $values['deputy_id_parameter']);
}
$deputysearch = new PermissionSearch('user',
_('Vertretung hinzufügen'),
'user_id',
['permission' => 'dozent',
'exclude_user' => array_keys($values['deputies'])]
);
$tpl->set_attribute('dsearch', QuickSearch::get('deputy_id', $deputysearch)
->withButton(['search_button_name' => 'search_deputy', 'reset_button_name' => 'reset_dsearch'])
->fireJSFunctionOnSelect('STUDIP.CourseWizard.addDeputy')
->render());
}
if (empty($values['tutors'])) {
$values['tutors'] = [];
}
[$lsearch, $tsearch] = array_values($this->getSearch($values['coursetype'],
array_merge([$values['institute']], array_keys($values['participating'])),
array_keys($values['lecturers']), array_keys($values['tutors'])));
// Quicksearch for lecturers.
$tpl->set_attribute('lsearch', $lsearch);
$tpl->set_attribute('tsearch', $tsearch);
$tpl->set_attribute('values', $values);
// AJAX URL needed for default deputy checking.
$tpl->set_attribute('ajax_url', $values['ajax_url'] ?? URLHelper::getLink('dispatch.php/course/wizard/ajax'));
$tpl->set_attribute('default_deputies_enabled',
($deputies && Config::get()->DEPUTIES_DEFAULTENTRY_ENABLE) ? 1 : 0);
return $tpl;
}
/**
* The function only needs to handle person adding and removing
* as other actions are handled by normal request processing.
* @param Array $values currently set values for the wizard.
* @return bool
*/
public function alterValues($values)
{
// We only need our own stored values here.
$values = $values[__CLASS__];
// Add a participating institute.
if (Request::submitted('add_part_inst') && Request::option('part_inst_id')) {
$values['participating'][Request::option('part_inst_id')] = true;
unset($values['part_inst_id']);
unset($values['part_inst_id_parameter']);
}
// Remove a participating institute.
if ($remove = array_keys(Request::getArray('remove_participating'))) {
$remove = $remove[0];
unset($values['participating'][$remove]);
}
// Add a lecturer.
if (Request::submitted('add_lecturer') && Request::option('lecturer_id')) {
$values['lecturers'][Request::option('lecturer_id')] = true;
unset($values['lecturer_id']);
unset($values['lecturer_id_parameter']);
// 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'))));
}
}
// Remove a lecturer.
if ($remove = array_keys(Request::getArray('remove_lecturer'))) {
$remove = $remove[0];
unset($values['lecturers'][$remove]);
}
// Add a deputy.
if (Request::submitted('add_deputy')) {
$values['deputies'][Request::option('deputy_id')] = true;
unset($values['deputy_id']);
unset($values['deputy_id_parameter']);
}
// Remove a deputy.
if ($remove = array_keys(Request::getArray('remove_deputy'))) {
$remove = $remove[0];
unset($values['deputies'][$remove]);
}
// Add a tutor.
if (Request::submitted('add_tutor') && Request::option('tutor_id')) {
$values['tutors'][Request::option('tutor_id')] = true;
unset($values['tutor_id']);
unset($values['tutor_id_parameter']);
}
// Remove a tutor.
if ($remove = array_keys(Request::getArray('remove_tutor'))) {
$remove = $remove[0];
unset($values['tutors'][$remove]);
}
return $values;
}
/**
* Validates if given values are sufficient for completing the current
* course wizard step and switch to another one. If not, all errors are
* collected and shown via PageLayout::postMessage.
*
* @param mixed $values Array of stored values
* @return bool Everything ok?
*/
public function validate($values)
{
// We only need our own stored values here.
$values = $values[__CLASS__];
$ok = true;
$errors = [];
if (!trim($values['name'])) {
$errors[] = _('Bitte geben Sie den Namen der Veranstaltung an.');
}
if (isset($values['number']) && $values['number'] != '') {
$course_number_format = Config::get()->COURSE_NUMBER_FORMAT;
if ($course_number_format && !preg_match('/^' . $course_number_format . '$/', $values['number'])) {
$errors[] = _('Die Veranstaltungsnummer hat ein ungültiges Format.');
}
}
if (empty($values['lecturers'])) {
$errors[] = sprintf(
_('Bitte tragen Sie mindestens eine Person als %s ein.'),
htmlReady(get_title_for_status('dozent', 1, $values['coursetype']))
);
}
if (empty($values['lecturers'][$GLOBALS['user']->id]) && !$GLOBALS['perm']->have_perm('admin')) {
if (Config::get()->DEPUTIES_ENABLE) {
if (empty($values['deputies'][$GLOBALS['user']->id])) {
$errors[] = sprintf(
_('Sie selbst müssen entweder als %s oder als Vertretung eingetragen sein.'),
htmlReady(get_title_for_status('dozent', 1, $values['coursetype']))
);
}
} else {
$errors[] = sprintf(
_('Sie müssen selbst als %s eingetragen sein.'),
htmlReady(get_title_for_status('dozent', 1, $values['coursetype']))
);
}
}
if (in_array($values['coursetype'], studygroup_sem_types())) {
if (!$values['accept']) {
$errors[] = _('Sie müssen die Nutzungsbedingungen akzeptieren.');
}
}
if ($errors) {
$ok = false;
PageLayout::postError(_('Bitte beheben Sie erst folgende Fehler, bevor Sie fortfahren:'), $errors);
}
return $ok;
}
/**
* Stores the given values to the given course.
*
* @param Course $course the course to store values for
* @param array $values values to set
*
* @return Course|false The course object with updated values.
*/
public function storeValues($course, $values)
{
// Fetch settings from $values before it is overwritten
$source_id = $values['source_id'] ?? null;
$copy_basic_data = !empty($values['copy_basic_data']);
$copy_participants = !empty($values['copy_participants']);
$copy_groups = !empty($values['copy_groups']);
$copy_members = !empty($values['copy_members']);
// We only need our own stored values here.
$values = $values[__CLASS__];
$seminar = new Seminar($course);
if ($copy_basic_data) {
$this->copyBasicData(
$course,
$source_id
);
}
$course->status = $values['coursetype'];
$course->name = new I18NString($values['name'], $values['name_i18n'] ?? []);
$course->veranstaltungsnummer = $values['number'] ?? null;
$course->beschreibung = new I18NString($values['description'], $values['description_i18n'] ?? []);
$course->start_semester = Semester::findByTimestamp($values['start_time']);
$course->institut_id = $values['institute'];
$semclass = $seminar->getSemClass();
$course->visible = $semclass['visible'];
$course->admission_prelim = $semclass['admission_prelim_default'];
$course->lesezugriff = $semclass['default_read_level'] ?: 1;
$course->schreibzugriff = $semclass['default_write_level'] ?: 1;
// Studygroups: access and description.
if (in_array($values['coursetype'], studygroup_sem_types())) {
$course->visible = 1;
$course->duration_time = -1;
switch ($values['access']) {
case 'all':
$course->admission_prelim = 0;
break;
case 'invisible':
if (!Config::get()->STUDYGROUPS_INVISIBLE_ALLOWED) {
$course->visible = 0;
}
case 'invite':
$course->admission_prelim = 1;
$course->admission_prelim_txt = Config::get()->STUDYGROUP_ACCEPTANCE_TEXT;
break;
}
}
if (!$course->store()) {
return false;
}
StudipLog::log('SEM_CREATE', $course->id, null, 'Veranstaltung mit Assistent angelegt');
$institutes = [$values['institute']];
if (isset($values['participating']) && is_array($values['participating'])) {
$institutes = array_merge($institutes, array_keys($values['participating']));
}
$seminar->setInstitutes($institutes);
if (isset($values['lecturers']) && is_array($values['lecturers'])) {
foreach (array_keys($values['lecturers']) as $user_id) {
$seminar->addMember($user_id, 'dozent');
}
}
if (isset($values['tutors']) && is_array($values['tutors'])) {
foreach (array_keys($values['tutors']) as $user_id) {
$seminar->addMember($user_id, 'tutor');
}
}
if (Config::get()->DEPUTIES_ENABLE && isset($values['deputies']) && is_array($values['deputies'])) {
foreach ($values['deputies'] as $d => $assigned) {
Deputy::addDeputy($d, $course->id);
}
}
if ($semclass['admission_type_default'] == 3) {
$course_set_id = CourseSet::getGlobalLockedAdmissionSetId();
CourseSet::addCourseToSet($course_set_id, $course->id);
}
self::copyParticipantsAndGroups($course, $source_id, $copy_participants, $copy_groups, $copy_members);
return $course;
}
/**
* Checks if the current step needs to be executed according
* 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
* @return bool Is the current step required for a new course?
*/
public function isRequired($values)
{
return true;
}
/**
* Copy values for basic data wizard step from given course.
* @param Course $course
* @param array $values
*/
public function copy($course, $values)
{
$data = [
'coursetype' => $course->status,
'start_time' => $course->start_time,
'name' => $course->name,
'name_i18n' => is_object($course->name) ? $course->name->toArray() : $course->name,
'number' => $course->veranstaltungsnummer,
'institute' => $course->institut_id,
'description' => $course->beschreibung,
'description_i18n' => is_object($course->beschreibung) ?
$course->beschreibung->toArray() : $course->beschreibung
];
$lecturers = $course->members->findBy('status', 'dozent')->pluck('user_id');
$data['lecturers'] = array_flip($lecturers);
$tutors = $course->members->findBy('status', 'tutor')->pluck('user_id');
$data['tutors'] = array_flip($tutors);
$participating = $course->institutes->pluck('institut_id');
$data['participating'] = array_flip($participating);
unset($data['participating'][$course->institut_id]);
if (Config::get()->DEPUTIES_ENABLE) {
$data['deputies'] = array_flip(Deputy::findDeputies($course->id)->pluck('user_id'));
}
$values[__CLASS__] = $data;
return $values;
}
/**
* 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.
*/
public function getDefaultDeputies($user_id)
{
if (Config::get()->DEPUTIES_ENABLE && Config::get()->DEPUTIES_DEFAULTENTRY_ENABLE) {
return Deputy::findDeputies($user_id)->map(function (Deputy $deputy): array {
return ['id' => $deputy->user_id, 'name' => $deputy->getDeputyFullname()];
});
}
return [];
}
public function getSearch($course_type, $institute_ids, $exclude_lecturers = [],$exclude_tutors = [])
{
if (SeminarCategories::getByTypeId($course_type)->only_inst_user) {
$search = 'user_inst';
} else {
$search = 'user';
}
$psearch = new PermissionSearch($search,
sprintf(_("%s hinzufügen"), get_title_for_status('dozent', 1, $course_type)),
'user_id',
__CLASS__ . '::lsearchHelper'
);
$lsearch = QuickSearch::get('lecturer_id', $psearch)
->withButton(['search_button_name' => 'search_lecturer', 'reset_button_name' => 'reset_lsearch'])
->fireJSFunctionOnSelect('STUDIP.CourseWizard.addLecturer')
->render();
$tutor_psearch = new PermissionSearch($search,
sprintf(_("%s hinzufügen"), get_title_for_status('tutor', 1, $course_type)),
'user_id',
__CLASS__ . '::tsearchHelper'
);
$tsearch = QuickSearch::get('tutor_id', $tutor_psearch)
->withButton(['search_button_name' => 'search_tutor', 'reset_button_name' => 'reset_tsearch'])
->fireJSFunctionOnSelect('STUDIP.CourseWizard.addTutor')
->render();
return compact('lsearch', 'tsearch');
}
public static function tsearchHelper($psearch, $context)
{
$ret['permission'] = ['tutor', 'dozent'];
$ret['exclude_user'] = array_keys((array) ($context['tutors'] ?? []));
$ret['institute'] = array_merge(
[$context['institute']],
array_keys((array) ($context['participating'] ?? []))
);
return $ret;
}
public static function lsearchHelper($psearch, $context)
{
$ret['permission'] = 'dozent';
$ret['exclude_user'] = array_keys((array) ($context['lecturers'] ?? []));
$ret['institute'] = array_merge(
[$context['institute']],
array_keys((array) ($context['participating'] ?? []))
);
return $ret;
}
/**
* Creates I18N strings from the given values at the given indices.
*
* @param array $values this step's set values
* @param array $indices the values to convert to I18NStrings
*
* @return array modified values
*/
protected function makeI18N($values, $indices)
{
// We only need to do something if there are several content languages.
if (count($GLOBALS['CONTENT_LANGUAGES']) > 1) {
/**
* Create array for configured content languages
*/
$translations = array_combine(
array_keys($GLOBALS['CONTENT_LANGUAGES']),
array_fill(0, count($GLOBALS['CONTENT_LANGUAGES']), '')
);
foreach ($indices as $index) {
// There are values given => create an I18NString
if (!empty($values[$index])) {
$values[$index] = new I18NString($values[$index], $values[$index . '_i18n'] ?? []);
// Current index is not set (yet), create an empty I18NString
} else {
$values[$index] = new I18NString('', $translations);
}
}
} 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',
])
]);
});
});
}
}