diff --git a/config/routes.php b/config/routes.php index 3c4ed5d4..e51df17a 100644 --- a/config/routes.php +++ b/config/routes.php @@ -57,8 +57,10 @@ $route->addGroup( '/schedule', function (RouteCollector $route) { $route->get('', 'Admin\\Schedule\\ImportSchedule@index'); - $route->post('/load', 'Admin\\Schedule\\ImportSchedule@loadSchedule'); - $route->post('/import', 'Admin\\Schedule\\ImportSchedule@importSchedule'); + $route->get('/edit[/{id:\d+}]', 'Admin\\Schedule\\ImportSchedule@edit'); + $route->post('/edit[/{id:\d+}]', 'Admin\\Schedule\\ImportSchedule@save'); + $route->get('/load/{id:\d+}', 'Admin\\Schedule\\ImportSchedule@loadSchedule'); + $route->post('/import/{id:\d+}', 'Admin\\Schedule\\ImportSchedule@importSchedule'); } ); diff --git a/db/migrations/2020_11_20_000000_add_name_minutes_and_timestamps_to_schedules.php b/db/migrations/2020_11_20_000000_add_name_minutes_and_timestamps_to_schedules.php new file mode 100644 index 00000000..fcd41c1c --- /dev/null +++ b/db/migrations/2020_11_20_000000_add_name_minutes_and_timestamps_to_schedules.php @@ -0,0 +1,76 @@ +schema->table( + 'schedules', + function (Blueprint $table) { + $table->string('name')->after('id'); + $table->integer('shift_type')->after('name'); + $table->integer('minutes_before')->after('shift_type'); + $table->integer('minutes_after')->after('minutes_before'); + $table->timestamps(); + } + ); + + Schedule::query() + ->update([ + 'created_at' => Carbon::now(), + 'minutes_before' => 15, + 'minutes_after' => 15, + ]); + + // Add legacy reference + if ($this->schema->hasTable('ShiftTypes')) { + $connection = $this->schema->getConnection(); + $query = $connection + ->table('Shifts') + ->select('Shifts.shifttype_id') + ->join('schedule_shift', 'Shifts.SID', 'schedule_shift.shift_id') + ->where('schedule_shift.schedule_id', $connection->raw('schedules.id')) + ->limit(1); + + Schedule::query() + ->update(['shift_type' => $connection->raw('(' . $query->toSql() . ')')]); + + $this->schema->table( + 'schedules', + function (Blueprint $table) { + $this->addReference($table, 'shift_type', 'ShiftTypes'); + } + ); + } + } + + /** + * Reverse the migration + */ + public function down() + { + $this->schema->table( + 'schedules', + function (Blueprint $table) { + $table->dropForeign('schedules_shift_type_foreign'); + $table->dropColumn('name'); + $table->dropColumn('shift_type'); + $table->dropColumn('minutes_before'); + $table->dropColumn('minutes_after'); + $table->dropTimestamps(); + } + ); + } +} diff --git a/db/migrations/Reference.php b/db/migrations/Reference.php index d0550686..7d08ab02 100644 --- a/db/migrations/Reference.php +++ b/db/migrations/Reference.php @@ -22,6 +22,7 @@ trait Reference * @param string $targetTable * @param string|null $fromColumn * @param bool $setPrimary + * * @return ColumnDefinition */ protected function references( @@ -37,11 +38,21 @@ trait Reference $table->primary($fromColumn); } + $this->addReference($table, $fromColumn, $targetTable); + + return $col; + } + + /** + * @param Blueprint $table + * @param string $fromColumn + * @param string $targetTable + */ + protected function addReference(Blueprint $table, string $fromColumn, string $targetTable) + { $table->foreign($fromColumn) ->references('id')->on($targetTable) ->onUpdate('cascade') ->onDelete('cascade'); - - return $col; } } diff --git a/includes/pages/schedule/ImportSchedule.php b/includes/pages/schedule/ImportSchedule.php index 0e50dba6..455f445c 100644 --- a/includes/pages/schedule/ImportSchedule.php +++ b/includes/pages/schedule/ImportSchedule.php @@ -6,6 +6,8 @@ namespace Engelsystem\Controllers\Admin\Schedule; use Carbon\Carbon; use Engelsystem\Controllers\BaseController; +use Engelsystem\Controllers\CleanupModel; +use Engelsystem\Controllers\HasUserNotifications; use Engelsystem\Helpers\Schedule\Event; use Engelsystem\Helpers\Schedule\Room; use Engelsystem\Helpers\Schedule\Schedule; @@ -20,7 +22,6 @@ use GuzzleHttp\Client as GuzzleClient; use Illuminate\Database\Connection as DatabaseConnection; use Illuminate\Database\Eloquent\Builder as QueryBuilder; use Illuminate\Database\Eloquent\Collection as DatabaseCollection; -use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Psr\Log\LoggerInterface; use stdClass; @@ -28,6 +29,9 @@ use Symfony\Component\HttpFoundation\Session\SessionInterface; class ImportSchedule extends BaseController { + use CleanupModel; + use HasUserNotifications; + /** @var DatabaseConnection */ protected $db; @@ -86,11 +90,76 @@ class ImportSchedule extends BaseController return $this->response->withView( 'admin/schedule/index.twig', [ - 'errors' => $this->getFromSession('errors'), - 'success' => $this->getFromSession('success'), + 'is_index' => true, + 'schedules' => ScheduleUrl::all(), + ] + $this->getNotifications() + ); + } + + /** + * @param Request $request + * + * @return Response + */ + public function edit(Request $request): Response + { + $schedule = ScheduleUrl::find($request->getAttribute('id')); + $this->cleanupModelNullValues($schedule); + + return $this->response->withView( + 'admin/schedule/edit.twig', + [ + 'schedule' => $schedule, 'shift_types' => $this->getShiftTypes(), + ] + $this->getNotifications() + ); + } + + /** + * @param Request $request + * + * @return Response + */ + public function save(Request $request): Response + { + $id = $request->getAttribute('id'); + /** @var ScheduleUrl $schedule */ + $schedule = ScheduleUrl::findOrNew($id); + + $data = $this->validate($request, [ + 'name' => 'required', + 'url' => 'required', + 'shift_type' => 'required|int', + 'minutes_before' => 'required|int', + 'minutes_after' => 'required|int', + ]); + + if (!isset($this->getShiftTypes()[$data['shift_type']])) { + throw new ErrorException('schedule.import.invalid-shift-type'); + } + + $schedule->name = $data['name']; + $schedule->url = $data['url']; + $schedule->shift_type = $data['shift_type']; + $schedule->minutes_before = $data['minutes_before']; + $schedule->minutes_after = $data['minutes_after']; + + $schedule->save(); + + $this->log->info( + 'Schedule {name}: Url {url}, Shift Type {shift_type}, minutes before/after {before}/{after}', + [ + 'name' => $schedule->name, + 'url' => $schedule->name, + 'shift_type' => $schedule->shift_type, + 'before' => $schedule->minutes_before, + 'after' => $schedule->minutes_after, ] ); + + $this->addNotification('schedule.edit.success'); + + return redirect('/admin/schedule/load/' . $schedule->id); } /** @@ -116,24 +185,19 @@ class ImportSchedule extends BaseController $changeEvents, $deleteEvents, $newRooms, - $shiftType, + , $scheduleUrl, - $schedule, - $minutesBefore, - $minutesAfter + $schedule ) = $this->getScheduleData($request); } catch (ErrorException $e) { - return back()->with('errors', [$e->getMessage()]); + $this->addNotification($e->getMessage(), 'errors'); + return back(); } return $this->response->withView( 'admin/schedule/load.twig', [ - 'errors' => $this->getFromSession('errors'), - 'schedule_url' => $scheduleUrl->url, - 'shift_type' => $shiftType, - 'minutes_before' => $minutesBefore, - 'minutes_after' => $minutesAfter, + 'schedule_id' => $scheduleUrl->id, 'schedule' => $schedule, 'rooms' => [ 'add' => $newRooms, @@ -143,7 +207,7 @@ class ImportSchedule extends BaseController 'update' => $changeEvents, 'delete' => $deleteEvents, ], - ] + ] + $this->getNotifications() ); } @@ -172,10 +236,11 @@ class ImportSchedule extends BaseController $scheduleUrl ) = $this->getScheduleData($request); } catch (ErrorException $e) { - return back()->with('errors', [$e->getMessage()]); + $this->addNotification($e->getMessage(), 'errors'); + return back(); } - $this->log('Started schedule "{schedule}" import', ['schedule' => $scheduleUrl->url]); + $this->log('Started schedule "{name}" import', ['name' => $scheduleUrl->name]); foreach ($newRooms as $room) { $this->createRoom($room); @@ -207,10 +272,11 @@ class ImportSchedule extends BaseController $this->deleteEvent($event); } - $this->log('Ended schedule "{schedule}" import', ['schedule' => $scheduleUrl->url]); + $scheduleUrl->touch(); + $this->log('Ended schedule "{name}" import', ['name' => $scheduleUrl->name]); return redirect($this->url, 303) - ->with('success', ['schedule.import.success']); + ->with('messages', ['schedule.import.success']); } /** @@ -337,17 +403,11 @@ class ImportSchedule extends BaseController */ protected function getScheduleData(Request $request) { - $data = $this->validate( - $request, - [ - 'schedule-url' => 'required|url', - 'shift-type' => 'required|int', - 'minutes-before' => 'optional|int', - 'minutes-after' => 'optional|int', - ] - ); + $id = $request->getAttribute('id'); + /** @var ScheduleUrl $scheduleUrl */ + $scheduleUrl = ScheduleUrl::findOrFail($id); - $scheduleResponse = $this->guzzle->get($data['schedule-url']); + $scheduleResponse = $this->guzzle->get($scheduleUrl->url); if ($scheduleResponse->getStatusCode() != 200) { throw new ErrorException('schedule.import.request-error'); } @@ -357,15 +417,10 @@ class ImportSchedule extends BaseController throw new ErrorException('schedule.import.read-error'); } - $shiftType = (int)$data['shift-type']; - if (!isset($this->getShiftTypes()[$shiftType])) { - throw new ErrorException('schedule.import.invalid-shift-type'); - } - - $scheduleUrl = $this->getScheduleUrl($data['schedule-url']); + $shiftType = $scheduleUrl->shift_type; $schedule = $this->parser->getSchedule(); - $minutesBefore = isset($data['minutes-before']) ? (int)$data['minutes-before'] : 15; - $minutesAfter = isset($data['minutes-after']) ? (int)$data['minutes-after'] : 15; + $minutesBefore = $scheduleUrl->minutes_before; + $minutesAfter = $scheduleUrl->minutes_after; $newRooms = $this->newRooms($schedule->getRooms()); return array_merge( $this->shiftsDiff($schedule, $scheduleUrl, $shiftType, $minutesBefore, $minutesAfter), @@ -373,18 +428,6 @@ class ImportSchedule extends BaseController ); } - /** - * @param string $name - * @return Collection - */ - protected function getFromSession(string $name): Collection - { - $data = Collection::make(Arr::flatten($this->session->get($name, []))); - $this->session->remove($name); - - return $data; - } - /** * @param Room[] $scheduleRooms * @return Room[] @@ -581,22 +624,6 @@ class ImportSchedule extends BaseController return $return; } - /** - * @param string $scheduleUrl - * @return ScheduleUrl - */ - protected function getScheduleUrl(string $scheduleUrl): ScheduleUrl - { - if (!$schedule = ScheduleUrl::whereUrl($scheduleUrl)->first()) { - $schedule = new ScheduleUrl(['url' => $scheduleUrl]); - $schedule->save(); - - $this->log('Created schedule "{schedule}"', ['schedule' => $schedule->url]); - } - - return $schedule; - } - /** * @param string $message * @param array $context diff --git a/resources/lang/de_DE/additional.po b/resources/lang/de_DE/additional.po index 42f6ad95..c3d383d5 100644 --- a/resources/lang/de_DE/additional.po +++ b/resources/lang/de_DE/additional.po @@ -35,8 +35,8 @@ msgstr "Deine Passwörter stimmen nicht überein." msgid "validation.password_confirmation.required" msgstr "Du musst dein Passwort bestätigen." -msgid "schedule.import" -msgstr "Programm importieren" +msgid "schedule.edit.success" +msgstr "Das Programm wurde erfolgreich konfiguriert." msgid "schedule.import.request-error" msgstr "Das Programm konnte nicht abgerufen werden." diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index 8edc5dfe..17623360 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -2787,12 +2787,27 @@ msgstr "Programm laden" msgid "form.import" msgstr "Importieren" +msgid "form.edit" +msgstr "Bearbeiten" + +msgid "form.save" +msgstr "Speichern" + +msgid "schedule.import" +msgstr "Programm importieren" + +msgid "schedule.edit.title" +msgstr "Programm bearbeiten" + msgid "schedule.import.title" msgstr "Programm importieren" +msgid "schedule.last_update" +msgstr "Aktualisiert am: %s" + msgid "schedule.import.text" msgstr "" -"Dieser Import erstellt Räume und erstellt, aktualisiert und löscht Schichten anhand des schedule.xml exportes." +"Importe erstellen Räume und erstellen, aktualisieren und löschen Schichten anhand eines schedule.xml exportes." msgid "schedule.import.load.title" msgstr "Programm importieren: Vorschau" @@ -2800,6 +2815,9 @@ msgstr "Programm importieren: Vorschau" msgid "schedule.import.load.info" msgstr "Importiere \"%s\" (Version \"%s\")" +msgid "schedule.name" +msgstr "Programm Name" + msgid "schedule.url" msgstr "Programm URL (schedule.xml)" diff --git a/resources/lang/en_US/additional.po b/resources/lang/en_US/additional.po index e5baf48c..fbdb0f34 100644 --- a/resources/lang/en_US/additional.po +++ b/resources/lang/en_US/additional.po @@ -33,8 +33,8 @@ msgstr "Your passwords are not equal." msgid "validation.password_confirmation.required" msgstr "You have to confirm your password." -msgid "schedule.import" -msgstr "Import schedule" +msgid "schedule.edit.success" +msgstr "The schedule was configured successfully." msgid "schedule.import.request-error" msgstr "The schedule could not be requested." diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index 5ed475e3..e6d39720 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -52,11 +52,26 @@ msgstr "Load schedule" msgid "form.import" msgstr "Import" +msgid "form.save" +msgstr "Save" + +msgid "form.edit" +msgstr "Bearbeiten" + +msgid "schedule.import" +msgstr "Import schedule" + +msgid "schedule.edit.title" +msgstr "Edit schedule" + msgid "schedule.import.title" msgstr "Import schedule" +msgid "schedule.last_update" +msgstr "Last updated: %s" + msgid "schedule.import.text" -msgstr "This import creates rooms and creates, updates and deletes shifts according to the schedule.xml export." +msgstr "Imports create rooms and create, update and delete shifts according to a schedule.xml export." msgid "schedule.import.load.title" msgstr "Import schedule: Preview" @@ -64,6 +79,9 @@ msgstr "Import schedule: Preview" msgid "schedule.import.load.info" msgstr "Import \"%s\" (version \"%s\")" +msgid "schedule.name" +msgstr "Programm name" + msgid "schedule.url" msgstr "Schedule URL (schedule.xml)" diff --git a/resources/views/admin/schedule/edit.twig b/resources/views/admin/schedule/edit.twig new file mode 100644 index 00000000..335b34e9 --- /dev/null +++ b/resources/views/admin/schedule/edit.twig @@ -0,0 +1,29 @@ +{% extends 'admin/schedule/index.twig' %} +{% import 'macros/base.twig' as m %} +{% import 'macros/form.twig' as f %} + +{% block title %}{{ schedule ? __('schedule.edit.title') : __('schedule.import.title') }}{% endblock %} + +{% block row_content %} + {% if schedule and schedule.updated_at %} +
{{ __('schedule.last_update', [schedule.updated_at.format(__('Y-m-d H:i'))]) }}
+