planning.plugin.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. <?php
  2. /*
  3. @name Planning
  4. @author Julien NARBONI <julien.narboni@core.fr>
  5. @licence Copyright
  6. @version 1.0.0
  7. @description Gestion du planning
  8. */
  9. //Déclaration d'un item de menu dans le menu principal
  10. function planning_menu(&$menuItems){
  11. global $myUser;
  12. if(!$myUser->can('planning','read')) return;
  13. $menuItems[] = array(
  14. 'sort'=>5,
  15. 'url'=>'index.php?module=planning',
  16. 'label'=>'Planning',
  17. 'icon'=> 'fas fa-calendar-alt',
  18. 'color'=> '#9b59b6'
  19. );
  20. }
  21. //Cette fonction va generer une page quand on clique sur planning dans menu
  22. function planning_page(){
  23. global $_;
  24. if(!isset($_['module']) || $_['module'] !='planning') return;
  25. User::check_access('planning','read');
  26. $page = !isset($_['page']) ? 'list' : $_['page'];
  27. $file = __DIR__.SLASH.'page.'.$page.'.php';
  28. if(!file_exists($file)) throw new Exception("Page ".$page." inexistante");
  29. require_once($file);
  30. }
  31. //Fonction executée lors de l'activation du plugin
  32. function planning_install($id){
  33. if($id != 'fr.core.planning') return;
  34. Entity::install(__DIR__);
  35. $type = new PlanningEventType();
  36. $type->editable = 0;
  37. $type->label = 'Rendez vous';
  38. $type->slug = 'rendez-vous';
  39. $type->color = '#8bc34a';
  40. $type->icon = 'far fa-calendar-check';
  41. $type->save();
  42. global $conf;
  43. $conf->put('planning_day_start','09:00');
  44. $conf->put('planning_day_end','17:00');
  45. $conf->put('planning_show_default',true);
  46. $conf->put('planning_allow_event_edit',true);
  47. $conf->put('planning_allow_share',true);
  48. $dictionary = new Dictionary();
  49. $dictionary->slug = 'planning_event_resource';
  50. if(Dictionary::rowCount(array('slug'=>$dictionary->slug)) ==0){
  51. $dictionary->label = 'Planning : Ressource';
  52. $dictionary->parent = 0;
  53. $dictionary->state = Dictionary::ACTIVE;
  54. $dictionary->save();
  55. }
  56. }
  57. //Fonction executée lors de la désactivation du plugin
  58. function planning_uninstall($id){
  59. if($id != 'fr.core.planning') return;
  60. Entity::uninstall(__DIR__);
  61. $dictionary = Dictionary::bySlug('planning_event_resource');
  62. if($dictionary!= false && $dictionary->id!=0){
  63. Dictionary::delete(array('parent'=>$dictionary->id));
  64. Dictionary::delete(array('id'=>$dictionary->id));
  65. }
  66. }
  67. //Déclaration des sections de droits du plugin
  68. //Déclaration des sections de droits du plugin
  69. Right::register('planning',array('label'=>'Gestion des droits sur le plugin planning'));
  70. //Comprends toutes les actions du plugin qui ne nécessitent pas de vue html
  71. require_once(__DIR__.SLASH.'action.php');
  72. //Déclaration du menu de réglages
  73. function planning_menu_setting(&$settingMenu){
  74. global $myUser;
  75. if(!$myUser->can('planning','configure')) return;
  76. $settingMenu[]= array(
  77. 'sort' =>1,
  78. 'url' => 'setting.php?section=planning',
  79. 'icon' => 'fas fa-angle-right',
  80. 'label' => 'Planning'
  81. );
  82. }
  83. //Déclaration des pages de réglages
  84. function planning_content_setting(){
  85. global $_;
  86. if(file_exists(__DIR__.SLASH.'setting.'.$_['section'].'.php'))
  87. require_once(__DIR__.SLASH.'setting.'.$_['section'].'.php');
  88. }
  89. function planning_save_event(&$data){
  90. require_once(__DIR__.SLASH.'PlanningEvent.class.php');
  91. $event = is_null($data['id']) ? new PlanningEvent() : PlanningEvent::getById($data['id']);
  92. foreach($data as $key=>$value)
  93. $event->$key = $value;
  94. $event->save();
  95. $data['id'] = $event->id;
  96. }
  97. function planning_delete_event($id){
  98. require_once(__DIR__.SLASH.'PlanningEvent.class.php');
  99. PlanningEvent::deleteById($id);
  100. }
  101. function planning_dav($command){
  102. if(substr($command, 0,12)!='dav/planning') return;
  103. require_once('common.php');
  104. global $conf,$myUser,$_;
  105. if(!$myUser || $myUser->login==''){
  106. foreach(explode('&',parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY)) as $parameter){
  107. $infos = explode('=',$parameter);
  108. $_[$infos[0]] = $infos[1];
  109. }
  110. if(!isset($_['u']) ){
  111. header('HTTP/1.0 401 Unauthorized');
  112. exit;
  113. }
  114. $myUser = User::check($_['u'],$_['p']);
  115. if(!$myUser->connected()){
  116. header('HTTP/1.0 403 Unauthorized');
  117. echo 'Bad login';
  118. exit;
  119. }
  120. global $myFirm;
  121. if($myUser->superadmin == 1){
  122. foreach(Firm::loadAll() as $firm)
  123. $firms[$firm->id] = $firm;
  124. $myUser->setFirms($firms);
  125. }
  126. if(is_numeric($myUser->preference('default_firm')) && $myUser->haveFirm($myUser->preference('default_firm'))){
  127. $_SESSION['firm'] = serialize(Firm::getById($myUser->preference('default_firm')));
  128. } else if(count($myUser->firms)!=0){
  129. $_SESSION['firm'] = serialize(reset($myUser->firms));
  130. } else {
  131. throw new Exception('Ce compte n\'est actif sur aucun établissement, veuillez contacter l\'administrateur');
  132. }
  133. $myFirm = isset($_SESSION['firm']) ? unserialize($_SESSION['firm']) : new Firm();
  134. $_SESSION['currentUser'] = serialize($myUser);
  135. }
  136. require_once(__DIR__.SLASH.'CalDavServer.class.php');
  137. $projectPath = preg_replace('|https?\:\/\/'.$_SERVER['REMOTE_ADDR'].'|i', '', ROOT_URL);
  138. $server = new CalDavServer();
  139. $server->root = $projectPath.'/dav/planning/';
  140. $server->calendarLastUpdate = function($user,$calendar){
  141. global $myUser,$myFirm;
  142. require_once(__DIR__.SLASH.'Planning.class.php');
  143. require_once(__DIR__.SLASH.'PlanningEvent.class.php');
  144. require_once(__DIR__.SLASH.'PlanningEventType.class.php');
  145. $planning = Planning::load(array('slug'=>$calendar,'owner'=>$user));
  146. $lastUpdated = PlanningEvent::load(array(),array('updated DESC'));
  147. return !$lastUpdated ? 0 : $lastUpdated->updated;
  148. };
  149. $server->searchEvents = function($user,$calendar){
  150. global $myUser,$myFirm;
  151. require_once(__DIR__.SLASH.'Planning.class.php');
  152. require_once(__DIR__.SLASH.'PlanningEvent.class.php');
  153. require_once(__DIR__.SLASH.'PlanningEventType.class.php');
  154. $events = array();
  155. $planning = Planning::load(array('slug'=>$calendar,'owner'=>$user));
  156. if(!$planning) return $events;
  157. $userObject = User::byLogin($user);
  158. foreach(PlanningEvent::getAll($user,$userObject->ranks[$myFirm->id],array($planning->id),null,null) as $event){
  159. $row = $event->toArray();
  160. $row['type'] = $row['type']->label;
  161. $row['label'] = htmlspecialchars(html_entity_decode($row['label'],ENT_QUOTES,'UTF-8'));
  162. $row['description'] =htmlspecialchars(html_entity_decode($row['description'],ENT_QUOTES,'UTF-8'));
  163. $row['type'] =htmlspecialchars(html_entity_decode($row['type'],ENT_QUOTES,'UTF-8'));
  164. $row['street'] =htmlspecialchars(html_entity_decode($row['street'],ENT_QUOTES,'UTF-8'));
  165. $row['city'] =htmlspecialchars(html_entity_decode($row['city'],ENT_QUOTES,'UTF-8'));
  166. $events[] = $row;
  167. }
  168. return $events;
  169. };
  170. $server->deleteEvent = function($user,$calendar,$event = null){
  171. global $myUser;
  172. require_once(__DIR__.SLASH.'PlanningEvent.class.php');
  173. require_once(__DIR__.SLASH.'PlanningEventType.class.php');
  174. $errors = PlanningEvent::removeAll($myUser->login,array($event));
  175. if(count($errors)>0) throw new Exception("Error Processing Request");
  176. };
  177. $server->saveEvent = function($user,$calendar,$event = null,$infos){
  178. global $myUser;
  179. require_once(__DIR__.SLASH.'Planning.class.php');
  180. require_once(__DIR__.SLASH.'PlanningEvent.class.php');
  181. require_once(__DIR__.SLASH.'PlanningEventType.class.php');
  182. $event = new PlanningEvent();
  183. if(strpos($infos->uid, '-')===false){
  184. $event = PlanningEvent::getById($infos->uid);
  185. $event = !$event ? new PlanningEvent():$event;
  186. }
  187. $planning = Planning::load(array('slug'=>$calendar,'owner'=>$user));
  188. if($planning->owner != $myUser->login){
  189. if(PlanningShare::rowCount(array('planning'=>$planning->id,'recipient'=>$myUser->login,'edit'=>1))==0)
  190. throw new Exception("Vous n'avez pas la permission d'éditer cet évenement");
  191. }
  192. $event->label = $infos->title;
  193. $event->planning = $planning->id;
  194. $event->startDate = $infos->start;
  195. $event->endDate = $infos->end;
  196. $event->street = $infos->location;
  197. $event->description = nl2br($infos->description);
  198. $event->save();
  199. };
  200. if($conf->get('planning_dav_log')=='1'){
  201. $logDirectory = File::dir().SLASH.'planning'.SLASH.'logs';
  202. if(!file_exists($logDirectory)) mkdir($logDirectory,0755,true);
  203. $server->log = function($type,$message,$isVerbose = false){
  204. file_put_contents($logDirectory.SLASH.'logs.txt','['.$type.'] : '.$message.PHP_EOL,FILE_APPEND);
  205. if(!$isVerbose) Log::put($message,'[CALDAV]['.$type.']');
  206. };
  207. }
  208. $server->start();
  209. }
  210. function planning_cron(){
  211. require_once(__DIR__.SLASH.'Planning.class.php');
  212. require_once(__DIR__.SLASH.'PlanningEvent.class.php');
  213. require_once(__DIR__.SLASH.'PlanningEventType.class.php');
  214. $query = 'SELECT e.*,t.label AS '.PlanningEventType::tableName().'_join_label FROM {{table}} e LEFT JOIN '.PlanningEventType::tableName().' t ON e.type=t.id WHERE notificationNumber!=? AND notificationState!=?';
  215. $data = array(0,PlanningEvent::NOTIFIED);
  216. $now = new DateTime();
  217. foreach(PlanningEvent::staticQuery($query,$data,true,1) as $event){
  218. $unity = 60 ;
  219. if($event->notificationUnity=='hour') $unity = 3600;
  220. if($event->notificationUnity=='day') $unity = 86400;
  221. $notificationDelay = $event->notificationNumber * $unity;
  222. $notificationDate = new DateTime();
  223. $notificationDate->setTimestamp($event->startDate - $notificationDelay);
  224. if($notificationDate > $now) continue;
  225. $type = $event->join('type');
  226. $html= 'Du <strong>'.date('d/m/Y H:i',$event->startDate).'</strong> au <strong>'.date('d/m/Y H:i',$event->endDate).'</strong>';
  227. if($event->street!='') $html.= ' au '.$event->street;
  228. $html.= '<br/><a class="btn btn-primary" href="index.php?module=planning&start='.date('Ymd',$event->startDate).'&event='.$event->id.'">Voir le rendez vous</a>';
  229. $recipients = array($event->creator);
  230. if($event->creator!=$event->updater) $recipients[] = $event->updater;
  231. // GESTION ENVOI NOTIFICATION
  232. Plugin::callHook('emit_notification',array(array(
  233. 'label' => $type->label.' : '.$event->label.' '.strtolower(relative_time($event->startDate)),
  234. 'html' => $html,
  235. 'type' => 'notice',
  236. 'meta' => array('link' => ROOT_URL.'/index.php?module=planning&start='.date('Ymd',$event->startDate).'&event='.$event->id),
  237. 'end' => strtotime('+2day'),
  238. 'recipients' => array_unique($recipients) // recipients contient login
  239. )
  240. ));
  241. $event->notificationState = PlanningEvent::NOTIFIED;
  242. $event->save();
  243. }
  244. }
  245. function planning_widget(&$widgets){
  246. require_once(__DIR__.SLASH.'..'.SLASH.'dashboard'.SLASH.'DashboardWidget.class.php');
  247. $modelWidget = new DashboardWidget();
  248. $modelWidget->model = 'planning';
  249. $modelWidget->title = 'Planning';
  250. $modelWidget->icon = 'far fa-calendar-alt';
  251. $modelWidget->background = '#ff7979';
  252. $modelWidget->load = 'action.php?action=planning_widget_load';
  253. $modelWidget->css = [Plugin::url().'/css/widget.css?v=2'];
  254. $modelWidget->description = "Affiche vos 10 prochains rendez vous";
  255. $widgets[] = $modelWidget;
  256. }
  257. //Déclaration des settings de base
  258. Configuration::setting('planning',array(
  259. "Configuration générale",
  260. 'planning_day_start' => array("label"=>"Heure de début de journée","legend"=>"Heure ouvrée pour votre établissement au format HH:mm","type"=>"hour", "placeholder"=>"08:00"),
  261. 'planning_day_end' => array("label"=>"Heure de fin de journée","legend"=>"Heure ouvrée pour votre établissement au format HH:mm","type"=>"hour", "placeholder"=>"17:00"),
  262. 'planning_default_view' => array("label"=>"Vue par défaut","type"=>"list", "values"=>array(
  263. 'teammate'=>'Vue equipe',
  264. 'dayGridMonth'=>'Vue mois',
  265. 'timeGridWeek'=>'Vue semaine',
  266. 'timeGridDay'=>'Vue jour',
  267. 'listMonth'=>'Vue liste'
  268. )
  269. ),
  270. 'planning_allow_event_edit' => array("label"=>"Autoriser l'édition d'évenement","type"=>"boolean"),
  271. 'planning_show_default' => array("label"=>"Afficher le planning par défaut (général)","type"=>"boolean"),
  272. 'planning_allow_share' => array("label"=>"Autoriser le partage de calendrier","type"=>"boolean"),
  273. "CalDAV",
  274. 'planning_dav_log' => array("label"=>"Activer les logs caldav","legend"=>"Les logs caldav peuvent consommer de la performance","type"=>"boolean")
  275. ));
  276. //Déclation des assets
  277. Plugin::addCss("/css/fullcalendar.min.css?v=1");
  278. Plugin::addJs("/js/rrule.min.js?v=1");
  279. Plugin::addJs("/js/fullcalendar.min.js?v=1");
  280. Plugin::addJs("/js/fullcalendar.kiss.js?v=1");
  281. Plugin::addJs("/js/rrule.connector.min.js?v=1");
  282. Plugin::addJs("/js/locale-all.min.js");
  283. Plugin::addJs("/js/main.js?v=2");
  284. Plugin::addCss("/css/fullcalendar.kiss.css?v=2");
  285. Plugin::addCss("/css/main.css?v=2");
  286. //Mapping hook / fonctions
  287. Plugin::addHook("install", "planning_install");
  288. Plugin::addHook("uninstall", "planning_uninstall");
  289. Plugin::addHook("menu_main", "planning_menu");
  290. Plugin::addHook("page", "planning_page");
  291. Plugin::addHook("menu_setting", "planning_menu_setting");
  292. Plugin::addHook("content_setting", "planning_content_setting");
  293. Plugin::addHook("save_planning_event", "planning_save_event");
  294. Plugin::addHook("delete_planning_event", "planning_delete_event");
  295. Plugin::addHook("rewrite", "planning_dav");
  296. Plugin::addHook("cron", "planning_cron");
  297. Plugin::addHook("widget", "planning_widget");
  298. /* EXEMPLE EVENT CUSTOM HOOK
  299. Plugin::addHook("planning_event_search", function(&$events){
  300. $events[] = array(
  301. 'id' => '148',
  302. 'title' => 'hello world',
  303. 'type' => 2,
  304. 'planning' => '',
  305. 'planningOwner' => 'admin',
  306. 'planningDefault' => 1,
  307. 'allDay' => false,
  308. 'street' => '04 pl de la rotonde',
  309. 'group' => '123456',
  310. 'city' => 'Pessac',
  311. 'zip' => '33600',
  312. 'notificationNumber' => 0,
  313. 'notificationUnity' => 0,
  314. 'start' => date('Y-m-d\TH:i:s',time()),
  315. 'end' => date('Y-m-d\TH:i:s',time()+3600),
  316. 'backgroundColor' => 'lime',
  317. 'typeColor' => 'lime',
  318. 'borderColor' => 'transparent',
  319. 'editable' => false,
  320. 'icon' => 'fas fa-user',
  321. 'textColor' => 'white',
  322. 'description' => 'test',
  323. 'location' => 'pessac'
  324. );
  325. });
  326. */
  327. ?>