%PDF- %PDF-
Direktori : /var/www/projetos/suporte.iigd.com.br/src/ |
Current File : /var/www/projetos/suporte.iigd.com.br/src/Calendar.php |
<?php /** * --------------------------------------------------------------------- * * GLPI - Gestionnaire Libre de Parc Informatique * * http://glpi-project.org * * @copyright 2015-2024 Teclib' and contributors. * @copyright 2003-2014 by the INDEPNET Development Team. * @licence https://www.gnu.org/licenses/gpl-3.0.html * * --------------------------------------------------------------------- * * LICENSE * * This file is part of GLPI. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * * --------------------------------------------------------------------- */ /** * Calendar Class **/ class Calendar extends CommonDropdown { use Glpi\Features\Clonable; // From CommonDBTM public $dohistory = true; public $can_be_translated = false; protected static $forward_entity_to = ['CalendarSegment']; public static $rightname = 'calendar'; public function getCloneRelations(): array { return [ Calendar_Holiday::class, CalendarSegment::class ]; } /** * @since 0.84 **/ public function getForbiddenStandardMassiveAction() { $forbidden = parent::getForbiddenStandardMassiveAction(); $forbidden[] = 'CommonDropdown' . MassiveAction::CLASS_ACTION_SEPARATOR . 'merge'; return $forbidden; } public static function getTypeName($nb = 0) { return _n('Calendar', 'Calendars', $nb); } public function defineTabs($options = []) { $ong = parent::defineTabs($options); $this->addStandardTab('CalendarSegment', $ong, $options); $this->addStandardTab('Calendar_Holiday', $ong, $options); return $ong; } public function getSpecificMassiveActions($checkitem = null) { $isadmin = static::canUpdate(); $actions = parent::getSpecificMassiveActions($checkitem); if ($isadmin) { $actions[__CLASS__ . MassiveAction::CLASS_ACTION_SEPARATOR . 'duplicate'] = _x('button', 'Duplicate'); $actions[__CLASS__ . MassiveAction::CLASS_ACTION_SEPARATOR . 'addholiday'] = __('Add a close time'); } return $actions; } public static function showMassiveActionsSubForm(MassiveAction $ma) { switch ($ma->getAction()) { case 'duplicate': Entity::dropdown(); echo "<br><br>"; echo Html::submit(_x('button', 'Duplicate'), ['name' => 'massiveaction']) . "</span>"; return true; case 'addholiday': Holiday::dropdown(); echo "<br><br>"; echo Html::submit(_x('button', 'Add'), ['name' => 'massiveaction']) . "</span>"; return true; } return parent::showMassiveActionsSubForm($ma); } public static function processMassiveActionsForOneItemtype( MassiveAction $ma, CommonDBTM $item, array $ids ) { switch ($ma->getAction()) { case 'duplicate': // For calendar duplicate in another entity if (Toolbox::hasTrait($item, \Glpi\Features\Clonable::class)) { $input = $ma->getInput(); $options = []; if ($item->isEntityAssign()) { $options = ['entities_id' => $input['entities_id']]; } foreach ($ids as $id) { if ($item->getFromDB($id)) { if ( !$item->isEntityAssign() || ($input['entities_id'] != $item->getEntityID()) ) { if ($item->can(-1, CREATE, $options)) { if ($item->clone($options)) { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK); } else { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO); $ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION)); } } else { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_NORIGHT); $ma->addMessage($item->getErrorMessage(ERROR_RIGHT)); } } else { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO); $ma->addMessage($item->getErrorMessage(ERROR_COMPAT)); } } else { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO); $ma->addMessage($item->getErrorMessage(ERROR_NOT_FOUND)); } } } else { $ma->itemDone($item->getType(), $ids, MassiveAction::ACTION_KO); } return; case 'addholiday': // add an holiday with massive action $input = $ma->getInput(); if ($input['holidays_id'] > 0) { $holiday = new Holiday(); $calendar_holiday = new Calendar_Holiday(); $holiday->getFromDB($input['holidays_id']); $entities = [$holiday->getEntityID() => $holiday->getEntityID()]; if ($holiday->isRecursive()) { $entities = getSonsOf("glpi_entities", $holiday->getEntityID()); } foreach ($ids as $id) { $entities_id = CommonDBTM::getItemEntity('Calendar', $id); if (isset($entities[$entities_id])) { $input = ['calendars_id' => $id, 'holidays_id' => $input['holidays_id'] ]; if ($calendar_holiday->add($input)) { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK); } else { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO); $ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION)); } } else { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO); $ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION)); } } } else { $ma->itemDone($item->getType(), $ids, MassiveAction::ACTION_KO); } return; } parent::processMassiveActionsForOneItemtype($ma, $item, $ids); } /** * @see Glpi\Features\Clonable::post_clone */ public function post_clone($source, $history) { $this->updateDurationCache($this->getID()); } public function cleanDBonPurge() { $this->deleteChildrenAndRelationsFromDb( [ Calendar_Holiday::class, CalendarSegment::class, ] ); } /** * Check if the given date is a holiday * * @param string $date Date of the day to check * * @return boolean **/ public function isHoliday($date) { $calendar_holiday = new Calendar_Holiday(); $holidays = $calendar_holiday->getHolidaysForCalendar($this->fields['id']); foreach ($holidays as $holiday) { if ($holiday['is_perpetual']) { // Compare only month and day for holidays that occurs every year. $date_to_compare = date('m-d', strtotime($date)); $begin_date = date('m-d', strtotime($holiday['begin_date'])); $end_date = date('m-d', strtotime($holiday['end_date'])); } else { // Normalize dates to Y-m-d $date_to_compare = date('Y-m-d', strtotime($date)); $begin_date = date('Y-m-d', strtotime($holiday['begin_date'])); $end_date = date('Y-m-d', strtotime($holiday['end_date'])); } if ($begin_date <= $date_to_compare && $date_to_compare <= $end_date) { return true; } } return false; } /** * Get active time between to date time for the active calendar * * @param string $start begin datetime * @param string $end end datetime * @param bool $include_inactive_time true to just get the time passed between start time and end time * * @return int timestamp of delay * * @FIXME Remove `$include_inactive_time` parameter in GLPI 10.1. It does not seems to be used and makes no sense. */ public function getActiveTimeBetween($start, $end, $include_inactive_time = false) { if (!isset($this->fields['id'])) { return 0; } if ($end < $start) { return 0; } $timestart = strtotime($start); $timeend = strtotime($end); $datestart = date('Y-m-d', $timestart); $dateend = date('Y-m-d', $timeend); // Need to finish at the closing day : set hour to midnight (23:59:59 for PHP) $timerealend = strtotime($dateend . ' 23:59:59'); $activetime = 0; if ($include_inactive_time) { $activetime = $timeend - $timestart; } else { $cache_duration = $this->getDurationsCache(); for ($actualtime = $timestart; $actualtime <= $timerealend; $actualtime += DAY_TIMESTAMP) { $actualdate = date('Y-m-d', $actualtime); if (!$this->isHoliday($actualdate)) { $beginhour = '00:00:00'; // Calendar segment work with '24:00:00' format for midnight $endhour = '24:00:00'; $dayofweek = self::getDayNumberInWeek($actualtime); $timeoftheday = 0; if ($actualdate == $datestart) { // First day : cannot use cache $beginhour = date('H:i:s', $timestart); } if ($actualdate == $dateend) { // Last day : cannot use cache $endhour = date('H:i:s', $timeend); } if ( (($actualdate == $datestart) || ($actualdate == $dateend)) && ($cache_duration[$dayofweek] > 0) ) { $timeoftheday = CalendarSegment::getActiveTimeBetween( $this->fields['id'], $dayofweek, $beginhour, $endhour ); } else { $timeoftheday = $cache_duration[$dayofweek]; } $activetime += $timeoftheday; } } } return $activetime; } /** * Check if the given time is on a working day (does not check working hours) * * @since 0.84 * * @param integer $time Time to check * * @return boolean */ public function isAWorkingDay($time) { $cache_duration = $this->getDurationsCache(); $dayofweek = self::getDayNumberInWeek($time); $date = date('Y-m-d', $time); return (($cache_duration[$dayofweek] > 0) && !$this->isHoliday($date)); } /** * Determines if calendar has, at least, one working day. * * @since 9.4.3 * * @return boolean */ public function hasAWorkingDay() { $durations = $this->getDurationsCache(); return false !== $durations && array_sum($durations) > 0; } /** * * Check if the given time is in a working hour * * @since 0.85 * * @param integer $time Time to check * * @return boolean */ public function isAWorkingHour($time) { if ($this->isAWorkingDay($time)) { $dayofweek = self::getDayNumberInWeek($time); return CalendarSegment::isAWorkingHour( $this->fields['id'], $dayofweek, date('H:i:s', $time) ); } return false; } /** * Add a delay to a date using the active calendar * * if delay >= DAY_TIMESTAMP : work in days * else work in minutes * * @param string $start begin * @param integer $delay delay to add (in seconds) * @param integer $additional_delay delay to add (default 0) * @param boolean $work_in_days force working in days (false by default) * @param boolean $end_of_working_day end of working day (false by default) * * @return boolean|string end date **/ public function computeEndDate($start, $delay, $additional_delay = 0, $work_in_days = false, $end_of_working_day = false) { // TODO 10.1: parameter $work_in_day make calculation for duration exprimed // in days (e.g "+ 5 days") but we don't have anything for month. // +1 month will push the date 30 working day when it should get the next // valid calendar date at least one month away from the starting date. if (!isset($this->fields['id'])) { return false; } if (!$this->hasAWorkingDay()) { // Invalid calendar (no working day = unable to find any date inside calendar hours) return false; } $cache_duration = $this->getDurationsCache(); $actualtime = strtotime($start); $timestart = strtotime($start); $datestart = date('Y-m-d', $timestart); // manage dates in past $negative_delay = false; if ($delay < 0) { $delay = -$delay; $negative_delay = true; } // Compute initial target date if ($end_of_working_day) { // Computation that will result in a target date that corresponds to the end of a working day. $numberofdays = $delay / DAY_TIMESTAMP; $dayofweek = self::getDayNumberInWeek($actualtime); $actualdate = date('Y-m-d', $actualtime); // Begin next day working if ( $this->isHoliday($actualdate) || ($cache_duration[$dayofweek] == 0) ) { while ( $this->isHoliday($actualdate) || ($cache_duration[$dayofweek] == 0) ) { $actualtime = self::getActualTime($actualtime, DAY_TIMESTAMP, $negative_delay); $actualdate = date('Y-m-d', $actualtime); $dayofweek = self::getDayNumberInWeek($actualtime); } } while ($numberofdays > 0) { if ( !$this->isHoliday($actualdate) && ($cache_duration[$dayofweek] > 0) ) { $numberofdays--; } $actualtime = self::getActualTime($actualtime, DAY_TIMESTAMP, $negative_delay); $actualdate = date('Y-m-d', $actualtime); $dayofweek = self::getDayNumberInWeek($actualtime); } // Get next working day if ( $this->isHoliday($actualdate) || ($cache_duration[$dayofweek] == 0) ) { while ( $this->isHoliday($actualdate) || ($cache_duration[$dayofweek] == 0) ) { $actualtime = self::getActualTime($actualtime, DAY_TIMESTAMP, $negative_delay); $actualdate = date('Y-m-d', $actualtime); $dayofweek = self::getDayNumberInWeek($actualtime); } } $lastworkinghour = CalendarSegment::getLastWorkingHour($this->fields['id'], $dayofweek); $actualtime = strtotime(date('Y-m-d', $actualtime) . ' ' . $lastworkinghour); } else if ($work_in_days) { // Computation that is based on a delay expressed in full days. // Compute Real starting time // If day is an holiday must start on the begin of next working day $actualdate = date('Y-m-d', $actualtime); $dayofweek = self::getDayNumberInWeek($actualtime); if ( $this->isHoliday($actualdate) || ($cache_duration[$dayofweek] == 0) ) { while ( $this->isHoliday($actualdate) || ($cache_duration[$dayofweek] == 0) ) { $actualtime = self::getActualTime($actualtime, DAY_TIMESTAMP, $negative_delay); $actualdate = date('Y-m-d', $actualtime); $dayofweek = self::getDayNumberInWeek($actualtime); } $firstworkhour = CalendarSegment::getFirstWorkingHour( $this->fields['id'], $dayofweek ); $actualtime = strtotime($actualdate . ' ' . $firstworkhour); } while ($delay > 0) { // Begin next day : do not take into account first day : must finish to a working day $actualtime = self::getActualTime($actualtime, DAY_TIMESTAMP, $negative_delay); $actualdate = date('Y-m-d', $actualtime); $dayofweek = self::getDayNumberInWeek($actualtime); if ( !$this->isHoliday($actualdate) && ($cache_duration[$dayofweek] > 0) ) { $delay -= DAY_TIMESTAMP; } if ($delay < 0) { // delay done : if < 0 delete hours $actualtime = self::getActualTime($actualtime, $delay, $negative_delay); } } // If > last working hour set last working hour $dayofweek = self::getDayNumberInWeek($actualtime); $lastworkinghour = CalendarSegment::getLastWorkingHour($this->fields['id'], $dayofweek); if ($lastworkinghour < date('H:i:s', $actualtime)) { $actualtime = strtotime(date('Y-m-d', $actualtime) . ' ' . $lastworkinghour); } } else { // Computation based on a delay expressed in working hours. while ($delay >= 0) { $actualdate = date('Y-m-d', $actualtime); $dayofweek = self::getDayNumberInWeek($actualtime); if (!$this->isHoliday($actualdate) && $cache_duration[$dayofweek] > 0) { $dayofweek = self::getDayNumberInWeek($actualtime); $beginhour = '00:00:00'; if ($actualdate == $datestart) { // First day cannot use cache $beginhour = date('H:i:s', $timestart); if (!$negative_delay) { // Count to end of day $timeoftheday = CalendarSegment::getActiveTimeBetween( $this->fields['id'], $dayofweek, $beginhour, '24:00:00' ); } else { // Count to start of day $timeoftheday = CalendarSegment::getActiveTimeBetween( $this->fields['id'], $dayofweek, "00:00:00", $beginhour ); } } else { $timeoftheday = $cache_duration[$dayofweek]; } if ($delay === 0 && $timeoftheday === 0 && $this->isAWorkingHour($timestart)) { // Special case: // - current day is a working day; // - there is no delay to add; // - current time it the exact last second of working hours of the current day. // -> we do not have to add any delay to current time break; } if ($delay >= $timeoftheday) { // Delay is greater or equal than remaining time in day // -> pass to next day $actualtime = self::getActualTime($actualtime, DAY_TIMESTAMP, $negative_delay); $delay -= $timeoftheday; } else { // End of the delay in the day : get hours with this delay $endhour = CalendarSegment::addDelayInDay( $this->fields['id'], $dayofweek, $beginhour, $delay, $negative_delay ); $actualtime = strtotime($actualdate . ' ' . $endhour); break; } } else { // Holiday or non working day : pass to next day $actualtime = self::getActualTime($actualtime, DAY_TIMESTAMP, $negative_delay); } } } $actualdate = date('Y-m-d H:i:s', $actualtime); if ($additional_delay) { // Add additional delay in `$work_in_days = false` mode. // For OLA/SLA, additional delay only include "waiting times" inside calendar working hours // and should therefore be added inside working hours only, in addition to the initial target date. $actualdate = $this->computeEndDate( $actualdate, $additional_delay ); } return $actualdate; } public static function getActualTime($current_time, $number = 0, $negative = false) { if ($negative) { return $current_time - $number; } else { return $current_time + $number; } } /** * Get days durations including all segments of the current calendar * * @return boolean|array **/ public function getDurationsCache() { if (!isset($this->fields['id'])) { return false; } $cache_duration = importArrayFromDB($this->fields['cache_duration']); // Invalid cache duration : recompute it if (!isset($cache_duration[0])) { $this->updateDurationCache($this->fields['id']); $cache_duration = importArrayFromDB($this->fields['cache_duration']); } return $cache_duration; } /** * Get days durations including all segments of the current calendar * * @return boolean|array **/ public function getDaysDurations() { if (!isset($this->fields['id'])) { return false; } $results = []; for ($i = 0; $i < 7; $i++) { $results[$i] = CalendarSegment::getActiveTimeBetween( $this->fields['id'], $i, '00:00:00', '24:00:00' ); } return $results; } /** * Update the calendar cache * * @param integer $calendars_id ID of the calendar * * @return bool True if successful in updating the cache, otherwise returns false. */ public function updateDurationCache($calendars_id) { if ($this->getFromDB($calendars_id)) { $input = [ 'id' => $calendars_id, 'cache_duration' => exportArrayToDB($this->getDaysDurations()), ]; return $this->update($input); } return false; } /** * Get day number (in week) for a date. * * @param integer $date Date as a UNIX timestamp * * @return integer */ public static function getDayNumberInWeek($date) { return (int)date('w', $date); } public static function getIcon() { return "far fa-calendar-alt"; } }