Merge pull request #685 from MyIgel/schedule-import

Rebuild Schedule import
main
msquare 5 years ago committed by GitHub
commit a3a938a121
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -65,7 +65,7 @@ The following instructions explain how to get, build and run the latest engelsys
``` ```
### Configuration and Setup ### Configuration and Setup
* The webserver must have write access to the ```import``` and ```storage``` directories and read access for all other directories * The webserver must have write access to the ```storage``` directory and read access for all other directories
* The webserver must point to the ```public``` directory. * The webserver must point to the ```public``` directory.
* The webserver must read the ```.htaccess``` file and ```mod_rewrite``` must be enabled * The webserver must read the ```.htaccess``` file and ```mod_rewrite``` must be enabled

@ -28,6 +28,7 @@
"doctrine/dbal": "^2.9", "doctrine/dbal": "^2.9",
"erusev/parsedown": "^1.7", "erusev/parsedown": "^1.7",
"gettext/gettext": "^4.6", "gettext/gettext": "^4.6",
"guzzlehttp/guzzle": "^6.3",
"illuminate/container": "5.8.*", "illuminate/container": "5.8.*",
"illuminate/database": "5.8.*", "illuminate/database": "5.8.*",
"illuminate/support": "5.8.*", "illuminate/support": "5.8.*",

@ -26,10 +26,12 @@ return [
\Engelsystem\Middleware\RequestHandlerServiceProvider::class, \Engelsystem\Middleware\RequestHandlerServiceProvider::class,
\Engelsystem\Middleware\SessionHandlerServiceProvider::class, \Engelsystem\Middleware\SessionHandlerServiceProvider::class,
\Engelsystem\Http\Validation\ValidationServiceProvider::class, \Engelsystem\Http\Validation\ValidationServiceProvider::class,
\Engelsystem\Http\RedirectServiceProvider::class,
// Additional services // Additional services
\Engelsystem\Helpers\VersionServiceProvider::class, \Engelsystem\Helpers\VersionServiceProvider::class,
\Engelsystem\Mail\MailerServiceProvider::class, \Engelsystem\Mail\MailerServiceProvider::class,
\Engelsystem\Http\HttpClientServiceProvider::class,
], ],
// Application middleware // Application middleware

@ -25,3 +25,19 @@ $route->get('/stats', 'Metrics\\Controller@stats');
// API // API
$route->get('/api[/{resource:.+}]', 'ApiController@index'); $route->get('/api[/{resource:.+}]', 'ApiController@index');
// Administration
$route->addGroup(
'/admin',
function (RouteCollector $route) {
// Schedule
$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');
}
);
}
);

@ -0,0 +1,48 @@
<?php
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
class MigrateAdminSchedulePermissions extends Migration
{
/**
* Run the migration
*/
public function up()
{
if (!$this->schema->hasTable('Privileges')) {
return;
}
$this->schema->getConnection()
->table('Privileges')
->where('name', 'admin_import')
->update(
[
'name' => 'schedule.import',
'desc' => 'Import rooms and shifts from schedule.xml',
]
);
}
/**
* Reverse the migration
*/
public function down()
{
if (!$this->schema->hasTable('Privileges')) {
return;
}
$this->schema->getConnection()
->table('Privileges')
->where('name', 'schedule.import')
->update(
[
'name' => 'admin_import',
'desc' => 'Import rooms and shifts from schedule.xcs/schedule.xcal',
]
);
}
}

@ -0,0 +1,90 @@
<?php
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateScheduleShiftTable extends Migration
{
use Reference;
/**
* Run the migration
*/
public function up()
{
$this->schema->create(
'schedules',
function (Blueprint $table) {
$table->increments('id');
$table->string('url');
}
);
$this->schema->create(
'schedule_shift',
function (Blueprint $table) {
$table->integer('shift_id')->index()->unique();
if ($this->schema->hasTable('Shifts')) {
// Legacy table access
$table->foreign('shift_id')
->references('SID')->on('Shifts')
->onUpdate('cascade')
->onDelete('cascade');
}
$this->references($table, 'schedules');
$table->uuid('guid');
}
);
if ($this->schema->hasTable('Shifts')) {
$this->schema->table(
'Shifts',
function (Blueprint $table) {
$table->dropColumn('PSID');
}
);
}
if ($this->schema->hasTable('Room')) {
$this->schema->table(
'Room',
function (Blueprint $table) {
$table->dropColumn('from_frab');
}
);
}
}
/**
* Reverse the migration
*/
public function down()
{
if ($this->schema->hasTable('Room')) {
$this->schema->table(
'Room',
function (Blueprint $table) {
$table->boolean('from_frab')
->default(false);
}
);
}
if ($this->schema->hasTable('Shifts')) {
$this->schema->table(
'Shifts',
function (Blueprint $table) {
$table->integer('PSID')
->nullable()->default(null)
->unique();
}
);
}
$this->schema->drop('schedule_shift');
$this->schema->drop('schedules');
}
}

@ -4,6 +4,7 @@ namespace Engelsystem\Migrations;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\ColumnDefinition; use Illuminate\Database\Schema\ColumnDefinition;
use Illuminate\Support\Str;
trait Reference trait Reference
{ {
@ -11,20 +12,25 @@ trait Reference
* @param Blueprint $table * @param Blueprint $table
* @param bool $setPrimary * @param bool $setPrimary
*/ */
protected function referencesUser(Blueprint $table, $setPrimary = false) protected function referencesUser(Blueprint $table, bool $setPrimary = false)
{ {
$this->references($table, 'users', 'user_id', $setPrimary); $this->references($table, 'users', null, $setPrimary);
} }
/** /**
* @param Blueprint $table * @param Blueprint $table
* @param string $targetTable * @param string $targetTable
* @param string $fromColumn * @param string|null $fromColumn
* @param bool $setPrimary * @param bool $setPrimary
* @return ColumnDefinition * @return ColumnDefinition
*/ */
protected function references(Blueprint $table, $targetTable, $fromColumn, $setPrimary = false): ColumnDefinition protected function references(
{ Blueprint $table,
string $targetTable,
?string $fromColumn = null,
bool $setPrimary = false
): ColumnDefinition {
$fromColumn = $fromColumn ?? Str::singular($targetTable) . '_id';
$col = $table->unsignedInteger($fromColumn); $col = $table->unsignedInteger($fromColumn);
if ($setPrimary) { if ($setPrimary) {

@ -15,7 +15,6 @@ COPY .babelrc .browserslistrc composer.json LICENSE package.json README.md webpa
COPY bin/ /app/bin COPY bin/ /app/bin
COPY config/ /app/config COPY config/ /app/config
COPY db/ /app/db COPY db/ /app/db
RUN mkdir /app/import/
COPY includes/ /app/includes COPY includes/ /app/includes
COPY public/ /app/public COPY public/ /app/public
COPY resources/views /app/resources/views COPY resources/views /app/resources/views
@ -35,7 +34,7 @@ WORKDIR /var/www
RUN apk add --no-cache icu-dev && \ RUN apk add --no-cache icu-dev && \
docker-php-ext-install intl pdo_mysql docker-php-ext-install intl pdo_mysql
COPY --from=data /app/ /var/www COPY --from=data /app/ /var/www
RUN chown -R www-data:www-data /var/www/import/ /var/www/storage/ && \ RUN chown -R www-data:www-data /var/www/storage/ && \
rm -r /var/www/html rm -r /var/www/html
ARG VERSION ARG VERSION

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -79,7 +79,7 @@ function angeltypes_about_controller()
function angeltype_delete_controller() function angeltype_delete_controller()
{ {
if (!auth()->can('admin_angel_types')) { if (!auth()->can('admin_angel_types')) {
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
$angeltype = load_angeltype(); $angeltype = load_angeltype();
@ -87,7 +87,7 @@ function angeltype_delete_controller()
if (request()->hasPostData('delete')) { if (request()->hasPostData('delete')) {
AngelType_delete($angeltype); AngelType_delete($angeltype);
success(sprintf(__('Angeltype %s deleted.'), AngelType_name_render($angeltype))); success(sprintf(__('Angeltype %s deleted.'), AngelType_name_render($angeltype)));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
return [ return [
@ -112,13 +112,13 @@ function angeltype_edit_controller()
$angeltype = load_angeltype(); $angeltype = load_angeltype();
if (!User_is_AngelType_supporter(auth()->user(), $angeltype)) { if (!User_is_AngelType_supporter(auth()->user(), $angeltype)) {
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
} else { } else {
// New angeltype // New angeltype
if ($supporter_mode) { if ($supporter_mode) {
// Supporters aren't allowed to create new angeltypes. // Supporters aren't allowed to create new angeltypes.
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
$angeltype = AngelType_new(); $angeltype = AngelType_new();
} }
@ -157,7 +157,7 @@ function angeltype_edit_controller()
} }
success('Angel type saved.'); success('Angel type saved.');
redirect(angeltype_link($angeltype['id'])); throw_redirect(angeltype_link($angeltype['id']));
} }
} }
@ -177,7 +177,7 @@ function angeltype_controller()
$user = auth()->user(); $user = auth()->user();
if (!auth()->can('angeltypes')) { if (!auth()->can('angeltypes')) {
redirect(page_link_to('/')); throw_redirect(page_link_to('/'));
} }
$angeltype = load_angeltype(); $angeltype = load_angeltype();
@ -275,7 +275,7 @@ function angeltypes_list_controller()
$user = auth()->user(); $user = auth()->user();
if (!auth()->can('angeltypes')) { if (!auth()->can('angeltypes')) {
redirect(page_link_to('/')); throw_redirect(page_link_to('/'));
} }
$angeltypes = AngelTypes_with_user($user->id); $angeltypes = AngelTypes_with_user($user->id);
@ -346,13 +346,13 @@ function load_angeltype()
{ {
$request = request(); $request = request();
if (!$request->has('angeltype_id')) { if (!$request->has('angeltype_id')) {
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
$angeltype = AngelType($request->input('angeltype_id')); $angeltype = AngelType($request->input('angeltype_id'));
if (empty($angeltype)) { if (empty($angeltype)) {
error(__('Angeltype doesn\'t exist . ')); error(__('Angeltype doesn\'t exist . '));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
return $angeltype; return $angeltype;

@ -17,7 +17,7 @@ function event_config_title()
function event_config_edit_controller() function event_config_edit_controller()
{ {
if (!auth()->can('admin_event_config')) { if (!auth()->can('admin_event_config')) {
redirect(page_link_to('/')); throw_redirect(page_link_to('/'));
} }
$request = request(); $request = request();
@ -118,7 +118,7 @@ function event_config_edit_controller()
) )
); );
success(__('Settings saved.')); success(__('Settings saved.'));
redirect(page_link_to('admin_event_config')); throw_redirect(page_link_to('admin_event_config'));
} }
} }

@ -15,7 +15,7 @@ use Engelsystem\ShiftsFilterRenderer;
function room_controller() function room_controller()
{ {
if (!auth()->can('view_rooms')) { if (!auth()->can('view_rooms')) {
redirect(page_link_to()); throw_redirect(page_link_to());
} }
$request = request(); $request = request();
@ -74,7 +74,7 @@ function rooms_controller()
return room_controller(); return room_controller();
case 'list': case 'list':
default: default:
redirect(page_link_to('admin_rooms')); throw_redirect(page_link_to('admin_rooms'));
} }
} }
@ -104,12 +104,12 @@ function room_edit_link($room)
function load_room() function load_room()
{ {
if (!test_request_int('room_id')) { if (!test_request_int('room_id')) {
redirect(page_link_to()); throw_redirect(page_link_to());
} }
$room = Room(request()->input('room_id')); $room = Room(request()->input('room_id'));
if (empty($room)) { if (empty($room)) {
redirect(page_link_to()); throw_redirect(page_link_to());
} }
return $room; return $room;

@ -13,12 +13,12 @@ function shift_entries_controller()
{ {
$user = auth()->user(); $user = auth()->user();
if (!$user) { if (!$user) {
redirect(page_link_to('login')); throw_redirect(page_link_to('login'));
} }
$action = strip_request_item('action'); $action = strip_request_item('action');
if (empty($action)) { if (empty($action)) {
redirect(user_link($user->id)); throw_redirect(user_link($user->id));
} }
switch ($action) { switch ($action) {
@ -40,12 +40,12 @@ function shift_entry_create_controller()
$request = request(); $request = request();
if (User_is_freeloader($user)) { if (User_is_freeloader($user)) {
redirect(page_link_to('user_myshifts')); throw_redirect(page_link_to('user_myshifts'));
} }
$shift = Shift($request->input('shift_id')); $shift = Shift($request->input('shift_id'));
if (empty($shift)) { if (empty($shift)) {
redirect(user_link($user->id)); throw_redirect(user_link($user->id));
} }
$angeltype = AngelType($request->input('angeltype_id')); $angeltype = AngelType($request->input('angeltype_id'));
@ -55,7 +55,7 @@ function shift_entry_create_controller()
} }
if (empty($angeltype)) { if (empty($angeltype)) {
redirect(user_link($user->id)); throw_redirect(user_link($user->id));
} }
if (User_is_AngelType_supporter($user, $angeltype)) { if (User_is_AngelType_supporter($user, $angeltype)) {
@ -82,7 +82,7 @@ function shift_entry_create_controller_admin($shift, $angeltype)
$signup_user = User::find($request->input('user_id')); $signup_user = User::find($request->input('user_id'));
} }
if (!$signup_user) { if (!$signup_user) {
redirect(shift_link($shift)); throw_redirect(shift_link($shift));
} }
$angeltypes = AngelTypes(); $angeltypes = AngelTypes();
@ -91,7 +91,7 @@ function shift_entry_create_controller_admin($shift, $angeltype)
} }
if (empty($angeltype)) { if (empty($angeltype)) {
if (count($angeltypes) == 0) { if (count($angeltypes) == 0) {
redirect(shift_link($shift)); throw_redirect(shift_link($shift));
} }
$angeltype = $angeltypes[0]; $angeltype = $angeltypes[0];
} }
@ -107,7 +107,7 @@ function shift_entry_create_controller_admin($shift, $angeltype)
]); ]);
success(sprintf(__('%s has been subscribed to the shift.'), User_Nick_render($signup_user))); success(sprintf(__('%s has been subscribed to the shift.'), User_Nick_render($signup_user)));
redirect(shift_link($shift)); throw_redirect(shift_link($shift));
} }
/** @var User[]|Collection $users */ /** @var User[]|Collection $users */
@ -147,7 +147,7 @@ function shift_entry_create_controller_supporter($shift, $angeltype)
} }
if (!UserAngelType_exists($signup_user->id, $angeltype)) { if (!UserAngelType_exists($signup_user->id, $angeltype)) {
error(__('User is not in angeltype.')); error(__('User is not in angeltype.'));
redirect(shift_link($shift)); throw_redirect(shift_link($shift));
} }
$needed_angeltype = NeededAngeltype_by_Shift_and_Angeltype($shift, $angeltype); $needed_angeltype = NeededAngeltype_by_Shift_and_Angeltype($shift, $angeltype);
@ -165,7 +165,7 @@ function shift_entry_create_controller_supporter($shift, $angeltype)
if ($shift_signup_state->getState() == ShiftSignupState::OCCUPIED) { if ($shift_signup_state->getState() == ShiftSignupState::OCCUPIED) {
error(__('This shift is already occupied.')); error(__('This shift is already occupied.'));
} }
redirect(shift_link($shift)); throw_redirect(shift_link($shift));
} }
if ($request->hasPostData('submit')) { if ($request->hasPostData('submit')) {
@ -179,7 +179,7 @@ function shift_entry_create_controller_supporter($shift, $angeltype)
]); ]);
success(sprintf(__('%s has been subscribed to the shift.'), User_Nick_render($signup_user))); success(sprintf(__('%s has been subscribed to the shift.'), User_Nick_render($signup_user)));
redirect(shift_link($shift)); throw_redirect(shift_link($shift));
} }
$users = Users_by_angeltype($angeltype); $users = Users_by_angeltype($angeltype);
@ -245,7 +245,7 @@ function shift_entry_create_controller_user($shift, $angeltype)
); );
if (!$shift_signup_state->isSignupAllowed()) { if (!$shift_signup_state->isSignupAllowed()) {
shift_entry_error_message($shift_signup_state); shift_entry_error_message($shift_signup_state);
redirect(shift_link($shift)); throw_redirect(shift_link($shift));
} }
$comment = ''; $comment = '';
@ -265,7 +265,7 @@ function shift_entry_create_controller_user($shift, $angeltype)
} }
success(__('You are subscribed. Thank you!')); success(__('You are subscribed. Thank you!'));
redirect(shift_link($shift)); throw_redirect(shift_link($shift));
} }
$room = Room($shift['RID']); $room = Room($shift['RID']);
@ -319,12 +319,12 @@ function shift_entry_load()
$request = request(); $request = request();
if (!$request->has('shift_entry_id') || !test_request_int('shift_entry_id')) { if (!$request->has('shift_entry_id') || !test_request_int('shift_entry_id')) {
redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
} }
$shiftEntry = ShiftEntry($request->input('shift_entry_id')); $shiftEntry = ShiftEntry($request->input('shift_entry_id'));
if (empty($shiftEntry)) { if (empty($shiftEntry)) {
error(__('Shift entry not found.')); error(__('Shift entry not found.'));
redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
} }
return $shiftEntry; return $shiftEntry;
@ -346,13 +346,13 @@ function shift_entry_delete_controller()
$signout_user = User::find($shiftEntry['UID']); $signout_user = User::find($shiftEntry['UID']);
if (!Shift_signout_allowed($shift, $angeltype, $signout_user->id)) { if (!Shift_signout_allowed($shift, $angeltype, $signout_user->id)) {
error(__('You are not allowed to remove this shift entry. If necessary, ask your supporter or heaven to do so.')); error(__('You are not allowed to remove this shift entry. If necessary, ask your supporter or heaven to do so.'));
redirect(user_link($signout_user->id)); throw_redirect(user_link($signout_user->id));
} }
if ($request->hasPostData('delete')) { if ($request->hasPostData('delete')) {
ShiftEntry_delete($shiftEntry); ShiftEntry_delete($shiftEntry);
success(__('Shift entry removed.')); success(__('Shift entry removed.'));
redirect(shift_link($shift)); throw_redirect(shift_link($shift));
} }
if ($user->id == $signout_user->id) { if ($user->id == $signout_user->id) {

@ -49,11 +49,11 @@ function shift_edit_controller()
$request = request(); $request = request();
if (!auth()->can('admin_shifts')) { if (!auth()->can('admin_shifts')) {
redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
} }
if (!$request->has('edit_shift') || !test_request_int('edit_shift')) { if (!$request->has('edit_shift') || !test_request_int('edit_shift')) {
redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
} }
$shift_id = $request->input('edit_shift'); $shift_id = $request->input('edit_shift');
@ -164,7 +164,7 @@ function shift_edit_controller()
); );
success(__('Shift updated.')); success(__('Shift updated.'));
redirect(shift_link([ throw_redirect(shift_link([
'SID' => $shift_id 'SID' => $shift_id
])); ]));
} }
@ -205,18 +205,18 @@ function shift_delete_controller()
$request = request(); $request = request();
if (!auth()->can('user_shifts_admin')) { if (!auth()->can('user_shifts_admin')) {
redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
} }
// Schicht komplett löschen (nur für admins/user mit user_shifts_admin privileg) // Schicht komplett löschen (nur für admins/user mit user_shifts_admin privileg)
if (!$request->has('delete_shift') || !preg_match('/^\d+$/', $request->input('delete_shift'))) { if (!$request->has('delete_shift') || !preg_match('/^\d+$/', $request->input('delete_shift'))) {
redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
} }
$shift_id = $request->input('delete_shift'); $shift_id = $request->input('delete_shift');
$shift = Shift($shift_id); $shift = Shift($shift_id);
if (empty($shift)) { if (empty($shift)) {
redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
} }
// Schicht löschen bestätigt // Schicht löschen bestätigt
@ -230,7 +230,7 @@ function shift_delete_controller()
. ' to ' . date('Y-m-d H:i', $shift['end']) . ' to ' . date('Y-m-d H:i', $shift['end'])
); );
success(__('Shift deleted.')); success(__('Shift deleted.'));
redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
} }
return page_with_title(shifts_title(), [ return page_with_title(shifts_title(), [
@ -256,17 +256,17 @@ function shift_controller()
$request = request(); $request = request();
if (!auth()->can('user_shifts')) { if (!auth()->can('user_shifts')) {
redirect(page_link_to('/')); throw_redirect(page_link_to('/'));
} }
if (!$request->has('shift_id')) { if (!$request->has('shift_id')) {
redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
} }
$shift = Shift($request->input('shift_id')); $shift = Shift($request->input('shift_id'));
if (empty($shift)) { if (empty($shift)) {
error(__('Shift could not be found.')); error(__('Shift could not be found.'));
redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
} }
$shifttype = ShiftType($shift['shifttype_id']); $shifttype = ShiftType($shift['shifttype_id']);
@ -309,7 +309,7 @@ function shifts_controller()
{ {
$request = request(); $request = request();
if (!$request->has('action')) { if (!$request->has('action')) {
redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
} }
switch ($request->input('action')) { switch ($request->input('action')) {
@ -319,7 +319,7 @@ function shifts_controller()
case 'next': case 'next':
shift_next_controller(); shift_next_controller();
default: default:
redirect(page_link_to('/')); throw_redirect(page_link_to('/'));
} }
return false; return false;
@ -331,16 +331,16 @@ function shifts_controller()
function shift_next_controller() function shift_next_controller()
{ {
if (!auth()->can('user_shifts')) { if (!auth()->can('user_shifts')) {
redirect(page_link_to('/')); throw_redirect(page_link_to('/'));
} }
$upcoming_shifts = ShiftEntries_upcoming_for_user(auth()->user()->id); $upcoming_shifts = ShiftEntries_upcoming_for_user(auth()->user()->id);
if (!empty($upcoming_shifts)) { if (!empty($upcoming_shifts)) {
redirect(shift_link($upcoming_shifts[0])); throw_redirect(shift_link($upcoming_shifts[0]));
} }
redirect(page_link_to('user_shifts')); throw_redirect(page_link_to('user_shifts'));
} }
/** /**

@ -18,12 +18,12 @@ function shifttype_delete_controller()
{ {
$request = request(); $request = request();
if (!$request->has('shifttype_id')) { if (!$request->has('shifttype_id')) {
redirect(page_link_to('shifttypes')); throw_redirect(page_link_to('shifttypes'));
} }
$shifttype = ShiftType($request->input('shifttype_id')); $shifttype = ShiftType($request->input('shifttype_id'));
if (empty($shifttype)) { if (empty($shifttype)) {
redirect(page_link_to('shifttypes')); throw_redirect(page_link_to('shifttypes'));
} }
if ($request->hasPostData('delete')) { if ($request->hasPostData('delete')) {
@ -31,7 +31,7 @@ function shifttype_delete_controller()
engelsystem_log('Deleted shifttype ' . $shifttype['name']); engelsystem_log('Deleted shifttype ' . $shifttype['name']);
success(sprintf(__('Shifttype %s deleted.'), $shifttype['name'])); success(sprintf(__('Shifttype %s deleted.'), $shifttype['name']));
redirect(page_link_to('shifttypes')); throw_redirect(page_link_to('shifttypes'));
} }
return [ return [
@ -59,7 +59,7 @@ function shifttype_edit_controller()
$shifttype = ShiftType($request->input('shifttype_id')); $shifttype = ShiftType($request->input('shifttype_id'));
if (empty($shifttype)) { if (empty($shifttype)) {
error(__('Shifttype not found.')); error(__('Shifttype not found.'));
redirect(page_link_to('shifttypes')); throw_redirect(page_link_to('shifttypes'));
} }
$shifttype_id = $shifttype['id']; $shifttype_id = $shifttype['id'];
$name = $shifttype['name']; $name = $shifttype['name'];
@ -99,7 +99,7 @@ function shifttype_edit_controller()
engelsystem_log('Created shifttype ' . $name); engelsystem_log('Created shifttype ' . $name);
success(__('Created shifttype.')); success(__('Created shifttype.'));
} }
redirect(page_link_to('shifttypes', ['action' => 'view', 'shifttype_id' => $shifttype_id])); throw_redirect(page_link_to('shifttypes', ['action' => 'view', 'shifttype_id' => $shifttype_id]));
} }
} }
@ -116,11 +116,11 @@ function shifttype_controller()
{ {
$request = request(); $request = request();
if (!$request->has('shifttype_id')) { if (!$request->has('shifttype_id')) {
redirect(page_link_to('shifttypes')); throw_redirect(page_link_to('shifttypes'));
} }
$shifttype = ShiftType($request->input('shifttype_id')); $shifttype = ShiftType($request->input('shifttype_id'));
if (empty($shifttype)) { if (empty($shifttype)) {
redirect(page_link_to('shifttypes')); throw_redirect(page_link_to('shifttypes'));
} }
$angeltype = []; $angeltype = [];

@ -45,18 +45,18 @@ function user_angeltypes_delete_all_controller()
if (!$request->has('angeltype_id')) { if (!$request->has('angeltype_id')) {
error(__('Angeltype doesn\'t exist.')); error(__('Angeltype doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
$angeltype = AngelType($request->input('angeltype_id')); $angeltype = AngelType($request->input('angeltype_id'));
if (empty($angeltype)) { if (empty($angeltype)) {
error(__('Angeltype doesn\'t exist.')); error(__('Angeltype doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
if (!User_is_AngelType_supporter(auth()->user(), $angeltype)) { if (!User_is_AngelType_supporter(auth()->user(), $angeltype)) {
error(__('You are not allowed to delete all users for this angeltype.')); error(__('You are not allowed to delete all users for this angeltype.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
if ($request->hasPostData('deny_all')) { if ($request->hasPostData('deny_all')) {
@ -64,7 +64,7 @@ function user_angeltypes_delete_all_controller()
engelsystem_log(sprintf('Denied all users for angeltype %s', AngelType_name_render($angeltype, true))); engelsystem_log(sprintf('Denied all users for angeltype %s', AngelType_name_render($angeltype, true)));
success(sprintf(__('Denied all users for angeltype %s.'), AngelType_name_render($angeltype))); success(sprintf(__('Denied all users for angeltype %s.'), AngelType_name_render($angeltype)));
redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype['id']])); throw_redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype['id']]));
} }
return [ return [
@ -85,18 +85,18 @@ function user_angeltypes_confirm_all_controller()
if (!$request->has('angeltype_id')) { if (!$request->has('angeltype_id')) {
error(__('Angeltype doesn\'t exist.')); error(__('Angeltype doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
$angeltype = AngelType($request->input('angeltype_id')); $angeltype = AngelType($request->input('angeltype_id'));
if (empty($angeltype)) { if (empty($angeltype)) {
error(__('Angeltype doesn\'t exist.')); error(__('Angeltype doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
if (!auth()->can('admin_user_angeltypes') && !User_is_AngelType_supporter($user, $angeltype)) { if (!auth()->can('admin_user_angeltypes') && !User_is_AngelType_supporter($user, $angeltype)) {
error(__('You are not allowed to confirm all users for this angeltype.')); error(__('You are not allowed to confirm all users for this angeltype.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
if ($request->hasPostData('confirm_all')) { if ($request->hasPostData('confirm_all')) {
@ -104,7 +104,7 @@ function user_angeltypes_confirm_all_controller()
engelsystem_log(sprintf('Confirmed all users for angeltype %s', AngelType_name_render($angeltype, true))); engelsystem_log(sprintf('Confirmed all users for angeltype %s', AngelType_name_render($angeltype, true)));
success(sprintf(__('Confirmed all users for angeltype %s.'), AngelType_name_render($angeltype))); success(sprintf(__('Confirmed all users for angeltype %s.'), AngelType_name_render($angeltype)));
redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype['id']])); throw_redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype['id']]));
} }
return [ return [
@ -125,30 +125,30 @@ function user_angeltype_confirm_controller()
if (!$request->has('user_angeltype_id')) { if (!$request->has('user_angeltype_id')) {
error(__('User angeltype doesn\'t exist.')); error(__('User angeltype doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
$user_angeltype = UserAngelType($request->input('user_angeltype_id')); $user_angeltype = UserAngelType($request->input('user_angeltype_id'));
if (empty($user_angeltype)) { if (empty($user_angeltype)) {
error(__('User angeltype doesn\'t exist.')); error(__('User angeltype doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
$angeltype = AngelType($user_angeltype['angeltype_id']); $angeltype = AngelType($user_angeltype['angeltype_id']);
if (empty($angeltype)) { if (empty($angeltype)) {
error(__('Angeltype doesn\'t exist.')); error(__('Angeltype doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
if (!User_is_AngelType_supporter($user, $angeltype)) { if (!User_is_AngelType_supporter($user, $angeltype)) {
error(__('You are not allowed to confirm this users angeltype.')); error(__('You are not allowed to confirm this users angeltype.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
$user_source = User::find($user_angeltype['user_id']); $user_source = User::find($user_angeltype['user_id']);
if (!$user_source) { if (!$user_source) {
error(__('User doesn\'t exist.')); error(__('User doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
if ($request->hasPostData('confirm_user')) { if ($request->hasPostData('confirm_user')) {
@ -164,7 +164,7 @@ function user_angeltype_confirm_controller()
User_Nick_render($user_source), User_Nick_render($user_source),
AngelType_name_render($angeltype) AngelType_name_render($angeltype)
)); ));
redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype['id']])); throw_redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype['id']]));
} }
return [ return [
@ -185,30 +185,30 @@ function user_angeltype_delete_controller()
if (!$request->has('user_angeltype_id')) { if (!$request->has('user_angeltype_id')) {
error(__('User angeltype doesn\'t exist.')); error(__('User angeltype doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
$user_angeltype = UserAngelType($request->input('user_angeltype_id')); $user_angeltype = UserAngelType($request->input('user_angeltype_id'));
if (empty($user_angeltype)) { if (empty($user_angeltype)) {
error(__('User angeltype doesn\'t exist.')); error(__('User angeltype doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
$angeltype = AngelType($user_angeltype['angeltype_id']); $angeltype = AngelType($user_angeltype['angeltype_id']);
if (empty($angeltype)) { if (empty($angeltype)) {
error(__('Angeltype doesn\'t exist.')); error(__('Angeltype doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
$user_source = User::find($user_angeltype['user_id']); $user_source = User::find($user_angeltype['user_id']);
if (!$user_source) { if (!$user_source) {
error(__('User doesn\'t exist.')); error(__('User doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
if ($user->id != $user_angeltype['user_id'] && !User_is_AngelType_supporter($user, $angeltype)) { if ($user->id != $user_angeltype['user_id'] && !User_is_AngelType_supporter($user, $angeltype)) {
error(__('You are not allowed to delete this users angeltype.')); error(__('You are not allowed to delete this users angeltype.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
if ($request->hasPostData('delete')) { if ($request->hasPostData('delete')) {
@ -217,7 +217,7 @@ function user_angeltype_delete_controller()
engelsystem_log(sprintf('User %s removed from %s.', User_Nick_render($user_source, true), $angeltype['name'])); engelsystem_log(sprintf('User %s removed from %s.', User_Nick_render($user_source, true), $angeltype['name']));
success(sprintf(__('User %s removed from %s.'), User_Nick_render($user_source), $angeltype['name'])); success(sprintf(__('User %s removed from %s.'), User_Nick_render($user_source), $angeltype['name']));
redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype['id']])); throw_redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype['id']]));
} }
return [ return [
@ -238,37 +238,37 @@ function user_angeltype_update_controller()
if (!auth()->can('admin_angel_types')) { if (!auth()->can('admin_angel_types')) {
error(__('You are not allowed to set supporter rights.')); error(__('You are not allowed to set supporter rights.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
if (!$request->has('user_angeltype_id')) { if (!$request->has('user_angeltype_id')) {
error(__('User angeltype doesn\'t exist.')); error(__('User angeltype doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
if ($request->has('supporter') && preg_match('/^[01]$/', $request->input('supporter'))) { if ($request->has('supporter') && preg_match('/^[01]$/', $request->input('supporter'))) {
$supporter = $request->input('supporter') == '1'; $supporter = $request->input('supporter') == '1';
} else { } else {
error(__('No supporter update given.')); error(__('No supporter update given.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
$user_angeltype = UserAngelType($request->input('user_angeltype_id')); $user_angeltype = UserAngelType($request->input('user_angeltype_id'));
if (empty($user_angeltype)) { if (empty($user_angeltype)) {
error(__('User angeltype doesn\'t exist.')); error(__('User angeltype doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
$angeltype = AngelType($user_angeltype['angeltype_id']); $angeltype = AngelType($user_angeltype['angeltype_id']);
if (empty($angeltype)) { if (empty($angeltype)) {
error(__('Angeltype doesn\'t exist.')); error(__('Angeltype doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
$user_source = User::find($user_angeltype['user_id']); $user_source = User::find($user_angeltype['user_id']);
if (!$user_source) { if (!$user_source) {
error(__('User doesn\'t exist.')); error(__('User doesn\'t exist.'));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
if ($request->hasPostData('submit')) { if ($request->hasPostData('submit')) {
@ -288,7 +288,7 @@ function user_angeltype_update_controller()
User_Nick_render($user_source) User_Nick_render($user_source)
)); ));
redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype['id']])); throw_redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype['id']]));
} }
return [ return [
@ -343,7 +343,7 @@ function user_angeltype_add_controller()
AngelType_name_render($angeltype, true) AngelType_name_render($angeltype, true)
)); ));
redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype['id']])); throw_redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype['id']]));
} }
} }
@ -366,7 +366,7 @@ function user_angeltype_join_controller($angeltype)
$user_angeltype = UserAngelType_by_User_and_AngelType($user->id, $angeltype); $user_angeltype = UserAngelType_by_User_and_AngelType($user->id, $angeltype);
if (!empty($user_angeltype)) { if (!empty($user_angeltype)) {
error(sprintf(__('You are already a %s.'), $angeltype['name'])); error(sprintf(__('You are already a %s.'), $angeltype['name']));
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
if (request()->hasPostData('submit')) { if (request()->hasPostData('submit')) {
@ -389,7 +389,7 @@ function user_angeltype_join_controller($angeltype)
)); ));
} }
redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype['id']])); throw_redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype['id']]));
} }
return [ return [
@ -407,7 +407,7 @@ function user_angeltypes_controller()
{ {
$request = request(); $request = request();
if (!$request->has('action')) { if (!$request->has('action')) {
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
switch ($request->input('action')) { switch ($request->input('action')) {
@ -424,6 +424,6 @@ function user_angeltypes_controller()
case 'add': case 'add':
return user_angeltype_add_controller(); return user_angeltype_add_controller();
default: default:
redirect(page_link_to('angeltypes')); throw_redirect(page_link_to('angeltypes'));
} }
} }

@ -42,7 +42,7 @@ function user_driver_licenses_controller()
$user = auth()->user(); $user = auth()->user();
if (!$user) { if (!$user) {
redirect(page_link_to('')); throw_redirect(page_link_to(''));
} }
$action = strip_request_item('action', 'edit'); $action = strip_request_item('action', 'edit');
@ -82,7 +82,7 @@ function user_driver_license_load_user()
if ($request->has('user_id')) { if ($request->has('user_id')) {
$user_source = User::find($request->input('user_id')); $user_source = User::find($request->input('user_id'));
if (empty($user_source)) { if (empty($user_source)) {
redirect(user_driver_license_edit_link()); throw_redirect(user_driver_license_edit_link());
} }
} }
@ -102,7 +102,7 @@ function user_driver_license_edit_controller()
// only privilege admin_user can edit other users driver license information // only privilege admin_user can edit other users driver license information
if ($user->id != $user_source->id && !auth()->can('admin_user')) { if ($user->id != $user_source->id && !auth()->can('admin_user')) {
redirect(user_driver_license_edit_link()); throw_redirect(user_driver_license_edit_link());
} }
$user_driver_license = UserDriverLicense($user_source->id); $user_driver_license = UserDriverLicense($user_source->id);
@ -131,7 +131,7 @@ function user_driver_license_edit_controller()
} }
engelsystem_log('Driver license information updated.'); engelsystem_log('Driver license information updated.');
success(__('Your driver license information has been saved.')); success(__('Your driver license information has been saved.'));
redirect(user_link($user_source->id)); throw_redirect(user_link($user_source->id));
} else { } else {
error(__('Please select at least one driving license.')); error(__('Please select at least one driving license.'));
} }
@ -139,7 +139,7 @@ function user_driver_license_edit_controller()
UserDriverLicenses_delete($user_source->id); UserDriverLicenses_delete($user_source->id);
engelsystem_log('Driver license information removed.'); engelsystem_log('Driver license information removed.');
success(__('Your driver license information has been removed.')); success(__('Your driver license information has been removed.'));
redirect(user_link($user_source->id)); throw_redirect(user_link($user_source->id));
} }
} }

@ -12,7 +12,7 @@ function user_worklog_delete_controller()
$request = request(); $request = request();
$userWorkLog = UserWorkLog($request->input('user_worklog_id')); $userWorkLog = UserWorkLog($request->input('user_worklog_id'));
if (empty($userWorkLog)) { if (empty($userWorkLog)) {
redirect(user_link(auth()->user()->id)); throw_redirect(user_link(auth()->user()->id));
} }
$user_source = User::find($userWorkLog['user_id']); $user_source = User::find($userWorkLog['user_id']);
@ -20,7 +20,7 @@ function user_worklog_delete_controller()
UserWorkLog_delete($userWorkLog); UserWorkLog_delete($userWorkLog);
success(__('Work log entry deleted.')); success(__('Work log entry deleted.'));
redirect(user_link($user_source->id)); throw_redirect(user_link($user_source->id));
} }
return [ return [
@ -39,7 +39,7 @@ function user_worklog_edit_controller()
$request = request(); $request = request();
$userWorkLog = UserWorkLog($request->input('user_worklog_id')); $userWorkLog = UserWorkLog($request->input('user_worklog_id'));
if (empty($userWorkLog)) { if (empty($userWorkLog)) {
redirect(user_link(auth()->user()->id)); throw_redirect(user_link(auth()->user()->id));
} }
$user_source = User::find($userWorkLog['user_id']); $user_source = User::find($userWorkLog['user_id']);
@ -50,7 +50,7 @@ function user_worklog_edit_controller()
UserWorkLog_update($userWorkLog); UserWorkLog_update($userWorkLog);
success(__('Work log entry updated.')); success(__('Work log entry updated.'));
redirect(user_link($user_source->id)); throw_redirect(user_link($user_source->id));
} }
} }
@ -109,7 +109,7 @@ function user_worklog_add_controller()
$request = request(); $request = request();
$user_source = User::find($request->input('user_id')); $user_source = User::find($request->input('user_id'));
if (!$user_source) { if (!$user_source) {
redirect(user_link(auth()->user()->id)); throw_redirect(user_link(auth()->user()->id));
} }
$userWorkLog = UserWorkLog_new($user_source->id); $userWorkLog = UserWorkLog_new($user_source->id);
@ -121,7 +121,7 @@ function user_worklog_add_controller()
UserWorkLog_create($userWorkLog); UserWorkLog_create($userWorkLog);
success(__('Work log entry created.')); success(__('Work log entry created.'));
redirect(user_link($user_source->id)); throw_redirect(user_link($user_source->id));
} }
} }
@ -185,13 +185,13 @@ function user_worklog_controller()
$user = auth()->user(); $user = auth()->user();
if (!auth()->can('admin_user_worklog')) { if (!auth()->can('admin_user_worklog')) {
redirect(user_link($user->id)); throw_redirect(user_link($user->id));
} }
$request = request(); $request = request();
$action = $request->input('action'); $action = $request->input('action');
if (!$request->has('action')) { if (!$request->has('action')) {
redirect(user_link($user->id)); throw_redirect(user_link($user->id));
} }
switch ($action) { switch ($action) {

@ -17,7 +17,7 @@ function users_controller()
$request = request(); $request = request();
if (!$user) { if (!$user) {
redirect(page_link_to('')); throw_redirect(page_link_to(''));
} }
$action = 'list'; $action = 'list';
@ -56,13 +56,13 @@ function user_delete_controller()
} }
if (!auth()->can('admin_user')) { if (!auth()->can('admin_user')) {
redirect(page_link_to('')); throw_redirect(page_link_to(''));
} }
// You cannot delete yourself // You cannot delete yourself
if ($user->id == $user_source->id) { if ($user->id == $user_source->id) {
error(__('You cannot delete yourself.')); error(__('You cannot delete yourself.'));
redirect(user_link($user->id)); throw_redirect(user_link($user->id));
} }
if ($request->hasPostData('submit')) { if ($request->hasPostData('submit')) {
@ -85,7 +85,7 @@ function user_delete_controller()
success(__('User deleted.')); success(__('User deleted.'));
engelsystem_log(sprintf('Deleted %s', User_Nick_render($user_source, true))); engelsystem_log(sprintf('Deleted %s', User_Nick_render($user_source, true)));
redirect(users_link()); throw_redirect(users_link());
} }
} }
@ -145,7 +145,7 @@ function user_edit_vouchers_controller()
} }
if (!auth()->can('admin_user')) { if (!auth()->can('admin_user')) {
redirect(page_link_to('')); throw_redirect(page_link_to(''));
} }
if ($request->hasPostData('submit')) { if ($request->hasPostData('submit')) {
@ -171,7 +171,7 @@ function user_edit_vouchers_controller()
engelsystem_log(User_Nick_render($user_source, true) . ': ' . sprintf('Got %s vouchers', engelsystem_log(User_Nick_render($user_source, true) . ': ' . sprintf('Got %s vouchers',
$user_source->state->got_voucher)); $user_source->state->got_voucher));
redirect(user_link($user_source->id)); throw_redirect(user_link($user_source->id));
} }
} }
@ -194,7 +194,7 @@ function user_controller()
$user_source = User::find($request->input('user_id')); $user_source = User::find($request->input('user_id'));
if (!$user_source) { if (!$user_source) {
error(__('User not found.')); error(__('User not found.'));
redirect(page_link_to('/')); throw_redirect(page_link_to('/'));
} }
} }
@ -261,7 +261,7 @@ function users_list_controller()
$request = request(); $request = request();
if (!auth()->can('admin_user')) { if (!auth()->can('admin_user')) {
redirect(page_link_to('')); throw_redirect(page_link_to(''));
} }
$order_by = 'name'; $order_by = 'name';
@ -319,13 +319,13 @@ function load_user()
{ {
$request = request(); $request = request();
if (!$request->has('user_id')) { if (!$request->has('user_id')) {
redirect(page_link_to()); throw_redirect(page_link_to());
} }
$user = User::find($request->input('user_id')); $user = User::find($request->input('user_id'));
if (!$user) { if (!$user) {
error(__('User doesn\'t exist.')); error(__('User doesn\'t exist.'));
redirect(page_link_to()); throw_redirect(page_link_to());
} }
return $user; return $user;

@ -69,7 +69,6 @@ $includeFiles = [
__DIR__ . '/../includes/pages/admin_arrive.php', __DIR__ . '/../includes/pages/admin_arrive.php',
__DIR__ . '/../includes/pages/admin_free.php', __DIR__ . '/../includes/pages/admin_free.php',
__DIR__ . '/../includes/pages/admin_groups.php', __DIR__ . '/../includes/pages/admin_groups.php',
__DIR__ . '/../includes/pages/admin_import.php',
__DIR__ . '/../includes/pages/admin_log.php', __DIR__ . '/../includes/pages/admin_log.php',
__DIR__ . '/../includes/pages/admin_questions.php', __DIR__ . '/../includes/pages/admin_questions.php',
__DIR__ . '/../includes/pages/admin_rooms.php', __DIR__ . '/../includes/pages/admin_rooms.php',
@ -82,6 +81,8 @@ $includeFiles = [
__DIR__ . '/../includes/pages/user_questions.php', __DIR__ . '/../includes/pages/user_questions.php',
__DIR__ . '/../includes/pages/user_settings.php', __DIR__ . '/../includes/pages/user_settings.php',
__DIR__ . '/../includes/pages/user_shifts.php', __DIR__ . '/../includes/pages/user_shifts.php',
__DIR__ . '/../includes/pages/schedule/ImportSchedule.php',
]; ];
foreach ($includeFiles as $file) { foreach ($includeFiles as $file) {

@ -61,36 +61,21 @@ function Room_delete($room_id)
engelsystem_log('Room deleted: ' . $room['Name']); engelsystem_log('Room deleted: ' . $room['Name']);
} }
/**
* Delete a room by its name
*
* @param string $name
*/
function Room_delete_by_name($name)
{
DB::delete('DELETE FROM `Room` WHERE `Name` = ?', [
$name
]);
engelsystem_log('Room deleted: ' . $name);
}
/** /**
* Create a new room * Create a new room
* *
* @param string $name Name of the room * @param string $name Name of the room
* @param boolean $from_frab Is this a frab imported room?
* @param string $map_url URL to a map tha can be displayed in an iframe * @param string $map_url URL to a map tha can be displayed in an iframe
* @param string description Markdown description * @param string description Markdown description
* @return false|int * @return false|int
*/ */
function Room_create($name, $from_frab, $map_url, $description) function Room_create($name, $map_url, $description)
{ {
DB::insert(' DB::insert('
INSERT INTO `Room` (`Name`, `from_frab`, `map_url`, `description`) INSERT INTO `Room` (`Name`, `map_url`, `description`)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?)
', [ ', [
$name, $name,
(int)$from_frab,
$map_url, $map_url,
$description $description
]); ]);
@ -98,7 +83,6 @@ function Room_create($name, $from_frab, $map_url, $description)
engelsystem_log( engelsystem_log(
'Room created: ' . $name 'Room created: ' . $name
. ', frab import: ' . ($from_frab ? 'Yes' : '')
. ', map_url: ' . $map_url . ', map_url: ' . $map_url
. ', description: ' . $description . ', description: ' . $description
); );
@ -107,28 +91,25 @@ function Room_create($name, $from_frab, $map_url, $description)
} }
/** /**
* update a room * Update a room
* *
* @param int $room_id The rooms id * @param int $room_id The rooms id
* @param string $name Name of the room * @param string $name Name of the room
* @param boolean $from_frab Is this a frab imported room?
* @param string $map_url URL to a map tha can be displayed in an iframe * @param string $map_url URL to a map tha can be displayed in an iframe
* @param string $description Markdown description * @param string $description Markdown description
* @return int * @return int
*/ */
function Room_update($room_id, $name, $from_frab, $map_url, $description) function Room_update($room_id, $name, $map_url, $description)
{ {
$result = DB::update(' $result = DB::update('
UPDATE `Room` UPDATE `Room`
SET SET
`Name`=?, `Name`=?,
`from_frab`=?,
`map_url`=?, `map_url`=?,
`description`=? `description`=?
WHERE `RID`=? WHERE `RID`=?
LIMIT 1', [ LIMIT 1', [
$name, $name,
(int)$from_frab,
$map_url, $map_url,
$description, $description,
$room_id $room_id
@ -136,7 +117,6 @@ function Room_update($room_id, $name, $from_frab, $map_url, $description)
engelsystem_log( engelsystem_log(
'Room updated: ' . $name . 'Room updated: ' . $name .
', frab import: ' . ($from_frab ? 'Yes' : '') .
', map_url: ' . $map_url . ', map_url: ' . $map_url .
', description: ' . $description ', description: ' . $description
); );

@ -14,17 +14,19 @@ function Shifts_by_angeltype($angeltype)
return DB::select(' return DB::select('
SELECT DISTINCT `Shifts`.* FROM `Shifts` SELECT DISTINCT `Shifts`.* FROM `Shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id` = `Shifts`.`SID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id` = `Shifts`.`SID`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `NeededAngelTypes`.`angel_type_id` = ? WHERE `NeededAngelTypes`.`angel_type_id` = ?
AND `NeededAngelTypes`.`count` > 0 AND `NeededAngelTypes`.`count` > 0
AND `Shifts`.`PSID` IS NULL AND s.shift_id IS NULL
UNION UNION
SELECT DISTINCT `Shifts`.* FROM `Shifts` SELECT DISTINCT `Shifts`.* FROM `Shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id` = `Shifts`.`RID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id` = `Shifts`.`RID`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `NeededAngelTypes`.`angel_type_id` = ? WHERE `NeededAngelTypes`.`angel_type_id` = ?
AND `NeededAngelTypes`.`count` > 0 AND `NeededAngelTypes`.`count` > 0
AND NOT `Shifts`.`PSID` IS NULL AND NOT s.shift_id IS NULL
', [$angeltype['id'], $angeltype['id']]); ', [$angeltype['id'], $angeltype['id']]);
} }
@ -41,19 +43,21 @@ function Shifts_free($start, $end)
SELECT * FROM ( SELECT * FROM (
SELECT * SELECT *
FROM `Shifts` FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE (`end` > ? AND `start` < ?) WHERE (`end` > ? AND `start` < ?)
AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`) AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`)
> (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0) > (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
AND `Shifts`.`PSID` IS NULL AND s.shift_id IS NULL
UNION UNION
SELECT * SELECT *
FROM `Shifts` FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE (`end` > ? AND `start` < ?) WHERE (`end` > ? AND `start` < ?)
AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`Shifts`.`RID`) AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`Shifts`.`RID`)
> (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0) > (SELECT COUNT(*) FROM `ShiftEntry` WHERE `ShiftEntry`.`SID`=`Shifts`.`SID` AND `freeloaded`=0)
AND NOT `Shifts`.`PSID` IS NULL AND NOT s.shift_id IS NULL
) AS `tmp` ) AS `tmp`
ORDER BY `tmp`.`start` ORDER BY `tmp`.`start`
", [ ", [
@ -69,16 +73,6 @@ function Shifts_free($start, $end)
return $free_shifts; return $free_shifts;
} }
/**
* Returns all shifts with a PSID (from frab import)
*
* @return array[]
*/
function Shifts_from_frab()
{
return DB::select('SELECT * FROM `Shifts` WHERE `PSID` IS NOT NULL ORDER BY `start`');
}
/** /**
* @param array|int $room * @param array|int $room
* @return array[] * @return array[]
@ -103,11 +97,12 @@ function Shifts_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
JOIN `Room` USING (`RID`) JOIN `Room` USING (`RID`)
JOIN `ShiftTypes` ON `ShiftTypes`.`id` = `Shifts`.`shifttype_id` JOIN `ShiftTypes` ON `ShiftTypes`.`id` = `Shifts`.`shifttype_id`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id` = `Shifts`.`SID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id` = `Shifts`.`SID`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ') WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
AND `start` BETWEEN ? AND ? AND `start` BETWEEN ? AND ?
AND `NeededAngelTypes`.`angel_type_id` IN (' . implode(',', $shiftsFilter->getTypes()) . ') AND `NeededAngelTypes`.`angel_type_id` IN (' . implode(',', $shiftsFilter->getTypes()) . ')
AND `NeededAngelTypes`.`count` > 0 AND `NeededAngelTypes`.`count` > 0
AND `Shifts`.`PSID` IS NULL AND s.shift_id IS NULL
UNION UNION
@ -116,11 +111,12 @@ function Shifts_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
JOIN `Room` USING (`RID`) JOIN `Room` USING (`RID`)
JOIN `ShiftTypes` ON `ShiftTypes`.`id` = `Shifts`.`shifttype_id` JOIN `ShiftTypes` ON `ShiftTypes`.`id` = `Shifts`.`shifttype_id`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`Shifts`.`RID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`Shifts`.`RID`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ') WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
AND `start` BETWEEN ? AND ? AND `start` BETWEEN ? AND ?
AND `NeededAngelTypes`.`angel_type_id` IN (' . implode(',', $shiftsFilter->getTypes()) . ') AND `NeededAngelTypes`.`angel_type_id` IN (' . implode(',', $shiftsFilter->getTypes()) . ')
AND `NeededAngelTypes`.`count` > 0 AND `NeededAngelTypes`.`count` > 0
AND NOT `Shifts`.`PSID` IS NULL) AS tmp_shifts AND NOT s.shift_id IS NULL) AS tmp_shifts
ORDER BY `room_name`, `start`'; ORDER BY `room_name`, `start`';
@ -152,9 +148,10 @@ function NeededAngeltypes_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
FROM `Shifts` FROM `Shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id`=`Shifts`.`SID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`
JOIN `AngelTypes` ON `AngelTypes`.`id`= `NeededAngelTypes`.`angel_type_id` JOIN `AngelTypes` ON `AngelTypes`.`id`= `NeededAngelTypes`.`angel_type_id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ') WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
AND `start` BETWEEN ? AND ? AND `start` BETWEEN ? AND ?
AND `Shifts`.`PSID` IS NULL AND s.shift_id IS NULL
UNION UNION
@ -168,9 +165,10 @@ function NeededAngeltypes_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
FROM `Shifts` FROM `Shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`Shifts`.`RID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`Shifts`.`RID`
JOIN `AngelTypes` ON `AngelTypes`.`id`= `NeededAngelTypes`.`angel_type_id` JOIN `AngelTypes` ON `AngelTypes`.`id`= `NeededAngelTypes`.`angel_type_id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ') WHERE `Shifts`.`RID` IN (' . implode(',', $shiftsFilter->getRooms()) . ')
AND `start` BETWEEN ? AND ? AND `start` BETWEEN ? AND ?
AND NOT `Shifts`.`PSID` IS NULL'; AND NOT s.shift_id IS NULL';
return DB::select( return DB::select(
$sql, $sql,
@ -201,9 +199,10 @@ function NeededAngeltype_by_Shift_and_Angeltype($shift, $angeltype)
FROM `Shifts` FROM `Shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id`=`Shifts`.`SID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`
JOIN `AngelTypes` ON `AngelTypes`.`id`= `NeededAngelTypes`.`angel_type_id` JOIN `AngelTypes` ON `AngelTypes`.`id`= `NeededAngelTypes`.`angel_type_id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `Shifts`.`SID`=? WHERE `Shifts`.`SID`=?
AND `AngelTypes`.`id`=? AND `AngelTypes`.`id`=?
AND `Shifts`.`PSID` IS NULL AND s.shift_id IS NULL
UNION UNION
@ -217,9 +216,10 @@ function NeededAngeltype_by_Shift_and_Angeltype($shift, $angeltype)
FROM `Shifts` FROM `Shifts`
JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`Shifts`.`RID` JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`Shifts`.`RID`
JOIN `AngelTypes` ON `AngelTypes`.`id`= `NeededAngelTypes`.`angel_type_id` JOIN `AngelTypes` ON `AngelTypes`.`id`= `NeededAngelTypes`.`angel_type_id`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `Shifts`.`SID`=? WHERE `Shifts`.`SID`=?
AND `AngelTypes`.`id`=? AND `AngelTypes`.`id`=?
AND NOT `Shifts`.`PSID` IS NULL AND NOT s.shift_id IS NULL
', ',
[ [
$shift['SID'], $shift['SID'],
@ -494,16 +494,6 @@ function Shift_signup_allowed(
); );
} }
/**
* Delete a shift by its external id.
*
* @param int $shift_psid
*/
function Shift_delete_by_psid($shift_psid)
{
DB::delete('DELETE FROM `Shifts` WHERE `PSID`=?', [$shift_psid]);
}
/** /**
* Delete a shift. * Delete a shift.
* *
@ -535,7 +525,6 @@ function Shift_update($shift)
`RID` = ?, `RID` = ?,
`title` = ?, `title` = ?,
`URL` = ?, `URL` = ?,
`PSID` = ?,
`edited_by_user_id` = ?, `edited_by_user_id` = ?,
`edited_at_timestamp` = ? `edited_at_timestamp` = ?
WHERE `SID` = ? WHERE `SID` = ?
@ -547,7 +536,6 @@ function Shift_update($shift)
$shift['RID'], $shift['RID'],
$shift['title'], $shift['title'],
$shift['URL'], $shift['URL'],
$shift['PSID'],
$user->id, $user->id,
time(), time(),
$shift['SID'] $shift['SID']
@ -555,25 +543,6 @@ function Shift_update($shift)
); );
} }
/**
* Update a shift by its external id.
*
* @param array $shift
* @return int
* @throws Exception
*/
function Shift_update_by_psid($shift)
{
$shift_source = DB::selectOne('SELECT `SID` FROM `Shifts` WHERE `PSID`=?', [$shift['PSID']]);
if (empty($shift_source)) {
throw new Exception('Shift not found.');
}
$shift['SID'] = $shift_source['SID'];
return Shift_update($shift);
}
/** /**
* Create a new shift. * Create a new shift.
* *
@ -590,12 +559,11 @@ function Shift_create($shift)
`RID`, `RID`,
`title`, `title`,
`URL`, `URL`,
`PSID`,
`created_by_user_id`, `created_by_user_id`,
`edited_at_timestamp`, `edited_at_timestamp`,
`created_at_timestamp` `created_at_timestamp`
) )
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
', ',
[ [
$shift['shifttype_id'], $shift['shifttype_id'],
@ -604,7 +572,6 @@ function Shift_create($shift)
$shift['RID'], $shift['RID'],
$shift['title'], $shift['title'],
$shift['URL'], $shift['URL'],
$shift['PSID'],
auth()->user()->id, auth()->user()->id,
time(), time(),
time(), time(),

@ -39,8 +39,9 @@ function stats_hours_to_work()
(SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`) (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`Shifts`.`SID`)
* (`Shifts`.`end` - `Shifts`.`start`)/3600 AS `count` * (`Shifts`.`end` - `Shifts`.`start`)/3600 AS `count`
FROM `Shifts` FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `end` >= ? WHERE `end` >= ?
AND `Shifts`.`PSID` IS NULL AND s.shift_id IS NULL
UNION ALL UNION ALL
@ -48,8 +49,9 @@ function stats_hours_to_work()
(SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`Shifts`.`RID`) (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`Shifts`.`RID`)
* (`Shifts`.`end` - `Shifts`.`start`)/3600 AS `count` * (`Shifts`.`end` - `Shifts`.`start`)/3600 AS `count`
FROM `Shifts` FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `end` >= ? WHERE `end` >= ?
AND NOT `Shifts`.`PSID` IS NULL AND NOT s.shift_id IS NULL
) AS `tmp` ) AS `tmp`
", [ ", [
time(), time(),
@ -90,8 +92,9 @@ function stats_angels_needed_three_hours()
) )
AS `count` AS `count`
FROM `Shifts` FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `end` > ? AND `start` < ? WHERE `end` > ? AND `start` < ?
AND `Shifts`.`PSID` IS NULL AND s.shift_id IS NULL
UNION ALL UNION ALL
@ -113,8 +116,9 @@ function stats_angels_needed_three_hours()
) )
AS `count` AS `count`
FROM `Shifts` FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `end` > ? AND `start` < ? WHERE `end` > ? AND `start` < ?
AND NOT `Shifts`.`PSID` IS NULL AND NOT s.shift_id IS NULL
) AS `tmp`", [ ) AS `tmp`", [
$now, $now,
$in3hours, $in3hours,
@ -163,8 +167,9 @@ function stats_angels_needed_for_nightshifts()
) )
AS `count` AS `count`
FROM `Shifts` FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `end` > ? AND `start` < ? WHERE `end` > ? AND `start` < ?
AND `Shifts`.`PSID` IS NULL AND s.shift_id IS NULL
UNION ALL UNION ALL
@ -186,8 +191,9 @@ function stats_angels_needed_for_nightshifts()
) )
AS `count` AS `count`
FROM `Shifts` FROM `Shifts`
LEFT JOIN schedule_shift AS s on Shifts.SID = s.shift_id
WHERE `end` > ? AND `start` < ? WHERE `end` > ? AND `start` < ?
AND NOT `Shifts`.`PSID` IS NULL AND NOT s.shift_id IS NULL
) AS `tmp`", [ ) AS `tmp`", [
$night_start, $night_start,
$night_end, $night_end,

@ -42,11 +42,11 @@ function admin_active()
__('At least %s angels are forced to be active. The number has to be greater.'), __('At least %s angels are forced to be active. The number has to be greater.'),
$forced_count $forced_count
)); ));
redirect(page_link_to('admin_active')); throw_redirect(page_link_to('admin_active'));
} }
} else { } else {
$msg .= error(__('Please enter a number of angels to be marked as active.')); $msg .= error(__('Please enter a number of angels to be marked as active.'));
redirect(page_link_to('admin_active')); throw_redirect(page_link_to('admin_active'));
} }
if ($request->hasPostData('ack')) { if ($request->hasPostData('ack')) {

@ -39,7 +39,7 @@ function admin_arrive()
engelsystem_log('User set to not arrived: ' . User_Nick_render($user_source, true)); engelsystem_log('User set to not arrived: ' . User_Nick_render($user_source, true));
success(__('Reset done. Angel has not arrived.')); success(__('Reset done. Angel has not arrived.'));
redirect(user_link($user_source->id)); throw_redirect(user_link($user_source->id));
} else { } else {
$msg = error(__('Angel not found.'), true); $msg = error(__('Angel not found.'), true);
} }
@ -57,7 +57,7 @@ function admin_arrive()
engelsystem_log('User set has arrived: ' . User_Nick_render($user_source, true)); engelsystem_log('User set has arrived: ' . User_Nick_render($user_source, true));
success(__('Angel has been marked as arrived.')); success(__('Angel has been marked as arrived.'));
redirect(user_link($user_source->id)); throw_redirect(user_link($user_source->id));
} else { } else {
$msg = error(__('Angel not found.'), true); $msg = error(__('Angel not found.'), true);
} }

@ -148,7 +148,7 @@ function admin_groups()
'Group privileges of group ' . $group['Name'] 'Group privileges of group ' . $group['Name']
. ' edited: ' . join(', ', $privilege_names) . ' edited: ' . join(', ', $privilege_names)
); );
redirect(page_link_to('admin_groups')); throw_redirect(page_link_to('admin_groups'));
} else { } else {
return error('No Group found.', true); return error('No Group found.', true);
} }

@ -1,478 +0,0 @@
<?php
/**
* @return string
*/
function admin_import_title()
{
return __('Frab import');
}
/**
* @return string
*/
function admin_import()
{
global $rooms_import;
$user = auth()->user();
$html = '';
$import_dir = __DIR__ . '/../../import';
$request = request();
$step = 'input';
if (
$request->has('step')
&& in_array($request->input('step'), [
'input',
'check',
'import'
])
) {
$step = $request->input('step');
}
try {
$test_handle = @fopen($import_dir . '/tmp', 'w');
fclose($test_handle);
@unlink($import_dir . '/tmp');
} catch (Exception $e) {
error(__('Webserver has no write-permission on import directory.'));
}
$import_file = $import_dir . '/import_' . $user->id . '.xml';
$shifttype_id = null;
$add_minutes_start = 15;
$add_minutes_end = 15;
$shifttypes_source = ShiftTypes();
$shifttypes = [];
foreach ($shifttypes_source as $shifttype) {
$shifttypes[$shifttype['id']] = $shifttype['name'];
}
switch ($step) {
case 'input':
$valid = false;
if ($request->hasPostData('submit')) {
$valid = true;
if ($request->has('shifttype_id') && isset($shifttypes[$request->input('shifttype_id')])) {
$shifttype_id = $request->input('shifttype_id');
} else {
$valid = false;
error(__('Please select a shift type.'));
}
$minutes_start = trim($request->input('add_minutes_start'));
if ($request->has('add_minutes_start') && is_numeric($minutes_start)) {
$add_minutes_start = $minutes_start;
} else {
$valid = false;
error(__('Please enter an amount of minutes to add to a talk\'s begin.'));
}
if ($request->has('add_minutes_end') && is_numeric(trim($request->input('add_minutes_end')))) {
$add_minutes_end = trim($request->input('add_minutes_end'));
} else {
$valid = false;
error(__('Please enter an amount of minutes to add to a talk\'s end.'));
}
if (isset($_FILES['xcal_file']) && ($_FILES['xcal_file']['error'] == 0)) {
if (move_uploaded_file($_FILES['xcal_file']['tmp_name'], $import_file)) {
libxml_use_internal_errors(true);
if (simplexml_load_file($import_file) === false) {
$valid = false;
error(__('No valid xml/xcal file provided.'));
unlink($import_file);
}
} else {
$valid = false;
error(__('File upload went wrong.'));
}
} else {
$valid = false;
error(__('Please provide some data.'));
}
}
if ($valid) {
redirect(
page_link_to('admin_import', [
'step' => 'check',
'shifttype_id' => $shifttype_id,
'add_minutes_end' => $add_minutes_end,
'add_minutes_start' => $add_minutes_start,
])
);
} else {
$html .= div('well well-sm text-center', [
__('File Upload')
. mute(glyph('arrow-right'))
. mute(__('Validation'))
. mute(glyph('arrow-right'))
. mute(__('Import'))
]) . div('row', [
div('col-md-offset-3 col-md-6', [
form([
form_info(
'',
__('This import will create/update/delete rooms and shifts by given FRAB-export file. The needed file format is xcal.')
),
form_select('shifttype_id', __('Shifttype'), $shifttypes, $shifttype_id),
form_spinner('add_minutes_start', __('Add minutes to start'), $add_minutes_start),
form_spinner('add_minutes_end', __('Add minutes to end'), $add_minutes_end),
form_file('xcal_file', __('xcal-File (.xcal)')),
form_submit('submit', __('Import'))
])
])
]);
}
break;
case 'check':
if (!file_exists($import_file)) {
error(__('Missing import file.'));
redirect(page_link_to('admin_import'));
}
if ($request->has('shifttype_id') && isset($shifttypes[$request->input('shifttype_id')])) {
$shifttype_id = $request->input('shifttype_id');
} else {
error(__('Please select a shift type.'));
redirect(page_link_to('admin_import'));
}
if ($request->has('add_minutes_start') && is_numeric(trim($request->input('add_minutes_start')))) {
$add_minutes_start = trim($request->input('add_minutes_start'));
} else {
error(__('Please enter an amount of minutes to add to a talk\'s begin.'));
redirect(page_link_to('admin_import'));
}
if ($request->has('add_minutes_end') && is_numeric(trim($request->input(('add_minutes_end'))))) {
$add_minutes_end = trim($request->input('add_minutes_end'));
} else {
error(__('Please enter an amount of minutes to add to a talk\'s end.'));
redirect(page_link_to('admin_import'));
}
list($rooms_new, $rooms_deleted) = prepare_rooms($import_file);
list($events_new, $events_updated, $events_deleted) = prepare_events(
$import_file,
$shifttype_id,
$add_minutes_start,
$add_minutes_end
);
$html .= div(
'well well-sm text-center',
[
'<span class="text-success">' . __('File Upload') . glyph('ok-circle') . '</span>'
. mute(glyph('arrow-right'))
. __('Validation')
. mute(glyph('arrow-right'))
. mute(__('Import'))
]
)
. form(
[
div('row', [
div('col-sm-6', [
'<h3>' . __('Rooms to create') . '</h3>',
table(__('Name'), $rooms_new)
]),
div('col-sm-6', [
'<h3>' . __('Rooms to delete') . '</h3>',
table(__('Name'), $rooms_deleted)
])
]),
'<h3>' . __('Shifts to create') . '</h3>',
table([
'day' => __('Day'),
'start' => __('Start'),
'end' => __('End'),
'shifttype' => __('Shift type'),
'title' => __('Title'),
'room' => __('Room')
], shifts_printable($events_new, $shifttypes)),
'<h3>' . __('Shifts to update') . '</h3>',
table([
'day' => __('Day'),
'start' => __('Start'),
'end' => __('End'),
'shifttype' => __('Shift type'),
'title' => __('Title'),
'room' => __('Room')
], shifts_printable($events_updated, $shifttypes)),
'<h3>' . __('Shifts to delete') . '</h3>',
table([
'day' => __('Day'),
'start' => __('Start'),
'end' => __('End'),
'shifttype' => __('Shift type'),
'title' => __('Title'),
'room' => __('Room')
], shifts_printable($events_deleted, $shifttypes)),
form_submit('submit', __('Import'))
],
page_link_to('admin_import', [
'step' => 'import',
'shifttype_id' => $shifttype_id,
'add_minutes_end' => $add_minutes_end,
'add_minutes_start' => $add_minutes_start,
])
);
break;
case 'import':
if (!file_exists($import_file)) {
error(__('Missing import file.'));
redirect(page_link_to('admin_import'));
}
if (!file_exists($import_file)) {
redirect(page_link_to('admin_import'));
}
if ($request->has('shifttype_id') && isset($shifttypes[$request->input('shifttype_id')])) {
$shifttype_id = $request->input('shifttype_id');
} else {
error(__('Please select a shift type.'));
redirect(page_link_to('admin_import'));
}
if ($request->has('add_minutes_start') && is_numeric(trim($request->input('add_minutes_start')))) {
$add_minutes_start = trim($request->input('add_minutes_start'));
} else {
error(__('Please enter an amount of minutes to add to a talk\'s begin.'));
redirect(page_link_to('admin_import'));
}
if ($request->has('add_minutes_end') && is_numeric(trim($request->input('add_minutes_end')))) {
$add_minutes_end = trim($request->input('add_minutes_end'));
} else {
error(__('Please enter an amount of minutes to add to a talk\'s end.'));
redirect(page_link_to('admin_import'));
}
list($rooms_new, $rooms_deleted) = prepare_rooms($import_file);
foreach ($rooms_new as $room) {
$result = Room_create($room, true, null, null);
$rooms_import[trim($room)] = $result;
}
foreach ($rooms_deleted as $room) {
Room_delete_by_name($room);
}
list($events_new, $events_updated, $events_deleted) = prepare_events(
$import_file,
$shifttype_id,
$add_minutes_start,
$add_minutes_end
);
foreach ($events_new as $event) {
Shift_create($event);
}
foreach ($events_updated as $event) {
Shift_update_by_psid($event);
}
foreach ($events_deleted as $event) {
Shift_delete_by_psid($event['PSID']);
}
engelsystem_log('Frab import done');
unlink($import_file);
$html .= div('well well-sm text-center', [
'<span class="text-success">' . __('File Upload') . glyph('ok-circle') . '</span>'
. mute(glyph('arrow-right'))
. '<span class="text-success">' . __('Validation') . glyph('ok-circle') . '</span>'
. mute(glyph('arrow-right'))
. '<span class="text-success">' . __('Import') . glyph('ok-circle') . '</span>'
]) . success(__('It\'s done!'), true);
break;
default:
redirect(page_link_to('admin_import'));
}
return page_with_title(admin_import_title(), [
msg(),
$html
]);
}
/**
* @param string $file
* @return array
*/
function prepare_rooms($file)
{
global $rooms_import;
$data = read_xml($file);
// Load rooms from db for compare with input
$rooms = Rooms();
// Contains rooms from db with from_frab==true
$rooms_db = [];
// Contains all rooms from db
$rooms_db_all = [];
// Contains all rooms from db and frab
$rooms_import = [];
foreach ($rooms as $room) {
if ($room['from_frab']) {
$rooms_db[] = $room['Name'];
}
$rooms_db_all[] = $room['Name'];
$rooms_import[$room['Name']] = $room['RID'];
}
$events = $data->vcalendar->vevent;
$rooms_frab = [];
foreach ($events as $event) {
$rooms_frab[] = (string)$event->location;
if (!isset($rooms_import[trim($event->location)])) {
$rooms_import[trim($event->location)] = trim($event->location);
}
}
$rooms_frab = array_unique($rooms_frab);
$rooms_new = array_diff($rooms_frab, $rooms_db_all);
$rooms_deleted = array_diff($rooms_db, $rooms_frab);
return [
$rooms_new,
$rooms_deleted
];
}
/**
* @param string $file
* @param int $shifttype_id
* @param int $add_minutes_start
* @param int $add_minutes_end
* @return array
*/
function prepare_events($file, $shifttype_id, $add_minutes_start, $add_minutes_end)
{
global $rooms_import;
$data = read_xml($file);
$rooms = Rooms();
$rooms_db = [];
foreach ($rooms as $room) {
$rooms_db[$room['Name']] = $room['RID'];
}
$events = $data->vcalendar->vevent;
$shifts_pb = [];
foreach ($events as $event) {
$event_pb = $event->children('http://pentabarf.org');
$event_id = trim($event_pb->{'event-id'});
$shifts_pb[$event_id] = [
'shifttype_id' => $shifttype_id,
'start' => parse_date("Ymd\THis", $event->dtstart) - $add_minutes_start * 60,
'end' => parse_date("Ymd\THis", $event->dtend) + $add_minutes_end * 60,
'RID' => $rooms_import[trim($event->location)],
'title' => trim($event->summary),
'URL' => trim($event->url),
'PSID' => $event_id
];
}
$shifts = Shifts_from_frab();
$shifts_db = [];
foreach ($shifts as $shift) {
$shifts_db[$shift['PSID']] = $shift;
}
$shifts_new = [];
$shifts_updated = [];
foreach ($shifts_pb as $shift) {
if (!isset($shifts_db[$shift['PSID']])) {
$shifts_new[] = $shift;
} else {
$tmp = $shifts_db[$shift['PSID']];
if (
$shift['shifttype_id'] != $tmp['shifttype_id']
|| $shift['title'] != $tmp['title']
|| $shift['start'] != $tmp['start']
|| $shift['end'] != $tmp['end']
|| $shift['RID'] != $tmp['RID']
|| $shift['URL'] != $tmp['URL']
) {
$shifts_updated[] = $shift;
}
}
}
$shifts_deleted = [];
foreach ($shifts_db as $shift) {
if (!isset($shifts_pb[$shift['PSID']])) {
$shifts_deleted[] = $shift;
}
}
return [
$shifts_new,
$shifts_updated,
$shifts_deleted
];
}
/**
* @param string $file
* @return SimpleXMLElement
*/
function read_xml($file)
{
global $xml_import;
if (!isset($xml_import)) {
libxml_use_internal_errors(true);
$xml_import = simplexml_load_file($file);
}
return $xml_import;
}
/**
* @param array $shifts
* @param array $shifttypes
* @return array
*/
function shifts_printable($shifts, $shifttypes)
{
global $rooms_import;
$rooms = array_flip($rooms_import);
uasort($shifts, 'shift_sort');
$shifts_printable = [];
foreach ($shifts as $shift) {
$shifts_printable[] = [
'day' => date('l, Y-m-d', $shift['start']),
'start' => date('H:i', $shift['start']),
'shifttype' => ShiftType_name_render([
'id' => $shift['shifttype_id'],
'name' => $shifttypes[$shift['shifttype_id']]
]),
'title' => shorten($shift['title']),
'end' => date('H:i', $shift['end']),
'room' => $rooms[$shift['RID']]
];
}
return $shifts_printable;
}
/**
* @param array $shift_a
* @param array $shift_b
* @return int
*/
function shift_sort($shift_a, $shift_b)
{
return ($shift_a['start'] < $shift_b['start']) ? -1 : 1;
}

@ -10,7 +10,7 @@ function admin_news()
$request = request(); $request = request();
if (!$request->has('action')) { if (!$request->has('action')) {
redirect(page_link_to('news')); throw_redirect(page_link_to('news'));
} }
$html = '<div class="col-md-12"><h1>' . __('Edit news entry') . '</h1>' . msg(); $html = '<div class="col-md-12"><h1>' . __('Edit news entry') . '</h1>' . msg();
@ -70,17 +70,17 @@ function admin_news()
engelsystem_log('News updated: ' . $request->postData('eBetreff')); engelsystem_log('News updated: ' . $request->postData('eBetreff'));
success(__('News entry updated.')); success(__('News entry updated.'));
redirect(page_link_to('news')); throw_redirect(page_link_to('news'));
break; break;
case 'delete': case 'delete':
$news->delete(); $news->delete();
engelsystem_log('News deleted: ' . $news->title); engelsystem_log('News deleted: ' . $news->title);
success(__('News entry deleted.')); success(__('News entry deleted.'));
redirect(page_link_to('news')); throw_redirect(page_link_to('news'));
break; break;
default: default:
redirect(page_link_to('news')); throw_redirect(page_link_to('news'));
} }
return $html . '</div>'; return $html . '</div>';
} }

@ -122,7 +122,7 @@ function admin_questions()
. ' answered: ' . ' answered: '
. $answer . $answer
); );
redirect(page_link_to('admin_questions')); throw_redirect(page_link_to('admin_questions'));
} else { } else {
return error('Enter an answer!', true); return error('Enter an answer!', true);
} }
@ -145,7 +145,7 @@ function admin_questions()
if (!empty($question)) { if (!empty($question)) {
$question->delete(); $question->delete();
engelsystem_log('Question deleted: ' . $question['Question']); engelsystem_log('Question deleted: ' . $question['Question']);
redirect(page_link_to('admin_questions')); throw_redirect(page_link_to('admin_questions'));
} else { } else {
return error('No question found.', true); return error('No question found.', true);
} }

@ -19,7 +19,6 @@ function admin_rooms()
foreach ($rooms_source as $room) { foreach ($rooms_source as $room) {
$rooms[] = [ $rooms[] = [
'name' => Room_name_render($room), 'name' => Room_name_render($room),
'from_frab' => glyph_bool($room['from_frab']),
'map_url' => glyph_bool(!empty($room['map_url'])), 'map_url' => glyph_bool(!empty($room['map_url'])),
'actions' => table_buttons([ 'actions' => table_buttons([
button( button(
@ -40,7 +39,6 @@ function admin_rooms()
if ($request->has('show')) { if ($request->has('show')) {
$msg = ''; $msg = '';
$name = ''; $name = '';
$from_frab = false;
$map_url = null; $map_url = null;
$description = null; $description = null;
$room_id = 0; $room_id = 0;
@ -56,12 +54,11 @@ function admin_rooms()
if (test_request_int('id')) { if (test_request_int('id')) {
$room = Room($request->input('id')); $room = Room($request->input('id'));
if (empty($room)) { if (empty($room)) {
redirect(page_link_to('admin_rooms')); throw_redirect(page_link_to('admin_rooms'));
} }
$room_id = $request->input('id'); $room_id = $request->input('id');
$name = $room['Name']; $name = $room['Name'];
$from_frab = $room['from_frab'];
$map_url = $room['map_url']; $map_url = $room['map_url'];
$description = $room['description']; $description = $room['description'];
@ -88,8 +85,6 @@ function admin_rooms()
$msg .= error(__('Please enter a name.'), true); $msg .= error(__('Please enter a name.'), true);
} }
$from_frab = $request->has('from_frab');
if ($request->has('map_url')) { if ($request->has('map_url')) {
$map_url = strip_request_item('map_url'); $map_url = strip_request_item('map_url');
} }
@ -118,9 +113,9 @@ function admin_rooms()
if ($valid) { if ($valid) {
if (empty($room_id)) { if (empty($room_id)) {
$room_id = Room_create($name, $from_frab, $map_url, $description); $room_id = Room_create($name, $map_url, $description);
} else { } else {
Room_update($room_id, $name, $from_frab, $map_url, $description); Room_update($room_id, $name, $map_url, $description);
} }
NeededAngelTypes_delete_by_room($room_id); NeededAngelTypes_delete_by_room($room_id);
@ -140,7 +135,7 @@ function admin_rooms()
. ' to: ' . join(', ', $needed_angeltype_info) . ' to: ' . join(', ', $needed_angeltype_info)
); );
success(__('Room saved.')); success(__('Room saved.'));
redirect(page_link_to('admin_rooms')); throw_redirect(page_link_to('admin_rooms'));
} }
} }
$angeltypes_count_form = []; $angeltypes_count_form = [];
@ -159,7 +154,6 @@ function admin_rooms()
div('row', [ div('row', [
div('col-md-6', [ div('col-md-6', [
form_text('name', __('Name'), $name, false, 35), form_text('name', __('Name'), $name, false, 35),
form_checkbox('from_frab', __('Frab import'), $from_frab),
form_text('map_url', __('Map URL'), $map_url), form_text('map_url', __('Map URL'), $map_url),
form_info('', __('The map url is used to display an iframe on the room page.')), form_info('', __('The map url is used to display an iframe on the room page.')),
form_textarea('description', __('Description'), $description), form_textarea('description', __('Description'), $description),
@ -190,7 +184,7 @@ function admin_rooms()
Room_delete($room_id); Room_delete($room_id);
success(sprintf(__('Room %s deleted.'), $name)); success(sprintf(__('Room %s deleted.'), $name));
redirect(page_link_to('admin_rooms')); throw_redirect(page_link_to('admin_rooms'));
} }
return page_with_title(admin_rooms_title(), [ return page_with_title(admin_rooms_title(), [
@ -212,7 +206,6 @@ function admin_rooms()
msg(), msg(),
table([ table([
'name' => __('Name'), 'name' => __('Name'),
'from_frab' => __('Frab import'),
'map_url' => __('Map'), 'map_url' => __('Map'),
'actions' => '' 'actions' => ''
], $rooms) ], $rooms)

@ -345,12 +345,11 @@ function admin_shifts()
!is_array($session->get('admin_shifts_shifts')) !is_array($session->get('admin_shifts_shifts'))
|| !is_array($session->get('admin_shifts_types')) || !is_array($session->get('admin_shifts_types'))
) { ) {
redirect(page_link_to('admin_shifts')); throw_redirect(page_link_to('admin_shifts'));
} }
foreach ($session->get('admin_shifts_shifts', []) as $shift) { foreach ($session->get('admin_shifts_shifts', []) as $shift) {
$shift['URL'] = null; $shift['URL'] = null;
$shift['PSID'] = null;
$shift_id = Shift_create($shift); $shift_id = Shift_create($shift);
engelsystem_log( engelsystem_log(
@ -389,7 +388,7 @@ function admin_shifts()
} }
success('Schichten angelegt.'); success('Schichten angelegt.');
redirect(page_link_to('admin_shifts')); throw_redirect(page_link_to('admin_shifts'));
} else { } else {
$session->remove('admin_shifts_shifts'); $session->remove('admin_shifts_shifts');
$session->remove('admin_shifts_types'); $session->remove('admin_shifts_types');

@ -22,7 +22,7 @@ function admin_user()
$html = ''; $html = '';
if (!$request->has('id')) { if (!$request->has('id')) {
redirect(users_link()); throw_redirect(users_link());
} }
$user_id = $request->input('id'); $user_id = $request->input('id');
@ -30,7 +30,7 @@ function admin_user()
$user_source = User::find($user_id); $user_source = User::find($user_id);
if (!$user_source) { if (!$user_source) {
error(__('This user does not exist.')); error(__('This user does not exist.'));
redirect(users_link()); throw_redirect(users_link());
} }
$html .= 'Hallo,<br />' $html .= 'Hallo,<br />'

@ -239,7 +239,7 @@ function guest_register()
// User is already logged in - that means a supporter has registered an angel. Return to register page. // User is already logged in - that means a supporter has registered an angel. Return to register page.
if ($authUser) { if ($authUser) {
redirect(page_link_to('register')); throw_redirect(page_link_to('register'));
} }
// If a welcome message is present, display it on the next page // If a welcome message is present, display it on the next page
@ -247,7 +247,7 @@ function guest_register()
info((new Parsedown())->text($message)); info((new Parsedown())->text($message));
} }
redirect(page_link_to('/')); throw_redirect(page_link_to('/'));
} }
} }

@ -0,0 +1,614 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Controllers\Admin\Schedule;
use Carbon\Carbon;
use Engelsystem\Controllers\BaseController;
use Engelsystem\Helpers\Schedule\Event;
use Engelsystem\Helpers\Schedule\Room;
use Engelsystem\Helpers\Schedule\Schedule;
use Engelsystem\Helpers\Schedule\XmlParser;
use Engelsystem\Http\Request;
use Engelsystem\Http\Response;
use Engelsystem\Models\Shifts\Schedule as ScheduleUrl;
use Engelsystem\Models\Shifts\ScheduleShift;
use ErrorException;
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;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class ImportSchedule extends BaseController
{
/** @var DatabaseConnection */
protected $db;
/** @var LoggerInterface */
protected $log;
/** @var array */
protected $permissions = [
'schedule.import',
];
/** @var XmlParser */
protected $parser;
/** @var Response */
protected $response;
/** @var SessionInterface */
protected $session;
/** @var string */
protected $url = '/admin/schedule';
/** @var GuzzleClient */
protected $guzzle;
/**
* @param Response $response
* @param SessionInterface $session
* @param GuzzleClient $guzzle
* @param XmlParser $parser
* @param DatabaseConnection $db
* @param LoggerInterface $log
*/
public function __construct(
Response $response,
SessionInterface $session,
GuzzleClient $guzzle,
XmlParser $parser,
DatabaseConnection $db,
LoggerInterface $log
) {
$this->guzzle = $guzzle;
$this->parser = $parser;
$this->response = $response;
$this->session = $session;
$this->db = $db;
$this->log = $log;
}
/**
* @return Response
*/
public function index(): Response
{
return $this->response->withView(
'admin/schedule/index.twig',
[
'errors' => $this->getFromSession('errors'),
'success' => $this->getFromSession('success'),
'shift_types' => $this->getShiftTypes(),
]
);
}
/**
* @param Request $request
* @return Response
*/
public function loadSchedule(Request $request): Response
{
try {
/**
* @var Event[] $newEvents
* @var Event[] $changeEvents
* @var Event[] $deleteEvents
* @var Room[] $newRooms
* @var int $shiftType
* @var ScheduleUrl $scheduleUrl
* @var Schedule $schedule
* @var int $minutesBefore
* @var int $minutesAfter
*/
list(
$newEvents,
$changeEvents,
$deleteEvents,
$newRooms,
$shiftType,
$scheduleUrl,
$schedule,
$minutesBefore,
$minutesAfter
) = $this->getScheduleData($request);
} catch (ErrorException $e) {
return back()->with('errors', [$e->getMessage()]);
}
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' => $schedule,
'rooms' => [
'add' => $newRooms,
],
'shifts' => [
'add' => $newEvents,
'update' => $changeEvents,
'delete' => $deleteEvents,
],
]
);
}
/**
* @param Request $request
*
* @return Response
*/
public function importSchedule(Request $request): Response
{
try {
/**
* @var Event[] $newEvents
* @var Event[] $changeEvents
* @var Event[] $deleteEvents
* @var Room[] $newRooms
* @var int $shiftType
* @var ScheduleUrl $scheduleUrl
*/
list(
$newEvents,
$changeEvents,
$deleteEvents,
$newRooms,
$shiftType,
$scheduleUrl
) = $this->getScheduleData($request);
} catch (ErrorException $e) {
return back()->with('errors', [$e->getMessage()]);
}
$this->log('Started schedule "{schedule}" import', ['schedule' => $scheduleUrl->url]);
foreach ($newRooms as $room) {
$this->createRoom($room);
}
$rooms = $this->getAllRooms();
foreach ($newEvents as $event) {
$this->createEvent(
$event,
(int)$shiftType,
$rooms
->where('name', $event->getRoom()->getName())
->first(),
$scheduleUrl
);
}
foreach ($changeEvents as $event) {
$this->updateEvent(
$event,
(int)$shiftType,
$rooms
->where('name', $event->getRoom()->getName())
->first()
);
}
foreach ($deleteEvents as $event) {
$this->deleteEvent($event);
}
$this->log('Ended schedule "{schedule}" import', ['schedule' => $scheduleUrl->url]);
return redirect($this->url, 303)
->with('success', ['schedule.import.success']);
}
/**
* @param Room $room
*/
protected function createRoom(Room $room): void
{
$this->db
->table('Room')
->insert(
[
'Name' => $room->getName(),
]
);
$this->log('Created schedule room "{room}"', ['room' => $room->getName()]);
}
/**
* @param Event $shift
* @param int $shiftTypeId
* @param stdClass $room
* @param ScheduleUrl $scheduleUrl
*/
protected function createEvent(Event $shift, int $shiftTypeId, stdClass $room, ScheduleUrl $scheduleUrl): void
{
$user = auth()->user();
$this->db
->table('Shifts')
->insert(
[
'title' => $shift->getTitle(),
'shifttype_id' => $shiftTypeId,
'start' => $shift->getDate()->unix(),
'end' => $shift->getEndDate()->unix(),
'RID' => $room->id,
'URL' => $shift->getUrl(),
'created_by_user_id' => $user->id,
'created_at_timestamp' => time(),
'edited_by_user_id' => null,
'edited_at_timestamp' => 0,
]
);
$shiftId = $this->db->getDoctrineConnection()->lastInsertId();
$scheduleShift = new ScheduleShift(['shift_id' => $shiftId, 'guid' => $shift->getGuid()]);
$scheduleShift->schedule()->associate($scheduleUrl);
$scheduleShift->save();
$this->log(
'Created schedule shift "{shift}" in "{room}" ({from} {to}, {guid})',
[
'shift' => $shift->getTitle(),
'room' => $room->name,
'from' => $shift->getDate()->format(Carbon::RFC3339),
'to' => $shift->getEndDate()->format(Carbon::RFC3339),
'guid' => $shift->getGuid(),
]
);
}
/**
* @param Event $shift
* @param int $shiftTypeId
* @param stdClass $room
*/
protected function updateEvent(Event $shift, int $shiftTypeId, stdClass $room): void
{
$user = auth()->user();
$this->db
->table('Shifts')
->join('schedule_shift', 'Shifts.SID', 'schedule_shift.shift_id')
->where('schedule_shift.guid', $shift->getGuid())
->update(
[
'title' => $shift->getTitle(),
'shifttype_id' => $shiftTypeId,
'start' => $shift->getDate()->unix(),
'end' => $shift->getEndDate()->unix(),
'RID' => $room->id,
'URL' => $shift->getUrl(),
'edited_by_user_id' => $user->id,
'edited_at_timestamp' => time(),
]
);
$this->log(
'Updated schedule shift "{shift}" in "{room}" ({from} {to}, {guid})',
[
'shift' => $shift->getTitle(),
'room' => $room->name,
'from' => $shift->getDate()->format(Carbon::RFC3339),
'to' => $shift->getEndDate()->format(Carbon::RFC3339),
'guid' => $shift->getGuid(),
]
);
}
/**
* @param Event $shift
*/
protected function deleteEvent(Event $shift): void
{
$this->db
->table('Shifts')
->join('schedule_shift', 'Shifts.SID', 'schedule_shift.shift_id')
->where('schedule_shift.guid', $shift->getGuid())
->delete();
$this->log(
'Deleted schedule shift "{shift}" ({from} {to}, {guid})',
[
'shift' => $shift->getTitle(),
'from' => $shift->getDate()->format(Carbon::RFC3339),
'to' => $shift->getEndDate()->format(Carbon::RFC3339),
'guid' => $shift->getGuid(),
]
);
}
/**
* @param Request $request
* @return Event[]|Room[]|ScheduleUrl|Schedule|string
* @throws ErrorException
*/
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',
]
);
$scheduleResponse = $this->guzzle->get($data['schedule-url']);
if ($scheduleResponse->getStatusCode() != 200) {
throw new ErrorException('schedule.import.request-error');
}
$scheduleData = (string)$scheduleResponse->getBody();
if (!$this->parser->load($scheduleData)) {
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']);
$schedule = $this->parser->getSchedule();
$minutesBefore = isset($data['minutes-before']) ? (int)$data['minutes-before'] : 15;
$minutesAfter = isset($data['minutes-after']) ? (int)$data['minutes-after'] : 15;
$newRooms = $this->newRooms($schedule->getRooms());
return array_merge(
$this->shiftsDiff($schedule, $scheduleUrl, $shiftType, $minutesBefore, $minutesAfter),
[$newRooms, $shiftType, $scheduleUrl, $schedule, $minutesBefore, $minutesAfter]
);
}
/**
* @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[]
*/
protected function newRooms(array $scheduleRooms): array
{
$newRooms = [];
$allRooms = $this->getAllRooms();
foreach ($scheduleRooms as $room) {
if ($allRooms->where('name', $room->getName())->count()) {
continue;
}
$newRooms[] = $room;
}
return $newRooms;
}
/**
* @param Schedule $schedule
* @param ScheduleUrl $scheduleUrl
* @param int $shiftType
* @param int $minutesBefore
* @param int $minutesAfter
* @return Event[]
*/
protected function shiftsDiff(
Schedule $schedule,
ScheduleUrl $scheduleUrl,
int $shiftType,
int $minutesBefore,
int $minutesAfter
): array {
/** @var Event[] $newEvents */
$newEvents = [];
/** @var Event[] $changeEvents */
$changeEvents = [];
/** @var Event[] $scheduleEvents */
$scheduleEvents = [];
/** @var Event[] $deleteEvents */
$deleteEvents = [];
$rooms = $this->getAllRooms();
foreach ($schedule->getDay() as $day) {
foreach ($day->getRoom() as $room) {
foreach ($room->getEvent() as $event) {
$scheduleEvents[$event->getGuid()] = $event;
$event->getDate()->subMinutes($minutesBefore);
$event->getEndDate()->addMinutes($minutesAfter);
$event->setTitle(sprintf('%s [%s]', $event->getTitle(), $event->getLanguage()));
}
}
}
$scheduleEventsGuidList = array_keys($scheduleEvents);
$existingShifts = $this->getScheduleShiftsByGuid($scheduleUrl, $scheduleEventsGuidList);
foreach ($existingShifts as $shift) {
$guid = $shift->guid;
$shift = $this->loadShift($shift->shift_id);
$event = $scheduleEvents[$guid];
$room = $rooms->where('name', $event->getRoom()->getName())->first();
if (
$shift->title != $event->getTitle()
|| $shift->shift_type_id != $shiftType
|| Carbon::createFromTimestamp($shift->start) != $event->getDate()
|| Carbon::createFromTimestamp($shift->end) != $event->getEndDate()
|| $shift->room_id != ($room->id ?? '')
|| $shift->url != $event->getUrl()
) {
$changeEvents[$guid] = $event;
}
unset($scheduleEvents[$guid]);
}
foreach ($scheduleEvents as $scheduleEvent) {
$newEvents[$scheduleEvent->getGuid()] = $scheduleEvent;
}
$scheduleShifts = $this->getScheduleShiftsWhereNotGuid($scheduleUrl, $scheduleEventsGuidList);
foreach ($scheduleShifts as $shift) {
$event = $this->eventFromScheduleShift($shift);
$deleteEvents[$event->getGuid()] = $event;
}
return [$newEvents, $changeEvents, $deleteEvents];
}
/**
* @param ScheduleShift $scheduleShift
* @return Event
*/
protected function eventFromScheduleShift(ScheduleShift $scheduleShift): Event
{
$shift = $this->loadShift($scheduleShift->shift_id);
$start = Carbon::createFromTimestamp($shift->start);
$end = Carbon::createFromTimestamp($shift->end);
$duration = $start->diff($end);
$event = new Event(
$scheduleShift->guid,
0,
new Room($shift->room_name),
$shift->title,
'',
'n/a',
Carbon::createFromTimestamp($shift->start),
$start->format('H:i'),
$duration->format('%H:%I'),
'',
'',
''
);
return $event;
}
/**
* @return Collection
*/
protected function getAllRooms(): Collection
{
return new Collection($this->db->select('SELECT RID as id, Name as name FROM Room'));
}
/**
* @param ScheduleUrl $scheduleUrl
* @param string[] $events
* @return QueryBuilder[]|DatabaseCollection|ScheduleShift[]
*/
protected function getScheduleShiftsByGuid(ScheduleUrl $scheduleUrl, array $events)
{
return ScheduleShift::query()
->whereIn('guid', $events)
->where('schedule_id', $scheduleUrl->id)
->get();
}
/**
* @param ScheduleUrl $scheduleUrl
* @param string[] $events
* @return QueryBuilder[]|DatabaseCollection|ScheduleShift[]
*/
protected function getScheduleShiftsWhereNotGuid(ScheduleUrl $scheduleUrl, array $events)
{
return ScheduleShift::query()
->whereNotIn('guid', $events)
->where('schedule_id', $scheduleUrl->id)
->get();
}
/**
* @param $id
* @return stdClass|null
*/
protected function loadShift($id): ?stdClass
{
return $this->db->selectOne(
'
SELECT
s.SID AS id,
s.title,
s.start,
s.end,
s.shifttype_id AS shift_type_id,
s.RID AS room_id,
r.Name AS room_name,
s.URL as url
FROM Shifts AS s
LEFT JOIN Room r on s.RID = r.RID
WHERE SID = ?
',
[$id]
);
}
/**
* @return string[]
*/
protected function getShiftTypes()
{
$return = [];
/** @var stdClass[] $shiftTypes */
$shiftTypes = $this->db->select('SELECT t.id, t.name FROM ShiftTypes AS t');
foreach ($shiftTypes as $shiftType) {
$return[$shiftType->id] = $shiftType->name;
}
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
*/
protected function log(string $message, array $context = []): void
{
$user = auth()->user();
$message = sprintf('%s (%u): %s', $user->name, $user->id, $message);
$this->log->info($message, $context);
}
}

@ -141,7 +141,7 @@ function user_messages()
'UPDATE `Messages` SET `isRead`=\'Y\' WHERE `id`=? LIMIT 1', 'UPDATE `Messages` SET `isRead`=\'Y\' WHERE `id`=? LIMIT 1',
[$message_id] [$message_id]
); );
redirect(page_link_to('user_messages')); throw_redirect(page_link_to('user_messages'));
} else { } else {
return error(__('No Message found.'), true); return error(__('No Message found.'), true);
} }
@ -160,7 +160,7 @@ function user_messages()
); );
if (!empty($message) && $message['SUID'] == $user->id) { if (!empty($message) && $message['SUID'] == $user->id) {
DB::delete('DELETE FROM `Messages` WHERE `id`=? LIMIT 1', [$message_id]); DB::delete('DELETE FROM `Messages` WHERE `id`=? LIMIT 1', [$message_id]);
redirect(page_link_to('user_messages')); throw_redirect(page_link_to('user_messages'));
} else { } else {
return error(__('No Message found.'), true); return error(__('No Message found.'), true);
} }
@ -168,7 +168,7 @@ function user_messages()
case 'send': case 'send':
if (Message_send($request->input('to'), $request->input('text'))) { if (Message_send($request->input('to'), $request->input('text'))) {
redirect(page_link_to('user_messages')); throw_redirect(page_link_to('user_messages'));
} else { } else {
return error(__('Transmitting was terminated with an Error.'), true); return error(__('Transmitting was terminated with an Error.'), true);
} }

@ -37,7 +37,7 @@ function user_myshifts()
if ($request->input('reset') == 'ack') { if ($request->input('reset') == 'ack') {
User_reset_api_key($user); User_reset_api_key($user);
success(__('Key changed.')); success(__('Key changed.'));
redirect(page_link_to('users', ['action' => 'view', 'user_id' => $shifts_user->id])); throw_redirect(page_link_to('users', ['action' => 'view', 'user_id' => $shifts_user->id]));
} }
return page_with_title(__('Reset API key'), [ return page_with_title(__('Reset API key'), [
error( error(
@ -109,7 +109,7 @@ function user_myshifts()
. '. Freeloaded: ' . ($freeloaded ? 'YES Comment: ' . $freeload_comment : 'NO') . '. Freeloaded: ' . ($freeloaded ? 'YES Comment: ' . $freeload_comment : 'NO')
); );
success(__('Shift saved.')); success(__('Shift saved.'));
redirect(page_link_to('users', ['action' => 'view', 'user_id' => $shifts_user->id])); throw_redirect(page_link_to('users', ['action' => 'view', 'user_id' => $shifts_user->id]));
} }
} }
@ -125,10 +125,10 @@ function user_myshifts()
auth()->can('user_shifts_admin') auth()->can('user_shifts_admin')
); );
} else { } else {
redirect(page_link_to('user_myshifts')); throw_redirect(page_link_to('user_myshifts'));
} }
} }
redirect(page_link_to('users', ['action' => 'view', 'user_id' => $shifts_user->id])); throw_redirect(page_link_to('users', ['action' => 'view', 'user_id' => $shifts_user->id]));
return ''; return '';
} }

@ -197,7 +197,7 @@ function user_news()
engelsystem_log('Created news: ' . $news->title . ', is meeting: ' . ($news->is_meeting ? 'yes' : 'no')); engelsystem_log('Created news: ' . $news->title . ', is meeting: ' . ($news->is_meeting ? 'yes' : 'no'));
success(__('Entry saved.')); success(__('Entry saved.'));
redirect(page_link_to('news')); throw_redirect(page_link_to('news'));
} }
if (preg_match('/^\d{1,}$/', $request->input('page', 0))) { if (preg_match('/^\d{1,}$/', $request->input('page', 0))) {

@ -38,7 +38,7 @@ function user_questions()
]); ]);
success(__('You question was saved.')); success(__('You question was saved.'));
redirect(page_link_to('user_questions')); throw_redirect(page_link_to('user_questions'));
} else { } else {
return page_with_title(questions_title(), [ return page_with_title(questions_title(), [
error(__('Please enter a question!'), true) error(__('Please enter a question!'), true)
@ -59,7 +59,7 @@ function user_questions()
$question = Question::find($question_id); $question = Question::find($question_id);
if (!empty($question) && $question->user_id == $user->id) { if (!empty($question) && $question->user_id == $user->id) {
$question->delete(); $question->delete();
redirect(page_link_to('user_questions')); throw_redirect(page_link_to('user_questions'));
} else { } else {
return page_with_title(questions_title(), [ return page_with_title(questions_title(), [
error(__('No question found.'), true) error(__('No question found.'), true)

@ -91,7 +91,7 @@ function user_settings_main($user_source, $enable_tshirt_size, $tshirt_sizes)
$user_source->settings->save(); $user_source->settings->save();
success(__('Settings saved.')); success(__('Settings saved.'));
redirect(page_link_to('user_settings')); throw_redirect(page_link_to('user_settings'));
} }
return $user_source; return $user_source;
@ -119,7 +119,7 @@ function user_settings_password($user_source)
$auth->setPassword($user_source, $request->postData('new_password')); $auth->setPassword($user_source, $request->postData('new_password'));
success(__('Password saved.')); success(__('Password saved.'));
} }
redirect(page_link_to('user_settings')); throw_redirect(page_link_to('user_settings'));
} }
/** /**
@ -144,7 +144,7 @@ function user_settings_theme($user_source, $themes)
$user_source->settings->save(); $user_source->settings->save();
success(__('Theme changed.')); success(__('Theme changed.'));
redirect(page_link_to('user_settings')); throw_redirect(page_link_to('user_settings'));
} }
return $user_source; return $user_source;
@ -174,7 +174,7 @@ function user_settings_locale($user_source, $locales)
$session->set('locale', $user_source->settings->language); $session->set('locale', $user_source->settings->language);
success('Language changed.'); success('Language changed.');
redirect(page_link_to('user_settings')); throw_redirect(page_link_to('user_settings'));
} }
return $user_source; return $user_source;

@ -25,7 +25,7 @@ function user_shifts()
$request = request(); $request = request();
if (User_is_freeloader(auth()->user())) { if (User_is_freeloader(auth()->user())) {
redirect(page_link_to('user_myshifts')); throw_redirect(page_link_to('user_myshifts'));
} }
if ($request->has('edit_shift')) { if ($request->has('edit_shift')) {
@ -99,7 +99,7 @@ function load_rooms()
); );
if (empty($rooms)) { if (empty($rooms)) {
error(__('The administration has not configured any rooms yet.')); error(__('The administration has not configured any rooms yet.'));
redirect(page_link_to('/')); throw_redirect(page_link_to('/'));
} }
return $rooms; return $rooms;
} }
@ -120,7 +120,7 @@ function load_days()
error(__('The administration has not configured any shifts yet.')); error(__('The administration has not configured any shifts yet.'));
// Do not try to redirect to the current page // Do not try to redirect to the current page
if (config('home_site') != 'user_shifts') { if (config('home_site') != 'user_shifts') {
redirect(page_link_to('/')); throw_redirect(page_link_to('/'));
} }
} }
return $days; return $days;
@ -135,7 +135,7 @@ function load_types()
if (!count(DB::select('SELECT `id`, `name` FROM `AngelTypes`'))) { if (!count(DB::select('SELECT `id`, `name` FROM `AngelTypes`'))) {
error(__('The administration has not configured any angeltypes yet - or you are not subscribed to any angeltype.')); error(__('The administration has not configured any angeltypes yet - or you are not subscribed to any angeltype.'));
redirect(page_link_to('/')); throw_redirect(page_link_to('/'));
} }
$types = DB::select(' $types = DB::select('
SELECT SELECT

@ -108,30 +108,38 @@ function make_navigation()
$admin_menu = []; $admin_menu = [];
$admin_pages = [ $admin_pages = [
'admin_arrive' => __('Arrived angels'), 'admin_arrive' => 'Arrived angels',
'admin_active' => __('Active angels'), 'admin_active' => 'Active angels',
'admin_user' => __('All Angels'), 'admin_user' => 'All Angels',
'admin_free' => __('Free angels'), 'admin_free' => 'Free angels',
'admin_questions' => __('Answer questions'), 'admin_questions' => 'Answer questions',
'shifttypes' => __('Shifttypes'), 'shifttypes' => 'Shifttypes',
'admin_shifts' => __('Create shifts'), 'admin_shifts' => 'Create shifts',
'admin_rooms' => __('Rooms'), 'admin_rooms' => 'Rooms',
'admin_groups' => __('Grouprights'), 'admin_groups' => 'Grouprights',
'admin_import' => __('Frab import'), 'admin/schedule' => ['schedule.import', 'schedule.import'],
'admin_log' => __('Log'), 'admin_log' => 'Log',
'admin_event_config' => __('Event config'), 'admin_event_config' => 'Event config',
]; ];
if (config('autoarrive')) { if (config('autoarrive')) {
unset($admin_pages['admin_arrive']); unset($admin_pages['admin_arrive']);
} }
foreach ($admin_pages as $menu_page => $title) { foreach ($admin_pages as $menu_page => $options) {
if (auth()->can($menu_page)) { $options = (array)$options;
$permissions = $menu_page;
$title = $options[0];
if (isset($options[1])) {
$permissions = $options[1];
}
if (auth()->can($permissions)) {
$admin_menu[] = toolbar_item_link( $admin_menu[] = toolbar_item_link(
page_link_to($menu_page), page_link_to($menu_page),
'', '',
$title, __($title),
$menu_page == $page $menu_page == $page
); );
} }

@ -1,6 +1,7 @@
<?php <?php
use Carbon\Carbon; use Carbon\Carbon;
use Engelsystem\Http\Exceptions\HttpTemporaryRedirect;
use Engelsystem\ValidationResult; use Engelsystem\ValidationResult;
/** /**
@ -55,10 +56,9 @@ function parse_date($pattern, $value)
* *
* @param string $url * @param string $url
*/ */
function redirect($url) function throw_redirect($url)
{ {
header('Location: ' . $url, true, 302); throw new HttpTemporaryRedirect($url);
raw_output('');
} }
/** /**

@ -409,42 +409,6 @@ function table_buttons($buttons = [])
return '<div class="btn-group">' . join(' ', $buttons) . '</div>'; return '<div class="btn-group">' . join(' ', $buttons) . '</div>';
} }
/**
* @param string $str
* @param int $length
* @return string
*/
function shorten($str, $length = 50)
{
if (strlen($str) < $length) {
return $str;
}
return '<span title="' . htmlentities($str, ENT_COMPAT, 'UTF-8') . '">'
. substr($str, 0, $length - 3)
. '...</span>';
}
/**
* @param array[] $array
* @return string
*/
function table_body($array)
{
$html = '';
foreach ($array as $line) {
$html .= '<tr>';
if (is_array($line)) {
foreach ($line as $td) {
$html .= '<td>' . $td . '</td>';
}
} else {
$html .= '<td>' . $line . '</td>';
}
$html .= '</tr>';
}
return $html;
}
/** /**
* @param string $msg * @param string $msg
* @return mixed * @return mixed

@ -34,3 +34,36 @@ msgstr "Deine Passwörter stimmen nicht überein."
msgid "validation.password_confirmation.required" msgid "validation.password_confirmation.required"
msgstr "Du musst dein Passwort bestätigen." msgstr "Du musst dein Passwort bestätigen."
msgid "schedule.import"
msgstr "Programm importieren"
msgid "schedule.import.request-error"
msgstr "Das Programm konnte nicht abgerufen werden."
msgid "schedule.import.read-error"
msgstr "Das Programm konnte nicht gelesen werden."
msgid "schedule.import.invalid-shift-type"
msgstr "Der Schichttyp konnte nicht gefunden werden."
msgid "schedule.import.success"
msgstr "Das Programm wurde erfolgreich importiert."
msgid "validation.schedule-url.required"
msgstr "Bitte gib eine Programm URL an."
msgid "validation.schedule-url.url"
msgstr "Die Programm URL muss eine URL sein."
msgid "validation.shift-type.required"
msgstr "Der Schichttyp ist erforderlich."
msgid "validation.shift-type.int"
msgstr "Der Schichttyp muss eine Zahl sein."
msgid "validation.minutes-before.int"
msgstr "Die Minuten vor dem Talk müssen eine Zahl sein."
msgid "validation.minutes-after.int"
msgstr "Die Minuten nach dem Talk müssen eine Zahl sein."

@ -2806,3 +2806,60 @@ msgstr ""
#~ msgid "auth.no-nickname" #~ msgid "auth.no-nickname"
#~ msgstr "Gib bitte einen Nick an." #~ msgstr "Gib bitte einen Nick an."
msgid "form.load_schedule"
msgstr "Programm laden"
msgid "form.import"
msgstr "Importieren"
msgid "schedule.import.title"
msgstr "Programm importieren"
msgid "schedule.import.text"
msgstr "Dieser Import erstellt Räume und erstellt, aktualisiert und löscht Schichten anhand des schedule.xml exportes."
msgid "schedule.import.load.title"
msgstr "Programm importieren: Vorschau"
msgid "schedule.import.load.info"
msgstr "Importiere \"%s\" (Version \"%s\")"
msgid "schedule.url"
msgstr "Programm URL (schedule.xml)"
msgid "schedule.shift-type"
msgstr "Schichttyp"
msgid "schedule.minutes-before"
msgstr "Minuten vor Talk beginn hinzufügen"
msgid "schedule.minutes-after"
msgstr "Minuten nach Talk ende hinzufügen"
msgid "schedule.import.rooms.add"
msgstr "Neue Räume"
msgid "schedule.import.shifts.add"
msgstr "Neue Schichten"
msgid "schedule.import.shifts.update"
msgstr "Zu aktualisierende Schichten"
msgid "schedule.import.shifts.delete"
msgstr "Zu löschende Schichten"
msgid "schedule.import.rooms.name"
msgstr "Name"
msgid "schedule.import.shift.dates"
msgstr "Zeit"
msgid "schedule.import.shift.type"
msgstr "Typ"
msgid "schedule.import.shift.title"
msgstr "Titel"
msgid "schedule.import.shift.room"
msgstr "Raum"

@ -32,3 +32,36 @@ msgstr "Your passwords are not equal."
msgid "validation.password_confirmation.required" msgid "validation.password_confirmation.required"
msgstr "You have to confirm your password." msgstr "You have to confirm your password."
msgid "schedule.import"
msgstr "Import schedule"
msgid "schedule.import.request-error"
msgstr "The schedule could not be requested."
msgid "schedule.import.read-error"
msgstr "Unable to parse schedule."
msgid "schedule.import.invalid-shift-type"
msgstr "The shift type can't not be found."
msgid "schedule.import.success"
msgstr "Schedule import successful."
msgid "validation.schedule-url.required"
msgstr "The schedule URL is required."
msgid "validation.schedule-url.url"
msgstr "The schedule URL needs to be of type URL."
msgid "validation.shift-type.required"
msgstr "The shift type is required."
msgid "validation.shift-type.int"
msgstr "The shift type has to ba a number."
msgid "validation.minutes-before.int"
msgstr "The minutes before the talk have to be an integer."
msgid "validation.minutes-after.int"
msgstr "The minutes after the talk have to be an integer."

@ -45,3 +45,63 @@ msgstr ""
"Please have a look at the " "Please have a look at the "
"[contributors list on GitHub](https://github.com/engelsystem/engelsystem/graphs/contributors)" "[contributors list on GitHub](https://github.com/engelsystem/engelsystem/graphs/contributors)"
" for a complete list." " for a complete list."
msgid "form.load_schedule"
msgstr "Load schedule"
msgid "form.import"
msgstr "Import"
msgid "schedule.import.title"
msgstr "Import schedule"
msgid "schedule.import.text"
msgstr "This import creates rooms and creates, updates and deletes shifts according to the schedule.xml export."
msgid "schedule.import.load.title"
msgstr "Import schedule: Preview"
msgid "schedule.import.load.info"
msgstr "Import \"%s\" (version \"%s\")"
msgid "schedule.url"
msgstr "Schedule URL (schedule.xml)"
msgid "schedule.shift-type"
msgstr "Shift type"
msgid "schedule.minutes-before"
msgstr "Add minutes before talk begins"
msgid "schedule.minutes-after"
msgstr "Add minutes after talk ends"
msgid "schedule.import.request_error"
msgstr "Unable to load schedule."
msgid "schedule.import.rooms.add"
msgstr "Rooms to create"
msgid "schedule.import.shifts.add"
msgstr "Shifts to create"
msgid "schedule.import.shifts.update"
msgstr "Shifts to update"
msgid "schedule.import.shifts.delete"
msgstr "Shifts to delete"
msgid "schedule.import.rooms.name"
msgstr "Name"
msgid "schedule.import.shift.dates"
msgstr "Times"
msgid "schedule.import.shift.type"
msgstr "Type"
msgid "schedule.import.shift.title"
msgstr "Title"
msgid "schedule.import.shift.room"
msgstr "Room"

@ -0,0 +1,41 @@
{% extends 'layouts/app.twig' %}
{% import 'macros/base.twig' as m %}
{% import 'macros/form.twig' as f %}
{% set title %}{% block title %}{{ __('schedule.import.title') }}{% endblock %}{% endset %}
{% block content %}
<div class="container">
<h1>{% block content_title %}{{ title }}{% endblock %}</h1>
{% for message in errors|default([]) %}
{{ m.alert(__(message), 'danger') }}
{% endfor %}
{% for message in success|default([]) %}
{{ m.alert(__(message), 'success') }}
{% endfor %}
<div class="row">
{% block row_content %}
<form method="POST" action="{{ url('/admin/schedule/load') }}">
{{ csrf() }}
<div class="col-md-12">
<p>{{ __('schedule.import.text') }}</p>
</div>
<div class="col-lg-6">
{{ f.input('schedule-url', __('schedule.url'), 'url', {'required': true}) }}
{{ f.select('shift-type', shift_types|default([]), __('schedule.shift-type')) }}
{{ f.input('minutes-before', __('schedule.minutes-before'), 'number', {'value': 15, 'required': true}) }}
{{ f.input('minutes-after', __('schedule.minutes-after'), 'number', {'value': 15, 'required': true}) }}
{{ f.submit(__('form.load_schedule')) }}
</div>
</form>
{% endblock %}
</div>
</div>
{% endblock %}

@ -0,0 +1,79 @@
{% extends 'admin/schedule/index.twig' %}
{% import 'macros/form.twig' as f %}
{% block title %}{{ __('schedule.import.load.title') }}{% endblock %}
{% block row_content %}
<form method="POST" action="{{ url('/admin/schedule/import') }}">
{{ csrf() }}
{{ f.hidden('schedule-url', schedule_url) }}
{{ f.hidden('shift-type', shift_type) }}
{{ f.hidden('minutes-before', minutes_before) }}
{{ f.hidden('minutes-after', minutes_after) }}
<div class="col-lg-12">
<p>{{ __('schedule.import.load.info', [schedule.conference.title, schedule.version]) }}</p>
<h2>{{ __('schedule.import.rooms.add') }}</h2>
{{ _self.roomsTable(rooms.add) }}
<h2>{{ __('schedule.import.shifts.add') }}</h2>
{{ _self.shiftsTable(shifts.add) }}
<h2>{{ __('schedule.import.shifts.update') }}</h2>
{{ _self.shiftsTable(shifts.update) }}
<h2>{{ __('schedule.import.shifts.delete') }}</h2>
{{ _self.shiftsTable(shifts.delete) }}
{{ f.submit(__('form.import')) }}
</div>
</form>
{% endblock %}
{% macro roomsTable(rooms) %}
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>{{ __('schedule.import.rooms.name') }}</th>
</tr>
</thead>
<tbody>
{% for room in rooms %}
<tr>
<td>{{ room.name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endmacro %}
{% macro shiftsTable(shifts) %}
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>{{ __('schedule.import.shift.dates') }}</th>
<th>{{ __('schedule.import.shift.type') }}</th>
<th>{{ __('schedule.import.shift.title') }}</th>
<th>{{ __('schedule.import.shift.room') }}</th>
</tr>
</thead>
<tbody>
{% for shift in shifts %}
<tr>
<td>{{ shift.date.format(__('Y-m-d H:i')) }} - {{ shift.endDate.format(__('H:i')) }}</td>
<td>{{ shift.type }}</td>
<td>{{ shift.title }}{% if shift.subtitle %}<br><small>{{ shift.subtitle }}</small>{% endif %}</td>
<td>{{ shift.room.name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endmacro %}

@ -1,18 +1,35 @@
{% macro input(name, label, type, required) %} {% macro input(name, label, type, opt) %}
<div class="form-group"> <div class="form-group">
{% if label %} {% if label -%}
<label for="{{ name }}">{{ label }}</label> <label for="{{ name }}">{{ label }}</label>
{% endif %} {%- endif %}
<input type="{{ type|default('text') }}" class="form-control" id="{{ name }}" name="{{ name }}" <input type="{{ type|default('text') }}" class="form-control"
{%- if required|default(false) %} required="required"{% endif -%} id="{{ name }}" name="{{ name }}"
value="{{ opt.value|default('') }}"
{%- if opt.required|default(false) %}
required="required"
{%- endif -%}
> >
</div> </div>
{% endmacro %} {%- endmacro %}
{% macro select(name, data, label, selected) %}
<div class="form-group">
{% if label -%}
<label for="{{ name }}">{{ label }}</label>
{% endif %}
<select id="{{ name }}" name="{{ name }}" class="form-control">
{% for value,decription in data -%}
<option value="{{ value }}" {% if name == selected %} selected{% endif %}>{{ decription }}</option>
{% endfor %}
</select>
</div>
{%- endmacro %}
{% macro hidden(name, value) %} {% macro hidden(name, value) %}
<input type="hidden" id="{{ name }}" name="{{ name }}" value="{{ value }}"> <input type="hidden" id="{{ name }}" name="{{ name }}" value="{{ value }}">
{% endmacro %} {%- endmacro %}
{% macro submit(label) %} {% macro submit(label) %}
<button type="submit" class="btn btn-default">{{ label|default(__('form.submit')) }}</button> <button type="submit" class="btn btn-primary">{{ label|default(__('form.submit')) }}</button>
{% endmacro %} {%- endmacro %}

@ -7,8 +7,8 @@
<form action="" enctype="multipart/form-data" method="post"> <form action="" enctype="multipart/form-data" method="post">
{{ csrf() }} {{ csrf() }}
{{ f.input('password', __('Password'), 'password', true) }} {{ f.input('password', __('Password'), 'password', {'required': true}) }}
{{ f.input('password_confirmation', __('Confirm password'), 'password', true) }} {{ f.input('password_confirmation', __('Confirm password'), 'password', {'required': true}) }}
<div class="form-group"> <div class="form-group">
{{ f.submit(__('Save')) }} {{ f.submit(__('Save')) }}

@ -19,7 +19,7 @@
{{ csrf() }} {{ csrf() }}
{{ __('We will send you an e-mail with a password recovery link. Please use the email address you used for registration.') }} {{ __('We will send you an e-mail with a password recovery link. Please use the email address you used for registration.') }}
{{ f.input('email', __('E-Mail'), 'email', true) }} {{ f.input('email', __('E-Mail'), 'email', {'required': true}) }}
<div class="form-group"> <div class="form-group">
{{ f.submit(__('Recover')) }} {{ f.submit(__('Recover')) }}

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Helpers\Schedule;
trait CalculatesTime
{
/**
* @param string $time
* @return int
*/
protected function secondsFromTime(string $time): int
{
$seconds = 0;
$duration = explode(':', $time);
foreach (array_slice($duration, 0, 2) as $key => $times) {
$seconds += [60 * 60, 60][$key] * $times;
}
return $seconds;
}
}

@ -0,0 +1,129 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Helpers\Schedule;
class Conference
{
use CalculatesTime;
/** @var string required */
protected $title;
/** @var string required */
protected $acronym;
/** @var string|null */
protected $start;
/** @var string|null */
protected $end;
/** @var int|null */
protected $days;
/** @var string|null */
protected $timeslotDuration;
/** @var string|null */
protected $baseUrl;
/**
* Event constructor.
*
* @param string $title
* @param string $acronym
* @param string|null $start
* @param string|null $end
* @param int|null $days
* @param string|null $timeslotDuration
* @param string|null $baseUrl
*/
public function __construct(
string $title,
string $acronym,
?string $start = null,
?string $end = null,
?int $days = null,
?string $timeslotDuration = null,
?string $baseUrl = null
) {
$this->title = $title;
$this->acronym = $acronym;
$this->start = $start;
$this->end = $end;
$this->days = $days;
$this->timeslotDuration = $timeslotDuration;
$this->baseUrl = $baseUrl;
}
/**
* @return string
*/
public function getTitle(): string
{
return $this->title;
}
/**
* @return string
*/
public function getAcronym(): string
{
return $this->acronym;
}
/**
* @return string|null
*/
public function getStart(): ?string
{
return $this->start;
}
/**
* @return string|null
*/
public function getEnd(): ?string
{
return $this->end;
}
/**
* @return int|null
*/
public function getDays(): ?int
{
return $this->days;
}
/**
* @return string|null
*/
public function getTimeslotDuration(): ?string
{
return $this->timeslotDuration;
}
/**
* @return int|null
*/
public function getTimeslotDurationSeconds(): ?int
{
$duration = $this->getTimeslotDuration();
if (!$duration) {
return null;
}
return $this->secondsFromTime($duration);
}
/**
* @return string|null
*/
public function getBaseUrl(): ?string
{
return $this->baseUrl;
}
}

@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Helpers\Schedule;
use Carbon\Carbon;
class Day
{
/** @var string required */
protected $date;
/** @var Carbon required */
protected $start;
/** @var Carbon required */
protected $end;
/** @var int required */
protected $index;
/** @var Room[] */
protected $room;
/**
* Day constructor.
*
* @param string $date
* @param Carbon $start
* @param Carbon $end
* @param int $index
* @param Room[] $rooms
*/
public function __construct(
string $date,
Carbon $start,
Carbon $end,
int $index,
array $rooms = []
) {
$this->date = $date;
$this->start = $start;
$this->end = $end;
$this->index = $index;
$this->room = $rooms;
}
/**
* @return string
*/
public function getDate(): string
{
return $this->date;
}
/**
* @return Carbon
*/
public function getStart(): Carbon
{
return $this->start;
}
/**
* @return Carbon
*/
public function getEnd(): Carbon
{
return $this->end;
}
/**
* @return int
*/
public function getIndex(): int
{
return $this->index;
}
/**
* @return Room[]
*/
public function getRoom(): array
{
return $this->room;
}
}

@ -0,0 +1,345 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Helpers\Schedule;
use Carbon\Carbon;
class Event
{
use CalculatesTime;
/** @var string required globally unique */
protected $guid;
/** @var int required globally unique */
protected $id;
/** @var Room required, string in XML */
protected $room;
/** @var string required */
protected $title;
/** @var string required */
protected $subtitle;
/** @var string required */
protected $type;
/** @var Carbon required */
protected $date;
/** @var string required time (hh:mm:ss || hh:mm) */
protected $start;
/** @var string required (h?h:mm:ss || h?h:mm) */
protected $duration;
/** @var string required */
protected $abstract;
/** @var string required globally unique */
protected $slug;
/** @var string required */
protected $track;
/** @var string|null */
protected $logo;
/** @var string[] id => name */
protected $persons;
/** @var string|null two letter code */
protected $language;
/** @var string|null */
protected $description;
/** @var string|null license (and opt out in XML, null if not recorded, empty if no license defined) */
protected $recording;
/** @var array href => title */
protected $links;
/** @var array href => name */
protected $attachments;
/** @var string|null */
protected $url;
/** @var string|null */
protected $videoDownloadUrl;
/** @var Carbon Calculated */
protected $endDate;
/**
* Event constructor.
*
* @param string $guid
* @param int $id
* @param Room $room
* @param string $title
* @param string $subtitle
* @param string $type
* @param Carbon $date
* @param string $start
* @param string $duration
* @param string $abstract
* @param string $slug
* @param string $track
* @param string|null $logo
* @param string[] $persons
* @param string|null $language
* @param string|null $description
* @param string|null $recording license
* @param array $links
* @param array $attachments
* @param string|null $url
* @param string|null $videoDownloadUrl
*/
public function __construct(
string $guid,
int $id,
Room $room,
string $title,
string $subtitle,
string $type,
Carbon $date,
string $start,
string $duration,
string $abstract,
string $slug,
string $track,
?string $logo = null,
array $persons = [],
?string $language = null,
?string $description = null,
string $recording = '',
array $links = [],
array $attachments = [],
?string $url = null,
?string $videoDownloadUrl = null
) {
$this->guid = $guid;
$this->id = $id;
$this->room = $room;
$this->title = $title;
$this->subtitle = $subtitle;
$this->type = $type;
$this->date = $date;
$this->start = $start;
$this->duration = $duration;
$this->abstract = $abstract;
$this->slug = $slug;
$this->track = $track;
$this->logo = $logo;
$this->persons = $persons;
$this->language = $language;
$this->description = $description;
$this->recording = $recording;
$this->links = $links;
$this->attachments = $attachments;
$this->url = $url;
$this->videoDownloadUrl = $videoDownloadUrl;
$this->endDate = $this->date
->copy()
->addSeconds($this->getDurationSeconds());
}
/**
* @return string
*/
public function getGuid(): string
{
return $this->guid;
}
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return Room
*/
public function getRoom(): Room
{
return $this->room;
}
/**
* @return string
*/
public function getTitle(): string
{
return $this->title;
}
/**
* @param string $title
*/
public function setTitle(string $title): void
{
$this->title = $title;
}
/**
* @return string
*/
public function getSubtitle(): string
{
return $this->subtitle;
}
/**
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* @return Carbon
*/
public function getDate(): Carbon
{
return $this->date;
}
/**
* @return string
*/
public function getStart(): string
{
return $this->start;
}
/**
* @return string
*/
public function getDuration(): string
{
return $this->duration;
}
/**
* @return int
*/
public function getDurationSeconds(): int
{
return $this->secondsFromTime($this->duration);
}
/**
* @return string
*/
public function getAbstract(): string
{
return $this->abstract;
}
/**
* @return string
*/
public function getSlug(): string
{
return $this->slug;
}
/**
* @return string
*/
public function getTrack(): string
{
return $this->track;
}
/**
* @return string|null
*/
public function getLogo(): ?string
{
return $this->logo;
}
/**
* @return string[]
*/
public function getPersons(): array
{
return $this->persons;
}
/**
* @return string|null
*/
public function getLanguage(): ?string
{
return $this->language;
}
/**
* @return string|null
*/
public function getDescription(): ?string
{
return $this->description;
}
/**
* @return string|null
*/
public function getRecording(): ?string
{
return $this->recording;
}
/**
* @return array
*/
public function getLinks(): array
{
return $this->links;
}
/**
* @return array
*/
public function getAttachments(): array
{
return $this->attachments;
}
/**
* @return string|null
*/
public function getUrl(): ?string
{
return $this->url;
}
/**
* @return string|null
*/
public function getVideoDownloadUrl(): ?string
{
return $this->videoDownloadUrl;
}
/**
* @return Carbon
*/
public function getEndDate(): Carbon
{
return $this->endDate;
}
}

@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Helpers\Schedule;
class Room
{
/** @var string required */
protected $name;
/** @var Event[] */
protected $event;
/**
* Room constructor.
*
* @param string $name
* @param Event[] $events
*/
public function __construct(
string $name,
array $events = []
) {
$this->name = $name;
$this->event = $events;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @return Event[]
*/
public function getEvent(): array
{
return $this->event;
}
/**
* @param Event[] $event
*/
public function setEvent(array $event): void
{
$this->event = $event;
}
}

@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Helpers\Schedule;
use Carbon\Carbon;
class Schedule
{
/** @var string */
protected $version;
/** @var Conference */
protected $conference;
/** @var Day[] */
protected $day;
/**
* @param string $version
* @param Conference $conference
* @param Day[] $days
*/
public function __construct(
string $version,
Conference $conference,
array $days
) {
$this->version = $version;
$this->conference = $conference;
$this->day = $days;
}
/**
* @return string
*/
public function getVersion(): string
{
return $this->version;
}
/**
* @return Conference
*/
public function getConference(): Conference
{
return $this->conference;
}
/**
* @return Day[]
*/
public function getDay(): array
{
return $this->day;
}
/**
* @return Room[]
*/
public function getRooms(): array
{
$rooms = [];
foreach ($this->day as $day) {
foreach ($day->getRoom() as $room) {
$name = $room->getName();
$rooms[$name] = $room;
}
}
return $rooms;
}
/**
* @return Carbon|null
*/
public function getStartDateTime(): ?Carbon
{
$start = null;
foreach ($this->day as $day) {
$time = $day->getStart();
if ($time > $start && $start) {
continue;
}
$start = $time;
}
return $start;
}
/**
* @return Carbon|null
*/
public function getEndDateTime(): ?Carbon
{
$end = null;
foreach ($this->day as $day) {
$time = $day->getEnd();
if ($time < $end && $end) {
continue;
}
$end = $time;
}
return $end;
}
}

@ -0,0 +1,172 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Helpers\Schedule;
use Carbon\Carbon;
use SimpleXMLElement;
class XmlParser
{
/** @var SimpleXMLElement */
protected $scheduleXML;
/** @var Schedule */
protected $schedule;
/**
* @param string $xml
* @return bool
*/
public function load(string $xml): bool
{
$this->scheduleXML = simplexml_load_string($xml);
if (!$this->scheduleXML) {
return false;
}
$this->parseXml();
return true;
}
/**
* Parse the predefined XML content
*/
protected function parseXml(): void
{
$version = $this->getFirstXpathContent('version');
$conference = new Conference(
$this->getFirstXpathContent('conference/title'),
$this->getFirstXpathContent('conference/acronym'),
$this->getFirstXpathContent('conference/start'),
$this->getFirstXpathContent('conference/end'),
(int)$this->getFirstXpathContent('conference/days'),
$this->getFirstXpathContent('conference/timeslot_duration'),
$this->getFirstXpathContent('conference/base_url')
);
$days = [];
foreach ($this->scheduleXML->xpath('day') as $day) {
$rooms = [];
foreach ($day->xpath('room') as $roomElement) {
$room = new Room(
(string)$roomElement->attributes()['name']
);
$events = $this->parseEvents($roomElement->xpath('event'), $room);
$room->setEvent($events);
$rooms[] = $room;
}
$days[] = new Day(
(string)$day->attributes()['date'],
new Carbon($day->attributes()['start']),
new Carbon($day->attributes()['end']),
(int)$day->attributes()['index'],
$rooms
);
}
$this->schedule = new Schedule(
$version,
$conference,
$days
);
}
/**
* @param SimpleXMLElement[] $eventElements
* @param Room $room
* @return array
*/
protected function parseEvents(array $eventElements, Room $room): array
{
$events = [];
foreach ($eventElements as $event) {
$persons = $this->getListFromSequence($event, 'persons', 'person', 'id');
$links = $this->getListFromSequence($event, 'links', 'link', 'href');
$attachments = $this->getListFromSequence($event, 'attachments', 'attachment', 'href');
$recording = '';
$recordingElement = $event->xpath('recording')[0];
if ($this->getFirstXpathContent('optout', $recordingElement) == 'false') {
$recording = $this->getFirstXpathContent('license', $recordingElement);
}
$events[] = new Event(
(string)$event->attributes()['guid'],
(int)$event->attributes()['id'],
$room,
$this->getFirstXpathContent('title', $event),
$this->getFirstXpathContent('subtitle', $event),
$this->getFirstXpathContent('type', $event),
new Carbon($this->getFirstXpathContent('date', $event)),
$this->getFirstXpathContent('start', $event),
$this->getFirstXpathContent('duration', $event),
$this->getFirstXpathContent('abstract', $event),
$this->getFirstXpathContent('slug', $event),
$this->getFirstXpathContent('track', $event),
$this->getFirstXpathContent('logo', $event) ?: null,
$persons,
$this->getFirstXpathContent('language', $event) ?: null,
$this->getFirstXpathContent('description', $event) ?: null,
$recording,
$links,
$attachments,
$this->getFirstXpathContent('url', $event) ?: null,
$this->getFirstXpathContent('video_download_url', $event) ?: null
);
}
return $events;
}
/**
* @param string $path
* @param SimpleXMLElement|null $xml
* @return string
*/
protected function getFirstXpathContent(string $path, ?SimpleXMLElement $xml = null): string
{
$element = ($xml ?: $this->scheduleXML)->xpath($path);
return $element ? (string)$element[0] : '';
}
/**
* Resolves a list from a sequence of elements
*
* @param SimpleXMLElement $element
* @param string $firstElement
* @param string $secondElement
* @param string $idAttribute
* @return array
*/
protected function getListFromSequence(
SimpleXMLElement $element,
string $firstElement,
string $secondElement,
string $idAttribute
): array {
$items = [];
foreach ($element->xpath($firstElement)[0]->xpath($secondElement) as $item) {
$items[(string)$item->attributes()[$idAttribute]] = (string)$item;
}
return $items;
}
/**
* @return Schedule
*/
public function getSchedule(): Schedule
{
return $this->schedule;
}
}

@ -0,0 +1,25 @@
<?php
namespace Engelsystem\Http;
use Engelsystem\Container\ServiceProvider;
use GuzzleHttp\Client as GuzzleClient;
class HttpClientServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->when(GuzzleClient::class)
->needs('$config')
->give(
function () {
return [
// No exception on >= 400 responses
'http_errors' => false,
// Wait max n seconds for a response
'timeout' => 2.0,
];
}
);
}
}

@ -0,0 +1,13 @@
<?php
namespace Engelsystem\Http;
use Engelsystem\Container\ServiceProvider;
class RedirectServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('redirect', Redirector::class);
}
}

@ -0,0 +1,55 @@
<?php
namespace Engelsystem\Http;
class Redirector
{
/** @var Request */
protected $request;
/** @var Response */
protected $response;
/**
* @param Request $request
* @param Response $response
*/
public function __construct(Request $request, Response $response)
{
$this->request = $request;
$this->response = $response;
}
/**
* @param string $path
* @param int $status
* @param array $headers
* @return Response
*/
public function to(string $path, int $status = 302, array $headers = []): Response
{
return $this->response->redirectTo($path, $status, $headers);
}
/**
* @param int $status
* @param array $headers
* @return Response
*/
public function back(int $status = 302, array $headers = []): Response
{
return $this->to($this->getPreviousUrl(), $status, $headers);
}
/**
* @return string
*/
protected function getPreviousUrl(): string
{
if ($header = $this->request->getHeader('referer')) {
return array_pop($header);
}
return '/';
}
}

@ -6,27 +6,37 @@ use Engelsystem\Renderer\Renderer;
use InvalidArgumentException; use InvalidArgumentException;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse; use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class Response extends SymfonyResponse implements ResponseInterface class Response extends SymfonyResponse implements ResponseInterface
{ {
use MessageTrait; use MessageTrait;
/**
* @var SessionInterface
*/
protected $session;
/** @var Renderer */ /** @var Renderer */
protected $renderer; protected $renderer;
/** /**
* @param string $content * @param string $content
* @param int $status * @param int $status
* @param array $headers * @param array $headers
* @param Renderer $renderer * @param Renderer $renderer
* @param SessionInterface $session
*/ */
public function __construct( public function __construct(
$content = '', $content = '',
int $status = 200, int $status = 200,
array $headers = [], array $headers = [],
Renderer $renderer = null Renderer $renderer = null,
SessionInterface $session = null
) { ) {
$this->renderer = $renderer; $this->renderer = $renderer;
$this->session = $session;
parent::__construct($content, $status, $headers); parent::__construct($content, $status, $headers);
} }
@ -155,4 +165,44 @@ class Response extends SymfonyResponse implements ResponseInterface
{ {
$this->renderer = $renderer; $this->renderer = $renderer;
} }
/**
* Sets a session attribute (which is mutable)
*
* @param string $key
* @param mixed|mixed[] $value
* @return Response
*/
public function with(string $key, $value)
{
if (!$this->session instanceof SessionInterface) {
throw new InvalidArgumentException('Session not defined');
}
$data = $this->session->get($key);
if (is_array($data) && is_array($value)) {
$value = array_merge_recursive($data, $value);
}
$this->session->set($key, $value);
return $this;
}
/**
* Sets form data to the mutable session
*
* @param array $input
* @return Response
*/
public function withInput(array $input)
{
if (!$this->session instanceof SessionInterface) {
throw new InvalidArgumentException('Session not defined');
}
$this->session->set('form-data', $input);
return $this;
}
} }

@ -63,17 +63,11 @@ class ErrorHandler implements MiddlewareInterface
} catch (HttpException $e) { } catch (HttpException $e) {
$response = $this->createResponse($e->getMessage(), $e->getStatusCode(), $e->getHeaders()); $response = $this->createResponse($e->getMessage(), $e->getStatusCode(), $e->getHeaders());
} catch (ValidationException $e) { } catch (ValidationException $e) {
$response = $this->createResponse('', 302, ['Location' => $this->getPreviousUrl($request)]); $response = $this->redirectBack();
$response->with('errors', ['validation' => $e->getValidator()->getErrors()]);
if ($request instanceof Request) { if ($request instanceof Request) {
$session = $request->getSession(); $response->withInput(Arr::except($request->request->all(), $this->formIgnore));
$errors = array_merge_recursive(
$session->get('errors', []),
['validation' => $e->getValidator()->getErrors()]
);
$session->set('errors', $errors);
$session->set('form-data', Arr::except($request->request->all(), $this->formIgnore));
} }
} }
@ -140,15 +134,12 @@ class ErrorHandler implements MiddlewareInterface
} }
/** /**
* @param ServerRequestInterface $request * Create a redirect back response
* @return string *
* @return Response
*/ */
protected function getPreviousUrl(ServerRequestInterface $request) protected function redirectBack()
{ {
if ($header = $request->getHeader('referer')) { return back();
return array_pop($header);
}
return '/';
} }
} }

@ -205,10 +205,6 @@ class LegacyMiddleware implements MiddlewareInterface
$title = admin_groups_title(); $title = admin_groups_title();
$content = admin_groups(); $content = admin_groups();
return [$title, $content]; return [$title, $content];
case 'admin_import':
$title = admin_import_title();
$content = admin_import();
return [$title, $content];
case 'admin_shifts': case 'admin_shifts':
$title = admin_shifts_title(); $title = admin_shifts_title();
$content = admin_shifts(); $content = admin_shifts();
@ -219,7 +215,7 @@ class LegacyMiddleware implements MiddlewareInterface
return [$title, $content]; return [$title, $content];
} }
redirect(page_link_to('login')); throw_redirect(page_link_to('login'));
return []; return [];
} }
@ -239,9 +235,15 @@ class LegacyMiddleware implements MiddlewareInterface
return response($content, (int)$page); return response($content, (int)$page);
} }
return response(view('layouts/app', [ return response(
'title' => $title, view(
'content' => msg() . $content, 'layouts/app',
]), 200); [
'title' => $title,
'content' => msg() . $content,
]
),
200
);
} }
} }

@ -0,0 +1,31 @@
<?php
namespace Engelsystem\Models\Shifts;
use Engelsystem\Models\BaseModel;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Query\Builder as QueryBuilder;
/**
* @property int $id
* @property string $url
*
* @property-read QueryBuilder|Collection|ScheduleShift[] $scheduleShifts
*
* @method static QueryBuilder|Schedule[] whereId($value)
* @method static QueryBuilder|Schedule[] whereUrl($value)
*/
class Schedule extends BaseModel
{
/** @var array Values that are mass assignable */
protected $fillable = ['url'];
/**
* @return HasMany
*/
public function scheduleShifts()
{
return $this->hasMany(ScheduleShift::class);
}
}

@ -0,0 +1,38 @@
<?php
namespace Engelsystem\Models\Shifts;
use Engelsystem\Models\BaseModel;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Query\Builder as QueryBuilder;
/**
* @property int $shift_id
* @property int $schedule_id
* @property string $guid
*
* @property-read QueryBuilder|Schedule $schedule
*
* @method static QueryBuilder|ScheduleShift[] whereShiftId($value)
* @method static QueryBuilder|ScheduleShift[] whereScheduleId($value)
* @method static QueryBuilder|ScheduleShift[] whereGuid($value)
*/
class ScheduleShift extends BaseModel
{
/** @var string The primary key for the model */
protected $primaryKey = 'shift_id';
/** @var string Required because it is not schedule_shifts */
protected $table = 'schedule_shift';
/** @var array Values that are mass assignable */
protected $fillable = ['shift_id', 'schedule_id', 'guid'];
/**
* @return BelongsTo
*/
public function schedule()
{
return $this->belongsTo(Schedule::class);
}
}

@ -4,6 +4,7 @@ use Engelsystem\Application;
use Engelsystem\Config\Config; use Engelsystem\Config\Config;
use Engelsystem\Helpers\Authenticator; use Engelsystem\Helpers\Authenticator;
use Engelsystem\Helpers\Translation\Translator; use Engelsystem\Helpers\Translation\Translator;
use Engelsystem\Http\Redirector;
use Engelsystem\Http\Request; use Engelsystem\Http\Request;
use Engelsystem\Http\Response; use Engelsystem\Http\Response;
use Engelsystem\Http\UrlGeneratorInterface; use Engelsystem\Http\UrlGeneratorInterface;
@ -28,7 +29,7 @@ function app($id = null)
/** /**
* @return Authenticator * @return Authenticator
*/ */
function auth() function auth(): Authenticator
{ {
return app('authenticator'); return app('authenticator');
} }
@ -37,11 +38,24 @@ function auth()
* @param string $path * @param string $path
* @return string * @return string
*/ */
function base_path($path = '') function base_path($path = ''): string
{ {
return app('path') . (empty($path) ? '' : DIRECTORY_SEPARATOR . $path); return app('path') . (empty($path) ? '' : DIRECTORY_SEPARATOR . $path);
} }
/**
* @param int $status
* @param array $headers
* @return Response
*/
function back($status = 302, $headers = []): Response
{
/** @var Redirector $redirect */
$redirect = app('redirect');
return $redirect->back($status, $headers);
}
/** /**
* Get or set config values * Get or set config values
* *
@ -70,11 +84,25 @@ function config($key = null, $default = null)
* @param string $path * @param string $path
* @return string * @return string
*/ */
function config_path($path = '') function config_path($path = ''): string
{ {
return app('path.config') . (empty($path) ? '' : DIRECTORY_SEPARATOR . $path); return app('path.config') . (empty($path) ? '' : DIRECTORY_SEPARATOR . $path);
} }
/**
* @param string $path
* @param int $status
* @param array $headers
* @return Response
*/
function redirect(string $path, $status = 302, $headers = []): Response
{
/** @var Redirector $redirect */
$redirect = app('redirect');
return $redirect->to($path, $status, $headers);
}
/** /**
* @param string $key * @param string $key
* @param mixed $default * @param mixed $default
@ -97,7 +125,7 @@ function request($key = null, $default = null)
* @param array $headers * @param array $headers
* @return Response * @return Response
*/ */
function response($content = '', $status = 200, $headers = []) function response($content = '', $status = 200, $headers = []): Response
{ {
/** @var Response $response */ /** @var Response $response */
$response = app('psr7.response'); $response = app('psr7.response');
@ -155,7 +183,7 @@ function trans($key = null, $replace = [])
* @param array $replace * @param array $replace
* @return string * @return string
*/ */
function __($key, $replace = []) function __($key, $replace = []): string
{ {
/** @var Translator $translator */ /** @var Translator $translator */
$translator = app('translator'); $translator = app('translator');
@ -172,7 +200,7 @@ function __($key, $replace = [])
* @param array $replace * @param array $replace
* @return string * @return string
*/ */
function _e($key, $keyPlural, $number, $replace = []) function _e($key, $keyPlural, $number, $replace = []): string
{ {
/** @var Translator $translator */ /** @var Translator $translator */
$translator = app('translator'); $translator = app('translator');

@ -13,7 +13,7 @@ class RoomModelTest extends TestCase
*/ */
public function createRoom() public function createRoom()
{ {
$this->room_id = Room_create('test', false, null, null); $this->room_id = Room_create('test', null, null);
} }
/** /**

@ -36,14 +36,17 @@ trait HasDatabase
$this->database $this->database
->getConnection() ->getConnection()
->table('migrations') ->table('migrations')
->insert([ ->insert(
['migration' => '2018_01_01_000001_import_install_sql'], [
['migration' => '2018_01_01_000002_import_update_sql'], ['migration' => '2018_01_01_000001_import_install_sql'],
['migration' => '2018_01_01_000003_fix_old_tables'], ['migration' => '2018_01_01_000002_import_update_sql'],
['migration' => '2018_01_01_000004_cleanup_group_privileges'], ['migration' => '2018_01_01_000003_fix_old_tables'],
['migration' => '2018_01_01_000005_add_angel_supporter_permissions'], ['migration' => '2018_01_01_000004_cleanup_group_privileges'],
['migration' => '2018_12_27_000000_fix_missing_arrival_dates'], ['migration' => '2018_01_01_000005_add_angel_supporter_permissions'],
]); ['migration' => '2018_12_27_000000_fix_missing_arrival_dates'],
['migration' => '2019_09_07_000000_migrate_admin_schedule_permissions'],
]
);
$migration->run(__DIR__ . '/../../db/migrations'); $migration->run(__DIR__ . '/../../db/migrations');
} }

@ -0,0 +1,46 @@
<?xml version='1.0' encoding='utf-8' ?>
<schedule>
<version>Some version string</version>
<conference>
<title>Test Event</title>
<acronym>Test1</acronym>
<start>2042-01-01</start>
<end>2042-01-01</end>
<days>1</days>
<timeslot_duration>00:15</timeslot_duration>
<base_url>https://foo.bar/baz/schedule/</base_url>
</conference>
<day index='1' date='2042-01-01' start='2042-01-01T01:00:00+02:00' end='2042-01-01T22:59:00+02:00'>
<room name='Rooming'>
<event guid='e427cfa9-9ba1-4b14-a99f-bce83ffe5a1c' id='1337'>
<date>2042-01-01T12:30:00+02:00</date>
<title>Foo Bar Test</title>
<subtitle>Some sub</subtitle>
<start>12:30</start>
<duration>00:30</duration>
<room>Rooming</room>
<slug>foo-bar-test</slug>
<recording>
<license>WTFPL</license>
<optout>false</optout>
</recording>
<track>Testing</track>
<type>Talk</type>
<language>de</language>
<abstract>Foo bar is da best</abstract>
<description>Any describing stuff?</description>
<url>https://foo.bar/baz/schedule/ipsum</url>
<logo>https://lorem.ipsum/foo/bar.png</logo>
<persons>
<person id='1234'>Some Person</person>
</persons>
<links>
<link href="https://foo.bar">Some Foo Bar</link>
</links>
<attachments>
<attachment href="https://foo.bar/stuff.pdf">A PDF File</attachment>
</attachments>
</event>
</room>
</day>
</schedule>

@ -0,0 +1,33 @@
<?php
namespace Engelsystem\Test\Unit\Helpers\Schedule;
use Engelsystem\Helpers\Schedule\CalculatesTime;
use Engelsystem\Test\Unit\TestCase;
class CalculatesTimeTest extends TestCase
{
/**
* @covers \Engelsystem\Helpers\Schedule\CalculatesTime::secondsFromTime
*/
public function testSecondsFromTime()
{
$calc = new class {
use CalculatesTime;
/**
* @param string $time
* @return int
*/
public function calc(string $time): int
{
return $this->secondsFromTime($time);
}
};
$this->assertEquals(0, $calc->calc('0:00'));
$this->assertEquals(60, $calc->calc('0:01'));
$this->assertEquals(60 * 60, $calc->calc('01:00'));
$this->assertEquals(60 * 60 * 10 + 60 * 11, $calc->calc('10:11'));
}
}

@ -0,0 +1,49 @@
<?php
namespace Engelsystem\Test\Unit\Helpers\Schedule;
use Engelsystem\Helpers\Schedule\Conference;
use Engelsystem\Test\Unit\TestCase;
class ConferenceTest extends TestCase
{
/**
* @covers \Engelsystem\Helpers\Schedule\Conference::__construct
* @covers \Engelsystem\Helpers\Schedule\Conference::getTitle
* @covers \Engelsystem\Helpers\Schedule\Conference::getAcronym
* @covers \Engelsystem\Helpers\Schedule\Conference::getStart
* @covers \Engelsystem\Helpers\Schedule\Conference::getEnd
* @covers \Engelsystem\Helpers\Schedule\Conference::getDays
* @covers \Engelsystem\Helpers\Schedule\Conference::getTimeslotDuration
* @covers \Engelsystem\Helpers\Schedule\Conference::getTimeslotDurationSeconds
* @covers \Engelsystem\Helpers\Schedule\Conference::getBaseUrl
*/
public function testCreate()
{
$conference = new Conference('Doing stuff', 'DS');
$this->assertEquals('Doing stuff', $conference->getTitle());
$this->assertEquals('DS', $conference->getAcronym());
$this->assertNull($conference->getStart());
$this->assertNull($conference->getEnd());
$this->assertNull($conference->getDays());
$this->assertNull($conference->getTimeslotDuration());
$this->assertNull($conference->getTimeslotDurationSeconds());
$this->assertNull($conference->getBaseUrl());
$conference = new Conference(
'Doing stuff',
'DS',
'2042-01-01',
'2042-01-10',
10,
'00:10',
'https://foo.bar/schedule'
);
$this->assertEquals('2042-01-01', $conference->getStart());
$this->assertEquals('2042-01-10', $conference->getEnd());
$this->assertEquals(10, $conference->getDays());
$this->assertEquals('00:10', $conference->getTimeslotDuration());
$this->assertEquals(60 * 10, $conference->getTimeslotDurationSeconds());
$this->assertEquals('https://foo.bar/schedule', $conference->getBaseUrl());
}
}

@ -0,0 +1,47 @@
<?php
namespace Engelsystem\Test\Unit\Helpers\Schedule;
use Carbon\Carbon;
use Engelsystem\Helpers\Schedule\Day;
use Engelsystem\Helpers\Schedule\Room;
use Engelsystem\Test\Unit\TestCase;
class DayTest extends TestCase
{
/**
* @covers \Engelsystem\Helpers\Schedule\Day::__construct
* @covers \Engelsystem\Helpers\Schedule\Day::getDate
* @covers \Engelsystem\Helpers\Schedule\Day::getStart
* @covers \Engelsystem\Helpers\Schedule\Day::getEnd
* @covers \Engelsystem\Helpers\Schedule\Day::getIndex
* @covers \Engelsystem\Helpers\Schedule\Day::getRoom
*/
public function testCreate()
{
$day = new Day(
'2000-01-01',
new Carbon('2000-01-01T03:00:00+01:00'),
new Carbon('2000-01-02T05:59:00+00:00'),
1
);
$this->assertEquals('2000-01-01', $day->getDate());
$this->assertEquals('2000-01-01T03:00:00+01:00', $day->getStart()->format(Carbon::RFC3339));
$this->assertEquals('2000-01-02T05:59:00+00:00', $day->getEnd()->format(Carbon::RFC3339));
$this->assertEquals(1, $day->getIndex());
$this->assertEquals([], $day->getRoom());
$rooms = [
new Room('Foo'),
new Room('Bar'),
];
$day = new Day(
'2001-01-01',
new Carbon('2001-01-01T03:00:00+01:00'),
new Carbon('2001-01-02T05:59:00+00:00'),
1,
$rooms
);
$this->assertEquals($rooms, $day->getRoom());
}
}

@ -0,0 +1,149 @@
<?php
namespace Engelsystem\Test\Unit\Helpers\Schedule;
use Carbon\Carbon;
use Engelsystem\Helpers\Schedule\Event;
use Engelsystem\Helpers\Schedule\Room;
use Engelsystem\Test\Unit\TestCase;
class EventTest extends TestCase
{
/**
* @covers \Engelsystem\Helpers\Schedule\Event::__construct
* @covers \Engelsystem\Helpers\Schedule\Event::getGuid
* @covers \Engelsystem\Helpers\Schedule\Event::getId
* @covers \Engelsystem\Helpers\Schedule\Event::getRoom
* @covers \Engelsystem\Helpers\Schedule\Event::getTitle
* @covers \Engelsystem\Helpers\Schedule\Event::getSubtitle
* @covers \Engelsystem\Helpers\Schedule\Event::getType
* @covers \Engelsystem\Helpers\Schedule\Event::getDate
* @covers \Engelsystem\Helpers\Schedule\Event::getStart
* @covers \Engelsystem\Helpers\Schedule\Event::getDuration
* @covers \Engelsystem\Helpers\Schedule\Event::getDurationSeconds
* @covers \Engelsystem\Helpers\Schedule\Event::getAbstract
* @covers \Engelsystem\Helpers\Schedule\Event::getSlug
* @covers \Engelsystem\Helpers\Schedule\Event::getTrack
* @covers \Engelsystem\Helpers\Schedule\Event::getLogo
* @covers \Engelsystem\Helpers\Schedule\Event::getPersons
* @covers \Engelsystem\Helpers\Schedule\Event::getLanguage
* @covers \Engelsystem\Helpers\Schedule\Event::getDescription
* @covers \Engelsystem\Helpers\Schedule\Event::getRecording
* @covers \Engelsystem\Helpers\Schedule\Event::getLinks
* @covers \Engelsystem\Helpers\Schedule\Event::getAttachments
* @covers \Engelsystem\Helpers\Schedule\Event::getUrl
* @covers \Engelsystem\Helpers\Schedule\Event::getVideoDownloadUrl
* @covers \Engelsystem\Helpers\Schedule\Event::getEndDate
*/
public function testCreate()
{
$room = new Room('Foo');
$date = new Carbon('2020-12-28T19:30:00+00:00');
$event = new Event(
'0-1-2-3',
1,
$room,
'Some stuff',
'sub stuff',
'Talk',
$date,
'19:30:00',
'00:50',
'Doing stuff is hard, plz try again',
'1-some-stuff',
'Security'
);
$this->assertEquals('0-1-2-3', $event->getGuid());
$this->assertEquals(1, $event->getId());
$this->assertEquals($room, $event->getRoom());
$this->assertEquals('Some stuff', $event->getTitle());
$this->assertEquals('sub stuff', $event->getSubtitle());
$this->assertEquals('Talk', $event->getType());
$this->assertEquals($date, $event->getDate());
$this->assertEquals('19:30:00', $event->getStart());
$this->assertEquals('00:50', $event->getDuration());
$this->assertEquals('Doing stuff is hard, plz try again', $event->getAbstract());
$this->assertEquals('1-some-stuff', $event->getSlug());
$this->assertEquals('Security', $event->getTrack());
$this->assertNull($event->getLogo());
$this->assertEquals([], $event->getPersons());
$this->assertNull($event->getLanguage());
$this->assertNull($event->getDescription());
$this->assertEquals('', $event->getRecording());
$this->assertEquals([], $event->getLinks());
$this->assertEquals([], $event->getAttachments());
$this->assertNull($event->getUrl());
$this->assertNull($event->getVideoDownloadUrl());
$this->assertEquals('2020-12-28T20:20:00+00:00', $event->getEndDate()->format(Carbon::RFC3339));
}
/**
* @covers \Engelsystem\Helpers\Schedule\Event::__construct
* @covers \Engelsystem\Helpers\Schedule\Event::getGuid
* @covers \Engelsystem\Helpers\Schedule\Event::getId
* @covers \Engelsystem\Helpers\Schedule\Event::getRoom
* @covers \Engelsystem\Helpers\Schedule\Event::getTitle
* @covers \Engelsystem\Helpers\Schedule\Event::setTitle
* @covers \Engelsystem\Helpers\Schedule\Event::getSubtitle
* @covers \Engelsystem\Helpers\Schedule\Event::getType
* @covers \Engelsystem\Helpers\Schedule\Event::getDate
* @covers \Engelsystem\Helpers\Schedule\Event::getStart
* @covers \Engelsystem\Helpers\Schedule\Event::getDuration
* @covers \Engelsystem\Helpers\Schedule\Event::getDurationSeconds
* @covers \Engelsystem\Helpers\Schedule\Event::getAbstract
* @covers \Engelsystem\Helpers\Schedule\Event::getSlug
* @covers \Engelsystem\Helpers\Schedule\Event::getTrack
* @covers \Engelsystem\Helpers\Schedule\Event::getLogo
* @covers \Engelsystem\Helpers\Schedule\Event::getPersons
* @covers \Engelsystem\Helpers\Schedule\Event::getLanguage
* @covers \Engelsystem\Helpers\Schedule\Event::getDescription
* @covers \Engelsystem\Helpers\Schedule\Event::getRecording
* @covers \Engelsystem\Helpers\Schedule\Event::getLinks
* @covers \Engelsystem\Helpers\Schedule\Event::getAttachments
* @covers \Engelsystem\Helpers\Schedule\Event::getUrl
* @covers \Engelsystem\Helpers\Schedule\Event::getVideoDownloadUrl
*/
public function testCreateNotDefault()
{
$persons = [1337 => 'Some Person'];
$links = ['https://foo.bar' => 'Foo Bar'];
$attachments = ['/files/foo.pdf' => 'Suspicious PDF'];
$event = new Event(
'3-2-1-0',
2,
new Room('Bar'),
'Lorem',
'Ipsum',
'Workshop',
new Carbon('2021-01-01T00:00:00+00:00'),
'00:00:00',
'00:30',
'Lorem ipsum dolor sit amet',
'2-lorem',
'DevOps',
'/foo/bar.png',
$persons,
'de',
'Foo bar is awesome! & That\'s why...',
'CC BY SA',
$links,
$attachments,
'https://foo.bar/2-lorem',
'https://videos.orem.ipsum/2-lorem.mp4'
);
$this->assertEquals('/foo/bar.png', $event->getLogo());
$this->assertEquals($persons, $event->getPersons());
$this->assertEquals('de', $event->getLanguage());
$this->assertEquals('Foo bar is awesome! & That\'s why...', $event->getDescription());
$this->assertEquals('CC BY SA', $event->getRecording());
$this->assertEquals($links, $event->getLinks());
$this->assertEquals($attachments, $event->getAttachments());
$this->assertEquals('https://foo.bar/2-lorem', $event->getUrl());
$this->assertEquals('https://videos.orem.ipsum/2-lorem.mp4', $event->getVideoDownloadUrl());
$event->setTitle('Event title');
$this->assertEquals('Event title', $event->getTitle());
}
}

@ -0,0 +1,31 @@
<?php
namespace Engelsystem\Test\Unit\Helpers\Schedule;
use Engelsystem\Helpers\Schedule\Event;
use Engelsystem\Helpers\Schedule\Room;
use Engelsystem\Test\Unit\TestCase;
class RoomTest extends TestCase
{
/**
* @covers \Engelsystem\Helpers\Schedule\Room::__construct
* @covers \Engelsystem\Helpers\Schedule\Room::getName
* @covers \Engelsystem\Helpers\Schedule\Room::getEvent
* @covers \Engelsystem\Helpers\Schedule\Room::setEvent
*/
public function testCreate()
{
$room = new Room('Test');
$this->assertEquals('Test', $room->getName());
$this->assertEquals([], $room->getEvent());
$events = [$this->createMock(Event::class), $this->createMock(Event::class)];
$events2 = [$this->createMock(Event::class)];
$room = new Room('Test2', $events);
$this->assertEquals($events, $room->getEvent());
$room->setEvent($events2);
$this->assertEquals($events2, $room->getEvent());
}
}

@ -0,0 +1,112 @@
<?php
namespace Engelsystem\Test\Unit\Helpers\Schedule;
use Carbon\Carbon;
use Engelsystem\Helpers\Schedule\Conference;
use Engelsystem\Helpers\Schedule\Day;
use Engelsystem\Helpers\Schedule\Room;
use Engelsystem\Helpers\Schedule\Schedule;
use Engelsystem\Test\Unit\HasDatabase;
use Engelsystem\Test\Unit\TestCase;
class ScheduleTest extends TestCase
{
use HasDatabase;
/**
* @covers \Engelsystem\Helpers\Schedule\Schedule::__construct
* @covers \Engelsystem\Helpers\Schedule\Schedule::getVersion
* @covers \Engelsystem\Helpers\Schedule\Schedule::getConference
* @covers \Engelsystem\Helpers\Schedule\Schedule::getDay
*/
public function testCreate()
{
$conference = new Conference('Foo Bar', 'FooB');
$days = [$this->createMock(Day::class)];
$schedule = new Schedule('Foo\'ing stuff 1.0', $conference, $days);
$this->assertEquals('Foo\'ing stuff 1.0', $schedule->getVersion());
$this->assertEquals($conference, $schedule->getConference());
$this->assertEquals($days, $schedule->getDay());
}
/**
* @covers \Engelsystem\Helpers\Schedule\Schedule::getRooms
*/
public function testGetRooms()
{
$conference = new Conference('Test', 'T');
$room1 = new Room('Test 1');
$room2 = new Room('Test 2');
$room3 = new Room('Test 3');
$days = [
new Day(
'2042-01-01',
new Carbon('2042-01-01T00:00:00+00:00'),
new Carbon('2042-01-01T23:59:00+00:00'),
1,
[$room1, $room2]
),
new Day(
'2042-01-02',
new Carbon('2042-02-01T00:00:00+00:00'),
new Carbon('2042-02-01T23:59:00+00:00'),
2,
[new Room('Test 2'), $room3]
),
];
$schedule = new Schedule('Lorem 1.3.3.7', $conference, $days);
$this->assertEquals(['Test 1' => $room1, 'Test 2' => $room2, 'Test 3' => $room3], $schedule->getRooms());
$schedule = new Schedule('Lorem 1.3.3.0', $conference, []);
$this->assertEquals([], $schedule->getRooms());
}
/**
* @covers \Engelsystem\Helpers\Schedule\Schedule::getStartDateTime
* @covers \Engelsystem\Helpers\Schedule\Schedule::getEndDateTime
*/
public function testGetDateTimes()
{
$conference = new Conference('Some Conference', 'SC');
$days = [
new Day(
'2042-01-02',
new Carbon('2042-01-02T00:00:00+00:00'),
new Carbon('2042-01-02T23:59:00+00:00'),
2
),
new Day(
'2042-01-01',
new Carbon('2042-01-01T00:00:00+00:00'),
new Carbon('2042-01-01T23:59:00+00:00'),
1
),
new Day(
'2042-01-04',
new Carbon('2042-01-04T00:00:00+00:00'),
new Carbon('2042-01-04T23:59:00+00:00'),
3
),
];
$schedule = new Schedule('Ipsum tester', $conference, $days);
$this->assertEquals('2042-01-01T00:00:00+00:00', $schedule->getStartDateTime()->format(Carbon::RFC3339));
$this->assertEquals('2042-01-04T23:59:00+00:00', $schedule->getEndDateTime()->format(Carbon::RFC3339));
$schedule = new Schedule('Ipsum old', $conference, []);
$this->assertNull($schedule->getStartDateTime());
$this->assertNull($schedule->getEndDateTime());
}
/**
* Prepare test
*/
protected function setUp(): void
{
parent::setUp();
$this->initDatabase();
}
}

@ -0,0 +1,54 @@
<?php
namespace Engelsystem\Test\Unit\Helpers\Schedule;
use Engelsystem\Helpers\Schedule\Day;
use Engelsystem\Helpers\Schedule\Event;
use Engelsystem\Helpers\Schedule\Room;
use Engelsystem\Helpers\Schedule\XmlParser;
use Engelsystem\Test\Unit\TestCase;
use Illuminate\Support\Arr;
class XmlParserTest extends TestCase
{
/**
* @covers \Engelsystem\Helpers\Schedule\XmlParser::load
* @covers \Engelsystem\Helpers\Schedule\XmlParser::parseXml
* @covers \Engelsystem\Helpers\Schedule\XmlParser::parseEvents
* @covers \Engelsystem\Helpers\Schedule\XmlParser::getFirstXpathContent
* @covers \Engelsystem\Helpers\Schedule\XmlParser::getListFromSequence
* @covers \Engelsystem\Helpers\Schedule\XmlParser::getSchedule
*/
public function testLoad()
{
libxml_use_internal_errors(true);
$parser = new XmlParser();
$this->assertFalse($parser->load('foo'));
$this->assertTrue($parser->load(file_get_contents(__DIR__ . '/Assets/schedule.xml')));
$schedule = $parser->getSchedule();
$this->assertEquals('Some version string', $schedule->getVersion());
$this->assertEquals('Test Event', $schedule->getConference()->getTitle());
/** @var Room $room */
$room = Arr::first($schedule->getRooms());
$this->assertEquals('Rooming', $room->getName());
/** @var Day $day */
$day = Arr::first($schedule->getDay());
$this->assertEquals('2042-01-01', $day->getDate());
$this->assertEquals(1, $day->getIndex());
/** @var Room $room */
$room = Arr::first($day->getRoom());
/** @var Event $event */
$event = Arr::first($room->getEvent());
$this->assertEquals('Foo Bar Test', $event->getTitle());
$this->assertEquals('WTFPL', $event->getRecording());
$this->assertEquals('de', $event->getLanguage());
$this->assertEquals('12:30', $event->getStart());
$this->assertEquals([1234 => 'Some Person'], $event->getPersons());
}
}

@ -7,6 +7,7 @@ use Engelsystem\Config\Config;
use Engelsystem\Container\Container; use Engelsystem\Container\Container;
use Engelsystem\Helpers\Authenticator; use Engelsystem\Helpers\Authenticator;
use Engelsystem\Helpers\Translation\Translator; use Engelsystem\Helpers\Translation\Translator;
use Engelsystem\Http\Redirector;
use Engelsystem\Http\Request; use Engelsystem\Http\Request;
use Engelsystem\Http\Response; use Engelsystem\Http\Response;
use Engelsystem\Http\UrlGeneratorInterface; use Engelsystem\Http\UrlGeneratorInterface;
@ -98,6 +99,29 @@ class HelpersTest extends TestCase
$this->assertEquals(['user' => 'FooBar'], config('mail')); $this->assertEquals(['user' => 'FooBar'], config('mail'));
} }
/**
* @covers \back
*/
public function testBack()
{
$response = new Response();
/** @var Redirector|MockObject $redirect */
$redirect = $this->createMock(Redirector::class);
$redirect->expects($this->exactly(2))
->method('back')
->withConsecutive([302, []], [303, ['test' => 'ing']])
->willReturn($response);
$app = new Application();
$app->instance('redirect', $redirect);
$return = back();
$this->assertEquals($response, $return);
$return = back(303, ['test' => 'ing']);
$this->assertEquals($response, $return);
}
/** /**
* @covers \config_path * @covers \config_path
*/ */
@ -117,6 +141,29 @@ class HelpersTest extends TestCase
$this->assertEquals('/foo/conf/bar.php', config_path('bar.php')); $this->assertEquals('/foo/conf/bar.php', config_path('bar.php'));
} }
/**
* @covers \redirect
*/
public function testRedirect()
{
$response = new Response();
/** @var Redirector|MockObject $redirect */
$redirect = $this->createMock(Redirector::class);
$redirect->expects($this->exactly(2))
->method('to')
->withConsecutive(['/lorem', 302, []], ['/ipsum', 303, ['test' => 'er']])
->willReturn($response);
$app = new Application();
$app->instance('redirect', $redirect);
$return = redirect('/lorem');
$this->assertEquals($response, $return);
$return = redirect('/ipsum', 303, ['test' => 'er']);
$this->assertEquals($response, $return);
}
/** /**
* @covers \request * @covers \request
*/ */

@ -0,0 +1,29 @@
<?php
namespace Engelsystem\Test\Unit\Http;
use Engelsystem\Application;
use Engelsystem\Http\HttpClientServiceProvider;
use Engelsystem\Test\Unit\ServiceProviderTest;
use GuzzleHttp\Client as GuzzleClient;
class HttpClientServiceProviderTest extends ServiceProviderTest
{
/**
* @covers \Engelsystem\Http\HttpClientServiceProvider::register
*/
public function testRegister()
{
$app = new Application();
$serviceProvider = new HttpClientServiceProvider($app);
$serviceProvider->register();
/** @var GuzzleClient $guzzle */
$guzzle = $app->make(GuzzleClient::class);
$config = $guzzle->getConfig();
$this->assertFalse($config['http_errors']);
$this->assertArrayHasKey('timeout', $config);
}
}

@ -0,0 +1,23 @@
<?php
namespace Engelsystem\Test\Unit\Http;
use Engelsystem\Application;
use Engelsystem\Http\RedirectServiceProvider;
use Engelsystem\Test\Unit\ServiceProviderTest;
class RedirectServiceProviderTest extends ServiceProviderTest
{
/**
* @covers \Engelsystem\Http\RedirectServiceProvider::register
*/
public function testRegister()
{
$app = new Application();
$serviceProvider = new RedirectServiceProvider($app);
$serviceProvider->register();
$this->assertTrue($app->has('redirect'));
}
}

@ -0,0 +1,53 @@
<?php
namespace Engelsystem\Test\Unit\Http;
use Engelsystem\Http\Redirector;
use Engelsystem\Http\Request;
use Engelsystem\Http\Response;
use PHPUnit\Framework\TestCase;
class RedirectorTest extends TestCase
{
/**
* @covers \Engelsystem\Http\Redirector::__construct
* @covers \Engelsystem\Http\Redirector::to
*/
public function testTo()
{
$request = new Request();
$response = new Response();
$redirector = new Redirector($request, $response);
$return = $redirector->to('/test');
$this->assertEquals(['/test'], $return->getHeader('location'));
$this->assertEquals(302, $return->getStatusCode());
$return = $redirector->to('/foo', 303, ['test' => 'data']);
$this->assertEquals(['/foo'], $return->getHeader('location'));
$this->assertEquals(303, $return->getStatusCode());
$this->assertEquals(['data'], $return->getHeader('test'));
}
/**
* @covers \Engelsystem\Http\Redirector::back
* @covers \Engelsystem\Http\Redirector::getPreviousUrl
*/
public function testBack()
{
$request = new Request();
$response = new Response();
$redirector = new Redirector($request, $response);
$return = $redirector->back();
$this->assertEquals(['/'], $return->getHeader('location'));
$this->assertEquals(302, $return->getStatusCode());
$request = $request->withHeader('referer', '/old-page');
$redirector = new Redirector($request, $response);
$return = $redirector->back(303, ['foo' => 'bar']);
$this->assertEquals(303, $return->getStatusCode());
$this->assertEquals(['/old-page'], $return->getHeader('location'));
$this->assertEquals(['bar'], $return->getHeader('foo'));
}
}

@ -10,6 +10,8 @@ use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse; use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
class ResponseTest extends TestCase class ResponseTest extends TestCase
{ {
@ -116,4 +118,59 @@ class ResponseTest extends TestCase
$newResponse->getHeaders() $newResponse->getHeaders()
); );
} }
/**
* @covers \Engelsystem\Http\Response::with
*/
public function testWith()
{
$session = new Session(new MockArraySessionStorage());
$response = new Response('', 200, [], null, $session);
$response->with('foo', 'bar');
$this->assertEquals('bar', $session->get('foo'));
$response->with('lorem', ['ipsum', 'dolor' => ['foo' => 'bar']]);
$this->assertEquals(['ipsum', 'dolor' => ['foo' => 'bar']], $session->get('lorem'));
$response->with('lorem', ['dolor' => ['test' => 'er']]);
$this->assertEquals(['ipsum', 'dolor' => ['foo' => 'bar', 'test' => 'er']], $session->get('lorem'));
}
/**
* @covers \Engelsystem\Http\Response::with
*/
public function testWithNoSession()
{
$this->expectException(InvalidArgumentException::class);
$response = new Response();
$response->with('foo', 'bar');
}
/**
* @covers \Engelsystem\Http\Response::withInput
*/
public function testWithInput()
{
$session = new Session(new MockArraySessionStorage());
$response = new Response('', 200, [], null, $session);
$response->withInput(['some' => 'value']);
$this->assertEquals(['some' => 'value'], $session->get('form-data'));
$response->withInput(['lorem' => 'ipsum']);
$this->assertEquals(['lorem' => 'ipsum'], $session->get('form-data'));
}
/**
* @covers \Engelsystem\Http\Response::withInput
*/
public function testWithInputNoSession()
{
$this->expectException(InvalidArgumentException::class);
$response = new Response();
$response->withInput(['some' => 'value']);
}
} }

@ -6,6 +6,7 @@ use Engelsystem\Application;
use Engelsystem\Http\Exceptions\HttpException; use Engelsystem\Http\Exceptions\HttpException;
use Engelsystem\Http\Exceptions\ValidationException; use Engelsystem\Http\Exceptions\ValidationException;
use Engelsystem\Http\Psr7ServiceProvider; use Engelsystem\Http\Psr7ServiceProvider;
use Engelsystem\Http\RedirectServiceProvider;
use Engelsystem\Http\Request; use Engelsystem\Http\Request;
use Engelsystem\Http\Response; use Engelsystem\Http\Response;
use Engelsystem\Http\ResponseServiceProvider; use Engelsystem\Http\ResponseServiceProvider;
@ -18,6 +19,7 @@ use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Server\RequestHandlerInterface;
use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Twig\Loader\LoaderInterface as TwigLoader; use Twig\Loader\LoaderInterface as TwigLoader;
@ -155,7 +157,7 @@ class ErrorHandlerTest extends TestCase
/** /**
* @covers \Engelsystem\Middleware\ErrorHandler::process * @covers \Engelsystem\Middleware\ErrorHandler::process
* @covers \Engelsystem\Middleware\ErrorHandler::getPreviousUrl * @covers \Engelsystem\Middleware\ErrorHandler::redirectBack
*/ */
public function testProcessValidationException() public function testProcessValidationException()
{ {
@ -185,11 +187,13 @@ class ErrorHandlerTest extends TestCase
/** @var Application $app */ /** @var Application $app */
$app = app(); $app = app();
$app->instance(Session::class, $session);
$app->bind(SessionInterface::class, Session::class);
(new ResponseServiceProvider($app))->register(); (new ResponseServiceProvider($app))->register();
(new Psr7ServiceProvider($app))->register(); (new Psr7ServiceProvider($app))->register();
(new RedirectServiceProvider($app))->register();
$errorHandler = new ErrorHandler($twigLoader); $errorHandler = new ErrorHandler($twigLoader);
$return = $errorHandler->process($request, $handler); $return = $errorHandler->process($request, $handler);
$this->assertEquals(302, $return->getStatusCode()); $this->assertEquals(302, $return->getStatusCode());
@ -209,6 +213,7 @@ class ErrorHandlerTest extends TestCase
], $session->all()); ], $session->all());
$request = $request->withAddedHeader('referer', '/foo/batz'); $request = $request->withAddedHeader('referer', '/foo/batz');
$app->instance(Request::class, $request);
$return = $errorHandler->process($request, $handler); $return = $errorHandler->process($request, $handler);
$this->assertEquals('/foo/batz', $return->getHeaderLine('location')); $this->assertEquals('/foo/batz', $return->getHeaderLine('location'));

@ -0,0 +1,41 @@
<?php
namespace Engelsystem\Test\Unit\Models\Shifts;
use Engelsystem\Models\Shifts\Schedule;
use Engelsystem\Models\Shifts\ScheduleShift;
use Engelsystem\Test\Unit\HasDatabase;
use Engelsystem\Test\Unit\TestCase;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ScheduleShiftTest extends TestCase
{
use HasDatabase;
/**
* @covers \Engelsystem\Models\Shifts\ScheduleShift::schedule
*/
public function testScheduleShifts()
{
$schedule = new Schedule(['url' => 'https://lorem.ipsum/schedule.xml']);
$schedule->save();
$scheduleShift = new ScheduleShift(['shift_id' => 1, 'guid' => 'a']);
$scheduleShift->schedule()->associate($schedule);
$scheduleShift->save();
/** @var ScheduleShift $scheduleShift */
$scheduleShift = (new ScheduleShift())->find(1);
$this->assertInstanceOf(BelongsTo::class, $scheduleShift->schedule());
$this->assertEquals($schedule->id, $scheduleShift->schedule->id);
}
/**
* Prepare test
*/
protected function setUp(): void
{
parent::setUp();
$this->initDatabase();
}
}

@ -0,0 +1,37 @@
<?php
namespace Engelsystem\Test\Unit\Models\Shifts;
use Engelsystem\Models\Shifts\Schedule;
use Engelsystem\Models\Shifts\ScheduleShift;
use Engelsystem\Test\Unit\HasDatabase;
use Engelsystem\Test\Unit\TestCase;
class ScheduleTest extends TestCase
{
use HasDatabase;
/**
* @covers \Engelsystem\Models\Shifts\Schedule::scheduleShifts
*/
public function testScheduleShifts()
{
$schedule = new Schedule(['url' => 'https://foo.bar/schedule.xml']);
$schedule->save();
(new ScheduleShift(['shift_id' => 1, 'schedule_id' => $schedule->id, 'guid' => 'a']))->save();
(new ScheduleShift(['shift_id' => 2, 'schedule_id' => $schedule->id, 'guid' => 'b']))->save();
(new ScheduleShift(['shift_id' => 3, 'schedule_id' => $schedule->id, 'guid' => 'c']))->save();
$this->assertCount(3, $schedule->scheduleShifts);
}
/**
* Prepare test
*/
protected function setUp(): void
{
parent::setUp();
$this->initDatabase();
}
}
Loading…
Cancel
Save