1<?php 2/** 3 * @license http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.html 4 * @author Francois Merciol <dokuplugin@merciol.fr> 5 * 6 * Plugin Schedule: manage events per wiki @groups 7 * 8 * $df_ : 31/12/2000 9 * $ds_ : 20001231 10 * $dn_ : 978217200 11 * $dt_ : array ("Y" => 2000, "m" => 12, "d" => "31) 12 */ 13if (!defined ('DOKU_INC')) 14 define ('DOKU_INC', realpath (dirname (__FILE__).'/../../../').'/'); 15if (!defined ('DOKU_PLUGIN')) 16 define ('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); 17 18function cmpScheduleConfig ($a, $b) { 19 return ($a['nameSpace'] < $b['nameSpace']) ? -1 : 1; 20} 21 22// ============================================================ 23// admin class 24class scheduleRoot { 25 26 // ============================================================ 27 // Config attributs 28 // ============================================================ 29 var $cacheRootDir; // root cache directory 30 var $dataRootDir; // root data directory 31 32 var $scheduleGroup; // group who can display and fill form 33 var $groupsDir; // pages groups directories 34 var $scheduleDir; // schedule pages groups directories 35 var $sharedDir; // schedule directory for shared events 36 var $scheduleWhat = array (); // set of legal "what" 37 var $scheduleAudience = array (); // set of legal "audience" 38 var $iconName; // group icon name 39 var $iconWidth; // group icon width 40 // lang 41 var $scheduleShared = array (); // notshared/shared 42 var $currentDateFormat; 43 44 // ============================================================ 45 // Constant attributs $this->scheduleRoot-> 46 // ============================================================ 47 //var $imagesUrlDir = '/lib/plugins/schedule/images/'; // DOKU_REL 48 var $wizardIconUrl = '/lib/plugins/schedule/images/wizard.png'; // $this->imagesUrlDir 49 var $editIconUrl = '/lib/plugins/schedule/images/edit.png'; 50 var $repeatIconUrl = '/lib/plugins/schedule/images/repeat.png'; 51 var $emptyIconUrl = '/lib/plugins/schedule/images/empty.png'; 52 53 var $oldConfigFile = "config.xml"; // old file config name 54 var $configFile = "cfg.xml"; // file config name 55 var $mbrPrefix = "mbr-"; // member prefix file name 56 var $propFile = "prop.xml"; // member prefix file name 57 var $configAttributsName = array ("nameSpace", "lastNotification", "lastNotificationReset"); 58 // scheduleDir == NS ? 59 var $dateFormat = array ("dmY" => array ("shortScan" => "(?<d>[0-9]{1,2})[ /-](?<m>[0-9]{1,2})[ /-](?<Y>[0-9]{1,2})", 60 "longScan" => "(?<d>[0-9]{1,2})[ /-](?<m>[0-9]{1,2})[ /-](?<Y>[0-9]{4})", 61 "fieldName" => array ("d", "m", "Y"), 62 "prettyPrint" => "d/m/Y", 63 "dayMonth" => "d/m"), 64 65 "mdY" => array ("shortScan" => "(?<m>[0-9]{1,2})[ /-](?<d>[0-9]{1,2})[ /-](?<Y>[0-9]{1,2})", 66 "longScan" => "(?<m>[0-9]{1,2})[ /-](?<d>[0-9]{1,2})[ /-](?<Y>[0-9]{4})", 67 "fieldName" => array ("m", "d", "Y"), 68 "prettyPrint" => "m/d/Y", 69 "dayMonth" => "m/d")); 70 var $oddEvenValues = array ("odd", "even"); 71 var $scheduleRequestAttributsName = array ("id", "audience", "shared", 72 "what", "title", "lead", "posterURL", "paperURL", "remark", "rate", 73 "where", "lon", "lat", "addr", 74 "from", "to", "at", 75 "repeat", "repeatType", "weekRank", "dayInWeek", "dayRank", 76 "user"); 77 var $scheduleSaveAttributsName; 78 var $filterNames = array ("noMember" => "member", 79 "noWhat" => "what", 80 "NoAudience" => "audience"); 81 82 83 // ============================================================ 84 // Transcient attributs 85 // ============================================================ 86 var $message = array (); // messages to display : "notify" =>, "info" =>, "success" =>, "error" => 87 var $plugin; // link to wiki plugin inforation (conf, lang, ...) 88 var $isAdmin; // if user member of schedule adminGroup 89 var $oddEven = 0; // background color for table lines 90 91 // ============================================================ 92 // Util 93 // ============================================================ 94 /* messages container to be display before plugin */ 95 function message ($type, $text) { 96 if (isset ($this->message[$type])) 97 $this->message[$type] .= '<br/>'.NL; 98 $this->message[$type] .= $text; 99 } 100 function startMessage ($type, $text) { 101 if (isset ($this->message[$type])) 102 $this->message[$type] = '<br/>'.NL.$this->message[$type]; 103 $this->message[$type] = $text.$this->message[$type]; 104 } 105 /* debug messages for admin only */ 106 function debug ($text) { 107 global $INFO; 108 // XXX 109 if (isset ($INFO['userinfo'] ['grps']) && in_array ('admin', $INFO ['userinfo'] ['grps'])) 110 $this->message ('notify', '<pre>'.$text.'</pre>'); 111 } 112 /* Change date format from array to number */ 113 function dt2dn ($dt_date) { 114 $dn_date = mktime (0, 0, 0, $dt_date["m"], $dt_date["d"], $dt_date["Y"]); 115 if (!$dn_date) 116 $this->message ('error', $dt_date["Y"]."-".$dt_date["d"]."-".$dt_date["m"]." : ".$this->plugin->getLang ('mktimeError').'<br/>'.NL); 117 return $dn_date; 118 } 119 /* Check date format 31/12/2000 */ 120 function checkDS ($ds_date) { 121 return 122 preg_match ("#([0-9]{8})#", $ds_date, $dumy); 123 } 124 /* Change date format from MM/DD/AAAA to number */ 125 function df2ds ($df_date) { 126 $df_date = trim ($df_date); 127 if (!$df_date) 128 return; 129 if ("!" == $df_date) 130 $ds_date = date ("Ymd"); 131 elseif (preg_match ("#\+(?<delta>[0-9]+)#", $df_date, $matches)) 132 $ds_date = sprintf ("%08d", date ("Ymd", mktime (0, 0, 0, date ("n"), date ("j")+$matches["delta"], date ("Y")))); 133 elseif (preg_match ("#\-(?<delta>[0-9]+)#", $df_date, $matches)) 134 $ds_date = sprintf ("%08d", date ("Ymd", mktime (0, 0, 0, date ("n"), date ("j")-$matches["delta"], date ("Y")))); 135 elseif (preg_match ("#".$this->currentDateFormat ["longScan"]."#", $df_date, $dt_date)) 136 $ds_date = sprintf ("%04d%02d%02d", $dt_date["Y"], $dt_date["m"], $dt_date["d"]); 137 elseif (preg_match ("#".$this->currentDateFormat ["shortScan"]."#", $df_date, $dt_date)) 138 $ds_date = sprintf ("20%02d%02d%02d", $dt_date["Y"], $dt_date["m"], $dt_date["d"]); 139 else 140 $ds_date = "{$df_date}"; 141 return $ds_date; 142 } 143 /* Change date format from number to MM/DD/AAAA */ 144 function ds2df ($ds_date) { 145 $ds_date = trim ($ds_date); 146 if (preg_match ("#(?<Y>[0-9][0-9][0-9][0-9])(?<m>[0-9][0-9])(?<d>[0-9][0-9])#", $ds_date, $dt_date)) 147 return sprintf ("%02d/%02d/%04d", 148 $dt_date[$this->currentDateFormat["fieldName"][0]], 149 $dt_date[$this->currentDateFormat["fieldName"][1]], 150 $dt_date[$this->currentDateFormat["fieldName"][2]]); 151 else 152 return "{$ds_date}"; 153 } 154 function ds2ln ($ds_date, $bold = false) { 155 $ds_date = trim ($ds_date); 156 if (preg_match ("#(?<Y>[0-9][0-9][0-9][0-9])(?<m>[0-9][0-9])(?<d>[0-9][0-9])#", $ds_date, $dt_date)) { 157 // XXX prévoir le format anglais 158 setlocale (LC_TIME, 'fr_FR.utf8','fra'); 159 return strftime (($bold ? "**%A %e %B** %Y" : "%A %e %B %Y"), 160 mktime (0, 0, 0, 161 $dt_date[$this->currentDateFormat["fieldName"][1]], 162 $dt_date[$this->currentDateFormat["fieldName"][0]], 163 $dt_date[$this->currentDateFormat["fieldName"][2]])); 164 } else 165 return "{$ds_date}"; 166 } 167 168 /* Convert array on string (words seperated bay '|') */ 169 function array2string ($array) { 170 if (!$array) 171 return ""; 172 $array = array_unique ($array); 173 if (in_array ("", $array)) 174 $array = array_diff ("", $array); 175 if (count ($array) < 1) 176 return ""; 177 return implode ("|", $array); 178 } 179 /* Convert array on string (words seperated bay '|') */ 180 function assocImplode ($array, $vSep = '|', $kSep = ':') { 181 $result = ""; 182 foreach ($array as $k => $v) { 183 $result .= $vSepNeeded.$k.$kSep.$v; 184 $vSepNeeded = $vSep; 185 } 186 return $result; 187 } 188 /* Convert array on string (words seperated bay '|') */ 189 function assocExplode ($string, $vSep = '|', $kSep = ':') { 190 $result = array (); 191 foreach (explode ($vSep, $string) as $kv) { 192 $data = explode ($kSep, $kv); 193 $result [$data[0]] = $data[1]; 194 } 195 return $result; 196 } 197 /* Convert array on string (words seperated bay '|') */ 198 // $ find data/[mp]*/membres/*/agenda/*/* data/[mp]*/commun/agenda/*/* -type f -print > dumy 199 // $ emacs dumy 200 // C-x ( mv ' ' C-k C-y ' ' C-y C-f C-x ) 201 // M-1000 C-x e 202 // C-M-% 203 // ^\([^ ]* [^ ]* [^ ]*agenda/[^/ ]*_\)/ => \1 204 // C-M-% 205 // ^\([^ ]* [^ ]* [^ ]*agenda/[^/ ]*_\)/ => \1 206 // C-M-% 207 // ^\([^ ]* [^ ]* [^ ]*agenda/[^/ ]*\)/ => \1_ 208 // $ find data/[mp]*/membres/*/agenda/* -type d -exec rmdir {} \; -print 209 function removeSep ($titleId) { 210 return strtr ($titleId, ':/;', ' '); 211 } 212 213 function getPageId ($schedule) { 214 return ":". 215 $this->scheduleDir.":". 216 ($schedule->shared ? $this->sharedDir : $schedule->member).":". 217 $this->removeSep ($schedule->title); 218 } 219 220 function resetOddEven () { 221 $this->oddEven = 0; 222 } 223 function switchOddEven () { 224 $this->oddEven = 1 - $this->oddEven; 225 } 226 227 function getOddEven () { 228 return $this->oddEvenValues [$this->oddEven]; 229 } 230 231 function createDirIsNeeded ($dir) { 232 if (is_dir ($dir)) 233 return; 234 @mkdir ($dir); 235 @chmod ($dir, 0775); 236 } 237 238 // ============================================================ 239 function __construct ($plugin) { 240 $this->plugin = $plugin; 241 $this->scheduleSaveAttributsName = array_merge ($this->scheduleRequestAttributsName, array ("weekDays")); 242 global $conf; 243 $savedir = ((!$conf['savedir'] || strpos ($conf['savedir'], '.') === 0) ? DOKU_INC : "").$conf['savedir']."/"; 244 $this->cacheRootDir = $savedir."cache/schedule/"; 245 $this->dataRootDir = $savedir.trim ($this->plugin->getConf ('dataDir').'/'); 246 scheduleRoot::createDirIsNeeded ($this->cacheRootDir); 247 scheduleRoot::createDirIsNeeded ($this->dataRootDir); 248 $this->scheduleGroup = trim ($this->plugin->getConf ('scheduleGroup')); 249 $this->groupsDir = trim ($this->plugin->getConf ('groupsDir')); 250 $this->scheduleDir = trim ($this->plugin->getConf ('scheduleDir')); 251 $this->sharedDir = trim ($this->plugin->getConf ('sharedDir')); 252 253 foreach (explode ('|', $this->plugin->getConf ('scheduleWhat')) as $tmpCatDef) { 254 $tmpCatExp = explode (':', trim ($tmpCatDef)); 255 $tmpCat = trim ($tmpCatExp[0]); 256 foreach (explode (',', $tmpCatExp[1]) as $tmpWhat) { 257 $tmpWhat = trim ($tmpWhat); 258 $this->scheduleWhat[$tmpWhat] = $tmpCat; 259 } 260 } 261 $this->scheduleAudience [""] = $this->plugin->getLang ('audienceChoice'); 262 foreach (explode (',', $this->plugin->getConf ('scheduleAudience')) as $tmpAudience) 263 $this->scheduleAudience[trim ($tmpAudience)] = trim ($tmpAudience); 264 $this->iconName = $this->plugin->getConf ('iconName'); 265 $this->iconWidth = $this->plugin->getConf ('iconWidth'); 266 foreach (array (false, true) as $sharedVal) { 267 $tmp = $this->plugin->getLang ('shared'); 268 $this->scheduleShared[] = trim ($tmp[$sharedVal]); 269 } 270 $this->currentDateFormat = $this->dateFormat [$this->plugin->getLang ('dateFormat')]; 271 272 global $INFO; 273 $this->isAdmin = 274 isset ($INFO ['userinfo']) && 275 isset ($INFO ['userinfo']['grps']) && 276 in_array (trim ($this->plugin->getConf ('adminGroup')), $INFO ['userinfo']['grps']); 277 } 278 279 // ============================================================ 280 // Manage XML file 281 // ============================================================ 282 /* read lodging config */ 283 function readConfig ($dir) { 284 $oldFileName = $dir.$this->oldConfigFile; 285 $fileName = $dir.$this->configFile; 286 287 // rename old fashion membre file name 288 if (file_exists ($oldFileName)) { 289 // XXX migration 290 rename ($oldFileName, $fileName); 291 $exclude = ".|..|".$this->scheduleRoot->configFile; 292 $exclude_array = explode("|", $exclude); 293 $pathDir = rtrim ($dir, "/") . "/"; 294 if (is_dir($pathDir)) { 295 $pathDirObj = opendir ($pathDir); 296 while (false !== ($file = readdir ($pathDirObj))) { 297 if (in_array (strtolower ($file), $exclude_array)) 298 continue; 299 $pathFile = $pathDir.$file; 300 if (is_file ($pathFile) && preg_match ('#.*\.xml$#i', $file, $b)) 301 rename ($pathFile, $pathDir.$this->mbrPrefix.$file); 302 } 303 } 304 } 305 if (!file_exists ($fileName)) 306 return false; 307 $result = array (); 308 // if (! class_exists ('DOMDocument')) 309 // return $result; 310 $xml = new DOMDocument ("1.0", "utf8"); 311 $xml->load ($fileName); 312 $root = $xml->documentElement; 313 foreach ($this->configAttributsName as $field) { 314 $element = $root->getElementsByTagName ($field); 315 if ($element) 316 $result [$field] = $element->item (0)->nodeValue; 317 } 318 return $result; 319 } 320 /* write lodging config */ 321 function writeConfig ($schedules) { 322 $fileName = $schedules->dataDir.$this->configFile; 323 scheduleRoot::createDirIsNeeded ($schedules->dataDir); 324 // if (! class_exists ('DOMDocument')) 325 // return; 326 $xml = new DOMDocument ("1.0", "utf8"); 327 $root = $xml->createElement ("schedule"); 328 $xml->appendChild ($root); 329 foreach ($this->configAttributsName as $field) 330 $root->appendChild ($xml->createElement ($field, htmlspecialchars ($schedules->$field))); 331 $xml->formatOutput = true; 332 $xml->save ($fileName); 333 chmod ($fileName, 0664); 334 } 335 336 // ============================================================ 337 function manageAction ($request) { 338 if (!$this->isAdmin) 339 return; 340 $md5 = $request['md5']; 341 if (!$md5) 342 return; 343 $schedules = new schedules ($this, $request[$md5]['ns']); 344 switch ($request["action"]) { 345 346 case 'moveSchedules': 347 $md5 = $request['md5']; 348 $date = $this->df2ds ($request[$md5]['date']); 349 $md5bis = $request[$md5]['toNs']; 350 if (!$date) 351 return; 352 $src = new schedules ($this, $request[$md5]['ns']); 353 $src->load (); 354 $dst = new schedules ($this, $request[$md5bis]['ns']); 355 $dst->load (); 356 $filter = array (); 357 foreach ($src->allSchedules as $id => $schedule) 358 if (($schedule->to ? $schedule->to : $schedule->from) < $date) 359 $filter [$id] = $schedule; 360 foreach ($filter as $id => $schedule) { 361 $dst->addSchedule ($schedule); 362 $src->removeSchedule ($id); 363 } 364 $dst->writeSchedules (); 365 $src->writeSchedules (); 366 367 unset ($_REQUEST['schd']); 368 break; 369 default: 370 return; 371 } 372 } 373 374 // ============================================================ 375 function printForm () { 376 if (!$this->isAdmin) 377 return; 378 $list = $this->readAllSchedules (); 379 echo 380 '<form method="POST" action="" onSubmit="return false;" >'.NL. 381 ' <table class="admin" >'.NL. 382 ' <thead>'.NL. 383 // XXX getLang () 384 ' <tr><th>Description</th><th>Valeurs</th></tr>'.NL. 385 ' </thead>'.NL. 386 ' <tbody>'.NL; 387 $this->resetOddEven (); 388 foreach ($list as $md5 => $item) { 389 $nameSpace = $item ["nameSpace"]; 390 $schedules = $item ["schedules"]; 391 echo 392 ' <tr class="'.$this->getOddEven ().'">'.NL. 393 ' <td class="name">NameSpace</td>'.NL. 394 ' <td class="value"><input type="hidden" name="schd['.$md5.'][ns]" value="'.$nameSpace.'"/>'.$nameSpace.'</td>'.NL. 395 ' </tr>'.NL. 396 ' <tr class="'.$this->getOddEven ().'">'.NL. 397 ' <td class="name">MD5</td>'.NL. 398 ' <td class="value">'.$md5.'</td>'.NL. 399 ' </tr>'.NL. 400 ' <tr class="'.$this->getOddEven ().'">'.NL. 401 ' <td class="name">Members</td>'.NL. 402 ' <td class="value">'.count ($schedules->memberSchedules).'</td>'.NL. 403 ' </tr>'.NL. 404 ' <tr class="'.$this->getOddEven ().'">'.NL. 405 ' <td class="name">Events</td>'.NL. 406 ' <td class="value">'.count ($schedules->datedSchedules).'</td>'.NL. 407 ' </tr>'.NL. 408 ' <tr class="'.$this->getOddEven ().'">'.NL. 409 ' <td class="name">Repeated</td>'.NL. 410 ' <td class="value">'.count ($schedules->repeatedSchedules).'</td>'.NL. 411 ' </tr>'.NL. 412 ' <tr class="'.$this->getOddEven ().'">'.NL. 413 ' <td class="name">Date</td>'.NL. 414 ' <td><input type="text" name="schd['.$md5.'][date]" value="'.'" placeholder="'.$this->plugin->getLang ('rootMoveDatePH').'"/></td>'.NL. 415 ' </tr>'.NL. 416 ' <tr class="'.$this->getOddEven ().'">'.NL. 417 ' <td class="name">Vers</td>'.NL. 418 ' <td><select name="schd['.$md5.'][toNs]"/>'.NL; 419 foreach ($list as $md5bis => $itemBis) 420 if ($md5bis != $md5) 421 echo 422 ' <option value="'.$md5bis.'">'.$itemBis["nameSpace"].'</option>'.NL; 423 echo 424 ' </select></td>'.NL. 425 ' </tr>'.NL. 426 ' <tr class="'.$this->getOddEven ().'">'.NL. 427 ' <td class="name">Action</td>'.NL. 428 ' <td><input type="submit" name="action" value="moveSchedules" onClick="scheduleAjax(this.form,\'moveSchedules\',\''.$md5.'\')" /></td>'.NL. 429 ' </tr>'.NL; 430 $this->switchOddEven (); 431 } 432 echo 433 ' </tbody>'.NL. 434 ' </table>'.NL. 435 '</form>'.NL; 436 } 437 438 // ============================================================ 439 function readAllSchedules () { 440 $exclude_array = explode ("|", ".|.."); 441 if (!is_dir($this->dataRootDir)) 442 return; 443 $pathDirObj = opendir ($this->dataRootDir); 444 while (false !== ($file = readdir ($pathDirObj))) { 445 $subDir = $this->dataRootDir.$file.'/'; 446 if (in_array (strtolower ($file), $exclude_array) || !is_dir ($subDir)) 447 continue; 448 $list [$file] = $this->readConfig ($subDir); 449 $list [$file]["schedules"] = new schedules ($this, $list [$file]["nameSpace"]); 450 $list [$file]["schedules"]->load (); 451 } 452 uasort ($list, "cmpScheduleConfig"); 453 return $list; 454 } 455 456 function clearCache () { 457 $list = $this->readAllSchedules (); 458 foreach ($list as $md5 => $item) { 459 $schedules = $item ["schedules"]; 460 $schedules->clearCache (); 461 } 462 } 463 464 // ============================================================ 465} 466