1021 lines
34 KiB
PHP
1021 lines
34 KiB
PHP
<?php
|
|
|
|
/**
|
|
* ---------------------------------------------------------------------
|
|
* GLPI - Gestionnaire Libre de Parc Informatique
|
|
* Copyright (C) 2015-2020 Teclib' and contributors.
|
|
*
|
|
* http://glpi-project.org
|
|
*
|
|
* based on GLPI - Gestionnaire Libre de Parc Informatique
|
|
* Copyright (C) 2003-2014 by the INDEPNET Development Team.
|
|
*
|
|
* ---------------------------------------------------------------------
|
|
*
|
|
* LICENSE
|
|
*
|
|
* This file is part of GLPI.
|
|
*
|
|
* GLPI 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GLPI 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 GLPI. If not, see <http://www.gnu.org/licenses/>.
|
|
* ---------------------------------------------------------------------
|
|
*/
|
|
|
|
namespace Glpi\Features;
|
|
|
|
if (!defined('GLPI_ROOT')) {
|
|
die("Sorry. You can't access this file directly");
|
|
}
|
|
|
|
use RRule\RRule;
|
|
use RRule\RSet;
|
|
use Session;
|
|
use Toolbox;
|
|
use Planning;
|
|
use PlanningRecall;
|
|
use CommonDBVisible;
|
|
use Group_User;
|
|
use QueryExpression;
|
|
use PlanningEventCategory;
|
|
use Html;
|
|
use DateTime;
|
|
use DateTimeZone;
|
|
use Reminder;
|
|
use Dropdown;
|
|
use CommonITILTask;
|
|
use User;
|
|
use DateInterval;
|
|
use Entity;
|
|
|
|
trait PlanningEvent {
|
|
|
|
function post_getEmpty() {
|
|
if (isset($this->fields["users_id"])) {
|
|
$this->fields["users_id"] = Session::getLoginUserID();
|
|
}
|
|
|
|
if (isset($this->field['rrule'])) {
|
|
$this->field['rrule'] = json_decode($this->field['rrule'], true);
|
|
}
|
|
|
|
if (isset($this->fields['is_recursive'])) {
|
|
$this->fields['is_recursive'] = 1;
|
|
}
|
|
|
|
if (isset($this->fields['users_id_guests'])) {
|
|
$this->fields['users_id_guests'] = [];
|
|
}
|
|
|
|
parent::post_getEmpty();
|
|
}
|
|
|
|
function post_addItem() {
|
|
// Add document if needed
|
|
$this->input = $this->addFiles($this->input, [
|
|
'force_update' => true,
|
|
'content_field' => 'text']
|
|
);
|
|
|
|
if (!isset($this->input['_no_check_plan'])
|
|
&& isset($this->fields["users_id"])
|
|
&& isset($this->fields["begin"])
|
|
&& !empty($this->fields["begin"])) {
|
|
Planning::checkAlreadyPlanned(
|
|
$this->fields["users_id"],
|
|
$this->fields["begin"],
|
|
$this->fields["end"],
|
|
[
|
|
$this->getType() => [$this->fields['id']]
|
|
]
|
|
);
|
|
}
|
|
|
|
if (isset($this->input['_planningrecall'])) {
|
|
$this->input['_planningrecall']['items_id'] = $this->fields['id'];
|
|
PlanningRecall::manageDatas($this->input['_planningrecall']);
|
|
}
|
|
}
|
|
|
|
|
|
function prepareInputForAdd($input) {
|
|
global $DB;
|
|
|
|
if ($DB->fieldExists(static::getTable(), 'users_id')
|
|
&& (!isset($input['users_id'])
|
|
|| empty($input['users_id']))) {
|
|
$input['users_id'] = Session::getLoginUserID();
|
|
}
|
|
|
|
// manage guests
|
|
if (isset($input['users_id_guests']) && is_array($input['users_id_guests'])) {
|
|
$input['users_id_guests'] = exportArrayToDB($input['users_id_guests']);
|
|
}
|
|
|
|
Toolbox::manageBeginAndEndPlanDates($input['plan']);
|
|
|
|
if (!isset($input['uuid'])) {
|
|
$input['uuid'] = \Ramsey\Uuid\Uuid::uuid4();
|
|
}
|
|
|
|
$input["name"] = trim($input["name"]);
|
|
if (empty($input["name"])) {
|
|
$input["name"] = __('Without title');
|
|
}
|
|
|
|
$input["begin"] = $input["end"] = "NULL";
|
|
|
|
if (isset($input['plan'])) {
|
|
if (!empty($input['plan']["begin"])
|
|
&& !empty($input['plan']["end"])
|
|
&& ($input['plan']["begin"] < $input['plan']["end"])) {
|
|
|
|
$input['_plan'] = $input['plan'];
|
|
unset($input['plan']);
|
|
$input['is_planned'] = 1;
|
|
$input["begin"] = $input['_plan']["begin"];
|
|
$input["end"] = $input['_plan']["end"];
|
|
|
|
} else if (isset($this->fields['begin'])
|
|
&& isset($this->fields['end'])) {
|
|
Session::addMessageAfterRedirect(
|
|
__('Error in entering dates. The starting date is later than the ending date'),
|
|
false, ERROR);
|
|
}
|
|
}
|
|
|
|
// set new date.
|
|
$input["date"] = $_SESSION["glpi_currenttime"];
|
|
|
|
// encode rrule
|
|
if (isset($input['rrule']) && is_array($input['rrule'])) {
|
|
$input['rrule'] = $this->encodeRrule($input['rrule']);
|
|
}
|
|
|
|
return $input;
|
|
}
|
|
|
|
|
|
function prepareInputForUpdate($input) {
|
|
// manage guests
|
|
if (isset($input['users_id_guests']) && is_array($input['users_id_guests'])) {
|
|
$input['users_id_guests'] = exportArrayToDB($input['users_id_guests']);
|
|
|
|
// avoid warning on update method (string comparison with old value)
|
|
$this->fields['users_id_guests'] = exportArrayToDB($input['users_id_guests']);
|
|
}
|
|
|
|
Toolbox::manageBeginAndEndPlanDates($input['plan']);
|
|
|
|
if (isset($input['_planningrecall'])) {
|
|
PlanningRecall::manageDatas($input['_planningrecall']);
|
|
}
|
|
|
|
if (isset($input["name"])) {
|
|
$input["name"] = trim($input["name"]);
|
|
|
|
if (empty($input["name"])) {
|
|
$input["name"] = __('Without title');
|
|
}
|
|
}
|
|
|
|
if (isset($input['plan'])) {
|
|
|
|
if (!empty($input['plan']["begin"])
|
|
&& !empty($input['plan']["end"])
|
|
&& ($input['plan']["begin"] < $input['plan']["end"])) {
|
|
|
|
$input['_plan'] = $input['plan'];
|
|
unset($input['plan']);
|
|
$input['is_planned'] = 1;
|
|
$input["begin"] = $input['_plan']["begin"];
|
|
$input["end"] = $input['_plan']["end"];
|
|
|
|
} else if (isset($this->fields['begin'])
|
|
&& isset($this->fields['end'])) {
|
|
Session::addMessageAfterRedirect(
|
|
__('Error in entering dates. The starting date is later than the ending date'),
|
|
false, ERROR);
|
|
}
|
|
}
|
|
|
|
$input = $this->addFiles($input, ['content_field' => 'text']);
|
|
|
|
// encode rrule
|
|
if (isset($input['rrule']) && is_array($input['rrule'])) {
|
|
$input['rrule'] = $this->encodeRrule($input['rrule']);
|
|
}
|
|
|
|
return $input;
|
|
}
|
|
|
|
function encodeRrule(Array $rrule = []) {
|
|
|
|
if ($rrule['freq'] == null) {
|
|
return "";
|
|
}
|
|
|
|
if (isset($rrule['exceptions'])) {
|
|
if (is_string($rrule['exceptions']) && strlen($rrule['exceptions'])) {
|
|
$rrule['exceptions'] = explode(', ', $rrule['exceptions']);
|
|
}
|
|
if (!is_array($rrule['exceptions']) || count($rrule['exceptions']) === 0) {
|
|
unset($rrule['exceptions']);
|
|
}
|
|
}
|
|
|
|
if (count($rrule) > 0) {
|
|
$rrule = json_encode($rrule);
|
|
}
|
|
|
|
return $rrule;
|
|
}
|
|
|
|
|
|
function post_updateItem($history = 1) {
|
|
if (!isset($this->input['_no_check_plan'])
|
|
&& isset($this->fields["users_id"])
|
|
&& isset($this->fields["begin"])
|
|
&& !empty($this->fields["begin"])) {
|
|
Planning::checkAlreadyPlanned(
|
|
$this->fields["users_id"],
|
|
$this->fields["begin"],
|
|
$this->fields["end"],
|
|
[
|
|
$this->getType() => [$this->fields['id']]
|
|
]
|
|
);
|
|
}
|
|
if (in_array("begin", $this->updates)) {
|
|
PlanningRecall::managePlanningUpdates(
|
|
$this->getType(),
|
|
$this->getID(),
|
|
$this->fields["begin"]
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
function pre_updateInDB() {
|
|
// Set new user if initial user have been deleted
|
|
if (isset($this->fields['users_id'])
|
|
&& $this->fields['users_id'] == 0
|
|
&& $uid = Session::getLoginUserID()) {
|
|
$this->fields['users_id'] = $uid;
|
|
$this->updates[] ="users_id";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete a specific instance of a serie
|
|
* Add an exception into the serie
|
|
*
|
|
* @see addInstanceException
|
|
*/
|
|
function deleteInstance(int $id = 0, string $day = "") {
|
|
$this->addInstanceException($id, $day);
|
|
}
|
|
|
|
/**
|
|
* Add an exception into a serie
|
|
*
|
|
* @param int $id of the serie
|
|
* @param string $day the exception
|
|
*
|
|
* @return bool
|
|
*/
|
|
function addInstanceException(int $id = 0, string $day = "") {
|
|
$this->getFromDB($id);
|
|
$rrule = json_decode($this->fields['rrule'], true) ?? [];
|
|
$rrule = array_merge_recursive($rrule, [
|
|
'exceptions' => [
|
|
$day
|
|
]
|
|
]);
|
|
return $this->update([
|
|
'id' => $id,
|
|
'rrule' => $rrule,
|
|
'_no_check_plan' => true,
|
|
]);
|
|
}
|
|
|
|
|
|
/**
|
|
* Clone recurrent event into a non recurrent event
|
|
* (and add an exception to the orginal one)
|
|
*
|
|
* @param int $id of the serie
|
|
* @param string $start the new start for the event (in case of dragging)
|
|
*
|
|
* @return object the new object
|
|
*/
|
|
public function createInstanceClone(int $id = 0, string $start = "") {
|
|
$this->getFromDB($id);
|
|
$fields = $this->fields;
|
|
unset($fields['id'], $fields['uuid'], $fields['rrule']);
|
|
$fields['plan'] = [
|
|
'begin' => $fields['begin'],
|
|
'end' => $fields['end'],
|
|
];
|
|
// avoid checking availability, will be done after when updating new dates
|
|
$fields['_no_check_plan'] = true;
|
|
|
|
$instance = new self;
|
|
$new_id = $instance->add($fields);
|
|
$instance->getFromDB($new_id);
|
|
|
|
$this->addInstanceException($id, date("Y-m-d", strtotime($start)));
|
|
|
|
return $instance;
|
|
}
|
|
|
|
|
|
/**
|
|
* Populate the planning with planned event
|
|
*
|
|
* @param $options array of possible options:
|
|
* - who ID of the user (0 = undefined)
|
|
* - whogroup ID of the group of users (0 = undefined)
|
|
* - begin Date
|
|
* - end Date
|
|
* - color
|
|
* - event_type_color
|
|
* - check_planned (boolean)
|
|
* - display_done_events (boolean)
|
|
*
|
|
* @return array of planning item
|
|
**/
|
|
static function populatePlanning($options = []) :array {
|
|
global $DB, $CFG_GLPI;
|
|
|
|
$default_options = [
|
|
'genical' => false,
|
|
'color' => '',
|
|
'event_type_color' => '',
|
|
'check_planned' => false,
|
|
'display_done_events' => true,
|
|
];
|
|
$options = array_merge($default_options, $options);
|
|
|
|
$events = [];
|
|
$event_obj = new static;
|
|
$itemtype = $event_obj->getType();
|
|
$item_fk = getForeignKeyFieldForItemType($itemtype);
|
|
$table = self::getTable();
|
|
$has_bg = $DB->fieldExists($table, 'background');
|
|
|
|
if (!isset($options['begin']) || $options['begin'] == 'NULL'
|
|
|| !isset($options['end']) || $options['end'] == 'NULL') {
|
|
return $events;
|
|
}
|
|
|
|
$who = $options['who'];
|
|
$whogroup = $options['whogroup'];
|
|
$begin = $options['begin'];
|
|
$end = $options['end'];
|
|
|
|
if ($options['genical']) {
|
|
$_SESSION["glpiactiveprofile"][static::$rightname] = READ;
|
|
}
|
|
$visibility_criteria = [];
|
|
if ($event_obj instanceof CommonDBVisible) {
|
|
$visibility_criteria = self::getVisibilityCriteria(true);
|
|
}
|
|
$nreadpub = [];
|
|
$nreadpriv = [];
|
|
|
|
// See public event ?
|
|
if (!$options['genical']
|
|
&& (Session::getLoginUserID() !== false && $who == Session::getLoginUserID())
|
|
&& self::canView()
|
|
&& isset($visibility_criteria['WHERE'])) {
|
|
$nreadpub = $visibility_criteria['WHERE'];
|
|
}
|
|
unset($visibility_criteria['WHERE']);
|
|
|
|
if ($whogroup === "mine") {
|
|
if (isset($_SESSION['glpigroups'])) {
|
|
$whogroup = $_SESSION['glpigroups'];
|
|
} else if ($who > 0) {
|
|
$whogroup = array_column(Group_User::getUserGroups($who), 'id');
|
|
}
|
|
}
|
|
|
|
// See my private event ?
|
|
if ($who > 0) {
|
|
$nreadpriv = ["$table.users_id" => $who];
|
|
|
|
// guests accounts
|
|
if ($DB->fieldExists($table, 'users_id_guests')) {
|
|
$nreadpriv = ['OR' => [
|
|
"$table.users_id" => $who,
|
|
"$table.users_id_guests" => ['LIKE', '%"'.$who.'"%'],
|
|
]];
|
|
}
|
|
}
|
|
|
|
if ($whogroup > 0 && $itemtype == 'Reminder') {
|
|
$ngrouppriv = ["glpi_groups_reminders.groups_id" => $whogroup];
|
|
if (!empty($nreadpriv)) {
|
|
$nreadpriv['OR'] = [$nreadpriv, $ngrouppriv];
|
|
} else {
|
|
$nreadpriv = $ngrouppriv;
|
|
}
|
|
}
|
|
|
|
$NASSIGN = [];
|
|
|
|
if (count($nreadpub)
|
|
&& count($nreadpriv)) {
|
|
$NASSIGN = ['OR' => [$nreadpub, $nreadpriv]];
|
|
} else if (count($nreadpub)) {
|
|
$NASSIGN = $nreadpub;
|
|
} else {
|
|
$NASSIGN = $nreadpriv;
|
|
}
|
|
|
|
if (!count($NASSIGN)) {
|
|
return $events;
|
|
}
|
|
|
|
$WHERE = [
|
|
'begin' => ['<', $end],
|
|
'end' => ['>', $begin]
|
|
] + [$NASSIGN]; // "encapsulate" nassign to prevent OR overriding
|
|
|
|
if ($DB->fieldExists($table, 'is_planned')) {
|
|
$WHERE["$table.is_planned"] = 1;
|
|
}
|
|
|
|
if ($options['check_planned']) {
|
|
$WHERE['state'] = ['!=', Planning::INFO];
|
|
}
|
|
|
|
if (!$options['display_done_events']) {
|
|
$WHERE[] = [
|
|
'OR' => [
|
|
'state' => Planning::TODO,
|
|
'AND' => [
|
|
'state' => Planning::INFO,
|
|
'end' => ['>', new QueryExpression('NOW()')]
|
|
]
|
|
]
|
|
];
|
|
}
|
|
|
|
$event_obj->getEmpty();
|
|
if (isset($event_obj->fields['rrule'])) {
|
|
unset($WHERE['end']);
|
|
$WHERE[] = [
|
|
'OR' => [
|
|
'end' => ['>', $begin],
|
|
'rrule' => ['!=', ""],
|
|
]
|
|
];
|
|
}
|
|
|
|
$criteria = [
|
|
'SELECT' => ["$table.*"],
|
|
'DISTINCT' => true,
|
|
'FROM' => $table,
|
|
'WHERE' => $WHERE,
|
|
'ORDER' => 'begin'
|
|
] + $visibility_criteria;
|
|
|
|
if (isset($event_obj->fields['planningeventcategories_id'])) {
|
|
$c_table = PlanningEventCategory::getTable();
|
|
$criteria['SELECT'][] = "$c_table.color AS cat_color";
|
|
$criteria['JOIN'] = [
|
|
$c_table => [
|
|
'FKEY' => [
|
|
$c_table => 'id',
|
|
$table => 'planningeventcategories_id',
|
|
]
|
|
]
|
|
];
|
|
}
|
|
|
|
$iterator = $DB->request($criteria);
|
|
|
|
$events_toadd = [];
|
|
|
|
if (count($iterator)) {
|
|
while ($data = $iterator->next()) {
|
|
if ($event_obj->getFromDB($data["id"]) && $event_obj->canViewItem()) {
|
|
$key = $data["begin"].
|
|
"$$".$itemtype.
|
|
"$$".$data["id"].
|
|
"$$".$who.
|
|
"$$".$whogroup;
|
|
if (isset($options['from_group_users'])) {
|
|
$key.= "_gu";
|
|
}
|
|
|
|
$url = (!$options['genical'])
|
|
? $event_obj->getFormURLWithID($data['id'])
|
|
: $CFG_GLPI["url_base"].
|
|
self::getFormURLWithID($data['id'], false);
|
|
|
|
$is_rrule = isset($data['rrule']) && strlen($data['rrule']) > 0;
|
|
|
|
$events[$key] = [
|
|
'color' => $options['color'],
|
|
'event_type_color' => $options['event_type_color'],
|
|
'event_cat_color' => $data['cat_color'] ?? "",
|
|
'itemtype' => $itemtype,
|
|
$item_fk => $data['id'],
|
|
'id' => $data['id'],
|
|
'users_id' => $data["users_id"],
|
|
'state' => $data["state"],
|
|
'background' => $has_bg ? $data['background'] : false,
|
|
'name' => Html::clean(Html::resume_text($data["name"], $CFG_GLPI["cut"])),
|
|
'text' => Html::resume_text(Html::clean(Toolbox::unclean_cross_side_scripting_deep($data["text"])),
|
|
$CFG_GLPI["cut"]),
|
|
'ajaxurl' => $CFG_GLPI["root_doc"]."/ajax/planning.php".
|
|
"?action=edit_event_form".
|
|
"&itemtype=$itemtype".
|
|
"&id=".$data['id'].
|
|
"&url=$url",
|
|
'editable' => $event_obj->canUpdateItem(),
|
|
'url' => $url,
|
|
'begin' => !$is_rrule && (strcmp($begin, $data["begin"]) > 0)
|
|
? $begin
|
|
: $data["begin"],
|
|
'end' => !$is_rrule && (strcmp($end, $data["end"]) < 0)
|
|
? $end
|
|
: $data["end"],
|
|
'rrule' => isset($data['rrule']) && !empty($data['rrule'])
|
|
? json_decode($data['rrule'], true)
|
|
: []
|
|
];
|
|
|
|
// when checking avaibility, we need to explode rrules events
|
|
// to check if future occurences of the primary event
|
|
// doesn't match current range
|
|
if ($options['check_planned'] && count($events[$key]['rrule'])) {
|
|
$event = $events[$key];
|
|
$duration = strtotime($event['end']) - strtotime($event['begin']);
|
|
|
|
$rset = self::getRsetFromRRuleField($event['rrule'], $event['begin']);
|
|
|
|
// - rrule object doesn't any duration property,
|
|
// so we remove the duration from the begin part of the range
|
|
// (minus 1second to avoid mathing precise end date)
|
|
// to check if event started before begin and could be still valid
|
|
// - also set begin and end dates like it was as UTC
|
|
// (Rrule lib will always compare with UTC)
|
|
$begin_datetime = new DateTime($options['begin'], new DateTimeZone('UTC'));
|
|
$begin_datetime->sub(New DateInterval("PT".($duration - 1)."S"));
|
|
$end_datetime = new DateTime($options['end'], new DateTimeZone('UTC'));
|
|
$occurences = $rset->getOccurrencesBetween($begin_datetime, $end_datetime);
|
|
|
|
// add the found occurences to the final tab after replacing their dates
|
|
foreach ($occurences as $currentDate) {
|
|
$occurence_begin = $currentDate;
|
|
$occurence_end = (clone $currentDate)->add(new DateInterval("PT".$duration."S"));
|
|
|
|
$events_toadd[] = array_merge($event, [
|
|
'begin' => $occurence_begin->format('Y-m-d H:i:s'),
|
|
'end' => $occurence_end->format('Y-m-d H:i:s'),
|
|
]);
|
|
}
|
|
|
|
// remove primary event (with rrule)
|
|
// as the final array now have all the occurences
|
|
unset($events[$key]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (count($events_toadd)) {
|
|
$events = $events + $events_toadd;
|
|
}
|
|
|
|
return $events;
|
|
}
|
|
|
|
|
|
/**
|
|
* Display a Planning Item
|
|
*
|
|
* @param $val array of the item to display
|
|
* @param $who ID of the user (0 if all)
|
|
* @param $type position of the item in the time block (in, through, begin or end)
|
|
* default '')
|
|
* @param $complete complete display (more details) (default 0)
|
|
*
|
|
* @return Nothing (display function)
|
|
**/
|
|
static function displayPlanningItem(array $val, $who, $type = "", $complete = 0) {
|
|
global $CFG_GLPI;
|
|
|
|
$html = "";
|
|
$rand = mt_rand();
|
|
$users_id = ""; // show users_id reminder
|
|
$img = "rdv_private.png"; // default icon for reminder
|
|
$item_fk = getForeignKeyFieldForItemType(static::getType());
|
|
|
|
if ($val["users_id"] != Session::getLoginUserID()) {
|
|
$users_id = "<br>".sprintf(__('%1$s: %2$s'), __('By'), getUserName($val["users_id"]));
|
|
$img = "rdv_public.png";
|
|
}
|
|
|
|
$html.= "<img src='".$CFG_GLPI["root_doc"]."/pics/".$img."' alt='' title=\"".
|
|
self::getTypeName(1)."\"> ";
|
|
$html.= "<a id='reminder_".$val[$item_fk].$rand."' href='".
|
|
Reminder::getFormURLWithID($val[$item_fk])."'>";
|
|
|
|
$html.= $users_id;
|
|
$html.= "</a>";
|
|
$recall = '';
|
|
if (isset($val[$item_fk])) {
|
|
$pr = new PlanningRecall();
|
|
if ($pr->getFromDBForItemAndUser($val['itemtype'], $val[$item_fk],
|
|
Session::getLoginUserID())) {
|
|
$recall = "<br><span class='b'>".sprintf(__('Recall on %s'),
|
|
Html::convDateTime($pr->fields['when'])).
|
|
"<span>";
|
|
}
|
|
}
|
|
|
|
if ($complete) {
|
|
$html.= "<span>".Planning::getState($val["state"])."</span><br>";
|
|
$html.= "<div class='event-description rich_text_container'>".$val["text"].$recall."</div>";
|
|
} else {
|
|
$html.= Html::showToolTip("<span class='b'>".Planning::getState($val["state"])."</span><br>
|
|
".$val["text"].$recall,
|
|
['applyto' => "reminder_".$val[$item_fk].$rand,
|
|
'display' => false]);
|
|
}
|
|
return $html;
|
|
}
|
|
|
|
|
|
/**
|
|
* Display a mini form html for setup a reccuring event
|
|
* to construct an rrule array
|
|
*
|
|
* @param string $rrule existing rrule entry with ical format (https://www.kanzaki.com/docs/ical/rrule.html)
|
|
* @param array $options can contains theses keys:
|
|
* - 'rand' => random string for generated inputs
|
|
* @return string the generated html
|
|
*/
|
|
static function showRepetitionForm(string $rrule = "", array $options = []): string {
|
|
$rrule = json_decode($rrule, true) ?? [];
|
|
$defaults = [
|
|
'freq' => null,
|
|
'interval' => 1,
|
|
'until' => null,
|
|
'byday' => [],
|
|
'bymonth' => [],
|
|
'exceptions' => [],
|
|
];
|
|
$rrule = array_merge($defaults, $rrule);
|
|
|
|
$default_options = [
|
|
'rand' => mt_rand(),
|
|
];
|
|
$options = array_merge($default_options, $options);
|
|
$rand = $options['rand'];
|
|
|
|
$out = "<div class='card' style='padding: 5px; width: 100%;'>";
|
|
$out.= Dropdown::showFromArray('rrule[freq]', [
|
|
null => __("Never"),
|
|
'daily' => __("Each day"),
|
|
'weekly' => __("Each week"),
|
|
'monthly' => __("Each month"),
|
|
'yearly' => __("Each year"),
|
|
], [
|
|
'value' => strtolower($rrule['freq']),
|
|
'rand' => $rand,
|
|
'display' => false,
|
|
'on_change' => "$(\"#toggle_ar\").toggle($(\"#dropdown_rrule_freq_$rand\").val().length > 0)"
|
|
]);
|
|
|
|
$display_tar = $rrule['freq'] == null ? "none" : "inline";
|
|
$display_ar = $rrule['freq'] == null
|
|
|| !($rrule['interval'] > 1
|
|
|| $rrule['until'] != null
|
|
|| count($rrule['byday']) > 0
|
|
|| count($rrule['bymonth']) > 0)
|
|
? "none" : "table";
|
|
|
|
$out.= "<span id='toggle_ar' style='display: $display_tar'>";
|
|
$out.= "<a class='vsubmit'
|
|
title='".__("Personalization")."'
|
|
onclick='$(\"#advanced_repetition$rand\").toggle()'>
|
|
<i class='fas fa-cog'></i>
|
|
</a>";
|
|
$out.= "<div id='advanced_repetition$rand' style='display: $display_ar; max-width: 23'>";
|
|
|
|
$out.= "<div class='field'>";
|
|
$out.= "<label for='dropdown_interval$rand'>".__("Interval")."</label>";
|
|
$out.= "<div>".Dropdown::showNumber('rrule[interval]', [
|
|
'value' => $rrule['interval'],
|
|
'rand' => $rand,
|
|
'display' => false,
|
|
])."</div>";
|
|
$out.= "</div>";
|
|
|
|
$out.= "<div class='field'>";
|
|
$out.= "<label for='showdate$rand'>".__("Until")."</label>";
|
|
$out.= "<div>".Html::showDateField('rrule[until]', [
|
|
'value' => $rrule['until'],
|
|
'rand' => $rand,
|
|
'display' => false,
|
|
])."</div>";
|
|
$out.= "</div>";
|
|
|
|
$out.= "<div class='field'>";
|
|
$out.= "<label for='dropdown_byday$rand'>".__("By day")."</label>";
|
|
$out.= "<div>".Dropdown::showFromArray('rrule[byday]', [
|
|
'MO' => __('Monday'),
|
|
'TU' => __('Tuesday'),
|
|
'WE' => __('Wednesday'),
|
|
'TH' => __('Thursday'),
|
|
'FR' => __('Friday'),
|
|
'SA' => __('Saturday'),
|
|
'SU' => __('Sunday'),
|
|
], [
|
|
'values' => $rrule['byday'],
|
|
'rand' => $rand,
|
|
'display' => false,
|
|
'display_emptychoice' => true,
|
|
'width' => '100%',
|
|
'multiple' => true,
|
|
])."</div>";
|
|
$out.= "</div>";
|
|
|
|
$out.= "<div class='field'>";
|
|
$out.= "<label for='dropdown_bymonth$rand'>".__("By month")."</label>";
|
|
$out.= "<div>".Dropdown::showFromArray('rrule[bymonth]', [
|
|
1 => __('January'),
|
|
2 => __('February'),
|
|
3 => __('March'),
|
|
4 => __('April'),
|
|
5 => __('May'),
|
|
6 => __('June'),
|
|
7 => __('July'),
|
|
8 => __('August'),
|
|
9 => __('September'),
|
|
10 => __('October'),
|
|
11 => __('November'),
|
|
12 => __('December'),
|
|
], [
|
|
'values' => $rrule['bymonth'],
|
|
'rand' => $rand,
|
|
'display' => false,
|
|
'display_emptychoice' => true,
|
|
'width' => '100%',
|
|
'multiple' => true,
|
|
])."</div>";
|
|
$out.= "</div>";
|
|
|
|
$rand = mt_rand();
|
|
$out.= "<div class='field'>";
|
|
$out.= "<label for='showdate$rand'>".__("Exceptions")."</label>";
|
|
$out.= "<div>".Html::showDateField('rrule[exceptions]', [
|
|
'value' => implode(', ', $rrule['exceptions']),
|
|
'rand' => $rand,
|
|
'display' => false,
|
|
'multiple' => true,
|
|
'size' => 30,
|
|
])."</div>";
|
|
$out.= "</div>";
|
|
|
|
$out.= "</div>"; // #advanced_repetition
|
|
$out.= "</span>"; // #toggle_ar
|
|
$out.= "</div>"; // .card
|
|
return $out;
|
|
}
|
|
|
|
|
|
/**
|
|
* Display a Planning Item
|
|
*
|
|
* @param array $val the item to display
|
|
*
|
|
* @return string
|
|
**/
|
|
public function getAlreadyPlannedInformation(array $val) {
|
|
$itemtype = $this->getType();
|
|
if ($item = getItemForItemtype($itemtype)) {
|
|
$objectitemtype = (method_exists($item, 'getItilObjectItemType') ? $item->getItilObjectItemType() : $itemtype);
|
|
|
|
//TRANS: %1$s is a type, %2$$ is a date, %3$s is a date
|
|
$out = sprintf(__('%1$s: from %2$s to %3$s:'), $item->getTypeName(1),
|
|
Html::convDateTime($val["begin"]), Html::convDateTime($val["end"]));
|
|
$out .= "<br/><a href='".$objectitemtype::getFormURLWithID($val[getForeignKeyFieldForItemType($objectitemtype)]);
|
|
if ($item instanceof CommonITILTask) {
|
|
$out .= "&forcetab=".$itemtype."$1";
|
|
}
|
|
$out .= "'>";
|
|
$out .= Html::resume_text($val["name"], 80).'</a>';
|
|
|
|
return $out;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns RSet occurence corresponding to rrule field value.
|
|
*
|
|
* @param array $rrule RRule field value
|
|
* @param string $dtstart Start of first occurence
|
|
*
|
|
* @return \RRule\RSet
|
|
*/
|
|
public static function getRsetFromRRuleField(array $rrule, $dtstart): RSet {
|
|
$dtstart_datetime = new DateTime($dtstart);
|
|
$rrule['dtstart'] = $dtstart_datetime->format('Y-m-d\TH:i:s\Z');
|
|
|
|
// create a ruleset containing dtstart, the rrule, and the exclusions
|
|
$rset = new RSet();
|
|
|
|
// manage date exclusions,
|
|
// we need to set a top level property for that (not directly in rrule one)
|
|
if (isset($rrule['exceptions'])) {
|
|
foreach ($rrule['exceptions'] as $exception) {
|
|
$exdate = new DateTime($exception);
|
|
$exdate->setTime(
|
|
$dtstart_datetime->format('G'),
|
|
$dtstart_datetime->format('i'),
|
|
$dtstart_datetime->format('s')
|
|
);
|
|
$rset->addExDate($exdate->format('Y-m-d\TH:i:s\Z'));
|
|
}
|
|
|
|
// remove exceptions key (as libraries throw exception for unknow keys)
|
|
unset($rrule['exceptions']);
|
|
}
|
|
|
|
// remove specific change from js library to match rfc
|
|
if (isset($rrule['byweekday']) || isset($rrule['BYWEEKDAY'])) {
|
|
$rrule['byday'] = $rrule['byweekday'] ?? $rrule['BYWEEKDAY'];
|
|
unset($rrule['byweekday'], $rrule['BYWEEKDAY']);
|
|
}
|
|
|
|
$rset->addRRule(new RRule($rrule));
|
|
|
|
return $rset;
|
|
}
|
|
|
|
|
|
function rawSearchOptions() {
|
|
$tab = [
|
|
[
|
|
'id' => 'common',
|
|
'name' => self::GetTypeName()
|
|
], [
|
|
'id' => '1',
|
|
'table' => self::getTable(),
|
|
'field' => 'name',
|
|
'name' => __('Name'),
|
|
'datatype' => 'itemlink',
|
|
'massiveaction' => false,
|
|
'autocomplete' => true,
|
|
], [
|
|
'id' => '2',
|
|
'table' => self::getTable(),
|
|
'field' => 'id',
|
|
'name' => __('ID'),
|
|
'massiveaction' => false,
|
|
'datatype' => 'number'
|
|
], [
|
|
'id' => '80',
|
|
'table' => 'glpi_entities',
|
|
'field' => 'completename',
|
|
'name' => Entity::getTypeName(1),
|
|
'datatype' => 'dropdown'
|
|
], [
|
|
'id' => '3',
|
|
'table' => self::getTable(),
|
|
'field' => 'state',
|
|
'name' => __('Status'),
|
|
'datatype' => 'specific',
|
|
'massiveaction' => false,
|
|
'searchtype' => ['equals', 'notequals']
|
|
], [
|
|
'id' => '4',
|
|
'table' => $this->getTable(),
|
|
'field' => 'text',
|
|
'name' => __('Description'),
|
|
'massiveaction' => false,
|
|
'datatype' => 'text',
|
|
'htmltext' => true
|
|
], [
|
|
'id' => '5',
|
|
'table' => PlanningEventCategory::getTable(),
|
|
'field' => 'name',
|
|
'name' => PlanningEventCategory::getTypeName(),
|
|
'forcegroupby' => true,
|
|
'datatype' => 'dropdown'
|
|
], [
|
|
'id' => '6',
|
|
'table' => self::getTable(),
|
|
'field' => 'background',
|
|
'name' => __('Background event'),
|
|
'datatype' => 'bool'
|
|
], [
|
|
'id' => '10',
|
|
'table' => self::getTable(),
|
|
'field' => 'rrule',
|
|
'name' => __('Repeat'),
|
|
'datatype' => 'text'
|
|
], [
|
|
'id' => '19',
|
|
'table' => self::getTable(),
|
|
'field' => 'date_mod',
|
|
'name' => __('Last update'),
|
|
'datatype' => 'datetime',
|
|
'massiveaction' => false
|
|
], [
|
|
'id' => '121',
|
|
'table' => self::getTable(),
|
|
'field' => 'date_creation',
|
|
'name' => __('Creation date'),
|
|
'datatype' => 'datetime',
|
|
'massiveaction' => false
|
|
]
|
|
];
|
|
|
|
if (!count($this->fields)) {
|
|
$this->getEmpty();
|
|
}
|
|
|
|
if (isset($this->fields['is_recursive'])) {
|
|
$tab[] = [
|
|
'id' => 86,
|
|
'table' => self::getTable(),
|
|
'field' => 'is_recursive',
|
|
'name' => __('Child entities'),
|
|
'datatype' =>'bool'
|
|
];
|
|
}
|
|
|
|
if (isset($this->fields['users_id'])) {
|
|
$tab[] = [
|
|
'id' => '70',
|
|
'table' => User::getTable(),
|
|
'field' => 'name',
|
|
'name' => User::getTypeName(1),
|
|
'datatype' => 'dropdown',
|
|
'right' => 'all'
|
|
];
|
|
}
|
|
|
|
if (isset($this->fields['users_id_guests'])) {
|
|
$tab[] = [
|
|
'id' => '12',
|
|
'table' => self::getTable(),
|
|
'field' => 'users_id_guests',
|
|
'name' => __('Guests'),
|
|
'datatype' => 'text',
|
|
];
|
|
}
|
|
|
|
if (isset($this->fields['begin'])) {
|
|
$tab[] = [
|
|
'id' => '8',
|
|
'table' => self::getTable(),
|
|
'field' => 'begin',
|
|
'name' => __('Planning start date'),
|
|
'datatype' => 'datetime'
|
|
];
|
|
}
|
|
|
|
if (isset($this->fields['end'])) {
|
|
$tab[] = [
|
|
'id' => '9',
|
|
'table' => self::getTable(),
|
|
'field' => 'end',
|
|
'name' => __('Planning end date'),
|
|
'datatype' => 'datetime'
|
|
];
|
|
}
|
|
|
|
if (isset($this->fields['comment'])) {
|
|
$tab[] = [
|
|
'id' => '11',
|
|
'table' => $this->getTable(),
|
|
'field' => 'comment',
|
|
'name' => _n('Comment', 'Comments', 1),
|
|
'massiveaction' => false,
|
|
'datatype' => 'text',
|
|
];
|
|
}
|
|
|
|
return $tab;
|
|
}
|
|
}
|