grthtrhthjhtyjytjytkergtrhtrjytjerhrfh4:24 29/09/2026.
--------------------------------------------------------------------------
*/
/** @file
* @brief
*/
if (!defined('GLPI_ROOT')) {
die("Sorry. You can't access this file directly");
}
/**
* Calendar Class
**/
class Calendar extends CommonDropdown {
// From CommonDBTM
public $dohistory = true;
public $can_be_translated = false;
static protected $forward_entity_to = array('CalendarSegment');
static $rightname = 'calendar';
/**
* @since version 0.84
**/
function getForbiddenStandardMassiveAction() {
$forbidden = parent::getForbiddenStandardMassiveAction();
$forbidden[] = 'CommonDropdown'.MassiveAction::CLASS_ACTION_SEPARATOR.'merge';
return $forbidden;
}
static function getTypeName($nb=0) {
return _n('Calendar','Calendars',$nb);
}
function defineTabs($options=array()) {
$ong = parent::defineTabs($options);
$this->addStandardTab('CalendarSegment', $ong, $options);
$this->addStandardTab('Calendar_Holiday', $ong, $options);
return $ong;
}
/**
* @see CommonDBTM::getSpecificMassiveActions()
**/
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;
}
/**
* @since version 0.85
*
* @see CommonDBTM::showMassiveActionsSubForm()
**/
static function showMassiveActionsSubForm(MassiveAction $ma) {
switch ($ma->getAction()) {
case 'duplicate' :
Entity::dropdown();
echo "
";
echo Html::submit(_x('button', 'Duplicate'), array('name' => 'massiveaction'))."";
return true;
case 'addholiday' :
Holiday::dropdown();
echo "
";
echo Html::submit(_x('button', 'Add'), array('name' => 'massiveaction'))."";
return true;
}
return parent::showMassiveActionsSubForm($ma);
}
/**
* @since version 0.85
*
* @see CommonDBTM::processMassiveActionsForOneItemtype()
**/
static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBTM $item,
array $ids) {
switch ($ma->getAction()) {
case 'duplicate' : // For calendar duplicate in another entity
if (method_exists($item,'duplicate')) {
$input = $ma->getInput();
$options = array();
if ($item->isEntityAssign()) {
$options = array('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->duplicate($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 = array($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 = array('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);
}
/** Clone a calendar to another entity : name is updated
*
* @param $options array of new values to set
**/
function duplicate($options=array()) {
if (is_array($options) && count($options)) {
foreach ($options as $key => $val) {
if (isset($this->fields[$key])) {
$this->fields[$key] = $val;
}
}
}
$input = $this->fields;
$oldID = $input['id'];
unset($input['id']);
if ($newID = $this->add($input)) {
Calendar_Holiday::cloneCalendar($oldID, $newID);
CalendarSegment::cloneCalendar($oldID, $newID);
$this->updateDurationCache($newID);
return true;
}
return false;
}
function cleanDBonPurge() {
global $DB;
$query2 = "DELETE
FROM `glpi_calendars_holidays`
WHERE `calendars_id` = '".$this->fields['id']."'";
$DB->query($query2);
$query2 = "DELETE
FROM `glpi_calendarsegments`
WHERE `calendars_id` = '".$this->fields['id']."'";
$DB->query($query2);
}
/**
* is an holiday day ?
*
* @param $date date of the day to check
*
* @return boolean
**/
function isHoliday($date) {
global $DB;
$query = "SELECT COUNT(*) AS cpt
FROM `glpi_calendars_holidays`
INNER JOIN `glpi_holidays`
ON (`glpi_calendars_holidays`.`holidays_id` = `glpi_holidays`.`id`)
WHERE `glpi_calendars_holidays`.`calendars_id` = '".$this->fields['id']."'
AND (('$date' <= `glpi_holidays`.`end_date`
AND '$date' >= `glpi_holidays`.`begin_date`)
OR (`glpi_holidays`.`is_perpetual` = 1
AND MONTH(`end_date`)*100 + DAY(`end_date`)
>= ".date('nd',strtotime($date))."
AND MONTH(`begin_date`)*100 + DAY(`begin_date`)
<= ".date('nd',strtotime($date))."
)
)";
if ($result=$DB->query($query)) {
return $DB->result($result, 0, 'cpt');
}
return false;
}
/**
* Get active time between to date time for the active calendar
*
* @param $start datetime begin
* @param $end datetime end
* @param $work_in_days boolean force working in days (false by default)
*
* @return timestamp of delay
**/
function getActiveTimeBetween($start, $end, $work_in_days=false) {
if (!isset($this->fields['id'])) {
return false;
}
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 :
/// Before PHP 5.3 need to be 23:59:59 and not 24:00:00
$timerealend = strtotime($dateend.' 23:59:59');
$activetime = 0;
if ($work_in_days) {
$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';
/// Before PHP 5.3 need to be 23:59:59 and not 24:00:00
$endhour = '23:59:59';
$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];
}
// echo "time of the day = $timeoftheday ".Html::timestampToString($timeoftheday).'
';
$activetime += $timeoftheday;
// echo "cumulate time = $activetime ".Html::timestampToString($activetime).'
';
} else {
// echo "$actualdate is an holiday
";
}
}
}
return $activetime;
}
/**
* Is the time passed is in a working day
*
* @since version 0.84
*
* @param $time time time to check
*
* @return boolean
**/
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));
}
/**
* Is the time passed is in a working hour
*
* @since version 0.85
*
* @param $time time time to check
*
* @return boolean
**/
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 $start datetime begin
* @param $delay timestamp delay to add
* @param $additional_delay timestamp delay to add (default 0)
* @param $work_in_days boolean force working in days (false by default)
* @param $end_of_working_day boolean end of working day (false by default)
*
* @return end date
**/
function computeEndDate($start, $delay, $additional_delay=0, $work_in_days=false, $end_of_working_day=false) {
if (!isset($this->fields['id'])) {
return false;
}
$actualtime = strtotime($start);
$timestart = strtotime($start);
$datestart = date('Y-m-d', $timestart);
// End of working day
if ($end_of_working_day) {
$numberofdays = $delay / DAY_TIMESTAMP;
// Add $additional_delay to start time.
// If start + delay is next day : +1 day
$actualtime += $additional_delay;
$cache_duration = $this->getDurationsCache();
$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 += DAY_TIMESTAMP;
$actualdate = date('Y-m-d',$actualtime);
$dayofweek = self::getDayNumberInWeek($actualtime);
}
}
while ($numberofdays > 0) {
if (!$this->isHoliday($actualdate)
&& ($cache_duration[$dayofweek] > 0)) {
$numberofdays --;
}
$actualtime += DAY_TIMESTAMP;
$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 += DAY_TIMESTAMP;
$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);
return date('Y-m-d H:i:s', $actualtime);
}
// Add additional delay to initial delay
$delay += $additional_delay;
if ($work_in_days) { // only based on days
$cache_duration = $this->getDurationsCache();
// 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 += DAY_TIMESTAMP;
$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 += DAY_TIMESTAMP;
$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 += $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);
}
return date('Y-m-d H:i:s', $actualtime);
}
// else // based on working hours
$cache_duration = $this->getDurationsCache();
// Only if segments exists
if (countElementsInTable('glpi_calendarsegments',
"`calendars_id` = '".$this->fields['id']."'")) {
while ($delay >= 0) {
$actualdate = date('Y-m-d',$actualtime);
if (!$this->isHoliday($actualdate)) {
$dayofweek = self::getDayNumberInWeek($actualtime);
$beginhour = '00:00:00';
/// Before PHP 5.3 need to be 23:59:59 and not 24:00:00
$endhour = '23:59:59';
if ($actualdate == $datestart) { // First day cannot use cache
$beginhour = date('H:i:s',$timestart);
$timeoftheday = CalendarSegment::getActiveTimeBetween($this->fields['id'],
$dayofweek, $beginhour,
$endhour);
} else {
$timeoftheday = $cache_duration[$dayofweek];
}
// Day do not complete the delay : pass to next day
if ($timeoftheday<$delay) {
$actualtime += DAY_TIMESTAMP;
$delay -= $timeoftheday;
} else { // End of the delay in the day : get hours with this delay
$beginhour = '00:00:00';
/// Before PHP 5.3 need to be 23:59:59 and not 24:00:00
$endhour = '23:59:59';
if ($actualdate == $datestart) {
$beginhour = date('H:i:s',$timestart);
}
$endhour = CalendarSegment::addDelayInDay($this->fields['id'], $dayofweek,
$beginhour, $delay);
return $actualdate.' '.$endhour;
}
} else { // Holiday : pass to next day
$actualtime += DAY_TIMESTAMP;
}
}
}
return false;
}
/**
* Get days durations including all segments of the current calendar
*
* @return end date
**/
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 end date
**/
function getDaysDurations() {
if (!isset($this->fields['id'])) {
return false;
}
$results = array();
for ($i=0 ; $i<7 ; $i++) {
/// Before PHP 5.3 need to be 23:59:59 and not 24:00:00
$results[$i] = CalendarSegment::getActiveTimeBetween($this->fields['id'], $i, '00:00:00',
'23:59:59');
}
return $results;
}
/**
* Update the calendar cache
*
* @param $calendars_id integer calendar ID
**/
function updateDurationCache($calendars_id) {
if ($this->getFromDB($calendars_id)) {
$input['id'] = $calendars_id;
$input['cache_duration'] = exportArrayToDB($this->getDaysDurations());
return $this->update($input);
}
return false;
}
/**
* Get day number (in week) for a date
*
* @param $date date
**/
static function getDayNumberInWeek($date) {
return date('w', $date);
}
}