diff --git a/db/update.sql b/db/update.sql index 212cd97e..56663071 100644 --- a/db/update.sql +++ b/db/update.sql @@ -49,3 +49,11 @@ ALTER TABLE `Room` ADD `description` TEXT NULL AFTER `map_url`; -- Dashboard ALTER TABLE `AngelTypes` ADD `show_on_dashboard` BOOLEAN NOT NULL AFTER `contact_email`; UPDATE `AngelTypes` SET `show_on_dashboard` = TRUE; + +-- Work Log +CREATE TABLE `UserWorkLog` ( `id` INT NOT NULL AUTO_INCREMENT , `user_id` INT NOT NULL , `work_hours` DECIMAL NOT NULL , `comment` VARCHAR(200) NOT NULL , `created_user_id` INT NOT NULL , `created_timestamp` INT NOT NULL , PRIMARY KEY (`id`), INDEX (`user_id`), INDEX (`created_user_id`)) ENGINE = InnoDB; +ALTER TABLE `UserWorkLog` ADD FOREIGN KEY (`created_user_id`) REFERENCES `User`(`UID`) ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE `UserWorkLog` ADD FOREIGN KEY (`user_id`) REFERENCES `User`(`UID`) ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE `UserWorkLog` ADD INDEX(`created_timestamp`); +INSERT INTO `Privileges` (`id`, `name`, `desc`) VALUES (NULL, 'admin_user_worklog', 'Manage user work log entries.'); +ALTER TABLE `UserWorkLog` CHANGE `work_hours` `work_hours` DECIMAL(10,2) NOT NULL; +ALTER TABLE `UserWorkLog` ADD `work_timestamp` INT NOT NULL AFTER `user_id`; diff --git a/includes/controller/user_worklog_controller.php b/includes/controller/user_worklog_controller.php new file mode 100644 index 00000000..bd62c83c --- /dev/null +++ b/includes/controller/user_worklog_controller.php @@ -0,0 +1,195 @@ +input('user_worklog_id')); + if (empty($userWorkLog)) { + redirect(user_link($user)); + } + $user_source = User($userWorkLog['user_id']); + + if ($request->has('confirmed')) { + UserWorkLog_delete($userWorkLog); + + success(_('Work log entry deleted.')); + redirect(user_link($user_source)); + } + + return [ + UserWorkLog_delete_title(), + UserWorkLog_delete_view($user_source, $userWorkLog) + ]; +} + +/** + * Edit work log for user. + */ +function user_worklog_edit_controller() +{ + global $user; + + $request = request(); + $userWorkLog = UserWorkLog($request->input('user_worklog_id')); + if (empty($userWorkLog)) { + redirect(user_link($user)); + } + $user_source = User($userWorkLog['user_id']); + + if ($request->has('submit')) { + list ($valid, $userWorkLog) = user_worklog_from_request($userWorkLog); + + if ($valid) { + UserWorkLog_update($userWorkLog); + + success(_('Work log entry updated.')); + redirect(user_link($user_source)); + } + } + + return [ + UserWorkLog_edit_title(), + UserWorkLog_edit_view($user_source, $userWorkLog) + ]; +} + +/** + * + * @param UserWorkLog $userWorkLog + * @return [bool $valid, UserWorkLog $userWorkLog] + */ +function user_worklog_from_request($userWorkLog) +{ + $request = request(); + + $valid = true; + + $userWorkLog['work_timestamp'] = parse_date('Y-m-d H:i', $request->input('work_timestamp') . ' 00:00'); + if ($userWorkLog['work_timestamp'] == null) { + $valid = false; + error(_('Please enter work date.')); + } + + $userWorkLog['work_hours'] = $request->input('work_hours'); + if (! preg_match("/[0-9]+(\.[0-9]+)?/", $userWorkLog['work_hours'])) { + $valid = false; + error(_('Please enter work hours in format ##[.##].')); + } + + $userWorkLog['comment'] = $request->input('comment'); + if (empty($userWorkLog['comment'])) { + $valid = false; + error(_('Please enter a comment.')); + } + + return [ + $valid, + $userWorkLog + ]; +} + +/** + * Add work log entry to user. + */ +function user_worklog_add_controller() +{ + global $user; + + $request = request(); + $user_source = User($request->input('user_id')); + if (empty($user_source)) { + redirect(user_link($user)); + } + + $userWorkLog = UserWorkLog_new($user_source); + + if ($request->has('submit')) { + list ($valid, $userWorkLog) = user_worklog_from_request($userWorkLog); + + if ($valid) { + UserWorkLog_create($userWorkLog); + + success(_('Work log entry created.')); + redirect(user_link($user_source)); + } + } + + return [ + UserWorkLog_add_title(), + UserWorkLog_add_view($user_source, $userWorkLog) + ]; +} + +/** + * Link to work log entry add for given user. + * + * @param User $user + */ +function user_worklog_add_link($user) +{ + return page_link_to('user_worklog', [ + 'action' => 'add', + 'user_id' => $user['UID'] + ]); +} + +/** + * Link to work log entry edit. + * + * @param UserWorkLog $userWorkLog + */ +function user_worklog_edit_link($userWorkLog) +{ + return page_link_to('user_worklog', [ + 'action' => 'edit', + 'user_worklog_id' => $userWorkLog['id'] + ]); +} + +/** + * Link to work log entry delete. + * + * @param UserWorkLog $userWorkLog + * @param array[] $parameters + */ +function user_worklog_delete_link($userWorkLog, $parameters = []) +{ + return page_link_to('user_worklog', array_merge([ + 'action' => 'delete', + 'user_worklog_id' => $userWorkLog['id'] + ], $parameters)); +} + +/** + * Work log entry actions + */ +function user_worklogs_controller() +{ + global $user, $privileges; + + if (! in_array('admin_user_worklog', $privileges)) { + redirect(user_link($user)); + } + + $request = request(); + $action = $request->input('action'); + if (! $request->has('action')) { + redirect(user_link($user)); + } + + switch ($action) { + case 'add': + return user_worklog_add_controller(); + case 'edit': + return user_worklog_edit_controller(); + case 'delete': + return user_worklog_delete_controller(); + } +} + +?> \ No newline at end of file diff --git a/includes/controller/users_controller.php b/includes/controller/users_controller.php index a2a28410..8a325621 100644 --- a/includes/controller/users_controller.php +++ b/includes/controller/users_controller.php @@ -228,7 +228,7 @@ function user_controller() if($user_source['force_active']) { $tshirt_score = _('Enough'); } else { - $tshirt_score = round(User_tshirt_score($user_source), 2) . ' h'; + $tshirt_score = sprintf('%.2f', User_tshirt_score($user_source)) . ' h'; } return [ @@ -242,7 +242,9 @@ function user_controller() $shifts, $user['UID'] == $user_source['UID'], $tshirt_score, - in_array('admin_active', $privileges) + in_array('admin_active', $privileges), + in_array('admin_user_worklog', $privileges), + UserWorkLogsForUser($user_source) ) ]; } diff --git a/includes/includes.php b/includes/includes.php index e8e5ce29..e316e550 100644 --- a/includes/includes.php +++ b/includes/includes.php @@ -27,6 +27,7 @@ $includeFiles = [ __DIR__ . '/../includes/model/UserDriverLicenses_model.php', __DIR__ . '/../includes/model/UserGroups_model.php', __DIR__ . '/../includes/model/User_model.php', + __DIR__ . '/../includes/model/UserWorkLog_model.php', __DIR__ . '/../includes/model/ValidationResult.php', __DIR__ . '/../includes/view/AngelTypes_view.php', @@ -45,6 +46,7 @@ $includeFiles = [ __DIR__ . '/../includes/view/UserDriverLicenses_view.php', __DIR__ . '/../includes/view/UserHintsRenderer.php', __DIR__ . '/../includes/view/User_view.php', + __DIR__ . '/../includes/view/UserWorkLog_view.php', __DIR__ . '/../includes/controller/angeltypes_controller.php', __DIR__ . '/../includes/controller/event_config_controller.php', @@ -56,6 +58,7 @@ $includeFiles = [ __DIR__ . '/../includes/controller/users_controller.php', __DIR__ . '/../includes/controller/user_angeltypes_controller.php', __DIR__ . '/../includes/controller/user_driver_licenses_controller.php', + __DIR__ . '/../includes/controller/user_worklog_controller.php', __DIR__ . '/../includes/helper/graph_helper.php', __DIR__ . '/../includes/helper/internationalization_helper.php', diff --git a/includes/model/UserWorkLog_model.php b/includes/model/UserWorkLog_model.php new file mode 100644 index 00000000..36442943 --- /dev/null +++ b/includes/model/UserWorkLog_model.php @@ -0,0 +1,123 @@ + $user['UID'], + 'work_timestamp' => $work_date, + 'work_hours' => 0, + 'comment' => '' + ]; +} + +?> \ No newline at end of file diff --git a/includes/model/User_model.php b/includes/model/User_model.php index fa24aac1..5a31e7b8 100644 --- a/includes/model/User_model.php +++ b/includes/model/User_model.php @@ -26,7 +26,7 @@ function User_delete($user_id) function User_tshirt_score($user) { $shift_sum_formula = config('shift_sum_formula'); - $result = DB::selectOne(' + $result_shifts = DB::selectOne(' SELECT ROUND((' . $shift_sum_formula . ') / 3600, 2) AS `tshirt_score` FROM `User` LEFT JOIN `ShiftEntry` ON `User`.`UID` = `ShiftEntry`.`UID` LEFT JOIN `Shifts` ON `ShiftEntry`.`SID` = `Shifts`.`SID` @@ -37,8 +37,18 @@ function User_tshirt_score($user) { $user['UID'], time() ]); + $result_worklog = DB::selectOne(' + SELECT SUM(`work_hours`) AS `tshirt_score` + FROM `User` + LEFT JOIN `UserWorkLog` ON `User`.`UID` = `UserWorkLog`.`user_id` + WHERE `User`.`UID` = ? + AND `UserWorkLog`.`work_timestamp` < ? + ',[ + $user['UID'], + time() + ]); - return $result['tshirt_score']; + return $result_shifts['tshirt_score'] + $result_worklog['tshirt_score']; } /** diff --git a/includes/pages/admin_active.php b/includes/pages/admin_active.php index 576cdc49..2616de6c 100644 --- a/includes/pages/admin_active.php +++ b/includes/pages/admin_active.php @@ -58,7 +58,10 @@ function admin_active() SELECT `User`.*, COUNT(`ShiftEntry`.`id`) AS `shift_count`, - %s AS `shift_length` + (%s + ( + SELECT COALESCE(SUM(`work_hours`) * 3600, 0) FROM `UserWorkLog` WHERE `user_id`=`User`.`UID` + AND `work_timestamp` < %s + )) AS `shift_length` FROM `User` LEFT JOIN `ShiftEntry` ON `User`.`UID` = `ShiftEntry`.`UID` LEFT JOIN `Shifts` ON `ShiftEntry`.`SID` = `Shifts`.`SID` @@ -69,6 +72,7 @@ function admin_active() %s ', $shift_sum_formula, + time(), $limit )); $user_nicks = []; @@ -140,7 +144,10 @@ function admin_active() SELECT `User`.*, COUNT(`ShiftEntry`.`id`) AS `shift_count`, - %s AS `shift_length` + (%s + ( + SELECT COALESCE(SUM(`work_hours`) * 3600, 0) FROM `UserWorkLog` WHERE `user_id`=`User`.`UID` + AND `work_timestamp` < %s + )) AS `shift_length` FROM `User` LEFT JOIN `ShiftEntry` ON `User`.`UID` = `ShiftEntry`.`UID` LEFT JOIN `Shifts` ON `ShiftEntry`.`SID` = `Shifts`.`SID` ' . ($show_all_shifts ? '' : 'AND (`Shifts`.`end` < ' . time() . " OR `Shifts`.`end` IS NULL)") . ' @@ -150,6 +157,7 @@ function admin_active() %s ', $shift_sum_formula, + time(), $limit )); $matched_users = []; @@ -174,7 +182,7 @@ function admin_active() $usr['nick'] = User_Nick_render($usr); $usr['shirt_size'] = $tshirt_sizes[$usr['Size']]; $usr['work_time'] = round($usr['shift_length'] / 60) - . ' min (' . round($usr['shift_length'] / 3600) . ' h)'; + . ' min (' . sprintf('%.2f', $usr['shift_length'] / 3600) . ' h)'; $usr['active'] = glyph_bool($usr['Aktiv'] == 1); $usr['force_active'] = glyph_bool($usr['force_active'] == 1); $usr['tshirt'] = glyph_bool($usr['Tshirt'] == 1); @@ -192,22 +200,26 @@ function admin_active() . _('set active') . ''; } - if ($usr['Aktiv'] == 1 && $usr['Tshirt'] == 0) { + if ($usr['Aktiv'] == 1) { $parametersRemove = [ 'not_active' => $usr['UID'], 'search' => $search, ]; + if ($show_all_shifts) { + $parametersRemove['show_all_shifts'] = 1; + } + $actions[] = '' + . _('remove active') + . ''; + } + if ($usr['Tshirt'] == 0) { $parametersShirt = [ 'tshirt' => $usr['UID'], 'search' => $search, ]; if ($show_all_shifts) { - $parametersRemove['show_all_shifts'] = 1; $parametersShirt['show_all_shifts'] = 1; } - $actions[] = '' - . _('remove active') - . ''; $actions[] = '' . _('got t-shirt') . ''; @@ -233,12 +245,6 @@ function admin_active() $shirt_statistics = []; foreach (array_keys($tshirt_sizes) as $size) { if (!empty($size)) { - $sc = DB::selectOne( - 'SELECT count(*) FROM `User` WHERE `Size`=? AND `Gekommen`=1', - [$size] - ); - $sc = array_shift($sc); - $gc = DB::selectOne( 'SELECT count(*) FROM `User` WHERE `Size`=? AND `Tshirt`=1', [$size] @@ -247,19 +253,16 @@ function admin_active() $shirt_statistics[] = [ 'size' => $size, - 'needed' => (int)$sc, 'given' => (int)$gc ]; } } - $shirtCount = DB::selectOne('SELECT count(*) FROM `User` WHERE `Tshirt`=1'); - $shirtCount = array_shift($shirtCount); + $shirtCount = User_tshirts_count(); $shirt_statistics[] = [ 'size' => '' . _('Sum') . '', - 'needed' => '' . User_arrived_count() . '', - 'given' => '' . (int)$shirtCount . '' + 'given' => '' . $shirtCount . '' ]; return page_with_title(admin_active_title(), [ @@ -286,7 +289,6 @@ function admin_active() '

' . _('Shirt statistics') . '

', table([ 'size' => _('Size'), - 'needed' => _('Needed shirts'), 'given' => _('Given shirts') ], $shirt_statistics) ]); diff --git a/includes/pages/user_shifts.php b/includes/pages/user_shifts.php index 0be4aa67..81b405b1 100644 --- a/includes/pages/user_shifts.php +++ b/includes/pages/user_shifts.php @@ -313,11 +313,19 @@ function get_ids_from_array($array) */ function make_select($items, $selected, $name, $title = null, $additionalButtons = []) { + $html = ''; $htmlItems = []; if (isset($title)) { - $htmlItems[] = '

' . $title . '

' . "\n"; + $html .= '

' . $title . '

' . "\n"; } + $buttons = []; + $buttons[] = button('javascript: checkAll(\'selection_' . $name . '\', true)', _('All')); + $buttons[] = button('javascript: checkAll(\'selection_' . $name . '\', false)', _('None')); + $buttons = array_merge($buttons, $additionalButtons); + + $html .= buttons($buttons); + foreach ($items as $i) { $htmlItems[] = '
' . '