diff options
Diffstat (limited to 'includes/view')
-rw-r--r-- | includes/view/AngelTypes_view.php | 155 | ||||
-rw-r--r-- | includes/view/Rooms_view.php | 11 | ||||
-rw-r--r-- | includes/view/ShiftCalendarLane.php | 63 | ||||
-rw-r--r-- | includes/view/ShiftCalendarRenderer.php | 240 | ||||
-rw-r--r-- | includes/view/ShiftCalendarShiftRenderer.php | 181 | ||||
-rw-r--r-- | includes/view/ShiftsFilterRenderer.php | 69 | ||||
-rw-r--r-- | includes/view/Shifts_view.php | 6 | ||||
-rw-r--r-- | includes/view/UserAngelTypes_view.php | 8 | ||||
-rw-r--r-- | includes/view/UserDriverLicenses_view.php | 2 | ||||
-rw-r--r-- | includes/view/User_view.php | 88 |
10 files changed, 743 insertions, 80 deletions
diff --git a/includes/view/AngelTypes_view.php b/includes/view/AngelTypes_view.php index cdaa9f12..485ce384 100644 --- a/includes/view/AngelTypes_view.php +++ b/includes/view/AngelTypes_view.php @@ -25,12 +25,12 @@ function AngelType_render_membership($user_angeltype) { if ($user_angeltype['restricted']) { if ($user_angeltype['confirm_user_id'] == null) { return glyph('lock') . _("Unconfirmed"); - } elseif ($user_angeltype['coordinator']) { - return glyph_bool(true) . _("Coordinator"); + } elseif ($user_angeltype['supporter']) { + return glyph_bool(true) . _("supporter"); } return glyph_bool(true) . _("Member"); - } elseif ($user_angeltype['coordinator']) { - return glyph_bool(true) . _("Coordinator"); + } elseif ($user_angeltype['supporter']) { + return glyph_bool(true) . _("supporter"); } return glyph_bool(true) . _("Member"); } @@ -47,16 +47,16 @@ function AngelType_delete_view($angeltype) { ]); } -function AngelType_edit_view($name, $restricted, $description, $coordinator_mode, $requires_driver_license) { +function AngelType_edit_view($name, $restricted, $description, $supporter_mode, $requires_driver_license) { return page_with_title(sprintf(_("Edit %s"), $name), [ buttons([ button(page_link_to('angeltypes'), _("Angeltypes"), 'back') ]), msg(), form([ - $coordinator_mode ? form_info(_("Name"), $name) : form_text('name', _("Name"), $name), - $coordinator_mode ? form_info(_("Restricted"), $restricted ? _("Yes") : _("No")) : form_checkbox('restricted', _("Restricted"), $restricted), - $coordinator_mode ? form_info(_("Requires driver license"), $requires_driver_license ? _("Yes") : _("No")) : form_checkbox('requires_driver_license', _("Requires driver license"), $requires_driver_license), + $supporter_mode ? form_info(_("Name"), $name) : form_text('name', _("Name"), $name), + $supporter_mode ? form_info(_("Restricted"), $restricted ? _("Yes") : _("No")) : form_checkbox('restricted', _("Restricted"), $restricted), + $supporter_mode ? form_info(_("Requires driver license"), $requires_driver_license ? _("Yes") : _("No")) : form_checkbox('requires_driver_license', _("Requires driver license"), $requires_driver_license), form_info("", _("Restricted angel types can only be used by an angel if enabled by an archangel (double opt-in).")), form_textarea('description', _("Description"), $description), form_info("", _("Please use markdown for the description.")), @@ -65,7 +65,10 @@ function AngelType_edit_view($name, $restricted, $description, $coordinator_mode ]); } -function AngelType_view($angeltype, $members, $user_angeltype, $admin_user_angeltypes, $admin_angeltypes, $coordinator, $user_driver_license, $user) { +/** + * Renders the buttons for the angeltype view. + */ +function AngelType_view_buttons($angeltype, $user_angeltype, $admin_angeltypes, $supporter, $user_driver_license, $user) { $buttons = [ button(page_link_to('angeltypes'), _("Angeltypes"), 'back') ]; @@ -87,25 +90,23 @@ function AngelType_view($angeltype, $members, $user_angeltype, $admin_user_angel $buttons[] = button(page_link_to('user_angeltypes') . '&action=delete&user_angeltype_id=' . $user_angeltype['id'], _("leave"), 'cancel'); } - if ($admin_angeltypes || $coordinator) { + if ($admin_angeltypes || $supporter) { $buttons[] = button(page_link_to('angeltypes') . '&action=edit&angeltype_id=' . $angeltype['id'], _("edit"), 'edit'); } if ($admin_angeltypes) { $buttons[] = button(page_link_to('angeltypes') . '&action=delete&angeltype_id=' . $angeltype['id'], _("delete"), 'delete'); } - $page = [ - msg(), - buttons($buttons) - ]; - - $page[] = '<h3>' . _("Description") . '</h3>'; - $parsedown = new Parsedown(); - if ($angeltype['description'] != "") { - $page[] = '<div class="well">' . $parsedown->parse($angeltype['description']) . '</div>'; - } - - $coordinators = []; + return buttons($buttons); +} + +/** + * Renders and sorts the members of an angeltype into supporters, members and unconfirmed members. + * + * @return [supporters, members, unconfirmed members] + */ +function AngelType_view_members($angeltype, $members, $admin_user_angeltypes, $admin_angeltypes) { + $supporters = []; $members_confirmed = []; $members_unconfirmed = []; foreach ($members as $member) { @@ -127,19 +128,19 @@ function AngelType_view($angeltype, $members, $user_angeltype, $admin_user_angel button(page_link_to('user_angeltypes') . '&action=delete&user_angeltype_id=' . $member['user_angeltype_id'], _("deny"), 'btn-xs') ]); $members_unconfirmed[] = $member; - } elseif ($member['coordinator']) { + } elseif ($member['supporter']) { if ($admin_angeltypes) { $member['actions'] = table_buttons([ - button(page_link_to('user_angeltypes') . '&action=update&user_angeltype_id=' . $member['user_angeltype_id'] . '&coordinator=0', _("Remove coordinator rights"), 'btn-xs') + button(page_link_to('user_angeltypes') . '&action=update&user_angeltype_id=' . $member['user_angeltype_id'] . '&supporter=0', _("Remove supporter rights"), 'btn-xs') ]); } else { $member['actions'] = ''; } - $coordinators[] = $member; + $supporters[] = $member; } else { if ($admin_user_angeltypes) { $member['actions'] = table_buttons([ - $admin_angeltypes ? button(page_link_to('user_angeltypes') . '&action=update&user_angeltype_id=' . $member['user_angeltype_id'] . '&coordinator=1', _("Add coordinator rights"), 'btn-xs') : '', + $admin_angeltypes ? button(page_link_to('user_angeltypes') . '&action=update&user_angeltype_id=' . $member['user_angeltype_id'] . '&supporter=1', _("Add supporter rights"), 'btn-xs') : '', button(page_link_to('user_angeltypes') . '&action=delete&user_angeltype_id=' . $member['user_angeltype_id'], _("remove"), 'btn-xs') ]); } @@ -147,14 +148,19 @@ function AngelType_view($angeltype, $members, $user_angeltype, $admin_user_angel } } - $table_headers = [ - 'Nick' => _("Nick"), - 'DECT' => _("DECT"), - 'actions' => '' + return [ + $supporters, + $members_confirmed, + $members_unconfirmed ]; - - if ($angeltype['requires_driver_license'] && ($coordinator || $admin_angeltypes)) { - $table_headers = [ +} + +/** + * Creates the needed member table headers according to given rights and settings from the angeltype. + */ +function AngelType_view_table_headers($angeltype, $supporter, $admin_angeltypes) { + if ($angeltype['requires_driver_license'] && ($supporter || $admin_angeltypes)) { + return [ 'Nick' => _("Nick"), 'DECT' => _("DECT"), 'wants_to_drive' => _("Driver"), @@ -167,10 +173,34 @@ function AngelType_view($angeltype, $members, $user_angeltype, $admin_user_angel 'actions' => '' ]; } + return [ + 'Nick' => _("Nick"), + 'DECT' => _("DECT"), + 'actions' => '' + ]; +} + +/** + * Render an angeltype page containing the member lists. + */ +function AngelType_view($angeltype, $members, $user_angeltype, $admin_user_angeltypes, $admin_angeltypes, $supporter, $user_driver_license, $user) { + $page = [ + msg(), + AngelType_view_buttons($angeltype, $user_angeltype, $admin_angeltypes, $supporter, $user_driver_license, $user) + ]; + + $page[] = '<h3>' . _("Description") . '</h3>'; + $parsedown = new Parsedown(); + if ($angeltype['description'] != "") { + $page[] = '<div class="well">' . $parsedown->parse($angeltype['description']) . '</div>'; + } + + list($supporters, $members_confirmed, $members_unconfirmed) = AngelType_view_members($angeltype, $members, $admin_user_angeltypes, $admin_angeltypes); + $table_headers = AngelType_view_table_headers($angeltype, $supporter, $admin_angeltypes); - if (count($coordinators) > 0) { - $page[] = '<h3>' . _("Coordinators") . '</h3>'; - $page[] = table($table_headers, $coordinators); + if (count($supporters) > 0) { + $page[] = '<h3>' . _("supporters") . '</h3>'; + $page[] = table($table_headers, $supporters); } if (count($members_confirmed) > 0) { @@ -230,6 +260,38 @@ function AngelTypes_list_view($angeltypes, $admin_angeltypes) { ]); } +/** + * Renders the about info for an angeltype. + */ +function AngelTypes_about_view_angeltype($angeltype) { + $parsedown = new Parsedown(); + + $html = '<h2>' . $angeltype['name'] . '</h2>'; + + if (isset($angeltype['user_angeltype_id'])) { + $buttons = []; + if ($angeltype['user_angeltype_id'] != null) { + $buttons[] = button(page_link_to('user_angeltypes') . '&action=delete&user_angeltype_id=' . $angeltype['user_angeltype_id'], _("leave"), 'cancel'); + } else { + $buttons[] = button(page_link_to('user_angeltypes') . '&action=add&angeltype_id=' . $angeltype['id'], _("join"), 'add'); + } + $html .= buttons($buttons); + } + + if ($angeltype['restricted']) { + $html .= info(_("This angeltype is restricted by double-opt-in by a team supporter. Please show up at the according introduction meetings."), true); + } + if ($angeltype['description'] != "") { + $html .= '<div class="well">' . $parsedown->parse($angeltype['description']) . '</div>'; + } + $html .= '<hr />'; + + return $html; +} + +/** + * Renders a site that contains every angeltype and its description, basically as an overview of the needed help types. + */ function AngelTypes_about_view($angeltypes, $user_logged_in) { global $faq_url; @@ -243,27 +305,8 @@ function AngelTypes_about_view($angeltypes, $user_logged_in) { '<p>' . _("Here is the list of teams and their tasks. If you have questions, read the FAQ.") . '</p>', '<hr />' ]; - $parsedown = new Parsedown(); foreach ($angeltypes as $angeltype) { - $content[] = '<h2>' . $angeltype['name'] . '</h2>'; - - if (isset($angeltype['user_angeltype_id'])) { - $buttons = []; - if ($angeltype['user_angeltype_id'] != null) { - $buttons[] = button(page_link_to('user_angeltypes') . '&action=delete&user_angeltype_id=' . $angeltype['user_angeltype_id'], _("leave"), 'cancel'); - } else { - $buttons[] = button(page_link_to('user_angeltypes') . '&action=add&angeltype_id=' . $angeltype['id'], _("join"), 'add'); - } - $content[] = buttons($buttons); - } - - if ($angeltype['restricted']) { - $content[] = info(_("This angeltype is restricted by double-opt-in by a team coordinator. Please show up at the according introduction meetings."), true); - } - if ($angeltype['description'] != "") { - $content[] = '<div class="well">' . $parsedown->parse($angeltype['description']) . '</div>'; - } - $content[] = '<hr />'; + $content[] = AngelTypes_about_view_angeltype($angeltype); } return page_with_title(_("Teams/Job description"), $content); diff --git a/includes/view/Rooms_view.php b/includes/view/Rooms_view.php index c820e983..7afdc67b 100644 --- a/includes/view/Rooms_view.php +++ b/includes/view/Rooms_view.php @@ -1,8 +1,17 @@ <?php +use Engelsystem\ShiftsFilterRenderer; +use Engelsystem\ShiftCalendarRenderer; + +function Room_view($room, ShiftsFilterRenderer $shiftsFilterRenderer, ShiftCalendarRenderer $shiftCalendarRenderer) { + return page_with_title(glyph('map-marker') . $room['Name'], [ + $shiftsFilterRenderer->render(room_link($room)) , + $shiftCalendarRenderer->render() + ]); +} function Room_name_render($room) { global $privileges; - if (in_array('admin_rooms', $privileges)) { + if (in_array('view_rooms', $privileges)) { return '<a href="' . room_link($room) . '">' . glyph('map-marker') . $room['Name'] . '</a>'; } return glyph('map-marker') . $room['Name']; diff --git a/includes/view/ShiftCalendarLane.php b/includes/view/ShiftCalendarLane.php new file mode 100644 index 00000000..33fccec3 --- /dev/null +++ b/includes/view/ShiftCalendarLane.php @@ -0,0 +1,63 @@ +<?php + +namespace Engelsystem; + +/** + * Represents a single lane in a shifts calendar. + */ +class ShiftCalendarLane { + + private $firstBlockStartTime; + + private $blockCount; + + private $header; + + private $shifts = []; + + public function __construct($header, $firstBlockStartTime, $blockCount) { + $this->header = $header; + $this->firstBlockStartTime = $firstBlockStartTime; + $this->blockCount = $blockCount; + } + + /** + * Adds a shift to the lane, but only if it fits. + * Returns true on success. + * + * @param Shift $shift + * The shift to add + * @return boolean true on success + */ + public function addShift($shift) { + if ($this->shiftFits($shift)) { + $this->shifts[] = $shift; + return true; + } + return false; + } + + /** + * Returns true if given shift fits into this lane. + * + * @param Shift $shift + * The shift to fit into this lane + */ + public function shiftFits($newShift) { + foreach ($this->shifts as $laneShift) { + if (! ($newShift['start'] >= $laneShift['end'] || $newShift['end'] <= $laneShift['start'])) { + return false; + } + } + return true; + } + + public function getHeader() { + return $this->header; + } + + public function getShifts() { + return $this->shifts; + } +} +?>
\ No newline at end of file diff --git a/includes/view/ShiftCalendarRenderer.php b/includes/view/ShiftCalendarRenderer.php new file mode 100644 index 00000000..89c1be4c --- /dev/null +++ b/includes/view/ShiftCalendarRenderer.php @@ -0,0 +1,240 @@ +<?php + +namespace Engelsystem; + +class ShiftCalendarRenderer { + + /** + * 15m * 60s/m = 900s + */ + const SECONDS_PER_ROW = 900; + + /** + * Height of a block in pixel. + * Do not change - corresponds with theme/css + */ + const BLOCK_HEIGHT = 30; + + /** + * Distance between two shifts in pixels + */ + const MARGIN = 5; + + private $lanes; + + private $shiftsFilter; + + private $firstBlockStartTime = null; + + private $lastBlockEndTime = null; + + private $blocksPerSlot = null; + + public function __construct($shifts, ShiftsFilter $shiftsFilter) { + $this->shiftsFilter = $shiftsFilter; + $this->firstBlockStartTime = $this->calcFirstBlockStartTime($shifts); + $this->lastBlockEndTime = $this->calcLastBlockEndTime($shifts); + $this->lanes = $this->assignShiftsToLanes($shifts); + } + + /** + * Assigns the shifts to different lanes per room if they collide + * + * @param Shift[] $shifts + * The shifts to assign + * + * @return Returns an array that assigns a room_id to an array of ShiftCalendarLane containing the shifts + */ + private function assignShiftsToLanes($shifts) { + // array that assigns a room id to a list of lanes (per room) + $lanes = []; + + foreach ($shifts as $shift) { + $room_id = $shift['RID']; + if (! isset($lanes[$room_id])) { + // initialize room with one lane + $header = Room_name_render([ + 'RID' => $room_id, + 'Name' => $shift['room_name'] + ]); + $lanes[$room_id] = [ + new ShiftCalendarLane($header, $this->getFirstBlockStartTime(), $this->getBlocksPerSlot()) + ]; + } + // Try to add the shift to the existing lanes for this room + $shift_added = false; + foreach ($lanes[$room_id] as $lane) { + $shift_added = $lane->addShift($shift); + if ($shift_added == true) { + break; + } + } + // If all lanes for this room are busy, create a new lane and add shift to it + if ($shift_added == false) { + $newLane = new ShiftCalendarLane("", $this->getFirstBlockStartTime(), $this->getBlocksPerSlot()); + if (! $newLane->addShift($shift)) { + engelsystem_error("Unable to add shift to new lane."); + } + $lanes[$room_id][] = $newLane; + } + } + + return $lanes; + } + + public function getFirstBlockStartTime() { + return $this->firstBlockStartTime; + } + + public function getLastBlockEndTime() { + return $this->lastBlockEndTime; + } + + public function getBlocksPerSlot() { + if ($this->blocksPerSlot == null) { + $this->blocksPerSlot = $this->calcBlocksPerSlot(); + } + return $this->blocksPerSlot; + } + + /** + * Renders the whole calendar + * + * @return the generated html + */ + public function render() { + return div('shift-calendar', [ + $this->renderTimeLane(), + $this->renderShiftLanes() + ]) . $this->renderLegend(); + } + + /** + * Renders the lanes containing the shifts + */ + private function renderShiftLanes() { + $html = ""; + foreach ($this->lanes as $room_lanes) { + foreach ($room_lanes as $lane) { + $html .= $this->renderLane($lane); + } + } + return $html; + } + + /** + * Renders a single lane + * + * @param ShiftCalendarLane $lane + * The lane to render + */ + private function renderLane(ShiftCalendarLane $lane) { + global $user; + + $shift_renderer = new ShiftCalendarShiftRenderer(); + $html = ""; + $rendered_until = $this->getFirstBlockStartTime(); + foreach ($lane->getShifts() as $shift) { + while ($rendered_until + ShiftCalendarRenderer::SECONDS_PER_ROW <= $shift['start']) { + $html .= $this->renderTick($rendered_until); + $rendered_until += ShiftCalendarRenderer::SECONDS_PER_ROW; + } + + list($shift_height, $shift_html) = $shift_renderer->render($shift, $user); + $html .= $shift_html; + $rendered_until += $shift_height * ShiftCalendarRenderer::SECONDS_PER_ROW; + } + while ($rendered_until < $this->getLastBlockEndTime()) { + $html .= $this->renderTick($rendered_until); + $rendered_until += ShiftCalendarRenderer::SECONDS_PER_ROW; + } + + return div('lane', [ + div('header', $lane->getHeader()), + $html + ]); + } + + /** + * Renders a tick/block for given time + * + * @param int $time + * unix timestamp + * @param boolean $label + * Should time labels be generated? + * @return rendered tick html + */ + private function renderTick($time, $label = false) { + if ($time % (24 * 60 * 60) == 23 * 60 * 60) { + if (! $label) { + return div('tick day'); + } + return div('tick day', [ + date('Y-m-d<b\r />H:i', $time) + ]); + } elseif ($time % (60 * 60) == 0) { + if (! $label) { + return div('tick hour'); + } + return div('tick hour', [ + date('H:i', $time) + ]); + } + return div('tick'); + } + + /** + * Renders the left time lane including hour/day ticks + */ + private function renderTimeLane() { + $time_slot = [ + div('header', [ + _("Time") + ]) + ]; + for ($block = 0; $block < $this->getBlocksPerSlot(); $block ++) { + $thistime = $this->getFirstBlockStartTime() + ($block * ShiftCalendarRenderer::SECONDS_PER_ROW); + $time_slot[] = $this->renderTick($thistime, true); + } + return div('lane time', $time_slot); + } + + private function calcFirstBlockStartTime($shifts) { + $start_time = $this->shiftsFilter->getEndTime(); + foreach ($shifts as $shift) { + if ($shift['start'] < $start_time) { + $start_time = $shift['start']; + } + } + return ShiftCalendarRenderer::SECONDS_PER_ROW * floor(($start_time - 60 * 60) / ShiftCalendarRenderer::SECONDS_PER_ROW); + } + + private function calcLastBlockEndTime($shifts) { + $end_time = $this->shiftsFilter->getStartTime(); + foreach ($shifts as $shift) { + if ($shift['end'] > $end_time) { + $end_time = $shift['end']; + } + } + return ShiftCalendarRenderer::SECONDS_PER_ROW * ceil(($end_time + 60 * 60) / ShiftCalendarRenderer::SECONDS_PER_ROW); + } + + private function calcBlocksPerSlot() { + return ceil(($this->getLastBlockEndTime() - $this->getFirstBlockStartTime()) / ShiftCalendarRenderer::SECONDS_PER_ROW); + } + + /** + * Renders a legend explaining the shift coloring + */ + private function renderLegend() { + return div('legend', [ + label(_('Your shift'), 'primary'), + label(_('Help needed'), 'danger'), + label(_('Other angeltype needed / collides with my shifts'), 'warning'), + label(_('Shift is full'), 'success'), + label(_('Shift running/ended'), 'default') + ]); + } +} + +?>
\ No newline at end of file diff --git a/includes/view/ShiftCalendarShiftRenderer.php b/includes/view/ShiftCalendarShiftRenderer.php new file mode 100644 index 00000000..7aa79e07 --- /dev/null +++ b/includes/view/ShiftCalendarShiftRenderer.php @@ -0,0 +1,181 @@ +<?php + +namespace Engelsystem; + +/** + * Renders a single shift for the shift calendar + */ +class ShiftCalendarShiftRenderer { + + /** + * Renders a shift + * + * @param Shift $shift + * The shift to render + * @param User $user + * The user who is viewing the shift calendar + */ + public function render($shift, $user) { + $info_text = ""; + if ($shift['title'] != '') { + $info_text = glyph('info-sign') . $shift['title'] . '<br>'; + } + list($shift_signup_state, $shifts_row) = $this->renderShiftNeededAngeltypes($shift, $user); + $class = $this->classForSignupState($shift_signup_state); + + $blocks = ceil(($shift["end"] - $shift["start"]) / ShiftCalendarRenderer::SECONDS_PER_ROW); + $blocks = max(1, $blocks); + return [ + $blocks, + '<td class="shift" rowspan="' . $blocks . '">' . div('shift panel panel-' . $class . '" style="height: ' . ($blocks * ShiftCalendarRenderer::BLOCK_HEIGHT - ShiftCalendarRenderer::MARGIN) . 'px"', [ + $this->renderShiftHead($shift), + div('panel-body', [ + $info_text, + Room_name_render([ + 'RID' => $shift['RID'], + 'Name' => $shift['room_name'] + ]) + ]), + $shifts_row, + div('shift-spacer') + ]) . '</td>' + ]; + } + + private function classForSignupState(ShiftSignupState $shiftSignupState) { + switch ($shiftSignupState->getState()) { + case ShiftSignupState::ADMIN: + case ShiftSignupState::OCCUPIED: + return 'success'; + + case ShiftSignupState::SIGNED_UP: + return 'primary'; + + case ShiftSignupState::SHIFT_ENDED: + return 'default'; + + case ShiftSignupState::ANGELTYPE: + case ShiftSignupState::COLLIDES: + return 'warning'; + + case ShiftSignupState::FREE: + return 'danger'; + } + } + + private function renderShiftNeededAngeltypes($shift, $user) { + global $privileges; + + $html = ""; + $shift_signup_state = null; + $angeltypes = NeededAngelTypes_by_shift($shift['SID']); + foreach ($angeltypes as $angeltype) { + list($angeltype_signup_state, $angeltype_html) = $this->renderShiftNeededAngeltype($shift, $angeltype, $user); + if ($shift_signup_state == null) { + $shift_signup_state = $angeltype_signup_state; + } else { + $shift_signup_state->combineWith($angeltype_signup_state); + } + $html .= $angeltype_html; + } + if (in_array('user_shifts_admin', $privileges)) { + $html .= '<li class="list-group-item">' . button(page_link_to('user_shifts') . '&shift_id=' . $shift['SID'] . '&type_id=' . $angeltype['id'], _("Add more angels"), 'btn-xs') . '</li>'; + } + if ($html != '') { + return [ + $shift_signup_state, + '<ul class="list-group">' . $html . '</ul>' + ]; + } + return [ + $shift_signup_state, + "" + ]; + } + + /** + * Renders a list entry containing the needed angels for an angeltype + * + * @param Shift $shift + * The shift which is rendered + * @param Angeltype $angeltype + * The angeltype, containing informations about needed angeltypes and already signed up angels + * @param User $user + * The user who is viewing the shift calendar + */ + private function renderShiftNeededAngeltype($shift, $angeltype, $user) { + $entry_list = []; + foreach ($angeltype['shift_entries'] as $entry) { + $style = $entry['freeloaded'] ? " text-decoration: line-through;" : ''; + $entry_list[] = "<span style=\"$style\">" . User_Nick_render(User($entry['UID'])) . "</span>"; + } + $shift_signup_state = Shift_signup_allowed($user, $shift, $angeltype); + $inner_text = sprintf(ngettext("%d helper needed", "%d helpers needed", $shift_signup_state->getFreeEntries()), $shift_signup_state->getFreeEntries()); + switch ($shift_signup_state->getState()) { + case ShiftSignupState::ADMIN: + case ShiftSignupState::FREE: + // When admin or free display a link + button for sign up + $entry_list[] = '<a href="' . page_link_to('user_shifts') . '&shift_id=' . $shift['SID'] . '&type_id=' . $angeltype['id'] . '">' . $inner_text . '</a> ' . button(page_link_to('user_shifts') . '&shift_id=' . $shift['SID'] . '&type_id=' . $angeltype['id'], _('Sign up'), 'btn-xs btn-primary'); + break; + + case ShiftSignupState::SHIFT_ENDED: + // No link and add a text hint, when the shift ended + $entry_list[] = $inner_text . ' (' . _('ended') . ')'; + break; + + case ShiftSignupState::ANGELTYPE: + if ($angeltype['restricted'] == 1) { + // User has to be confirmed on the angeltype first + $entry_list[] = $inner_text . glyph('lock'); + } else { + // Add link to join the angeltype first + $entry_list[] = $inner_text . '<br />' . button(page_link_to('user_angeltypes') . '&action=add&angeltype_id=' . $angeltype['id'], sprintf(_('Become %s'), $angeltype['name']), 'btn-xs'); + } + break; + + case ShiftSignupState::COLLIDES: + case ShiftSignupState::SIGNED_UP: + // Shift collides or user is already signed up: No signup allowed + $entry_list[] = $inner_text; + break; + + case ShiftSignupState::OCCUPIED: + // Shift is full + break; + } + + $shifts_row = '<li class="list-group-item">'; + $shifts_row .= '<strong>' . AngelType_name_render($angeltype) . ':</strong> '; + $shifts_row .= join(", ", $entry_list); + $shifts_row .= '</li>'; + return [ + $shift_signup_state, + $shifts_row + ]; + } + + /** + * Renders the shift header + * + * @param Shift $shift + * The shift + */ + private function renderShiftHead($shift) { + global $privileges; + + $header_buttons = ""; + if (in_array('admin_shifts', $privileges)) { + $header_buttons = '<div class="pull-right">' . table_buttons([ + button(page_link_to('user_shifts') . '&edit_shift=' . $shift['SID'], glyph('edit'), 'btn-xs'), + button(page_link_to('user_shifts') . '&delete_shift=' . $shift['SID'], glyph('trash'), 'btn-xs') + ]) . '</div>'; + } + $shift_heading = date('H:i', $shift['start']) . ' ‐ ' . date('H:i', $shift['end']) . ' — ' . ShiftType($shift['shifttype_id'])['name']; + return div('panel-heading', [ + '<a href="' . shift_link($shift) . '">' . $shift_heading . '</a>', + $header_buttons + ]); + } +} + +?>
\ No newline at end of file diff --git a/includes/view/ShiftsFilterRenderer.php b/includes/view/ShiftsFilterRenderer.php new file mode 100644 index 00000000..301f31a2 --- /dev/null +++ b/includes/view/ShiftsFilterRenderer.php @@ -0,0 +1,69 @@ +<?php + +namespace Engelsystem; + +class ShiftsFilterRenderer { + + /** + * The shiftFilter to render. + * + * @var ShiftsFilter + */ + private $shiftsFilter; + + /** + * Should the filter display a day selection. + * + * @var boolean + */ + private $daySelectionEnabled = false; + + /** + * Days that can be selected. + * Format Y-m-d + * + * @var string[] + */ + private $days = []; + + public function __construct(ShiftsFilter $shiftsFilter) { + $this->shiftsFilter = $shiftsFilter; + } + + /** + * Renders the filter. + * + * @return Generated HTML + */ + public function render($link_base) { + $toolbar = []; + if ($this->daySelectionEnabled && ! empty($this->days)) { + $selected_day = date("Y-m-d", $this->shiftsFilter->getStartTime()); + $day_dropdown_items = []; + foreach ($this->days as $day) { + $day_dropdown_items[] = toolbar_item_link($link_base . '&shifts_filter_day=' . $day, '', $day); + } + $toolbar[] = toolbar_dropdown('', $selected_day, $day_dropdown_items, 'active'); + } + return div('form-group', [ + toolbar_pills($toolbar) + ]); + } + + /** + * Should the filter display a day selection. + */ + public function enableDaySelection($days) { + $this->daySelectionEnabled = true; + $this->days = $days; + } + + /** + * Should the filter display a day selection. + */ + public function isDaySelectionEnabled() { + return $this->daySelectionEnabled; + } +} + +?>
\ No newline at end of file diff --git a/includes/view/Shifts_view.php b/includes/view/Shifts_view.php index 95282eb5..8e52eb9d 100644 --- a/includes/view/Shifts_view.php +++ b/includes/view/Shifts_view.php @@ -16,12 +16,10 @@ function Shift_signup_button_render($shift, $angeltype, $user_angeltype = null, if ($user_angeltype == null) { $user_angeltype = UserAngelType_by_User_and_AngelType($user, $angeltype); - if ($user_angeltype === false) { - engelsystem_error('Unable to load user angeltype.'); - } } - if (Shift_signup_allowed($shift, $angeltype, $user_angeltype, $user_shifts)) { + $shift_signup_state = Shift_signup_allowed($user, $shift, $angeltype, $user_angeltype, $user_shifts); + if ($shift_signup_state->isSignupAllowed()) { return button(page_link_to('user_shifts') . '&shift_id=' . $shift['SID'] . '&type_id=' . $angeltype['id'], _('Sign up')); } elseif ($user_angeltype == null) { return button(page_link_to('angeltypes') . '&action=view&angeltype_id=' . $angeltype['id'], sprintf(_('Become %s'), $angeltype['name'])); diff --git a/includes/view/UserAngelTypes_view.php b/includes/view/UserAngelTypes_view.php index 802ec5aa..da4a2352 100644 --- a/includes/view/UserAngelTypes_view.php +++ b/includes/view/UserAngelTypes_view.php @@ -1,12 +1,12 @@ <?php -function UserAngelType_update_view($user_angeltype, $user, $angeltype, $coordinator) { - return page_with_title($coordinator ? _("Add coordinator rights") : _("Remove coordinator rights"), [ +function UserAngelType_update_view($user_angeltype, $user, $angeltype, $supporter) { + return page_with_title($supporter ? _("Add supporter rights") : _("Remove supporter rights"), [ msg(), - info(sprintf($coordinator ? _("Do you really want to add coordinator rights for %s to %s?") : _("Do you really want to remove coordinator rights for %s from %s?"), $angeltype['name'], User_Nick_render($user)), true), + info(sprintf($supporter ? _("Do you really want to add supporter rights for %s to %s?") : _("Do you really want to remove supporter rights for %s from %s?"), $angeltype['name'], User_Nick_render($user)), true), buttons([ button(page_link_to('angeltypes') . '&action=view&angeltype_id=' . $angeltype['id'], _("cancel"), 'cancel'), - button(page_link_to('user_angeltypes') . '&action=update&user_angeltype_id=' . $user_angeltype['id'] . '&coordinator=' . ($coordinator ? '1' : '0') . '&confirmed', _("yes"), 'ok') + button(page_link_to('user_angeltypes') . '&action=update&user_angeltype_id=' . $user_angeltype['id'] . '&supporter=' . ($supporter ? '1' : '0') . '&confirmed', _("yes"), 'ok') ]) ]); } diff --git a/includes/view/UserDriverLicenses_view.php b/includes/view/UserDriverLicenses_view.php index 041c8734..c07e5e1f 100644 --- a/includes/view/UserDriverLicenses_view.php +++ b/includes/view/UserDriverLicenses_view.php @@ -18,7 +18,7 @@ function UserDriverLicense_edit_view($user_source, $wants_to_drive, $has_car, $h ]), msg(), form([ - form_info(_("Privacy"), _("Your driving license information is only visible for coordinators and admins.")), + form_info(_("Privacy"), _("Your driving license information is only visible for supporters and admins.")), form_checkbox('wants_to_drive', _("I am willing to operate cars for the PL"), $wants_to_drive), div('panel panel-default', [ div('panel-body', [ diff --git a/includes/view/User_view.php b/includes/view/User_view.php index a620aa80..7d677f33 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -20,6 +20,66 @@ $tshirt_sizes = [ ]; /** + * Renders user settings page + * + * @param User $user_source + * The user + * @param array<String> $locales + * Available languages + * @param array<String> $themes + * Available themes + */ +function User_settings_view($user_source, $locales, $themes, $buildup_start_date, $teardown_end_date, $enable_tshirt_size, $tshirt_sizes) { + return page_with_title(settings_title(), [ + msg(), + div('row', [ + div('col-md-6', [ + form([ + form_info('', _("Here you can change your user details.")), + form_info(entry_required() . ' = ' . _("Entry required!")), + form_text('nick', _("Nick"), $user_source['Nick'], true), + form_text('lastname', _("Last name"), $user_source['Name']), + form_text('prename', _("First name"), $user_source['Vorname']), + form_date('planned_arrival_date', _("Planned date of arrival") . ' ' . entry_required(), $user_source['planned_arrival_date'], $buildup_start_date, $teardown_end_date), + form_date('planned_departure_date', _("Planned date of departure"), $user_source['planned_departure_date'], $buildup_start_date, $teardown_end_date), + form_text('age', _("Age"), $user_source['Alter']), + form_text('tel', _("Phone"), $user_source['Telefon']), + form_text('dect', _("DECT"), $user_source['DECT']), + form_text('mobile', _("Mobile"), $user_source['Handy']), + form_text('mail', _("E-Mail") . ' ' . entry_required(), $user_source['email']), + form_checkbox('email_shiftinfo', _("The engelsystem is allowed to send me an email (e.g. when my shifts change)"), $user_source['email_shiftinfo']), + form_checkbox('email_by_human_allowed', _("Humans are allowed to send me an email (e.g. for ticket vouchers)"), $user_source['email_by_human_allowed']), + form_text('jabber', _("Jabber"), $user_source['jabber']), + form_text('hometown', _("Hometown"), $user_source['Hometown']), + $enable_tshirt_size ? form_select('tshirt_size', _("Shirt size"), $tshirt_sizes, $user_source['Size']) : '', + form_info('', _('Please visit the angeltypes page to manage your angeltypes.')), + form_submit('submit', _("Save")) + ]) + ]), + div('col-md-6', [ + form([ + form_info(_("Here you can change your password.")), + form_password('password', _("Old password:")), + form_password('new_password', _("New password:")), + form_password('new_password2', _("Password confirmation:")), + form_submit('submit_password', _("Save")) + ]), + form([ + form_info(_("Here you can choose your color settings:")), + form_select('theme', _("Color settings:"), $themes, $user_source['color']), + form_submit('submit_theme', _("Save")) + ]), + form([ + form_info(_("Here you can choose your language:")), + form_select('language', _("Language:"), $locales, $user_source['Sprache']), + form_submit('submit_language', _("Save")) + ]) + ]) + ]) + ]); +} + +/** * Displays the welcome message to the user and shows a login form. */ function User_registration_success_view($event_welcome_message) { @@ -112,24 +172,24 @@ function Users_view($users, $order_by, $arrived_count, $active_count, $force_act 'actions' => '<strong>' . count($users) . '</strong>' ]; - return page_with_title(_('All users'), [ + return page_with_title(_("All users"), [ msg(), buttons([ - button(page_link_to('register'), glyph('plus') . _('New user')) + 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), + '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), + '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) ]); @@ -332,7 +392,7 @@ function User_angeltypes_render($user_angeltypes) { } else { $class = 'text-success'; } - $output[] = '<a href="' . angeltype_link($angeltype['id']) . '" class="' . $class . '">' . ($angeltype['coordinator'] ? glyph('education') : '') . $angeltype['name'] . '</a>'; + $output[] = '<a href="' . angeltype_link($angeltype['id']) . '" class="' . $class . '">' . ($angeltype['supporter'] ? glyph('education') : '') . $angeltype['name'] . '</a>'; } return join('<br />', $output); } |