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 // TODO :
9 // passer des actions suivant langue en actions neutres
10 // selection automatique de cantons suivant lieu si existe le canton existe
11 // radius dans conf puis select
12
13 */
14if (!defined ('DOKU_INC'))
15    define ('DOKU_INC', realpath (dirname (__FILE__).'/../../../').'/');
16if (!defined ('DOKU_PLUGIN'))
17    define ('DOKU_PLUGIN', DOKU_INC.'lib/plugins/');
18
19require_once (realpath (dirname (__FILE__)).'/scheduleInseeCities.php');
20
21// ============================================================
22// management event class
23class schedules {
24
25    // ============================================================
26    // Config attributs
27    // ============================================================
28    var $scheduleRoot;					// commun data for all schedules
29
30    var $nameSpace;						// namespace where definition could be found
31    var $lastNotification;				// time of last proposal notification
32    var $lastNotificationReset;			// time of last administrators acknowledgement
33    var $md5ns;							// the ns directory
34    var $cacheDir;						// cache directory
35    var $dataDir;						// schedule database
36
37    var $migrate;						// performe data migration
38    var $showOldValues;					// highlight non migrate data
39
40    // ============================================================
41    // Transcient attributs
42    // ============================================================
43    var $allGroups = array ();			// dynamique set of groups name
44    var $allSchedules = array ();		// set of all events
45    var $memberSchedules = array ();	// set of events indexed by member
46    var $repeatedSchedules = array ();	// set of repeated events indexed by date
47    var $fixedSchedules = array ();		// set of non repeated events indexed by date
48    var $datedSchedules = array ();		// set of repeated or not events indexed by date
49    var $membersToSave = array ();		// set of modified members
50    var $allProposals = array ();		// set of all proposals
51    var $proposalsToSave = false;		// modified proposals
52
53    var $printProp = false;				// display proposal event form
54    var $printForm = false;				// display add event form
55    var $printCtrl = false;				// display action event form
56    var $isShow = false;				// date focus flag mode
57    var $userGroups = array ();			// set of user group wich are schedule groupe
58    var $defaultSchedule;				// empty schedule model
59
60    var $before;						// before filter date
61    var $date;							// exact filter date
62    var $after;							// after filter date
63    var $max;							// max events lines
64    var $filters;
65
66    // ============================================================
67    // Util
68    // ============================================================
69    function getConf ($name) {
70        return $this->scheduleRoot->plugin->getConf ($name);
71    }
72    function getLang ($name) {
73        return $this->scheduleRoot->plugin->getLang ($name);
74    }
75    function isAdmin () {
76        return $this->scheduleRoot->isAdmin;
77    }
78    function message ($type, $text) {
79        return $this->scheduleRoot->message ($type, $text);
80    }
81    function startMessage ($type, $text) {
82        return $this->scheduleRoot->startMessage ($type, $text);
83    }
84    function debug ($text) {
85        return $this->scheduleRoot->debug ($text);
86    }
87    function isMessage ($type) {
88        return isset ($this->scheduleRoot->message[$type]);
89    }
90
91    // ============================================================
92    function __construct ($scheduleRoot, $ns) {
93        $this->scheduleRoot = $scheduleRoot;
94        $this->defaultSchedule = new schedule ();
95        $this->nameSpace = cleanId (trim ($ns));
96        $this->md5ns = md5 ($this->nameSpace);
97        $this->cacheDir = $this->scheduleRoot->cacheRootDir.$this->md5ns.'/';
98        $this->dataDir = $this->scheduleRoot->dataRootDir.$this->md5ns.'/';
99        $this->migrate = $this->getConf ('migrate');
100        $this->showOldValues = $this->getConf ('showOldValues');
101        if ($this->migrate) {
102            global $scheduleInseeCities, $scheduleCitiesInsee;
103            $scheduleCitiesInsee = array ();
104            foreach ($scheduleInseeCities as $insee => $cityDef)
105                $scheduleCitiesInsee [strtolower ($cityDef[0])] = $insee;
106        }
107
108        $config = $this->scheduleRoot->readConfig ($this->dataDir);
109        if (!$config)
110            $this->scheduleRoot->writeConfig ($this);
111        else
112            foreach ($this->scheduleRoot->configAttributsName as $field)
113                if (isset ($config [$field]))
114                    $this->$field = $config [$field];
115    }
116    /** XXX utilisé par scheduleRoot ?! */
117    function load () {
118        $exclude_array = explode ('|', '.|..');
119        global $conf;
120        $pathDir = $conf['savedir'].'/pages/'.trim ($this->scheduleRoot->groupsDir, '/') . '/';
121        if (is_dir($pathDir)) {
122            $pathDirObj = opendir ($pathDir);
123            while (false !== ($file = readdir ($pathDirObj))) {
124                if (in_array (strtolower ($file), $exclude_array))
125                    continue;
126                $pathFile = $pathDir . $file;
127                // XXX precedent se trouve dans les membres ???
128                if (is_dir ($pathFile) && !in_array ($file, explode (',', trim ($this->getConf ('noSchedule')))))
129                    $this->allGroups [] = $file;
130            }
131        }
132        sort ($this->allGroups);
133
134        global $INFO;
135        if (isset ($INFO ['userinfo']) && isset ($INFO ['userinfo']['grps'])) {
136            $userGroups = array ();
137            $scheduleGroup = false;
138            foreach ($INFO['userinfo']['grps'] as $tmpMember) {
139                $tmpMember = trim ($tmpMember);
140                if ($this->scheduleRoot->scheduleGroup == $tmpMember)
141                    $scheduleGroup = true;
142                if (in_array ($tmpMember, $this->allGroups))
143                    $userGroups [] = $tmpMember;
144            }
145            if ($scheduleGroup)
146                $this->userGroups = $userGroups;
147        }
148        $this->readSchedules ($this->dataDir);
149        $this->readProp ();
150    }
151
152    function isMember ($member) {
153        return $this->isAdmin () || in_array ($member, $this->userGroups);
154    }
155
156    function isAMemberOf ($members) {
157        if ($this->isAdmin ())
158            return true;
159        foreach ($members as $member)
160            if (in_array ($member, $this->userGroups))
161                return true;
162        return false;
163    }
164
165    function isMemberOfAll ($members) {
166        if ($this->isAdmin ())
167            return true;
168        foreach ($members as $member)
169            if (!in_array ($member, $this->userGroups))
170                return false;
171        return true;
172    }
173
174    // ============================================================
175    function readCache ($key) {
176        if (!is_dir ($this->cacheDir))
177            return false;
178        $filename = $this->cacheDir.md5 ($key).'.cache';
179        if (!file_exists ($filename))
180            return false;
181        // YYY date du fichier (le calendrier change tous les jours)
182        if (filemtime ($filename) < strtotime ('today'))
183            return false;
184        // XXX ??? Warning: file_get_contents() [function.file-get-contents]: failed to open stream: No such file or directory in  on line 153
185        return file_get_contents ($filename);
186    }
187
188    function writeCache ($key, $text) {
189        scheduleRoot::createDirIsNeeded ($this->cacheDir);
190        $filename = $this->cacheDir.md5 ($key).'.cache';
191        file_put_contents ($filename, $text);
192    }
193
194    function clearCache () {
195        if (!$this->isAdmin ())
196            return;
197        $exclude = '.|..|'.$this->scheduleRoot->configFile;
198        $exclude_array = explode('|', $exclude);
199        $pathDir = $this->cacheDir;
200        if (!is_dir($pathDir))
201            return;
202        $pathDirObj = opendir ($pathDir);
203        while (false !== ($file = readdir ($pathDirObj))) {
204            if (in_array (strtolower ($file), $exclude_array))
205                continue;
206            $pathFile = $pathDir . $file;
207            if (is_file ($pathFile) && preg_match ('#.*\.cache#i', $file, $b))
208                unlink ($pathFile);
209        }
210        @rmdir ($pathDir);
211    }
212
213    // ============================================================
214    function setDateFilters ($before, $date, $after) {
215        $this->before = $this->scheduleRoot->df2ds ($before);
216        $this->after = $this->scheduleRoot->df2ds ($after);
217        $this->date = $this->scheduleRoot->df2ds ($date);
218        if ($this->date)
219            $this->before = $this->after = $this->date;
220    }
221
222    // ============================================================
223    function setFormLevel ($formLevel) {
224        global $INFO;
225        if ($formLevel > 2)
226            $this->printProp = true;
227        if (($formLevel > 0) &&
228            isset ($INFO ['userinfo']) &&
229            isset ($INFO ['userinfo']['grps']))
230            foreach ($INFO['userinfo']['grps'] as $tmpMember) {
231                $tmpMember = trim ($tmpMember);
232                if ($this->scheduleRoot->scheduleGroup == $tmpMember) {
233                    if ($formLevel > 1)
234                        $this->printForm = true;
235                    $this->printCtrl = true;
236                    $this->printProp = false;
237                    break;
238                }
239            }
240    }
241
242    // ============================================================
243    function setOtherFilters ($max, $filters, $grouped) {
244        $this->max = $max;
245        foreach ($filters as $name => $values) {
246            $newValues = array ();
247            foreach (explode (',', $values) as $value) {
248                $value = trim ($value);
249                if (! empty ($value))
250                    $newValues [] = $value;
251            }
252            if (!empty ($newValues))
253                $this->filters[$name] =  $newValues;
254        }
255        $this->repeatGrouped = trim ($this->getConf ('repeatPosition')) == 'grouped';
256        $grouped = trim ($grouped);
257        if (in_array ($grouped, array ('grouped', 'isolated')))
258            $this->repeatGrouped = $grouped == 'grouped';
259    }
260
261    // ============================================================
262    // Manage XML file
263    // ============================================================
264    /* Save a member data base */
265    function writeSchedule ($member) {
266        $xml = new DOMDocument ('1.0', 'utf8');
267        $root = $xml->createElement ('schedule');
268        $root->setAttribute ('member', $member);
269        $xml->appendChild ($root);
270        foreach ($this->memberSchedules [$member] as $id => $schedule) {
271            $event = $xml->createElement ('event');
272            foreach ($this->scheduleRoot->scheduleSaveAttributsName as $field)
273                $event->appendChild ($xml->createElement ($field, htmlspecialchars ($schedule->$field)));
274            $root->appendChild ($event);
275        }
276        $xml->formatOutput = true;
277        $fileName = $this->dataDir.$this->scheduleRoot->mbrPrefix.$member.'.xml';
278        $xml->save ($fileName);
279        chmod ($fileName, 0664);
280    }
281
282    function readProp () {
283        $this->readSchedule ($this->dataDir.$this->scheduleRoot->propFile, true);
284    }
285
286    function writeProp () {
287        // if (! class_exists ('DOMDocument'))
288        //     return;
289        $xml = new DOMDocument ('1.0', 'utf8');
290        $root = $xml->createElement ('schedule');
291        $root->setAttribute ('member', 'proposal');
292        $xml->appendChild ($root);
293        foreach ($this->allProposals as $id => $schedule) {
294            $event = $xml->createElement ('event');
295            foreach ($this->scheduleRoot->scheduleSaveAttributsName as $field)
296                $event->appendChild ($xml->createElement ($field, htmlspecialchars ($schedule->$field)));
297            $event->appendChild ($xml->createElement ('member', htmlspecialchars ($schedule->member)));
298            $root->appendChild ($event);
299        }
300        $xml->formatOutput = true;
301        $fileName = $this->dataDir.$this->scheduleRoot->propFile;
302        $xml->save ($fileName);
303        chmod ($fileName, 0664);
304    }
305
306    /* Migrate where format */
307    function whereMigrate ($schedule, $sep) {
308        global $scheduleCitiesInsee;
309        if (!$schedule)
310            return false;
311        if (preg_match ('/[0-9]/', $schedule->where)) {
312            return false;
313        }
314        $newFields = array ();
315        $updated = false;
316        foreach (explode ($sep, $schedule->where) as $city) {
317            $city = trim ($city);
318            if (!$city)
319                continue;
320            $insee = $scheduleCitiesInsee [strtolower ($city)];
321            $newFields [] = $insee ? $insee : $city;
322            if ($insee)
323                $updated = true;
324        }
325        $newWhere = implode (',', $newFields);
326        if (!$newWhere || !$updated)
327            return false;
328        $schedule->where = $newWhere;
329        return true;
330    }
331
332    /* Read a member data base */
333    function readSchedule ($file, $prop = false) {
334        if (!file_exists ($file))
335            return;
336        // if (! class_exists ('DOMDocument'))
337        //     return;
338        $xml = new DOMDocument ('1.0', 'utf8');
339        $xml->load ($file);
340        $root = $xml->documentElement;
341        $member = $root->getAttribute ('member');
342        $events = $root->getElementsByTagName ('event');
343        for ($i = $events->length; --$i >= 0; ) {
344            $event = $events->item ($i);
345            $schedule = new schedule ();
346            foreach ($this->scheduleRoot->scheduleSaveAttributsName as $field) {
347                $element = $event->getElementsByTagName ($field);
348                if ($element)
349                    $schedule->$field = $element->item (0)->nodeValue;
350            }
351            if ($prop) {
352                $element = $event->getElementsByTagName ('member');
353                if ($element)
354                    $schedule->member = $element->item (0)->nodeValue;
355            } else
356                $schedule->member = $member;
357            if ($this->migrate && !$prop &&
358                ($this->whereMigrate ($schedule, ',') ||
359                 $this->whereMigrate ($schedule, '/') ||
360                 $this->whereMigrate ($schedule, ' - ') ||
361                 $this->whereMigrate ($schedule, ' ')))
362                $this->membersToSave [$member] = true;
363            // XXX compatibilité
364            $schedule->shared = ($schedule->shared == true);
365            $schedule->repeatFlag = $schedule->to != '' && $schedule->from != $schedule->to;
366            if ($prop)
367                $this->allProposals [$schedule->id] = $schedule;
368            else
369                $this->allSchedules [$schedule->id] = $schedule;
370        }
371    }
372
373    // ============================================================
374    // IO functions
375    // ============================================================
376    /* Read all member data base */
377    function readSchedules ($pathDir) {
378        $exclude = '.|..|'.$this->scheduleRoot->configFile;
379        $exclude_array = explode('|', $exclude);
380        $pathDir = rtrim ($pathDir, '/') . '/';
381        if (!is_dir($pathDir))
382            return;
383        $pathDirObj = opendir ($pathDir);
384        while (false !== ($file = readdir ($pathDirObj))) {
385            if (in_array (strtolower ($file), $exclude_array))
386                continue;
387            $pathFile = $pathDir . $file;
388            if (is_file ($pathFile) &&
389                preg_match ('#'.$this->scheduleRoot->mbrPrefix.'.*\.xml$#i', $file, $b))
390                $this->readSchedule ($pathFile);
391        }
392        $this->updateSchedulesLists ();
393    }
394
395    // ============================================================
396    /* Save all member data base marked as modified */
397    function writeSchedules () {
398        if (!count ($this->membersToSave))
399            return;
400        foreach (array_keys ($this->membersToSave) as $member)
401            if (count ($this->memberSchedules [$member]) <= 0)
402                @unlink ($this->dataDir.$this->scheduleRoot->mbrPrefix.$member.'.xml');
403            else
404                $this->writeSchedule ($member);
405        $this->clearCache ();
406    }
407
408    // ============================================================
409    // YYY
410    // ============================================================
411    /* get all siblings shared events but the pattern */
412    function getOtherSharedEvents ($event) {
413        $result = array ();
414        if (!$event->from)
415            return $result;
416        foreach ($this->datedSchedules [$event->from] as $other)
417            if ($event->shared == $other->shared &&
418                $event->to == $other->to &&
419                $event->at == $other->at &&
420                $event->title == $other->title &&
421                $event->id != $other->id)
422                $result [] = $other;
423        return $result;
424    }
425
426    // ============================================================
427    /* XXX */
428    function getMembersSharedEvents ($event) {
429        if (!$event->shared || !$event->from || !$event->title)
430            return $event->requestMembers ? $event->requestMembers : array ($event->member);
431        $result = array ();
432        foreach ($this->datedSchedules [$event->from] as $other)
433            if ($event->shared == $other->shared &&
434                $event->to == $other->to &&
435                $event->at == $other->at &&
436                $event->title == $other->title)
437                $result [] = $other->member;
438        return $result;
439    }
440
441    // ============================================================
442    /* Performe action */
443    function manageAction ($request) {
444
445        if ($request['action'] == $this->getLang ('show')) {
446            $this->isShow = true;
447            if (isset ($request['date'])) {
448                $this->before = $this->after = $this->date = $this->scheduleRoot->df2ds ($request['date']);
449            }
450        } elseif ($request['action'] == $this->getLang ('selected')) {
451            if (isset ($request['id'])) {
452                $id = $request['id'];
453                if (isset ($this->allProposals [$id])) {
454                    $this->defaultSchedule =  $this->allProposals [$id];
455                    $this->defaultSchedule->prop = true;
456                } else if (isset ($this->allSchedules [$id])) {
457                    $this->defaultSchedule = $this->allSchedules [$id];
458                }
459            }
460
461        } elseif (in_array ($request['action'],
462                            array ($this->getLang ('modifyAll'),
463                                   $this->getLang ('modify'),
464                                   $this->getLang ('add'),
465                                   $this->getLang ('valid'),
466                                   $this->getLang ('prop')))) {
467            if (isset ($request['id']))
468                $id = $request['id'];
469            $this->manageAddAction ($request);
470            if ($request['action'] == $this->getLang ('valid'))
471                $this->removeSchedule ($id);
472
473        } elseif ($request['action'] == $this->getLang ('remove')) {
474            if (isset ($request['idl']))
475                foreach ($request['idl'] as $id)
476                    $this->removeSchedule ($id);
477            unset ($_REQUEST['schd']);
478
479        } elseif ($request['action'] == $this->getLang ('created'))
480              $this->manageCreateAction ($request);
481    }
482
483    // ============================================================
484    function manageAddAction ($request) {
485        if (isset ($request['id']))
486            $id = $request['id'];
487        $schedule = new schedule ();
488        $others = array ();
489        $oldMember = '';
490        if (isset ($this->allSchedules [$id]) &&
491            ($request['action'] == $this->getLang ('modifyAll') ||
492             $request['action'] == $this->getLang ('modify'))) {
493            $schedule = &$this->allSchedules [$id];
494            $oldMember = $schedule->member;
495            if ($request['action'] == $this->getLang ('modifyAll'))
496                $others = $this->getOtherSharedEvents ($schedule);
497        }
498        foreach ($this->scheduleRoot->scheduleRequestAttributsName as $field)
499            $schedule->$field = trim (str_replace (array ('"'), '', $request [$field]));
500        if ($request ['member']) {
501            $schedule->requestMembers = explode (',', trim (str_replace (',,', ',', $request ['member']), ','));
502            $schedule->member = trim ($schedule->requestMembers [0]);
503        }
504        $schedule->title = strtr ($schedule->title, '"', "''");
505        $schedule->lead = strtr ($schedule->lead, '"', "''");
506        $schedule->remark = strtr ($schedule->remark, '"', "''");
507
508        $schedule->weekDays = scheduleRoot::array2string ($request ['weekDays']);
509        $schedule->from = $this->scheduleRoot->df2ds ($request ['from']);
510        $schedule->to = $this->scheduleRoot->df2ds ($request ['to']);
511        if ('' == $schedule->from) {
512            $schedule->from = $schedule->to;
513            $schedule->to = '';
514        }
515        if ($schedule->from == $schedule->to)
516            $schedule->to = '';
517        if ('' == $schedule->from && '' == $schedule->where && '' == $schedule->title) {
518            $this->defaultSchedule = $schedule;
519            unset ($_REQUEST['schd']);
520            return;
521        }
522        if (!$this->printProp && $schedule->member && !$this->isMemberOfAll ($schedule->requestMembers)) {
523            $this->message ('info', $this->getLang ('notMemberError'));
524            $this->defaultSchedule = $schedule;
525            unset ($_REQUEST['schd']);
526            return;
527        }
528        if (!$request ['audience'])
529            $this->message ('error', $this->getLang ('noAudienceError'));
530        if (!$schedule->member)
531            $this->message ('error', $this->getLang ('noMemberError'));
532        if ('' == $schedule->what)
533            $this->message ('error', $this->getLang ('noTypeError'));
534        if ('' == $schedule->title)
535            $this->message ('error', $this->getLang ('noTitleError'));
536        if ('' == $schedule->where)
537            $this->message ('error', $this->getLang ('noWhereError'));
538        if (!$this->scheduleRoot->checkDS ($schedule->from))
539            $this->message ('error', $request ['from'].' : '.$this->getLang ('badDateError'));
540        if ($schedule->to && !$this->scheduleRoot->checkDS ($schedule->to))
541            $this->message ('error', $request ['to'].' : '.$this->getLang ('badDateError'));
542        else if ($this->after && ($schedule->to ? $schedule->to : $schedule->from) < $this->after)
543            $this->message ('error', $request ['from'].' : '.$this->getLang ('pastError'));
544        else if ($schedule->to != '' && $schedule->from > $schedule->to)
545            $this->message ('error', $request ['to'].' : '.$this->getLang ('pastToError'));
546        if ($this->isMessage ('error')) {
547            $this->startMessage ('error', $this->getLang ('startError'));
548            $this->defaultSchedule = $schedule;
549            unset ($_REQUEST['schd']);
550            return;
551        }
552        $schedule->repeatFlag = $schedule->to != '' && $schedule->from != $schedule->to;
553
554        // force un nouvel id, member et file
555        if (!$schedule->id || in_array ($request['action'],
556                                        array ($this->getLang ('add'),
557                                               $this->getLang ('valid'),
558                                               $this->getLang ('prop'))))
559            $schedule->id = date ('Ymdhis.0');
560
561        if ($this->printProp) {
562            unset ($_REQUEST['schd']);
563            if (!$captcha =& plugin_load ('helper', 'captcha') ||
564                !$captcha->check ()) {
565                $this->defaultSchedule = $schedule;
566                return;
567            }
568            $schedule->member = implode (',', $schedule->requestMembers);
569            $this->allProposals [] = $schedule;
570            $this->proposalsToSave = true;
571            $this->adminNotification ();
572            $this->message ('success', $this->getLang ('propSuccess'));
573            return;
574        }
575
576        if ($oldMember)
577            $this->membersToSave [$oldMember] = true;
578
579        global $INFO;
580        $schedule->user = $_SERVER['REMOTE_USER'];
581        $this->addSchedule ($schedule);
582        unset ($_REQUEST['schd']);
583
584        foreach ($others as $other) {
585            if (!$this->isMember ($others->member))
586                continue;
587            foreach ($this->scheduleRoot->scheduleSaveAttributsName as $field)
588                if ($field != 'id')
589                    $other->$field = $schedule->$field;
590            $this->membersToSave [$other->member] = true;
591        }
592
593        $sharedMembers = $this->getMembersSharedEvents ($schedule);
594        $i = 1;
595        foreach ($schedule->requestMembers as $newMember) {
596            $newMember = trim ($newMember);
597            if (!$newMember || in_array ($newMember, $sharedMembers))
598                continue;
599            if (!$this->isMember ($newMember))
600                continue;
601            $newSchedule = new schedule ();
602            foreach ($this->scheduleRoot->scheduleSaveAttributsName as $field)
603                $newSchedule->$field = $schedule->$field;
604            $newSchedule->id = date ('Ymdhis').$i;
605            $i++;
606            $newSchedule->member = $newMember;
607            $this->addSchedule ($newSchedule);
608        }
609
610        $pageId = $this->scheduleRoot->getPageId ($schedule);
611        resolve_pageid ($this->nameSpace, $pageId, $exists);
612        if (!$exists)
613            return;
614        $content = io_readFile (wikiFN ($pageId));
615        $start = "";
616        $option = "";
617        $end = $content;
618        $place = trim ($this->makeListPlaceForMap ($schedule));
619        if (preg_match ("#(?<start>(.|\n)*)<schedule(?<option>[^>]*)>(?<place>([^<]|\n)*)</schedule>(?<end>(.|\n)*)#", $content, $dumy)) {
620            $start = $dumy ['start'];
621            $option = $dumy ['option'];
622            $place2 = trim ($dumy ['place']);
623            $end = $dumy ['end'];
624            if ($place == $place2)
625                return;
626        } else if (preg_match ("#(?<start>(.|\n)*=+ Lieu =+\n)(?<place>([^=]|=[^=]|\n)*)(?<end>\n=(.|\n)*)#", $content, $dumy)) {
627            $start = $dumy ['start'].NL;
628            $end = $dumy ['end'];
629        } else {
630            $start = '===== Lieu ====='.NL.NL;
631            $end = $content;
632        }
633        $content = $start.'<schedule'.$option.'>'.NL.$place.NL.'</schedule>'.NL.$end;
634        saveWikiText ($pageId, $content, 'update place');
635    }
636
637    // ============================================================
638    function manageCreateAction ($request) {
639        $id = $request['id'];
640        $pageId = $request ['pageId'];
641        resolve_pageid ($this->nameSpace, $pageId, $exists);
642        if (!$exists && isset ($this->allSchedules [$id])) {
643            $schedule = $this->allSchedules [$id];
644            $sharedMembers = $this->getMembersSharedEvents ($schedule);
645            // XXX mettre date, lieu, sujet, tarif dans langue
646            $content = '';
647            foreach ($sharedMembers as $member)
648                $content .=
649                         '[[:'.$this->scheduleRoot->groupsDir.':'.$member.':|'.
650                         '{{ :'.$this->scheduleRoot->groupsDir.':'.$member.':'.$this->scheduleRoot->iconName.'?100}}]]'.NL;
651            $content .=
652                     '~~NOTOC~~'.NL.
653                     '====== '.$schedule->title.' ======'.NL.NL;
654            if ($schedule->lead)
655                $content .=
656                         str_replace ("''", '"', $schedule->lead).NL.NL;
657            $content .=
658                     '===== Date ====='.NL.NL.
659                     ($schedule->repeatFlag ?
660                      $this->makeRepeatString ($schedule, true) :
661                      ('Le '. $this->scheduleRoot->ds2ln ($schedule->from, true))).
662                     $this->makePrettyAt ($schedule->at).NL.NL.
663                     '===== Lieu ====='.NL.NL.
664                     '<schedule>'.NL.
665                     $this->makeListPlaceForMap ($schedule).
666                     '</schedule>'.NL.NL.
667                     '===== Sujet ====='.NL.NL;
668            if (!$schedule->remark)
669                $content .=
670                         '<wrap round todo>Sujet à préciser</wrap>'.NL.NL;
671            if ($schedule->posterURL)
672                $content .=
673                         '{{ '.$schedule->posterURL.'?direct&200|}}'.NL;
674            if ($schedule->paperURL)
675                $content .=
676                         '[['.$schedule->paperURL.' |]]'.NL;
677            if ($schedule->remark)
678                $content .=
679                         str_replace ("''", '"', $schedule->remark).NL.NL;
680            if ($schedule->rate)
681                $content .=
682                         '===== Tarif ====='.NL.NL.
683                         $schedule->rate.NL;
684
685            saveWikiText ($pageId, $content, 'wizard creation');
686            $this->clearCache ();
687        }
688        unset ($_REQUEST['schd']);
689    }
690
691    // ============================================================
692    /* Add event */
693    function addSchedule (&$schedule) {
694        if (isset ($this->allSchedules [$schedule->id]))
695            unset ($this->allSchedules [$schedule->id]);
696        $this->membersToSave [$schedule->member] = true;
697        // élimine les doublons
698        if (isset ($this->memberSchedules [$schedule->member]))
699            foreach ($this->memberSchedules [$schedule->member] as $event)
700                if ($event->from == $schedule->from &&
701                    $event->to == $schedule->to &&
702                    $event->at == $schedule->at &&
703                    $event->shared == $schedule->shared &&
704                    $event->title == $schedule->title &&
705                    $event->id != $schedule->id) {
706                    unset ($this->allSchedules [$event->id]);
707                }
708        $this->allSchedules [$schedule->id] = $schedule;
709        $this->updateSchedulesLists ();
710    }
711
712    // ============================================================
713    /* Remove event */
714    function removeSchedule ($id) {
715        if (isset ($this->allProposals [$id])) {
716            $this->proposalsToSave = true;
717            unset ($this->allProposals [$id]);
718            $this->resetAdminNotification ();
719        } else if (isset ($this->allSchedules [$id])) {
720            $this->membersToSave [$this->allSchedules [$id]->member] = true;
721            unset ($this->allSchedules [$id]);
722            $this->updateSchedulesLists ();
723        }
724    }
725
726    // ============================================================
727    /* Update data base memory indexes after modification */
728    function updateSchedulesLists () {
729        $this->memberSchedules = array ();
730        $this->repeatedSchedules = array ();
731        $this->fixedSchedules = array ();
732        $this->datedSchedules = array ();
733        foreach ($this->allSchedules as $id => $schedule) {
734            $this->memberSchedules [$schedule->member][$id] = $schedule;
735            $this->datedSchedules [$schedule->from][] = $schedule;
736            if ($schedule->repeatFlag)
737                $this->repeatedSchedules [$schedule->from][] = $schedule;
738            else
739                $this->fixedSchedules [$schedule->from][] = $schedule;
740        }
741        foreach (array_keys ($this->memberSchedules) as $member)
742            uasort ($this->memberSchedules [$member], 'cmpSchedule');
743        uasort ($this->allSchedules, 'cmpSchedule');
744    }
745
746    // ============================================================
747    /* Print schedule as a matrix of days */
748    function printScheduleCalendar ($mapId, $dn_showDay) {
749        $nbDaysPerWeek = 7;
750        $nbWeeks  = 5;
751        $nbDays   = $nbDaysPerWeek * $nbWeeks;
752        $dn_today = mktime (0, 0, 0, date ('n'), date ('j'), date ('Y'));
753        $dn_start = mktime (0, 0, 0, date ('m', $dn_showDay), date ('d', $dn_showDay)-date ('w', $dn_showDay), date ('Y', $dn_showDay));
754        $dn_end   = $dn_start + $nbDays * 86400;
755        $ds_start = date ('Ymd', $dn_start);
756        $ds_end   = date ('Ymd', $dn_end);
757        $dn_prev  = mktime (0, 0, 0, date ('m', $dn_showDay)-1, date ('d', $dn_showDay), date ('Y', $dn_showDay));
758        $dn_next  = mktime (0, 0, 0, date ('m', $dn_showDay)+1, date ('d', $dn_showDay), date ('Y', $dn_showDay));
759        $df_prev  = date ($this->scheduleRoot->currentDateFormat['prettyPrint'], $dn_prev);
760        $df_next  = date ($this->scheduleRoot->currentDateFormat['prettyPrint'], $dn_next);
761        $dayNames = $this->getLang ('days');
762
763        ptln ('<table class="short" summary="schedule">'.NL.
764              ' <caption class="'.($dn_showDay == $dn_today ? 'scheduleToday' : 'scheduleShow').'">'.NL.
765              '  <div class="showBubbleOnFocus" style="float:left;">'.NL.
766              '   <a onClick="scheduleChangeDate(this,\''.$this->nameSpace.'\',\''.$mapId.'\',\''.$df_prev.'\');">&lt;</a>'.NL.
767              '   <div class="bubble '.$this->getConf ('bubblePosition').'Bubble">'.$this->getLang ('tipPrevM').'</div>'.NL.
768              '  </div>'.NL.
769              '  '.date ($this->scheduleRoot->currentDateFormat['prettyPrint'], $dn_showDay).NL.
770              '  <div class="showBubbleOnFocus" style="float:right;">'.NL.
771              '   <a onClick="scheduleChangeDate(this,\''.$this->nameSpace.'\',\''.$mapId.'\',\''.$df_next.'\');">&gt;</a>'.
772              '   <div class="bubble '.$this->getConf ('bubblePosition').'Bubble">'.$this->getLang ('tipNextM').'</div>'.NL.
773              '  </div>'.
774              ' </caption>'.NL.
775              ' <thead><tr><th></th>');
776        if ($dayNames)
777            foreach ($dayNames as $dayName => $dayAbrv)
778                echo '<th scope="col"><abbr title="'.$dayName.'">'.$dayAbrv.'</abbr></th>';
779        ptln (' </tr></thead>'.NL.
780              ' <tbody onMouseOut="javascript:scheduleHighlightPOI (null)">');
781
782        $focusSchedules = array ();
783        $dn_day = $dn_start;
784        // copie des évènements non répété (à part quotidient)
785        for ($i = 0; $i < $nbDays; $i++) {
786            $ds_index = date ('Ymd', $dn_day);
787            if (isset ($this->fixedSchedules [$ds_index]))
788                $focusSchedules [$ds_index] = array_values ($this->fixedSchedules [$ds_index]);
789            $dn_day = mktime(0, 0, 0, date ('m', $dn_day)  , date ('d', $dn_day)+1, date ('Y', $dn_day));
790        }
791
792        // extends repeated events
793        foreach ($this->repeatedSchedules as $ds_index => $events) {
794            foreach ($events as $event) {
795                if ($event->from >= $ds_end)
796                    continue;
797                if ($event->to < $ds_start)
798                    continue;
799
800                preg_match ("#(?<Y>[0-9]{4})(?<m>[0-9]{2})(?<d>[0-9]{2})#", $event->from, $dt_from);
801                $dn_from = $this->scheduleRoot->dt2dn ($dt_from);
802                if ($event->to > $ds_end)
803                    $dn_to = $dn_end;
804                else {
805                    preg_match ("#(?<Y>[0-9]{4})(?<m>[0-9]{2})(?<d>[0-9]{2})#", $event->to, $dt_to);
806                    $dn_to = $this->scheduleRoot->dt2dn ($dt_to);
807                }
808
809                switch ($event->repeatType) {
810                    //    chercher le premier dans la fenêtre
811                    //    tant que dans la fenêtre répété la répétition
812                    //    ajouter l'évènement dans les autres
813
814                case 'day' :
815                    // répétition des évènements quotidients
816                    $dn_day = $dn_from;
817                    $step = 86400 * $event->repeat;
818                    if ($dn_from < $dn_start)
819                        $dn_day += ceil (($dn_start - $dn_from) / $step) * $step;
820                    for ( ; $dn_day <= $dn_to; $dn_day += $step)
821                        $focusSchedules [date ('Ymd', $dn_day)][] = $event;
822                    break;
823                case 'week' :
824                    $dn_startWeek = mktime (0, 0, 0, date ('m', $dn_from), date ('d', $dn_from) - date ('w', $dn_from), date ('Y', $dn_from));
825                    $step = 86400 * 7 * $event->repeat;
826                    if ($dn_startWeek < $dn_start)
827                        $dn_startWeek += floor (($dn_start - $dn_startWeek) / $step) * $step;
828                    for ( ; $dn_startWeek <= $dn_to; $dn_startWeek += $step)
829                        foreach (explode ('|', $event->weekDays) as $idd) {
830                            $dn_day = $dn_startWeek + $idd * 86400;
831                            if ($dn_day >= $dn_start && $dn_day >= $dn_from &&
832                                $dn_day <= $dn_to)
833                                $focusSchedules [date ('Ymd', $dn_day)][] = $event;
834                        }
835                    break;
836                case 'dayMonth' :
837                case 'dateMonth' :
838                    $dn_startMonth = mktime (0, 0, 0, date ('m', $dn_from), 1, date ('Y', $dn_from));
839                    if ($dn_startMonth < $dn_start) {
840                        $nbMonth = floor ((date ('m', $dn_start) + date ('Y', $dn_start)*12 - (date ('m', $dn_from) + date ('Y', $dn_from)*12))/$event->repeat)*$event->repeat;
841                        $dn_startMonth = mktime (0, 0, 0, date ('m', $dn_startMonth) + $nbMonth, 1, date ('Y', $dn_startMonth));
842                    }
843                    for ( ;
844                          $dn_startMonth <= $dn_to;
845                          $dn_startMonth = mktime (0, 0, 0, date ('m', $dn_startMonth) + $event->repeat, 1, date ('Y', $dn_startMonth))) {
846                        $dn_day = $dn_startMonth;
847                        if ($event->repeatType == 'dateMonth')
848                            // XXX pb si moins de jour dans le mois
849                            $dn_day = mktime (0, 0, 0, date ('m', $dn_startMonth), $event->dayRank, date ('Y', $dn_startMonth));
850                        else {
851                            $delta = $event->dayInWeek - date ('w', $dn_startMonth);
852                            if ($delta < 0)
853                                $delta += 7;
854                            $delta += 7 * $event->weekRank;
855                            // XXX pb si moins de jour dans le mois
856                            $dn_day = mktime (0, 0, 0, date ('m', $dn_startMonth), $delta+1, date ('Y', $dn_startMonth));
857                        }
858                        if ($dn_day >= $dn_start && $dn_day >= $dn_from &&
859                            $dn_day <= $dn_to)
860                            $focusSchedules [date ('Ymd', $dn_day)][] = $event;
861                    }
862                    break;
863                case 'year' :
864                    $dayInmonth = date ('j', $dn_from);
865                    $monthInYear = date ('n', $dn_from);
866                    $startYear = date ('Y', $dn_start) - (date ('Y', $dn_start) - date ('Y', $dn_from)) % $event->repeat;
867                    $endYear = date ('Y', $dn_end);
868                    for ( ; $startYear <= $endYear; $startYear += $event->repeat) {
869                        $dn_day = mktime (0, 0, 0, $monthInYear, $dayInmonth, $startYear);
870                        if ($dn_day >= $dn_start && $dn_day >= $dn_from &&
871                            $dn_day <= $dn_to)
872                            $focusSchedules [date ('Ymd', $dn_day)][] = $event;
873                    }
874                    break;
875                }
876            }
877        }
878
879        global $conf;
880        $dn_day = $dn_start;
881        $allLocations = '';
882        $LocationsEvents = array ();
883        for ($s = 0; $s < 5; $s++) {
884            $line = '';
885            for ($d = 0; $d < 7; $d++) {
886                $locations = '';
887                $df_day = date ($this->scheduleRoot->currentDateFormat['prettyPrint'], $dn_day);
888                $url = "javascript:scheduleSelectDate('".$df_day."');";
889                $bubble = '';
890                if ($d == 1)
891                    $w = date ('W', $dn_day);
892                $class = 'day showBubbleOnFocus';
893                $dayRank = date ('d', $dn_day);
894
895                $eventClass = '';
896                if (isset ($focusSchedules [date ('Ymd', $dn_day)])) {
897                    $eventClass = array ();
898                    $nbEvent = 0;
899                    $dayEvents = array ();
900                    foreach ($focusSchedules [date ('Ymd', $dn_day)] as $event) {
901                        $eventClass [preg_replace ("/\s/", '', htmlspecialchars (strtolower ($this->scheduleRoot->scheduleWhat [$event->what])))] = true;
902                        $nbEvent++;
903                        list ($LocationsEvents, $dayEvents, $locations)  = $this->addLocationsDayEvent ($LocationsEvents, $dayEvents, $locations, $dn_day, $event);
904                    }
905                    $bubble = $this->getDayBubble ($dn_day, $dayEvents);
906                    ksort ($eventClass);
907                    $eventClass = 'cat_'.implode ('', array_keys ($eventClass));
908                    $url = '?id='.$this->nameSpace.':'.$conf['start'].'&schd[ns]='.$this->nameSpace.'&schd[action]='.$this->getLang ('show').'&schd[date]='.$df_day;
909                    $class .= ' '.$eventClass;
910                    $class .= ' op_'.($nbEvent < 2 ? $nbEvent : 3);
911                }
912                if (!$bubble && $dn_day != $dn_today && $dn_day != $dn_showDay)
913                    $class .= ' empty';
914                $locations = trim (implode (',', array_unique (explode (',', $locations))), ',');
915                $allLocations = trim (implode (',', array_unique (explode (',', $locations.','.$allLocations))), ',');
916
917                $over = '<div class="over mapSelect" style="display:none" day="'.$df_day.'" localtions="'.$locations.'" >'.$dayRank.'</div>';
918                if ($dn_day < $dn_today)
919                    $over .= '<div class="over past">'.$dayRank.'</div>';
920                if ($d == 0 || $d == 6)
921                    $class .= ' weekend';
922                if ($dn_day == $dn_today)
923                    $over .= '<div class="over scheduleToday">'.$dayRank.'</div>';
924                elseif ($dn_day == $dn_showDay)
925                    $over .= '<div class="over scheduleShow">'.$dayRank.'</div>';
926
927                $js = ' onMouseOver="javascript:scheduleHighlightPOI (\''.$locations.'\')"';
928                $line .= '<td class="'.$class.'"'.$js.'>'.$over.$bubble.'<a href="'.$url.'">'.$dayRank.'</a></td>';
929                $dn_day = mktime(0, 0, 0, date ('m', $dn_day), date ('d', $dn_day)+1, date ('Y', $dn_day));
930            }
931            echo '<tr><td class="week">'.$w.'</td>'.$line.'</tr>'.NL;
932        }
933
934        ptln (' </tbody>'.NL.
935              ' <tfoot>'.NL.
936              '  <tr><td colspan="8" class="map">'.NL.
937              $this->getCityBubble ($LocationsEvents).
938              '   <div class="schedulePOI">'.NL.
939              '    <div id="'.$mapId.'" class="scheduleMap scheduleMapCalendar"></div>'.NL.
940              '    <span style="display:none;" class="poiLocations">'.$allLocations.'</span>'.NL.
941              '   </div>'.NL.
942              '  </td></tr>'.NL.
943              ' </tfoot>'.NL.
944              '</table>');
945    }
946
947    // ============================================================
948    function getLocationsFromEvent ($event) {
949        $locations = '';
950        if ($event->lat && $event->lon) {
951            foreach (array_combine (explode (',', $event->lat), explode (',', $event->lon)) as $lat => $lon) {
952                $location = '('.$lat.'|'.$lon.')';
953                $locations .= ','.$location;
954            }
955        } elseif ($event->where) {
956            $location = $event->where;
957            $locations .= ','.$location;
958        }
959        return $locations;
960    }
961
962    function addLocationsDayEvent ($LocationsEvents, $dayEvents, $locations, $dn_day, $event) {
963        $pageId = $this->scheduleRoot->getPageId ($event);
964        resolve_pageid ($this->nameSpace, $pageId, $exists);
965        $logoId = $this->scheduleRoot->groupsDir.':'.$event->member.':'.$this->scheduleRoot->iconName;
966        $pageIdAt = $pageId.'-'.$event->at;
967        if ($dayEvents [$pageIdAt])
968            $dayEvents [$pageIdAt]['logo'][] = $logoId;
969        else
970            $dayEvents [$pageIdAt] =
971                                   array ('pageId'=> $pageId, 'logo' => array ($logoId),
972                                          'what'=> $event->what, 'where'=> $event->where, 'at' =>$event->at, 'title' => $event->title,
973                                          'exists' => $exists);
974        if ($event->lat && $event->lon) {
975            foreach (array_combine (explode (',', $event->lat), explode (',', $event->lon)) as $lat => $lon) {
976                $location = '('.$lat.'|'.$lon.')';
977                $LocationsEvents [$location][$dn_day][$pageIdAt] = $dayEvents [$pageIdAt];
978                $locations .= ','.$location;
979            }
980        } elseif ($event->where) {
981            $location = $event->where;
982            $LocationsEvents [$location][$dn_day][$pageIdAt] = $dayEvents [$pageIdAt];
983            $locations .= ','.$location;
984        }
985        // YYY sauf répétition de jours
986        return array ($LocationsEvents, $dayEvents, $locations);
987    }
988
989
990    function formatDayEventsBubble ($dn_day, $dayEvents) {
991        $content = '';
992        $this->scheduleRoot->resetOddEven ();
993        foreach ($dayEvents as $pageIdAt => $values) {
994            $line = '';
995            sort ($values['logo']);
996            foreach ($values['logo'] as $logoId)
997                $line .= '<img src="'.ml ($logoId, array ('cache'=>$_REQUEST['cache'], 'w'=>20)).'" width="20"/> ';
998            if ($values['at'])
999                $line .= $values['at']. ' ';
1000            $line .= $values['what'];
1001            if ($values['where'])
1002                $line .= ' - '.$this->makeWhereString ($values['where']);
1003            $line .= ' : '.$values['title'];
1004            if ($values['exists'])
1005                $line = '<a href="?id='.$values['pageId'].'">'.$line.'</a>';
1006            $content .= '<div class="'.$this->scheduleRoot->getOddEven ().'">'.$line.'</div>'.NL;
1007            $this->scheduleRoot->switchOddEven ();
1008        }
1009        if (!$content)
1010            return '';
1011        return '<div class="date">'.date ($this->scheduleRoot->currentDateFormat['prettyPrint'], $dn_day).'</div>'.$content;
1012    }
1013
1014    function getDayBubble ($dn_day, $dayEvents) {
1015        uasort ($dayEvents, 'cmpScheduleInDay');
1016        $this->scheduleRoot->resetOddEven ();
1017        $content = $this->formatDayEventsBubble ($dn_day, $dayEvents);
1018        if (!$content)
1019            return '';
1020        return '<div class="bubble '.$this->getConf ('bubblePosition').'Bubble">'.$content.'</div>';
1021    }
1022
1023    // ============================================================
1024    function getCityBubble ($LocationsEvents) {
1025        $content = '';
1026        // XXX uasort ($dayEvents, 'cmpScheduleInCities');
1027        foreach ($LocationsEvents as $location => $datedEvents) {
1028            $locationContent = '';
1029            foreach ($datedEvents as $dn_day => $dayEvents)
1030                $locationContent .= $this->formatDayEventsBubble ($dn_day, $dayEvents);
1031            if ($locationContent)
1032                $content .= '<div class="cityBubble" location="'.$location.'"><div class="bubble '.$this->getConf ('bubblePosition').'Bubble">'.$locationContent.'</div></div>';
1033        }
1034        return $content;
1035    }
1036
1037    // ============================================================
1038    /* Print schedule as a matrix or list */
1039    function printScheduleList ($mapId) {
1040        if ($this->isAdmin ()) {
1041            echo '<div><form>';
1042            foreach (array ('clear', 'clearAll') as $action)
1043                echo '<input value="'.$this->getLang ($action).'" onclick="javascript:scheduleClearCache (\''.$action.'\', \''.$this->nameSpace.'\')" type="button">';
1044            echo '</form></div>';
1045        }
1046        ptln ('<div class="schedule">');
1047        if ($this->printProp || $this->printForm)
1048            $this->printScheduleInputForm ($mapId);
1049        if ($this->printCtrl)
1050            ptln ('</div>'.NL.
1051                  '<div class="schedule">'.NL.
1052                  ' <form method="POST" action="'.$_SERVER['PHP_SELF'].'">'.NL.
1053                  '  <input type="hidden" name="schd[ns]" value="'.$this->nameSpace.'"/>'.NL.
1054                  '  <input type="hidden" name="id" value="'.$_REQUEST['id'].'" />');
1055        ptln ('  <table class="long" onMouseOut="javascript:scheduleHighlightEvent (null, null)">');
1056        $this->printScheduleListHead ();
1057        if ($this->isAdmin ())
1058            $this->printScheduleListContent ($this->allProposals, true);
1059        $this->printScheduleListContent ($this->allSchedules);
1060        $this->printScheduleDeleteForm ();
1061        ptln ('  </table>');
1062        if ($this->printCtrl)
1063            ptln (' </form>');
1064        ptln ('</div>');
1065    }
1066
1067    // ============================================================
1068    /* Print schedule head list */
1069    function printScheduleListHead () {
1070        global $conf;
1071
1072        ptln ('   <thead>'.NL.
1073              '    <tr>'.NL.
1074              '     <th class="when" rowspan="2">'.
1075              ($this->isShow ?
1076               ('<a href="?id='.$this->nameSpace.':'.$conf ['start'].'" rel="nofollow" class="wikilink1">'.
1077                $this->getLang ('allDates').'</a>') :
1078               $this->getLang ('when')).'</th>'.NL.
1079              '     <th class="what">'.$this->getLang ('what').'</th>'.NL.
1080              '     <th class="where">'.$this->getLang ('where').'</th>'.NL.
1081              '     <th class="suject">'.$this->getLang ('title').'</th>'.NL.
1082              '    </tr>'.NL.
1083              '    <tr>'.NL.
1084              '     <th class="audience">'.$this->getLang ('audience').'</th>'.NL.
1085              '     <th class="by">'.$this->getLang ('proposedBy').'</th>'.NL.
1086              '     <th class="repeat">');
1087        if ($this->printCtrl)
1088            ptln ('<input class="button" type="button" onclick="scheduleSwitchSelection(this.form)" value="'.$this->getLang ('inverted').'"/>');
1089        ptln ('</th>'.NL.
1090              '    </tr>'.NL.
1091              '   </thead>');
1092    }
1093
1094    // ============================================================
1095    /* Print schedule line list */
1096    function printScheduleLineEvent ($pageId, $lineVals, $even, $prop = false) {
1097        $ns = $this->nameSpace;
1098
1099        $event = $lineVals['event'];
1100        $propClass = $prop ? ' proposal' : '';
1101
1102        $day = $this->scheduleRoot->ds2df ($event->syncStart ? $event->syncStart : $event->from);
1103        $overEvent=' onMouseOver="javascript:scheduleHighlightEvent (\''.$day.'\', \''.$this->getLocationsFromEvent ($event).'\')"';
1104
1105        ksort ($lineVals['member']);
1106        ptln ('    <tr class="'.$this->scheduleRoot->getOddEven ().' '.$lineVals ['class'].$propClass.'" '.$overEvent.'>'.NL.
1107              '     <td class="when" rowspan="2">');
1108        if ($prop)
1109            ptln ('<i>proposition</i><br/>');
1110        ptln ($day.(($event->syncStart || ('' == $event->to)) ? '' : '<br/>'.NL.$this->scheduleRoot->ds2df ($event->to)).('' == $event->at ? '' : '<br/>'.NL.$event->at).'</td>'.NL.
1111              '     <td class="what">'.$event->what.'</td>'.NL.
1112              '     <td class="where">'.$this->makeWhereString ($event->where).'</td>'.NL.
1113              '     <td class="title"><a class="'.$lineVals['css'].'" href="?id='.$pageId.'">'.$event->title.'</a></td>'.NL.
1114              '    </tr>'.NL.
1115              '    <tr class="'.$this->scheduleRoot->getOddEven ().' '.$lineVals ['class'].$propClass.'" '.$overEvent.'>'.NL.
1116              '     <td class="audience">'.$event->audience.'</td>'.NL.
1117              '     <td class="proposedBy">');
1118        if ($this->printCtrl) {
1119            ptln ($this->scheduleRoot->scheduleShared[$event->shared]);
1120            if (!$prop && !$lineVals['exists'] && $this->isAMemberOf (array_keys ($lineVals['member']))) {
1121                // petite baguette magique de création
1122                $id = reset (array_values($lineVals['member']));
1123                ptln ('<a href="?id='.$_REQUEST['id'].'&schd[id]='.$id.'&schd[ns]='.$ns.'&schd[pageId]='.$pageId.'&schd[action]='.$this->getLang ('created').'">'.
1124                      ' <img src="'.$this->scheduleRoot->wizardIconUrl.'"/></a>');
1125            }
1126        }
1127        foreach ($lineVals['member'] as $member => $id) {
1128            echo '<span style="white-space: nowrap;">';
1129            if ($this->printCtrl && $this->isMember ($member)) {
1130                // suppression ou édition pour un membre
1131                ptln ('<input type="checkbox" name="schd[idl][]" value="'.$id.'"/> '.
1132                      '<a href="?id='.$_REQUEST['id'].'&schd[id]='.$id.'&schd[ns]='.$ns.'&schd[action]='.$this->getLang ('selected').'">'.
1133                      '<img src="'.$this->scheduleRoot->editIconUrl.'"/></a>');
1134            }
1135            $logoId = $this->scheduleRoot->groupsDir.':'.$member.':'.$this->scheduleRoot->iconName;
1136            // YYY
1137            ptln ('
1138	  <a href="/'.$this->scheduleRoot->groupsDir.':'.$member.'/"><img src="'.ml ($logoId, array ('cache'=>$_REQUEST['cache'], 'w'=>$this->scheduleRoot->iconWidth)).'" width="'.$this->scheduleRoot->iconWidth.'"/></a></span> ');
1139        }
1140        ptln ('</td>'.NL.
1141              '     <td class="repeat">');
1142        if ($event->repeatFlag)
1143            ptln ('<img src="'.$this->scheduleRoot->repeatIconUrl.'" /> '.$this->makeRepeatString ($event, false));
1144        ptln ('</td>'.NL.
1145              '    </tr>');
1146    }
1147
1148    // ============================================================
1149    function makePrettyAt ($at) {
1150        if (!$at)
1151            return '';
1152        if (preg_match ("#(?<from>[^-]*)-(?<to>[^-]*)#", $at, $dumy))
1153            return ' '.$this->getLang ('fromHour').' **'.$dumy ['from'].'** '.$this->getLang ('toHour').'  **'.$dumy ['to'].'**';
1154        return ' '.$this->getLang ('at').' **'.$at.'**';
1155    }
1156
1157    function makeListPlaceForMap ($schedule) {
1158        global $scheduleInseeCities;
1159        $result= '';
1160        $cities = explode (',', $schedule->where);
1161        $lats = explode (',', $schedule->lat);
1162        $lons = explode (',', $schedule->lon);
1163        $addrs = explode ('|', $schedule->addr);
1164        $unknownAddr = false;
1165        for ($i = 0; $i < count ($cities); $i++) {
1166            if (!isset ($addrs [$i]))
1167                $unknownAddr = true;
1168            $result .=
1169                    $cities[$i].' | '.
1170                    (isset ($lats [$i]) ? $lats [$i] : '').' | '.
1171                    (isset ($lons [$i]) ? $lons [$i] : '').' | '.
1172                    (isset ($addrs [$i]) ? $addrs [$i] : '').
1173                    NL;
1174        }
1175        if ($unknownAddr)
1176            $result .= '<wrap todo>Les coordonnées par défaut correspondent aux centres-villes !</wrap>'.NL;
1177        return $result;
1178    }
1179
1180    // ============================================================
1181    /* Create a natural langage string for cities zipcode */
1182    function makeWhereString ($where) {
1183        global $scheduleInseeCities;
1184        $result = array ();
1185        foreach (explode (',', $where) as $zip)
1186            $result[] = (isset ($scheduleInseeCities[$zip]) ?
1187                         $scheduleInseeCities[$zip][0] :
1188                         ($this->showOldValues ? '<b>'.$zip.'</b>' : $zip));
1189        return implode (',', array_unique ($result));
1190    }
1191
1192    // ============================================================
1193    /* Create a natural langage string to formulate repeat event */
1194    function makeRepeatString ($event, $bold = false) {
1195        $orderedFormat = $this->getLang ('orderedFormat');
1196        $rt = $this->getLang ('repeatType');
1197        $result = '';
1198        switch ($event->repeatType) {
1199        case 'day' :
1200            return $this->getLang ('from').' '.$this->scheduleRoot->ds2ln ($event->from, $bold).' '.$this->getLang ('to').' '.$this->scheduleRoot->ds2ln ($event->to, $bold);
1201            break;
1202            // pour gagner du temps
1203        case 'week' :
1204            foreach (array_keys ($this->getLang ('days')) as $idd => $dayName)
1205                $result .= in_array ($idd, explode ('|', $event->weekDays)) ? $dayName.' ' : '';
1206                        break;
1207        case 'dayMonth' :
1208            $dayNames = array_keys ($this->getLang ('days'));
1209            // XXX pb OVH => utilisation de tableau au lieu de fonction
1210            // $result .= $f ($event->weekRank+1).' '.$d [$event->dayInWeek].' ';
1211            $result .= $orderedFormat [$event->weekRank+1].' '.$dayNames [$event->dayInWeek].' ';
1212            break;
1213        case 'dateMonth' :
1214            // XXX pb OVH => utilisation de tableau au lieu de fonction
1215            // $result .= $f ($event->dayRank).' ';
1216            $result .= $orderedFormat [$event->dayRank].' ';
1217            break;
1218        case 'year' :
1219            // XXX le Ie jour du Je mois
1220            preg_match ("#(?<Y>[0-9]{4})(?<m>[0-9]{2})(?<d>[0-9]{2})#", $event->from, $dt_from);
1221            $dn_from = $this->scheduleRoot->dt2dn ($dt_from);
1222            $result .= date ($this->scheduleRoot->currentDateFormat ['dayMonth'], $dn_from).' ';
1223        }
1224        $result .= $this->getLang ('all').' ';
1225        if ($event->repeat > 1)
1226            $result .= $event->repeat.' ';
1227        $result .= $rt[$event->repeatType];
1228
1229        return $result;
1230    }
1231
1232    // ============================================================
1233    /* Add a schedule line list in array for futur print */
1234    function addEventTab (&$tab, $event, $when, $pageId, $eventClass, $exists, $cssLink) {
1235        if (!$tab [$when])
1236            $tab [$when] = array ();
1237        if ($tab [$when][$pageId])
1238            $tab [$when][$pageId]['member'][$event->member] = $event->id;
1239        else
1240            $tab [$when][$pageId] =
1241                                  array ('member' => array ($event->member => $event->id), 'event' => $event, 'class' => $eventClass, 'exists' => $exists, 'css' => $cssLink);
1242    }
1243
1244    // ============================================================
1245    /* Print all schedule line list */
1246    function printScheduleListContent ($allSchedules, $prop = false) {
1247        $linesTab = array ();
1248        $repeatTab = array ();
1249        if ($this->after) {
1250            preg_match ("#(?<Y>[0-9]{4})(?<m>[0-9]{2})(?<d>[0-9]{2})#", $this->after, $dt_start);
1251            $dn_start = $this->scheduleRoot->dt2dn ($dt_start);
1252        }
1253        foreach ($allSchedules as $event) {
1254            // XXX même filtre pour printScheduleCalendar ?
1255            if ($this->before && strcmp ($event->from, $this->before) > 0)
1256                continue;
1257            // XXX repeat le même jour => calcul
1258            if ($this->date && (strcmp ($event->from, $this->date) > 0 ||
1259                                strcmp (('' != $event->to ? $event->to : $event->from), $this->date) < 0))
1260                continue;
1261            if ($this->after &&
1262                strcmp (('' != $event->to ? $event->to : $event->from), $this->after) < 0)
1263                continue;
1264            if ($this->max && $line > $this->max)
1265                break;
1266            $continue = false;
1267            if ($this->filters)
1268                foreach ($this->filters as $name => $values) {
1269                    if (in_array ($name, $this->scheduleRoot->filterNames) && !in_array ($event->$name, $values)) {
1270                        $continue = true;
1271                        break;
1272                    } elseif (in_array ($name,  array_keys ($this->scheduleRoot->filterNames))) {
1273                        $trueName = $this->scheduleRoot->filterNames[$name];
1274                        if (in_array ($event->$trueName, $values)) {
1275                            $continue = true;
1276                            break;
1277                        }
1278                    }
1279                }
1280            if ($continue)
1281                continue;
1282            $pageId = $this->scheduleRoot->getPageId ($event);
1283            resolve_pageid ($this->nameSpace, $pageId, $exists);
1284            $cssLink = $exists ? 'wikilink1' : 'wikilink2';
1285            $eventClass = 'cat_'.preg_replace ("/\s/", '', htmlspecialchars (strtolower ($this->scheduleRoot->scheduleWhat [$event->what])));
1286            $when = $event->from.'-'.$event->at.'-'.$event->to;
1287
1288            if ($event->repeatFlag) {
1289                if (!$this->after || strcmp ($event->from, $this->after) > 0)
1290                    $event->syncStart = $event->from;
1291                else {
1292                    // cas ou il faut déplacer le début de la répétition (on ne déplace si la 1re occurance est après le début de la fenêtre)
1293                    preg_match ("#(?<Y>[0-9]{4})(?<m>[0-9]{2})(?<d>[0-9]{2})#", $event->from, $dt_from);
1294                    $dn_day = $dn_from = $this->scheduleRoot->dt2dn ($dt_from);
1295                    // on déplace sur la 1re occurance
1296                    switch ($event->repeatType) {
1297                        // chercher le 1er évènement après 'after'
1298                    case 'day' :
1299                        $step = 86400 * $event->repeat;
1300                        $dn_day += ceil (($dn_start - $dn_from) / $step) * $step;
1301                        break;
1302                    case 'week' :
1303                        $dn_startWeek = mktime (0, 0, 0, date ('m', $dn_from), date ('d', $dn_from) - date ('w', $dn_from), date ('Y', $dn_from));
1304                        $step = 86400 * 7 * $event->repeat;
1305                        $dn_startWeek += floor (($dn_start - $dn_startWeek) / $step) * $step;
1306                        // au pire start commence après les jours répétés dans la semaine
1307                        for ($i = 0; $i < 2; $i++, $dn_startWeek += $step) {
1308                            foreach (explode ('|', $event->weekDays) as $idd) {
1309                                $dn_day = $dn_startWeek + $idd * 86400;
1310                                if ($dn_day >= $dn_start && $dn_day >= $dn_from) {
1311                                    break 2;
1312                                }
1313                            }
1314                        }
1315                        break;
1316                    case 'dayMonth' :
1317                    case 'dateMonth' : {
1318                        $dn_startMonth = mktime (0, 0, 0, date ('m', $dn_from), 1, date ('Y', $dn_from));
1319                        $nbMonth = floor ((date ('m', $dn_start) + date ('Y', $dn_start)*12 - (date ('m', $dn_from) + date ('Y', $dn_from)*12))/$event->repeat)*$event->repeat;
1320                        $dn_startMonth = mktime (0, 0, 0, date ('m', $dn_startMonth) + $nbMonth, 1, date ('Y', $dn_startMonth));
1321                        for ($i = 0; $i < 2; $i++, $dn_startMonth = mktime (0, 0, 0, date ('m', $dn_startMonth) + $event->repeat, 1, date ('Y', $dn_startMonth))) {
1322                            $dn_day = $dn_startMonth;
1323                            if ($event->repeatType == 'dateMonth')
1324                                // XXX pb si moins de jour dans le mois
1325                                $dn_day = mktime (0, 0, 0, date ('m', $dn_startMonth), $event->dayRank, date ('Y', $dn_startMonth));
1326                            else {
1327                                $delta = $event->dayInWeek - date ('w', $dn_startMonth);
1328                                if ($delta < 0)
1329                                    $delta += 7;
1330                                $delta += 7 * $event->weekRank;
1331                                // XXX pb si moins de jour dans le mois
1332                                $dn_day = mktime (0, 0, 0, date ('m', $dn_startMonth), $delta+1, date ('Y', $dn_startMonth));
1333                            }
1334                            if ($dn_day >= $dn_start && $dn_day >= $dn_from)
1335                                break;
1336                        }
1337                        break;
1338                    }
1339                    case 'year' :
1340                        $dayInmonth = date ('j', $dn_from);
1341                        $monthInYear = date ('n', $dn_from);
1342                        $startYear = date ('Y', $dn_start) - (date ('Y', $dn_start) - date ('Y', $dn_from)) % $event->repeat;
1343                        for ($i = 0; $i < 2; $i++, $startYear += $event->repeat) {
1344                            $dn_day = mktime (0, 0, 0, $monthInYear, $dayInmonth, $startYear);
1345                            if ($dn_day >= $dn_start && $dn_day >= $dn_from)
1346                                break;
1347                        }
1348                        break;
1349                    }
1350                    $ds_day = date ('Ymd', $dn_day);
1351                    if ($this->before && strcmp ($ds_day, $this->before) > 0)
1352                        continue;
1353                    if ($this->date && strcmp ($ds_day, $this->date) != 0)
1354                        continue;
1355                    $event->syncStart = $ds_day;
1356                    $when = $ds_day.'-'.$event->at.'-'.$event->to;
1357                }
1358            }
1359            if ($this->repeatGrouped && $event->repeatFlag)
1360                schedules::addEventTab ($repeatTab, $event, $when, $pageId, $eventClass, $exists, $cssLink);
1361            else
1362                schedules::addEventTab ($linesTab, $event, $when, $pageId, $eventClass, $exists, $cssLink);
1363        }
1364        ksort ($repeatTab);
1365        ksort ($linesTab);
1366
1367        $line = 1;
1368        foreach ($repeatTab as $when) {
1369            foreach ($when as $pageId => $lineVals) {
1370                schedules::printScheduleLineEvent ($pageId, $lineVals, $even, $prop);
1371                $this->scheduleRoot->switchOddEven ();
1372                $line++;
1373            }
1374        }
1375        foreach ($linesTab as $when) {
1376            foreach ($when as $pageId => $lineVals) {
1377                schedules::printScheduleLineEvent ($pageId, $lineVals, $even, $prop);
1378                $this->scheduleRoot->switchOddEven ();
1379                $line++;
1380            }
1381        }
1382        if ($line < 1)
1383            echo '
1384            <tr class="'.$this->scheduleRoot->getOddEven ().'"><td colspan="6">'.$this->getLang ('noEvent').'</td></tr>';
1385    }
1386
1387    // ============================================================
1388    /* Print delete button in form */
1389    function printScheduleDeleteForm () {
1390        if (!$this->printCtrl)
1391            return;
1392        echo '
1393      <tr>
1394	<th colspan="2"></th>
1395	<th><input type="submit" name="schd[action]" value="'.$this->getLang ('remove').'"/></th>
1396        <th></th>
1397      </tr>';
1398    }
1399
1400    // ============================================================
1401    /* Print add event form */
1402    function printRepeatForm () {
1403        $repeat = ($this->defaultSchedule->repeat > 1) ? $this->defaultSchedule->repeat : 1;
1404        ptln('
1405<table>
1406 <tr>
1407  <td colspan="8">'.$this->getLang ('all').' <input class="repeat" name="schd[repeat]" type="text" value="'.$repeat.'"/>
1408   <select name="schd[repeatType]" onChange="scheduleUpdateRepeatType (this)">
1409');
1410        foreach ($this->getLang ('repeatType') as $idrt => $repeatType) {
1411            $selected = ($this->defaultSchedule->repeatType == $idrt) ? ' selected="true"' : '';
1412            ptln ('    <option value="'.$idrt.'"'.$selected.'>'.$repeatType.'</option>'.NL);
1413        }
1414        ptln ('
1415   </select>
1416  </td>
1417 </tr><tr>
1418  <td rowspan="2">'.$this->getLang ('each').'</td>
1419');
1420        $disabled = ($this->defaultSchedule->repeatType !== "week") ? ' disabled="true"' : '';
1421        $eventWeekDays = explode ("|", $this->defaultSchedule->weekDays);
1422        $checkbox = "";
1423        $dayNames = $this->getLang ('days');
1424        foreach (array_values ($dayNames) as $idd => $dayAbrv) {
1425            ptln ('
1426  <td>'.$dayAbrv.'</td>
1427');
1428            $selected = in_array ($idd, $eventWeekDays) ? ' checked="true"' : '';
1429            $checkbox .= '  <td><input type="checkbox" name="schd[weekDays][]" onclick="scheduleUpdateWeekDays (this)" value="'.$idd.'"'.$selected.$disabled.'/></td>'.NL;
1430        }
1431        $disabled = ($this->defaultSchedule->repeatType !== "dayMonth") ? ' disabled="true"' : '';
1432        ptln ('
1433 </tr><tr>
1434'.$checkbox.'
1435 </tr><tr>
1436  <td colspan="8">
1437   <select name="schd[weekRank]"'.$disabled.'>'
1438        );
1439        $orderedFormat = $this->getLang ('orderedFormat');
1440        for ($week = 0; $week < 5; $week++) {
1441            $selected = ($week == $this->defaultSchedule->weekRank) ? ' selected="true"' : '';
1442            // XXX pb OVH => utilisation de tableau au lieu de fonction
1443            // echo '<option value="'.$week.'"'.$selected.'>'.$f ($week+1).'</option>'.NL;
1444            ptln ('          <option value="'.$week.'"'.$selected.'>'.$orderedFormat [$week+1].'</option>'.NL);
1445        }
1446        ptln ('
1447   </select>
1448   <select name="schd[dayInWeek]"'.$disabled.'>
1449');
1450        foreach (array_keys ($dayNames) as $idw => $week) {
1451            $selected = ($idw == $this->defaultSchedule->dayInWeek) ? ' selected="true"' : '';
1452            ptln ('    <option value="'.$idw.'"'.$selected.'>'.$week.'</option>');
1453        }
1454        $disabled = ($this->defaultSchedule->repeatType !== "dateMonth") ? ' disabled="true"' : '';
1455        ptln ('
1456   </select>
1457   <select name="schd[dayRank]"'.$disabled.'>
1458');
1459        for ($dayRank = 1; $dayRank < 32; $dayRank++) {
1460            $selected = ($dayRank == $this->defaultSchedule->dayRank) ? ' selected="true"' : '';
1461            ptln ('    <option value="'.$dayRank.'"'.$selected.'>'.$orderedFormat [$dayRank].'</option>'.NL);
1462        }
1463        ptln ('
1464   </select>
1465  </td>
1466 </tr>
1467</table>');
1468    }
1469
1470    // ============================================================
1471    function printScheduleInputForm ($mapId) {
1472        if (!($this->printForm || $this->printProp))
1473            return;
1474
1475        $from = (!$this->defaultSchedule->from && isset ($_REQUEST["schd"]["date"])) ?
1476              $_REQUEST["schd"]["date"] :
1477              $this->scheduleRoot->ds2df ($this->defaultSchedule->from);
1478
1479        ptln ('  <div class="scheduleTabForm">'.NL.
1480              '   <p>'.$this->getLang ($this->printProp ? 'proposedEvent' : 'addEvent').'</p>'.NL. // XXX getLang
1481              '   <ul>'.NL.
1482              '    <li><a class="warningPlace" href="#scheduleTabForm-0">1) '.$this->getLang ('who').'</a></li>'.NL.
1483              '    <li><a class="warningPlace" href="#scheduleTabForm-1">2) '.$this->getLang ('what').'</a></li>'.NL.
1484              '    <li><a class="warningPlace" href="#scheduleTabForm-2">3) '.$this->getLang ('where').'</a></li>'.NL.
1485              '    <li><a class="warningPlace" href="#scheduleTabForm-3">4) '.$this->getLang ('when').'</a></li>'.NL.
1486              '    <li><a class="warningPlace" href="#scheduleTabForm-4">5) '.$this->getLang ('validation').'</a></li>'.NL.
1487              '   </ul>'.NL.
1488
1489              '   <form class="scheduleMiscForm">'.NL.
1490              '   <div id="scheduleTabForm-0">'.NL.
1491              '    <div>'.NL.
1492              '     <div class="warningPlace"></div><select name="schd[audience]" onChange="scheduleCheckInputs ()">'.NL);
1493        foreach ($this->scheduleRoot->scheduleAudience as $audience => $label) {
1494            $selected = ($audience == $this->defaultSchedule->audience) ? ' selected="true" ' : '';
1495            ptln ('      <option value="'.$audience.'"'.$selected.'>'.$label.'</option>'.NL);
1496        }
1497        ptln ('     </select>'.NL.
1498              '     <select name="schd[shared]" onChange="scheduleSharedEvent (this.form)">'.NL);
1499        foreach (array (false, true) as $sharedVal) {
1500            $selected = ($sharedVal == $this->defaultSchedule->shared) ? ' selected="true" ' : '';
1501            ptln ('       <option value="'.$sharedVal.'"'.$selected.'>'.$this->scheduleRoot->scheduleShared[$sharedVal].'</option>'.NL);
1502        }
1503        ptln ('     </select>'.NL.
1504              '     <div class="warningPlace"></div><select class="members" name="schd[member]"'.($this->defaultSchedule->shared ? ' multiple="true"' : "").' size="'.($this->defaultSchedule->shared == 1 ? 9 : 1).'" onChange="scheduleCheckInputs ()">');
1505        $availableGroup = ($this->isAdmin () || $this->printProp)? $this->allGroups : $this->userGroups;
1506        $sharedMembers = $this->defaultSchedule->prop ?
1507                       explode (",", $this->defaultSchedule->member) :
1508                       $this->getMembersSharedEvents ($this->defaultSchedule);
1509        ptln ('      <option value="">'.$this->getLang ('memberChoice').'</option>'.NL);
1510        foreach ($availableGroup as $member) {
1511            $selected = in_array ($member, $sharedMembers) ? ' selected="true" ' : '';
1512            ptln ('      <option value="'.$member.'"'.$selected.'>'.$member.'</option>'.NL);
1513        }
1514        ptln ('     </select>'.NL.
1515              '    </div>'.NL.
1516              '   </div>'.NL.
1517
1518              '   <div id="scheduleTabForm-1">'.NL.
1519              '     <div class="warningPlace"></div><select name="schd[what]" onChange="scheduleCheckInputs ()" >'.NL.
1520              '      <option value="">'.$this->getLang ('eventTypeChoice').'</option>'.NL);
1521        foreach (array_keys ($this->scheduleRoot->scheduleWhat) as $what) {
1522            $selected = ($what == $this->defaultSchedule->what) ? ' selected="'.$what.' '.$this->defaultSchedule->what.'" ' : '';
1523            ptln ('      <option value="'.$what.'"'.$selected.'>'.$what.'</option>'.NL);
1524        }
1525        ptln ('     </select>'.NL.
1526              '     <input class="title" name="schd[title]" type="text" value="'.$this->defaultSchedule->title.'" placeholder="'.$this->getLang ('titlePH').'" onChange="scheduleCheckInputs ()"/>'.NL.
1527              '    <p>'.NL.
1528              '     '.$this->getLang ('lead').':<br/>'.NL.
1529              '     <textarea class="paper" name="schd[lead]" placeholder="'.$this->getLang ('leadPH').'">'.$this->defaultSchedule->lead.'</textarea>'.NL.
1530              '    </p><p>'.NL.
1531              '     '.$this->getLang ('posterURL').':<br/>'.NL.
1532              '     <input class="poster" name="schd[posterURL]" type="text" value="'.$this->defaultSchedule->posterURL.'" placeholder="'.$this->getLang ('posterPH').'"/>'.NL.
1533              '    </p><p>'.NL.
1534              '     '.$this->getLang ('paperURL').':<br/>'.NL.
1535              '     <input class="paper" name="schd[paperURL]" type="text" value="'.$this->defaultSchedule->paperURL.'" placeholder="'.$this->getLang ('paperPH').'"/>'.NL.
1536              '    </p><p>'.NL.
1537              '     '.$this->getLang ('remark').':<br/>'.NL.
1538              '     <textarea class="paper" name="schd[remark]" placeholder="'.$this->getLang ('remarkPH').'">'.$this->defaultSchedule->remark.'</textarea>'.NL.
1539              '    </p><p>'.NL.
1540              '     '.$this->getLang ('rate').':<br/>'.NL.
1541              '     <input class="rate" name="schd[rate]" type="text" value="'.$this->defaultSchedule->rate.'" placeholder="'.$this->getLang ('ratePH').'"/>'.NL.
1542              '    </p>'.NL.
1543              '   </div>'.NL.
1544              '   </form>'.NL.
1545
1546              '   <div id="scheduleTabForm-2">'.NL.
1547              '   <form class="scheduleCitiesForm insee">'.NL.
1548              '    <div>'.NL.
1549              '     <div id="'.$mapId.'" class="scheduleMap scheduleMapForm"></div>'.NL.
1550              '     <div class="ui-widget">'.NL.
1551              '      <div><ul class="cities warningPlace">'.NL);
1552
1553        if ($this->defaultSchedule->where) {
1554            global $scheduleInseeCities;
1555            $cities = explode (',', $this->defaultSchedule->where);
1556            $lats = explode (',', $this->defaultSchedule->lat);
1557            $lons = explode (',', $this->defaultSchedule->lon);
1558            $addrs = explode ('|', $this->defaultSchedule->addr);
1559            for ($i = 0; $i < count ($cities); $i++) {
1560                $insee = $cities[$i];
1561                $cityName =
1562                          isset ($scheduleInseeCities [$insee]) ?
1563                          $scheduleInseeCities [$insee][0] :
1564                          $insee;
1565                ptln ('        <li onclick="scheduleSelectCity (this)" insee="'.$insee.'" lat="'.
1566                      (isset ($lats [$i]) ? $lats [$i] : "").'" lon="'.
1567                      (isset ($lons [$i]) ? $lons [$i] : "").'"><img class="checked" src="'.
1568                      $this->scheduleRoot->emptyIconUrl.'" onclick="scheduleRemoveCity (this)"/>'.
1569                      '<span class="addresse">'.(isset ($addrs [$i]) ? str_replace ('\\\\', '<br/>', str_replace ('~br~', '<br/>', $addrs [$i])) : "").'</span> '.
1570                      '<span class="city">'.$cityName.'</span></li>');
1571            }
1572        }
1573        ptln ('      </ul></div>'.NL.
1574              '      <label>'.$this->getLang ('city').' :<br/></label><input name="city" type="text" placeholder="'.$this->getLang ('cityPH').'"/>'.NL.
1575              '      <span class="wrap_tip ">'.$this->getLang ('enterTip').'</span>'.NL.
1576              '      <label>'.$this->getLang ('addresse').' :<br/></label><input name="addr" type="text" placeholder="'.$this->getLang ('addrPH').'"/>'.NL.
1577              '      <span class="wrap_tip ">'.$this->getLang ('enterTip').'</span>'.NL.
1578              '     </div>'.NL.
1579              '    </div>'.NL.
1580              '   </form>'.NL);
1581        if ($this->isAdmin ())
1582            ptln ('    <div>'.NL.
1583                  '<input type="button" value="'.$this->getLang ('add').'" onClick="javascript:scheduleAddInsee ()" />'.NL.
1584                  '<input type="button" value="'.$this->getLang ('remove').'" onClick="javascript:scheduleRemoveInsee ()" />'.NL.
1585                  '<input type="button" value="'.$this->getLang ('test').'" onClick="javascript:scheduleTestInsee ()" />'.NL.
1586                  '</div>'.NL);
1587        ptln ('   </div>'.NL.
1588
1589              '   <form class="scheduleFinalForm" method="POST" action="'.$_SERVER["PHP_SELF"].'">'.NL.
1590              '    <div id="scheduleTabForm-3">'.NL.
1591              '     <p>'.NL.
1592              $this->getLang ('from').' <input class="date" id="scheduleFrom" name="schd[from]" type="text" value="'.$from.'" placeholder="'.$this->getLang ('datePH').'" onChange="scheduleCheckInputs ()" /> '.
1593              $this->getLang ('to').' <input class="date" name="schd[to]" type="text" value="'.$this->scheduleRoot->ds2df ($this->defaultSchedule->to).'" placeholder="'.$this->getLang ('datePH').'"/></p><p>'.
1594              $this->getLang ('at').' <input class="hour" name="schd[at]" type="text" value="'.$this->defaultSchedule->at.'" placeholder="'.$this->getLang ('hourPH').'"/></p>');
1595        $this->printRepeatForm ();
1596        ptln ('    </div>'.NL.
1597
1598              '    <div id="scheduleTabForm-4">'.NL.
1599              '     <p>'.NL.
1600
1601              '      <input type="hidden" name="schd[ns]" value="'.$this->nameSpace.'"/>'.NL.
1602              '      <input type="hidden" name="id" value="'.$_REQUEST["id"].'" />'.NL);
1603
1604        if ($this->printForm) {
1605            if ($this->defaultSchedule->id) {
1606                ptln ('<input type="hidden" name="schd[id]" value="'.$this->defaultSchedule->id.'"/>');
1607                if (!$this->defaultSchedule->prop) {
1608                    if (count ($this->getMembersSharedEvents ($this->defaultSchedule)) > 1)
1609                        ptln ('<input type="submit" name="schd[action]" value="'.$this->getLang ('modifyAll').'"/>');
1610                    ptln ('<input type="submit" name="schd[action]" value="'.$this->getLang ('modify').'"/> ');
1611                }
1612            }
1613            ptln ('<input type="submit" name="schd[action]" value="'.$this->getLang ($this->defaultSchedule->prop ? 'valid' : 'add').'"/>');
1614        }
1615        if ($this->printProp) {
1616            ptln ('<input type="submit" name="schd[action]" value="'.$this->getLang ('prop').'"/> ');
1617            if ($captcha =& plugin_load ('helper', 'captcha'))
1618                ptln ($captcha->getHTML ());
1619        }
1620        ptln ('     </p>'.NL.
1621              '    </div>'.NL.
1622              '   </form>'.NL.
1623              '   <div id="scheduleHelp">'.NL.
1624              '    <a href="#">Aide</a>'.NL.
1625              '    <div>'.NL.
1626              '     <div class="vshare__center">'.NL.
1627              '      <!--[if !IE]> --><object type="application/x-shockwave-flash" data="http://www.dailymotion.com/swf/x10xcw7_" height="350" width="623"><!-- <![endif]-->'.NL.
1628              '      <!--[if IE]><object width="623" height="350" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"> <param name="movie" value="http://www.dailymotion.com/swf/x10xcw7_" /><!--><!-- -->'.NL.
1629              '       <param name="FlashVars" value="">'.NL.
1630              '       </object>'.NL.
1631              '      <!-- <![endif]-->'.NL.
1632              '     </div>'.NL.
1633              '    </div>'.NL.
1634              '   </div>'.NL.
1635              '  </div>'.NL);
1636    }
1637
1638    // ============================================================
1639    function resetAdminNotification () {
1640        $this->lastNotificationReset = date ('YmdHis');
1641        $this->scheduleRoot->writeConfig ($this);
1642    }
1643
1644    function adminNotification () {
1645        if ($this->lastNotification > $this->lastNotificationReset)
1646            return;
1647
1648        global $auth;
1649        $users = $auth->retrieveUsers ();
1650        foreach ($users as $user => $userinfo) {
1651            $mailSubject = $this->getLang ('notifySubject');
1652            $mailContent = $this->getLang ('notifyContent');
1653            if (in_array (trim ($this->getConf ('adminGroup')), $userinfo ['grps'])) {
1654                mail_send ($userinfo['mail'], $mailSubject, $mailContent);
1655            }
1656        }
1657
1658        $this->lastNotification = date ('YmdHis');
1659        $this->scheduleRoot->writeConfig ($this);
1660    }
1661
1662    // ============================================================
1663}
1664