From 61b2e2ba3271a911308f80fcbe2073852312bf80 Mon Sep 17 00:00:00 2001 From: Finn Schneider Date: Tue, 16 Dec 2025 10:28:32 +0000 Subject: Resolve "Bulk-Aktions in der Themenansicht" Closes #10 Merge request studip/studip!4514 --- app/controllers/course/topics.php | 113 +++++++++++++--- app/views/course/topics/_dates.php | 13 ++ app/views/course/topics/_material.php | 34 +++++ app/views/course/topics/details.php | 28 ++++ app/views/course/topics/index.php | 236 +++++++++++++++++----------------- lib/models/CourseTopic.php | 2 +- 6 files changed, 285 insertions(+), 141 deletions(-) create mode 100644 app/views/course/topics/_dates.php create mode 100644 app/views/course/topics/_material.php create mode 100644 app/views/course/topics/details.php diff --git a/app/controllers/course/topics.php b/app/controllers/course/topics.php index e955ba1..026c2bf 100644 --- a/app/controllers/course/topics.php +++ b/app/controllers/course/topics.php @@ -21,7 +21,10 @@ class Course_TopicsController extends AuthenticatedController $this->forum_activated = $course->isToolActive(CoreForum::class); $this->documents_activated = $course->isToolActive(CoreDocuments::class); - if ($action !== 'index' && !$GLOBALS['perm']->have_studip_perm('tutor', Context::getId())) { + if ( + !in_array($action, ['index', 'details']) + && !User::findCurrent()->hasPermissionLevel('tutor', Context::get()) + ) { throw new AccessDeniedException(); } @@ -37,9 +40,7 @@ class Course_TopicsController extends AuthenticatedController public function delete_action(CourseTopic $topic) { - if (!Request::isPost()) { - throw new MethodNotAllowedException(); - } + CSRFProtection::verifyUnsafeRequest(); if ($topic->seminar_id && ($topic->seminar_id !== Context::getId())) { throw new AccessDeniedException(); @@ -102,7 +103,7 @@ class Course_TopicsController extends AuthenticatedController } PageLayout::postSuccess(_('Thema gespeichert.')); - $this->redirect($this->indexURL(['open' => $topic->id])); + $this->redirect($this->indexURL()); } public function swap_action(CourseTopic $a, CourseTopic $b) @@ -123,7 +124,7 @@ class Course_TopicsController extends AuthenticatedController $a->store(); $b->store(); - $this->redirect($this->indexURL(['open' => $a->id])); + $this->redirect($this->indexURL()); } public function allow_public_action() @@ -219,25 +220,62 @@ class Course_TopicsController extends AuthenticatedController $this->render_json($output); } + public function bulk_action(string $action): void + { + CSRFProtection::verifyUnsafeRequest(); + $topic_ids = Request::optionArray('topics'); + + [$callback, $success, $failure] = match ($action) { + 'ftopic' => [ + fn(CourseTopic $topic) => $topic->connectWithForumThread(), + _('Forumsthemen erfolgreich angelegt.'), + _('Fehler beim Anlegen von Forumsthemen zu:'), + ], + 'folder' => [ + fn(CourseTopic $topic) => $topic->connectWithDocumentFolder(), + _('Ordner erfolgreich angelegt.'), + _('Fehler beim Anlegen von Ordnern zu:'), + ], + 'delete' => [ + fn(CourseTopic $topic) => $topic->delete(), + _('Erfolgreich gelöscht.'), + _('Fehler beim Löschen von:'), + ], + default => throw new InvalidArgumentException('Unknown action'), + }; + + $errors = []; + CourseTopic::findEachMany( + function (CourseTopic $topic) use ($callback, &$errors) { + if (!$callback($topic)) { + $errors[] = $topic->title; + } + }, + $topic_ids + ); + if (count($errors) === 0) { + PageLayout::postSuccess($success); + } else { + PageLayout::postError( + $failure, + array_map('htmlReady', $errors) + ); + } + + $this->redirect($this->indexURL()); + } + + public function details_action(CourseTopic $topic) + { + PageLayout::setTitle(sprintf(_('Details: %s'), htmlReady($topic->title))); + $this->render_template('course/topics/details'); + } + private function setupSidebar($action) { $sidebar = Sidebar::get(); $actions = $sidebar->addWidget(new ActionsWidget()); - if ($action === 'index') { - $actions->addLink( - _('Alle Themen aufklappen'), - $this->url_for('course/topics/show'), - Icon::create('arr_1down'), - ['onclick' => "jQuery('table.withdetails > tbody > tr:not(.details):not(.open) > :first-child a').click(); return false;"] - ); - $actions->addLink( - _('Alle Themen zuklappen'), - $this->url_for('course/topics/hide'), - Icon::create('arr_1right'), - ['onclick' => "jQuery('table.withdetails > tbody > tr:not(.details).open > :first-child a').click(); return false;"] - ); - } if ($GLOBALS['perm']->have_studip_perm('tutor', Context::getId())) { $actions->addLink( _('Neues Thema erstellen'), @@ -286,4 +324,39 @@ class Course_TopicsController extends AuthenticatedController return $links; } + + public function getActionMenu(CourseTopic $topic): ActionMenu + { + $actions = ActionMenu::get(); + $actions->setContext(htmlReady($topic->title)); + + if (User::findCurrent()->hasPermissionLevel('tutor', Context::get())) { + $actions->addLink( + $this->editURL($topic), + _('Bearbeiten'), + Icon::create('edit') + )->asDialog(); + $actions->addButton( + 'delete', + _('Löschen'), + Icon::create('trash'), + [ + 'formaction' => $this->delete($topic), + 'data-confirm' => _('Das Thema wirklich löschen?'), + ] + ); + if (!$this->cancelled_dates_locked && count($topic->dates) > 0) { + $actions->addLink( + URLHelper::getURL( + 'dispatch.php/course/cancel_dates', + ['issue_id' => $topic->id] + ), + _('Alle Termine ausfallen lassen'), + Icon::create('date') + )->asDialog(); + } + } + + return $actions; + } } diff --git a/app/views/course/topics/_dates.php b/app/views/course/topics/_dates.php new file mode 100644 index 0000000..4fc37f6 --- /dev/null +++ b/app/views/course/topics/_dates.php @@ -0,0 +1,13 @@ + diff --git a/app/views/course/topics/_material.php b/app/views/course/topics/_material.php new file mode 100644 index 0000000..5e853ce --- /dev/null +++ b/app/views/course/topics/_material.php @@ -0,0 +1,34 @@ + diff --git a/app/views/course/topics/details.php b/app/views/course/topics/details.php new file mode 100644 index 0000000..df72454 --- /dev/null +++ b/app/views/course/topics/details.php @@ -0,0 +1,28 @@ +
+

title) ?>

+ + folders->first()) + || ($forum_activated && $topic->forum_thread_url) + ) : ?> +

+ render_partial('course/topics/_material.php', ['always_show' => true]) ?> + + + + dates) > 0) : ?> +

+ render_partial('course/topics/_dates.php') ?> + + + + description) : ?> +

+ description) ?> + + + + + + +
diff --git a/app/views/course/topics/index.php b/app/views/course/topics/index.php index e538542..29a9d98 100644 --- a/app/views/course/topics/index.php +++ b/app/views/course/topics/index.php @@ -4,130 +4,126 @@ * @var Course_TopicsController $controller * @var array $topic_links */ + +use Studip\Button; + ?> 0) : ?> - - - - - - - - - - - - - $topic) : ?> - getId() ? "open" : "" ?>"> - - - - - + + + +
- - paper_related): ?> - asSvg(array_merge( - ['class' => 'text-top'], - tooltip2(_('Thema behandelt eine Hausarbeit oder ein Referat')) - )) ?> - - - - - -
-
- - - - - - - - - + + + + + + + + - - - -
- - - - +
+ + + + + + + + + + hasPermissionLevel('tutor', Context::get())) : ?> + + + + + + + + + + + + + + - - -
+ +
+ + + + + + paper_related): ?> + asSvg(array_merge( + tooltip2(_('Thema behandelt eine Hausarbeit oder ein Referat')) + )) ?> + + + render_partial('course/topics/_dates.php', ['topic' => $topic]) ?> + + render_partial('course/topics/_material.php', ['topic' => $topic]) ?> + + + +
+ title)); + if ($topic_links[$topic->id]['previous']) : ?> + + + -
-
- have_studip_perm("tutor", Context::getId())) : ?> - editURL($topic), - ['data-dialog' => ''] - ) ?> - - - _('Das Thema wirklich löschen?')] - ) ?> - - - dates->count()) : ?> - $topic->getId()]), ['data-dialog' => '']) ?> - - - - id]['previous']) : ?> -
- -
- - id]['next']) : ?> -
- -
- -
+ title)); + if ($topic_links[$topic->id]['next']) : ?> + + + + + getActionMenu($topic) ?> +
+
+ + $controller->bulkURL('folder'), + 'data-confirm' => _('Sind Sie sicher, dass Sie für Ihre Auswahl je einen Dateiordner anlegen wollen?'), + ]) ?> + + + $controller->bulkURL('ftopic'), + 'data-confirm' => _('Sind Sie sicher, dass Sie für Ihre Auswahl je ein Forumsthema anlegen wollen?'), + ]) ?> - - -
+ $controller->bulkURL('delete'), + 'data-confirm' => _('Sind Sie sicher, dass Sie Ihre Auswahl löschen wollen?'), + ]) ?> +
+ diff --git a/lib/models/CourseTopic.php b/lib/models/CourseTopic.php index f61738a..ff1f967 100644 --- a/lib/models/CourseTopic.php +++ b/lib/models/CourseTopic.php @@ -129,7 +129,7 @@ class CourseTopic extends SimpleORMap $forum_topic->store(); $this->forum_topics[] = $forum_topic; - $this->store(); + return $this->store(); } return false; } -- cgit v1.0