text($event_welcome_message); return page_with_title(_('Registration successful'), [ msg(), div('row', [ div('col-md-4', [ $event_welcome_message ]), div('col-md-4', [ '

' . _('Login') . '

', form([ form_text('nick', _('Nick'), ''), form_password('password', _('Password')), form_submit('submit', _('Login')), buttons([ button(page_link_to('user_password_recovery'), _('I forgot my password')) ]), info(_('Please note: You have to activate cookies!'), true) ], page_link_to('login')) ]), div('col-md-4', [ '

' . _('What can I do?') . '

', '

' . _('Please read about the jobs you can do to help us.') . '

', buttons([ button(page_link_to('angeltypes', ['action' => 'about']), _('Teams/Job description') . ' »') ]) ]) ]) ]); } /** * Gui for deleting user with password field. * * @param array $user * @return string */ function User_delete_view($user) { return page_with_title(sprintf(_('Delete %s'), User_Nick_render($user)), [ msg(), buttons([ button(user_edit_link($user), glyph('chevron-left') . _('back')) ]), error( _('Do you really want to delete the user including all his shifts and every other piece of his data?'), true ), form([ form_password('password', _('Your password')), form_submit('submit', _('Delete')) ]) ]); } /** * View for editing the number of given vouchers * * @param array $user * @return string */ function User_edit_vouchers_view($user) { return page_with_title(sprintf(_('%s\'s vouchers'), User_Nick_render($user)), [ msg(), buttons([ button(user_link($user), glyph('chevron-left') . _('back')) ]), info(sprintf( _('Angel should receive at least %d vouchers.'), User_get_eligable_voucher_count($user) ), true), form( [ form_spinner('vouchers', _('Number of vouchers given out'), $user['got_voucher']), form_submit('submit', _('Save')) ], page_link_to('users', ['action' => 'edit_vouchers', 'user_id' => $user['UID']]) ) ]); } /** * @param array[] $users * @param string $order_by * @param int $arrived_count * @param int $active_count * @param int $force_active_count * @param int $freeloads_count * @param int $tshirts_count * @param int $voucher_count * @return string */ function Users_view( $users, $order_by, $arrived_count, $active_count, $force_active_count, $freeloads_count, $tshirts_count, $voucher_count ) { foreach ($users as &$user) { $user['Nick'] = User_Nick_render($user); $user['Gekommen'] = glyph_bool($user['Gekommen']); $user['Aktiv'] = glyph_bool($user['Aktiv']); $user['force_active'] = glyph_bool($user['force_active']); $user['Tshirt'] = glyph_bool($user['Tshirt']); $user['lastLogIn'] = date(_('m/d/Y h:i a'), $user['lastLogIn']); $user['actions'] = table_buttons([ button_glyph(page_link_to('admin_user', ['id' => $user['UID']]), 'edit', 'btn-xs') ]); } $users[] = [ 'Nick' => '' . _('Sum') . '', 'Gekommen' => $arrived_count, 'got_voucher' => $voucher_count, 'Aktiv' => $active_count, 'force_active' => $force_active_count, 'freeloads' => $freeloads_count, 'Tshirt' => $tshirts_count, 'actions' => '' . count($users) . '' ]; return page_with_title(_('All users'), [ msg(), buttons([ button(page_link_to('register'), glyph('plus') . _('New user')) ]), table([ 'Nick' => Users_table_header_link('Nick', _('Nick'), $order_by), 'Vorname' => Users_table_header_link('Vorname', _('Prename'), $order_by), 'Name' => Users_table_header_link('Name', _('Name'), $order_by), 'DECT' => Users_table_header_link('DECT', _('DECT'), $order_by), 'Gekommen' => Users_table_header_link('Gekommen', _('Arrived'), $order_by), 'got_voucher' => Users_table_header_link('got_voucher', _('Voucher'), $order_by), 'freeloads' => _('Freeloads'), 'Aktiv' => Users_table_header_link('Aktiv', _('Active'), $order_by), 'force_active' => Users_table_header_link('force_active', _('Forced'), $order_by), 'Tshirt' => Users_table_header_link('Tshirt', _('T-Shirt'), $order_by), 'Size' => Users_table_header_link('Size', _('Size'), $order_by), 'lastLogIn' => Users_table_header_link('lastLogIn', _('Last login'), $order_by), 'actions' => '' ], $users) ]); } /** * @param string $column * @param string $label * @param string $order_by * @return string */ function Users_table_header_link($column, $label, $order_by) { return '' . $label . ($order_by == $column ? ' ' : '') . ''; } /** * @param array $user * @return string|false */ function User_shift_state_render($user) { if (!$user['Gekommen']) { return ''; } $upcoming_shifts = ShiftEntries_upcoming_for_user($user); if (empty($upcoming_shifts)) { return '' . _('Free') . ''; } $nextShift = array_shift($upcoming_shifts); if ($nextShift['start'] > time()) { if ($nextShift['start'] - time() > 3600) { return '' . _('Next shift %c') . ''; } return '' . _('Next shift %c') . ''; } $halfway = ($nextShift['start'] + $nextShift['end']) / 2; if (time() < $halfway) { return '' . _('Shift started %c') . ''; } return '' . _('Shift ends %c') . ''; } /** * @param array $needed_angel_type * @return string */ function User_view_shiftentries($needed_angel_type) { $shift_info = '
' . $needed_angel_type['name'] . ': '; $shift_entries = []; foreach ($needed_angel_type['users'] as $user_shift) { $member = User_Nick_render($user_shift); if ($user_shift['freeloaded']) { $member = '' . $member . ''; } $shift_entries[] = $member; } $shift_info .= join(', ', $shift_entries); return $shift_info; } /** * Helper that renders a shift line for user view * * @param array $shift * @param array $user_source * @param bool $its_me * @return array */ function User_view_myshift($shift, $user_source, $its_me) { global $privileges; $shift_info = '' . $shift['name'] . ''; if ($shift['title']) { $shift_info .= '
' . $shift['title'] . ''; } foreach ($shift['needed_angeltypes'] as $needed_angel_type) { $shift_info .= User_view_shiftentries($needed_angel_type); } $myshift = [ 'date' => glyph('calendar') . date('Y-m-d', $shift['start']) . '
' . glyph('time') . date('H:i', $shift['start']) . ' - ' . date('H:i', $shift['end']), 'duration' => sprintf('%.2f', ($shift['end'] - $shift['start']) / 3600) . ' h', 'room' => Room_name_render($shift), 'shift_info' => $shift_info, 'comment' => '' ]; if ($its_me) { $myshift['comment'] = $shift['Comment']; } if ($shift['freeloaded']) { $myshift['duration'] = '

' . round(-($shift['end'] - $shift['start']) / 3600 * 2, 2) . ' h' . '

'; if (in_array('user_shifts_admin', $privileges)) { $myshift['comment'] .= '
' . '

' . _('Freeloaded') . ': ' . $shift['freeload_comment'] . '

'; } else { $myshift['comment'] .= '

' . _('Freeloaded') . '

'; } } $myshift['actions'] = [ button(shift_link($shift), glyph('eye-open') . _('view'), 'btn-xs') ]; if ($its_me || in_array('user_shifts_admin', $privileges)) { $myshift['actions'][] = button( page_link_to('user_myshifts', ['edit' => $shift['id'], 'id' => $user_source['UID']]), glyph('edit') . _('edit'), 'btn-xs' ); } if (Shift_signout_allowed($shift, ['id' => $shift['TID']], $user_source)) { $myshift['actions'][] = button( shift_entry_delete_link($shift), glyph('trash') . _('sign off'), 'btn-xs' ); } $myshift['actions'] = table_buttons($myshift['actions']); return $myshift; } /** * Helper that prepares the shift table for user view * * @param array[] $shifts * @param array $user_source * @param bool $its_me * @return array */ function User_view_myshifts($shifts, $user_source, $its_me, $tshirt_score, $tshirt_admin, $user_worklogs, $admin_user_worklog_privilege) { $myshifts_table = []; $timesum = 0; foreach ($shifts as $shift) { $myshifts_table[$shift['start']] = User_view_myshift($shift, $user_source, $its_me); if (!$shift['freeloaded']) { $timesum += ($shift['end'] - $shift['start']); } } if($its_me || $admin_user_worklog_privilege) { foreach($user_worklogs as $worklog) { $myshifts_table[$worklog['work_timestamp']] = User_view_worklog($worklog, $admin_user_worklog_privilege); $timesum += $worklog['work_hours'] * 3600; } } if (count($myshifts_table) > 0) { ksort($myshifts_table); $myshifts_table[] = [ 'date' => '' . _('Sum:') . '', 'duration' => '' . sprintf('%.2f', round($timesum / 3600, 2)) . ' h', 'room' => '', 'shift_info' => '', 'comment' => '', 'actions' => '' ]; if (config('enable_tshirt_size', false) && ($its_me || $tshirt_admin)) { $myshifts_table[] = [ 'date' => '' . _('Your t-shirt score') . '™:', 'duration' => '' . $tshirt_score . '', 'room' => '', 'shift_info' => '', 'comment' => '', 'actions' => '' ]; } } return $myshifts_table; } /** * Renders table entry for user work log * @param UserWorkLog $worklog * @param bool $admin_user_worklog_privilege */ function User_view_worklog($worklog, $admin_user_worklog_privilege) { $actions = ''; if($admin_user_worklog_privilege) { $actions = table_buttons([ button( user_worklog_edit_link($worklog), glyph('edit') . _('edit'), 'btn-xs' ), button( user_worklog_delete_link($worklog), glyph('trash') . _('delete'), 'btn-xs' ) ]); } return [ 'date' => glyph('calendar') . date('Y-m-d', $worklog['work_timestamp']), 'duration' => '' . sprintf('%.2f', $worklog['work_hours']) . '', 'room' => '', 'shift_info' => _('Work log entry'), 'comment' => $worklog['comment'] . '
' . sprintf( _('Added by %s at %s'), User_Nick_render(User($worklog['created_user_id'])), date('Y-m-d H:i', $worklog['created_timestamp']) ), 'actions' => $actions ]; } /** * Renders view for a single user * * @param array $user_source * @param bool $admin_user_privilege * @param bool $freeloader * @param array[] $user_angeltypes * @param array[] $user_groups * @param array[] $shifts * @param bool $its_me * @param int $tshirt_score * @param bool $tshirt_admin * @return string */ function User_view( $user_source, $admin_user_privilege, $freeloader, $user_angeltypes, $user_groups, $shifts, $its_me, $tshirt_score, $tshirt_admin, $admin_user_worklog_privilege, $user_worklogs ) { $user_name = htmlspecialchars($user_source['Vorname']) . ' ' . htmlspecialchars($user_source['Name']); $myshifts_table = ''; if($its_me || $admin_user_privilege) { $my_shifts = User_view_myshifts($shifts, $user_source, $its_me, $tshirt_score, $tshirt_admin, $user_worklogs, $admin_user_worklog_privilege); if(count($my_shifts) > 0) { $myshifts_table = table([ 'date' => _('Day & time'), 'duration' => _('Duration'), 'room' => _('Location'), 'shift_info' => _('Name & workmates'), 'comment' => _('Comment'), 'actions' => _('Action') ], $my_shifts); } elseif($user_source['force_active']) { $myshifts_table = success(_('You have done enough to get a t-shirt.'), true); } } return page_with_title( ' ' . htmlspecialchars($user_source['Nick']) . ' ' . $user_name . '', [ msg(), div('row space-top', [ div('col-md-12', [ buttons([ $admin_user_privilege ? button( page_link_to('admin_user', ['id' => $user_source['UID']]), glyph('edit') . _('edit') ) : '', $admin_user_privilege ? button( user_driver_license_edit_link($user_source), glyph('road') . _('driving license') ) : '', ($admin_user_privilege && !$user_source['Gekommen']) ? button( page_link_to('admin_arrive', ['arrived' => $user_source['UID']]), _('arrived') ) : '', $admin_user_privilege ? button( page_link_to( 'users', ['action' => 'edit_vouchers', 'user_id' => $user_source['UID']] ), glyph('cutlery') . _('Edit vouchers') ) : '', $admin_user_worklog_privilege ? button( user_worklog_add_link($user_source), glyph('list') . _('Add work log') ) : '', $its_me ? button( page_link_to('user_settings'), glyph('list-alt') . _('Settings') ) : '', $its_me ? button( page_link_to('ical', ['key' => $user_source['api_key']]), glyph('calendar') . _('iCal Export') ) : '', $its_me ? button( page_link_to('shifts_json_export', ['key' => $user_source['api_key']]), glyph('export') . _('JSON Export') ) : '', $its_me ? button( page_link_to('user_myshifts', ['reset' => 1]), glyph('repeat') . _('Reset API key') ) : '' ]) ]) ]), div('row', [ div('col-md-3', [ heading(glyph('phone') . $user_source['DECT'], 1) ]), User_view_state($admin_user_privilege, $freeloader, $user_source), User_angeltypes_render($user_angeltypes), User_groups_render($user_groups) ]), ($its_me || $admin_user_privilege) ? '

' . _('Shifts') . '

' : '', $myshifts_table, $its_me ? info( glyph('info-sign') . _('Your night shifts between 2 and 8 am count twice.'), true ) : '', $its_me && count($shifts) == 0 ? error(sprintf( _('Go to the shifts table to sign yourself up for some shifts.'), page_link_to('user_shifts') ), true) : '', ical_hint() ] ); } /** * Render the state section of user view * * @param bool $admin_user_privilege * @param bool $freeloader * @param array $user_source * @return string */ function User_view_state($admin_user_privilege, $freeloader, $user_source) { if ($admin_user_privilege) { $state = User_view_state_admin($freeloader, $user_source); } else { $state = User_view_state_user($user_source); } return div('col-md-3', [ heading(_('User state'), 4), join('
', $state) ]); } /** * Render the state section of user view for users. * * @param array $user_source * @return array */ function User_view_state_user($user_source) { $state = [ User_shift_state_render($user_source) ]; if ($user_source['Gekommen']) { $state[] = '' . glyph('home') . _('Arrived') . ''; } else { $state[] = '' . _('Not arrived') . ''; } return $state; } /** * Render the state section of user view for admins. * * @param bool $freeloader * @param array $user_source * @return array */ function User_view_state_admin($freeloader, $user_source) { $state = []; if ($freeloader) { $state[] = '' . glyph('exclamation-sign') . _('Freeloader') . ''; } $state[] = User_shift_state_render($user_source); if ($user_source['Gekommen']) { $state[] = '' . glyph('home') . sprintf(_('Arrived at %s'), date('Y-m-d', $user_source['arrival_date'])) . ''; if ($user_source['force_active']) { $state[] = '' . _('Active (forced)') . ''; } elseif ($user_source['Aktiv']) { $state[] = '' . _('Active') . ''; } if ($user_source['Tshirt']) { $state[] = '' . _('T-Shirt') . ''; } } else { $state[] = '' . sprintf(_('Not arrived (Planned: %s)'), date('Y-m-d', $user_source['planned_arrival_date'])) . ''; } if ($user_source['got_voucher'] > 0) { $state[] = '' . glyph('cutlery') . sprintf( ngettext('Got %s voucher', 'Got %s vouchers', $user_source['got_voucher']), $user_source['got_voucher'] ) . ''; } else { $state[] = '' . _('Got no vouchers') . ''; } return $state; } /** * View for password recovery step 1: E-Mail * * @return string */ function User_password_recovery_view() { return page_with_title(user_password_recovery_title(), [ msg(), _('We will send you an e-mail with a password recovery link. Please use the email address you used for registration.'), form([ form_text('email', _('E-Mail'), ''), form_submit('submit', _('Recover')) ]) ]); } /** * View for password recovery step 2: New password * * @return string */ function User_password_set_view() { return page_with_title(user_password_recovery_title(), [ msg(), _('Please enter a new password.'), form([ form_password('password', _('Password')), form_password('password2', _('Confirm password')), form_submit('submit', _('Save')) ]) ]); } /** * @param array[] $user_angeltypes * @return string */ function User_angeltypes_render($user_angeltypes) { $output = []; foreach ($user_angeltypes as $angeltype) { $class = 'text-success'; if ($angeltype['restricted'] == 1 && $angeltype['confirm_user_id'] == null) { $class = 'text-warning'; } $output[] = '' . ($angeltype['supporter'] ? glyph('education') : '') . $angeltype['name'] . ''; } return div('col-md-3', [ heading(_('Angeltypes'), 4), join('
', $output) ]); } /** * @param array[] $user_groups * @return string */ function User_groups_render($user_groups) { $output = []; foreach ($user_groups as $group) { $output[] = substr($group['Name'], 2); } return div('col-md-3', [ '

' . _('Rights') . '

', join('
', $output) ]); } /** * Render a user nickname. * * @param array $user_source * @return string */ function User_Nick_render($user_source) { return render_profile_link( ' ' . htmlspecialchars($user_source['Nick']) . '', $user_source['UID'], ($user_source['Gekommen'] ? '' : 'text-muted') ); } /** * @param string $text * @param int $user_id * @param string $class * @return string */ function render_profile_link($text, $user_id = null, $class = '') { $profile_link = page_link_to('user-settings'); if (!is_null($user_id)) { $profile_link = page_link_to('users', ['action' => 'view', 'user_id' => $user_id]); } return sprintf( '%s', $class, $profile_link, $text ); } /** * @return string|null */ function render_user_departure_date_hint() { global $user; if (!isset($user['planned_departure_date']) || $user['planned_departure_date'] == null) { $text = _('Please enter your planned date of departure on your settings page to give us a feeling for teardown capacities.'); return render_profile_link($text, null, 'alert-link'); } return null; } /** * @return string|null */ function render_user_freeloader_hint() { global $user; if (User_is_freeloader($user)) { return sprintf( _('You freeloaded at least %s shifts. Shift signup is locked. Please go to heavens desk to be unlocked again.'), config('max_freeloadable_shifts') ); } return null; } /** * Hinweis für Engel, die noch nicht angekommen sind * * @return string|null */ function render_user_arrived_hint() { global $user; if ($user['Gekommen'] == 0) { $event_config = EventConfig(); if (!is_null($event_config) && !is_null($event_config['buildup_start_date']) && time() > $event_config['buildup_start_date']) { return _('You are not marked as arrived. Please go to heaven\'s desk, get your angel badge and/or tell them that you arrived already.'); } } return null; } /** * @return string|null */ function render_user_tshirt_hint() { global $user; if (config('enable_tshirt_size') && $user['Size'] == '') { $text = _('You need to specify a tshirt size in your settings!'); return render_profile_link($text, null, 'alert-link'); } return null; } /** * @return string|null */ function render_user_dect_hint() { global $user; if ($user['Gekommen'] == 1 && $user['DECT'] == '') { $text = _('You need to specify a DECT phone number in your settings! If you don\'t have a DECT phone, just enter \'-\'.'); return render_profile_link($text, null, 'alert-link'); } return null; }