* Copyright (C) 2024 Frédéric France * * 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 . */ /** * \file htdocs/dav/dav.class.php * \ingroup dav * \brief Server DAV */ /** * Define Common function to access calendar items and format it in vCalendar */ class CdavLib { /** * @var DoliDB */ private $db; /** * @var User */ private $user; /** * @var Translate */ private $langs; // @phpstan-ignore-line /** * Constructor * * @param User $user user * @param DoliDB $db Database handler * @param Translate $langs translation */ public function __construct($user, $db, $langs) { $this->user = $user; $this->db = $db; $this->langs = $langs; } /** * Base sql request for calendar events * * @param int $calid Calendard id * @param int|boolean $oid Oid * @param int|boolean $ouri Ouri * @return string */ public function getSqlCalEvents($calid, $oid = false, $ouri = false) { // TODO : replace GROUP_CONCAT by $sql = 'SELECT a.tms AS lastupd, a.*, sp.firstname, sp.lastname, sp.address, sp.zip, sp.town, co.label country_label, sp.phone, sp.phone_perso, sp.phone_mobile, s.nom AS soc_nom, s.address soc_address, s.zip soc_zip, s.town soc_town, cos.label soc_country_label, s.phone soc_phone, ac.sourceuid, (SELECT GROUP_CONCAT(u.login) FROM '.MAIN_DB_PREFIX.'actioncomm_resources ar LEFT OUTER JOIN '.MAIN_DB_PREFIX.'user AS u ON (u.rowid=fk_element) WHERE ar.element_type=\'user\' AND fk_actioncomm=a.id) AS other_users FROM '.MAIN_DB_PREFIX.'actioncomm AS a'; $sql .= " LEFT JOIN '.MAIN_DB_PREFIX.'c_country as co ON co.rowid = sp.fk_pays LEFT JOIN '.MAIN_DB_PREFIX.'c_country as cos ON cos.rowid = s.fk_pays WHERE a.id IN (SELECT ar.fk_actioncomm FROM '.MAIN_DB_PREFIX.'actioncomm_resources ar WHERE ar.element_type='user' AND ar.fk_element=".((int) $calid).") AND a.code IN (SELECT cac.code FROM '.MAIN_DB_PREFIX.'c_actioncomm cac WHERE cac.type <> 'systemauto') AND a.entity IN (".getEntity('societe', 1).")"; // TODO Restrict on external users if ($oid !== false) { if ($ouri === false) { $sql .= ' AND a.id = '.((int) $oid); } else { $sql .= ' AND (a.id = '.((int) $oid)." OR ac.uuidext = '".$this->db->escape($ouri)."')"; } } return $sql; } /** * Convert calendar row to VCalendar string * * @param int $calid Calendar id * @param Object $obj Object id * @return string */ public function toVCalendar($calid, $obj) { /*$categ = array(); if($obj->soc_client) { $nick[] = $obj->soc_code_client; $categ[] = $this->langs->transnoentitiesnoconv('Customer'); }*/ $location = $obj->location; // contact address if (empty($location) && !empty($obj->address)) { $location = trim(str_replace(array("\r", "\t", "\n"), ' ', $obj->address)); $location = trim($location.', '.$obj->zip); $location = trim($location.' '.$obj->town); $location = trim($location.', '.$obj->country_label); } // contact address if (empty($location) && !empty($obj->soc_address)) { $location = trim(str_replace(array("\r", "\t", "\n"), ' ', $obj->soc_address)); $location = trim($location.', '.$obj->soc_zip); $location = trim($location.' '.$obj->soc_town); $location = trim($location.', '.$obj->soc_country_label); } /* $address = explode("\n", $obj->address, 2); foreach ($address as $kAddr => $vAddr) { $address[$kAddr] = trim(str_replace(array("\r", "\t"), ' ', str_replace("\n", ' | ', trim($vAddr)))); } $address[] = ''; $address[] = ''; */ if ($obj->percent == -1 && trim($obj->datep) != '') { $type = 'VEVENT'; } else { $type = 'VTODO'; } $timezone = date_default_timezone_get(); $caldata = "BEGIN:VCALENDAR\n"; $caldata .= "VERSION:2.0\n"; $caldata .= "METHOD:PUBLISH\n"; $caldata .= "PRODID:-//Dolibarr CDav//FR\n"; $caldata .= "BEGIN:".$type."\n"; $caldata .= "CREATED:".gmdate('Ymd\THis', strtotime($obj->datec))."Z\n"; $caldata .= "LAST-MODIFIED:".gmdate('Ymd\THis', strtotime($obj->lastupd))."Z\n"; $caldata .= "DTSTAMP:".gmdate('Ymd\THis', strtotime($obj->lastupd))."Z\n"; if ($obj->sourceuid == '') { $caldata .= "UID:".$obj->id.'-ev-'.$calid.'-cal-'.constant('CDAV_URI_KEY')."\n"; } else { $caldata .= "UID:".$obj->sourceuid."\n"; } $caldata .= "SUMMARY:".$obj->label."\n"; $caldata .= "LOCATION:".$location."\n"; $caldata .= "PRIORITY:".$obj->priority."\n"; if ($obj->fulldayevent) { $caldata .= "DTSTART;VALUE=DATE:".date('Ymd', strtotime($obj->datep))."\n"; if ($type == 'VEVENT') { if (trim($obj->datep2) != '') { $caldata .= "DTEND;VALUE=DATE:".date('Ymd', strtotime($obj->datep2) + 1)."\n"; } else { $caldata .= "DTEND;VALUE=DATE:".date('Ymd', strtotime($obj->datep) + (25 * 3600))."\n"; } } elseif (trim($obj->datep2) != '') { $caldata .= "DUE;VALUE=DATE:".date('Ymd', strtotime($obj->datep2) + 1)."\n"; } } else { $caldata .= "DTSTART;TZID=".$timezone.":".strtr($obj->datep, array(" "=>"T", ":"=>"", "-"=>""))."\n"; if ($type == 'VEVENT') { if (trim($obj->datep2) != '') { $caldata .= "DTEND;TZID=".$timezone.":".strtr($obj->datep2, array(" "=>"T", ":"=>"", "-"=>""))."\n"; } else { $caldata .= "DTEND;TZID=".$timezone.":".strtr($obj->datep, array(" "=>"T", ":"=>"", "-"=>""))."\n"; } } elseif (trim($obj->datep2) != '') { $caldata .= "DUE;TZID=".$timezone.":".strtr($obj->datep2, array(" "=>"T", ":"=>"", "-"=>""))."\n"; } } $caldata .= "CLASS:PUBLIC\n"; if ($obj->transparency == 1) { $caldata .= "TRANSP:TRANSPARENT\n"; } else { $caldata .= "TRANSP:OPAQUE\n"; } if ($type == 'VEVENT') { $caldata .= "STATUS:CONFIRMED\n"; } elseif ($obj->percent == 0) { $caldata .= "STATUS:NEEDS-ACTION\n"; } elseif ($obj->percent == 100) { $caldata .= "STATUS:COMPLETED\n"; } else { $caldata .= "STATUS:IN-PROCESS\n"; $caldata .= "PERCENT-COMPLETE:".$obj->percent."\n"; } $caldata .= "DESCRIPTION:"; $caldata .= strtr($obj->note, array("\n"=>"\\n", "\r"=>"")); if (!empty($obj->soc_nom)) { $caldata .= "\\n*DOLIBARR-SOC: ".$obj->soc_nom; } if (!empty($obj->soc_phone)) { $caldata .= "\\n*DOLIBARR-SOC-TEL: ".$obj->soc_phone; } if (!empty($obj->firstname) || !empty($obj->lastname)) { $caldata .= "\\n*DOLIBARR-CTC: ".trim($obj->firstname.' '.$obj->lastname); } if (!empty($obj->phone) || !empty($obj->phone_perso) || !empty($obj->phone_mobile)) { $caldata .= "\\n*DOLIBARR-CTC-TEL: ".trim($obj->phone.' '.$obj->phone_perso.' '.$obj->phone_mobile); } if (strpos($obj->other_users, ',')) { // several $caldata .= "\\n*DOLIBARR-USR: ".$obj->other_users; } $caldata .= "\n"; $caldata .= "END:".$type."\n"; $caldata .= "END:VCALENDAR\n"; return $caldata; } /** * getFullCalendarObjects * * @param int $calendarId Calendar id * @param int $bCalendarData Add calendar data * @return array|string[][] */ public function getFullCalendarObjects($calendarId, $bCalendarData) { $calid = (int) $calendarId; $calevents = array(); if (!$this->user->rights->agenda->myactions->read) { return $calevents; } if ($calid != $this->user->id && (!isset($this->user->rights->agenda->allactions->read) || !$this->user->rights->agenda->allactions->read)) { return $calevents; } $sql = $this->getSqlCalEvents($calid); $result = $this->db->query($sql); if ($result) { while ($obj = $this->db->fetch_object($result)) { $calendardata = $this->toVCalendar($calid, $obj); if ($bCalendarData) { $calevents[] = array( 'calendardata' => $calendardata, 'uri' => $obj->id.'-ev-'.constant('CDAV_URI_KEY'), 'lastmodified' => strtotime($obj->lastupd), 'etag' => '"'.md5($calendardata).'"', 'calendarid' => $calendarId, 'size' => strlen($calendardata), 'component' => strpos($calendardata, 'BEGIN:VEVENT') > 0 ? 'vevent' : 'vtodo', ); } else { $calevents[] = array( // 'calendardata' => $calendardata, not necessary because etag+size are present 'uri' => $obj->id.'-ev-'.constant('CDAV_URI_KEY'), 'lastmodified' => strtotime($obj->lastupd), 'etag' => '"'.md5($calendardata).'"', 'calendarid' => $calendarId, 'size' => strlen($calendardata), 'component' => strpos($calendardata, 'BEGIN:VEVENT') > 0 ? 'vevent' : 'vtodo', ); } } } return $calevents; } }