1<?php
2
3/**
4  * SquirrelMail Shared Calendar Plugin
5  * Copyright (C) 2004-2005 Paul Lesneiwski <pdontthink@angrynerds.com>
6  * This program is licensed under GPL. See COPYING for details
7  *
8  */
9
10
11
12include_once(SM_PATH . 'functions/date.php');
13include_once(SM_PATH . 'plugins/calendar/constants.php');
14include_once(SM_PATH . 'plugins/calendar/classes/calendar.php');
15include_once(SM_PATH . 'plugins/calendar/classes/event.php');
16include_once(SM_PATH . 'plugins/calendar/classes/property.php');
17include_once(SM_PATH . 'plugins/calendar/backend_functions.php');
18include_once(SM_PATH . 'plugins/calendar/data/config.php');
19include_once(SM_PATH . 'plugins/calendar/timezone_offsets.php');
20include_once(SM_PATH . 'plugins/calendar/load_prefs.php');
21
22
23
24// load prefs (can't use loading_prefs hook due to a chicken-and-egg
25// problems with i18n'd strings in constants file (that is needed by
26// config file)
27//
28cal_load_prefs_do();
29
30
31
32global $externalCalendars;
33if (!is_array($externalCalendars)) $externalCalendars = array();
34
35
36
37/**
38  * Because there will always be someone who forgets
39  * to enable a backend, check for one here.  Errors
40  * otherwise are hard to diagnose
41  *
42  */
43global $squirrelmail_plugin_hooks, $color;
44if (empty($squirrelmail_plugin_hooks['get_calendar']))
45{
46   plain_error_message('ERROR: Please enable a calendar backend', $color);
47   exit;
48}
49
50
51
52/**
53  * Can we use JavaScript-enabled display elements?
54  *
55  * Ideally, this function would just be replaced with a
56  * call to checkForJavascript() but we need newest version
57  * of the Compatibility plugin for that, so we just do the
58  * dirty work ourselves (it's not much trouble anyway)
59  *
60  * @return mixed TRUE (or non-zero) if JavaScript is allowable,
61  *               FALSE (or zero) otherwise.
62  *
63  */
64function cal_use_javascript()
65{
66
67   // only in 1.5.x
68   //
69   if (function_exists('checkForJavascript'))
70      return checkForJavascript();
71
72
73   // distilled from checkForJavascript()
74   //
75   if (sqGetGlobalVar('javascript_on', $javascript_on, SQ_SESSION))
76      return $javascript_on;
77
78   global $javascript_setting;
79   if (!isset($javascript_setting))
80      $javascript_setting = getPref($data_dir, $username, 'javascript_setting', SMPREF_JS_OFF);
81   return $javascript_setting;
82
83}
84
85
86
87/**
88  * Get list of all external calendar from user prefs
89  *
90  * @return array Returns a list of all external calendars
91  *
92  */
93function get_all_external_calendars()
94{
95
96   global $username, $data_dir;
97
98   // get external calendars
99   //
100   $external_calendars = getPref($data_dir, $username, 'external_calendars', '');
101   $externalCals = explode('||||', $external_calendars);
102   $external_list = array();
103   foreach ($externalCals as $externalCal)
104   {
105      if (empty($externalCal)) continue;
106
107      list($id, $name, $uri) = explode('^^^^', $externalCal);
108      $cal = loadExternalCalendar($uri);
109//TODO: if just a temporary network outage, we might not want to just remove this... too harsh
110// so figure out how to deal with that better
111      if ($cal == FALSE)
112      {
113         removeExternalCalendarFromUserPrefs($id);
114         continue;
115      }
116      $external_list[] = $cal;
117   }
118
119   return $external_list;
120
121}
122
123
124
125/**
126  * Load calendar from user prefs
127  *
128  * @param string $calendarID The ID of the calendar to be loaded
129  *
130  * @return mixed Returns the Calendar object that represents the
131  *               loaded calendar, or FALSE if the calendar could
132  *               not be loaded
133  *
134  */
135function loadExternalCalendarFromUserPrefs($calendarID)
136{
137
138   global $username, $data_dir;
139   $external_calendars = getPref($data_dir, $username, 'external_calendars', '');
140
141   $externalCals = explode('||||', $external_calendars);
142   foreach ($externalCals as $externalCal)
143   {
144      if (empty($externalCal)) continue;
145
146      list($id, $name, $uri) = explode('^^^^', $externalCal);
147
148      if ($id == $calendarID)
149         return loadExternalCalendar($uri);
150   }
151
152   return FALSE;
153
154}
155
156
157
158/**
159  * Load event from external calendar
160  *
161  * @param string $calendarID The ID of the calendar from which to get the event
162  * @param string $eventID The ID of the event to be loaded
163  *
164  * @return mixed Returns the Event object that represents the
165  *               loaded event, or FALSE if the event could
166  *               not be loaded
167  *
168  */
169function loadExternalEvent($calendarID, $eventID)
170{
171
172   $cal = loadExternalCalendarFromUserPrefs($calendarID);
173   if ($cal === FALSE) return FALSE;
174
175   return $cal->getEvent($eventID);
176
177}
178
179
180
181/**
182  * Load calendar from external URI
183  *
184  * @param string $URI The location of the calendar to be loaded
185  *
186  * @return mixed Returns the Calendar object that represents the
187  *               loaded calendar, or FALSE if the calendar could
188  *               not be loaded
189  *
190  */
191function loadExternalCalendar($URI, $calID='')
192{
193
194   global $username, $data_dir, $external_calendar_clock_skew,
195          $externalCalendars;
196   sqgetGlobalVar('externalCalendars', $externalCalendars, SQ_SESSION);
197
198
199   // since calendar IDs are date/time-encoded (based on creation
200   // date), we need to manually override the ID to what is given
201   // in user's prefs for when it was first linked (only if found
202   // in user prefs, that is)
203   //
204   $correctID = '';
205   $correctName = '';
206   $external_calendars = getPref($data_dir, $username, 'external_calendars', '');
207
208   $externalCals = explode('||||', $external_calendars);
209   foreach ($externalCals as $externalCal)
210   {
211      if (empty($externalCal)) continue;
212
213      list($id, $name, $calUri) = explode('^^^^', $externalCal);
214
215      if ($calUri == $URI)
216      {
217         $correctID = $id;
218         $correctName = $name;
219         break;
220      }
221   }
222
223
224   $headers = get_headers($URI, 1);
225   if ($headers === FALSE) $headers = array();
226
227
228   // look in cache first
229   //
230   if (!empty($correctID) && isset($externalCalendars[$correctID]))
231   {
232      $cal = unserialize($externalCalendars[$correctID]);
233
234      // check if URI has since been modified...
235      //
236      if (!isset($headers['Last-Modified']))
237         $mtime = $cal->createdOn();
238      else
239         $mtime = strtotime($headers['Last-Modified']);
240// don't need the clock skew here, since mod time on the calendar object
241// itself should have come from the original file's mtime
242// and if not, we add it below
243//      if ($mtime - ($external_calendar_clock_skew * 60) > $cal->lastUpdatedOn())
244      if ($mtime > $cal->lastUpdatedOn())
245      {
246         unset($externalCalendars[$cal->getID()]);
247         sqsession_register($externalCalendars, 'externalCalendars');
248      }
249      else
250         return $cal;
251
252   }
253
254
255   $CAL = @fopen($URI, 'rb');
256   if ($CAL === FALSE) return FALSE;
257
258   $calContents = '';
259   while (!feof($CAL)) $calContents .= fread($CAL, 4096);
260
261   fclose($CAL);
262
263   $calContentArray = explode("\r\n", $calContents);
264   if (sizeof($calContentArray) == 1) $calContentArray = explode("\n", $calContents);
265
266   $cal = Calendar::getCalendarFromICal($calContentArray);
267   $cal->setExternal(TRUE);
268   if (!empty($correctID)) $cal->setID($correctID);
269   if (!empty($correctName)) $cal->setName($correctName);
270   if (!isset($headers['Last-Modified']))
271      $mtime = $cal->createdOn() + ($external_calendar_clock_skew * 60);
272   else
273      $mtime = strtotime($headers['Last-Modified']);
274   $cal->setLastUpdateDate(gmdate('Ymd\THis\Z', $mtime));
275
276
277   // cache it
278   //
279   $externalCalendars[$cal->getID()] = serialize($cal);
280   sqsession_register($externalCalendars, 'externalCalendars');
281
282
283   return $cal;
284
285}
286
287
288
289/**
290  * Remove calendar from user prefs
291  *
292  * @param string $calendarID The ID of the calendar to be removed
293  *
294  */
295function removeExternalCalendarFromUserPrefs($calendarID)
296{
297
298   global $username, $data_dir, $externalCalendars, $small_calendar_calID,
299          $small_calendar_calID_default;
300
301   $external_calendars = getPref($data_dir, $username, 'external_calendars', '');
302   sqgetGlobalVar('externalCalendars', $externalCalendars, SQ_SESSION);
303   $new_external_list = '';
304
305   $first = TRUE;
306   $externalCals = explode('||||', $external_calendars);
307   foreach ($externalCals as $externalCal)
308   {
309      if (empty($externalCal)) continue;
310
311      list($id, $name, $uri) = explode('^^^^', $externalCal);
312
313      if ($id == $calendarID)
314      {
315         // remove from cache
316         //
317         if (isset($externalCalendars[$id]))
318         {
319            unset($externalCalendars[$id]);
320            sqsession_register($externalCalendars, 'externalCalendars');
321         }
322
323         continue;
324      }
325
326      if (!$first) $new_external_list .= '||||';
327
328      $new_external_list .= $externalCal;
329
330      $first = FALSE;
331   }
332
333   setPref($data_dir, $username, 'external_calendars', $new_external_list);
334
335
336   // if small calendar was using this calendar, just
337   // fall back to default calendar
338   //
339   if ($small_calendar_calID == $calendarID)
340   {
341      setPref($data_dir, $username, 'small_calendar_calID', $small_calendar_calID_default);
342   }
343
344}
345
346
347
348/**
349  * Get calendar ID for personal calendar
350  *
351  * Retrieves the calendar ID for the given user's
352  * personal calendar
353  *
354  * This function also checks to make sure that this personal
355  * calendar actually exists and creates it if it does not.
356  *
357  * @param string $user The user whose personal calendar ID is being retrieved
358  * @param string $domain The user's domain
359  * @param boolean $dontCheckExists If TRUE, this function will not check
360  *                                 to make sure the calendar exists (optional;
361  *                                 default = FALSE)
362  *
363  * @return string The desired calendar ID
364  *
365  */
366function get_personal_cal_id($user, $domain, $dontCheckExists=FALSE)
367{
368
369   global $useDomainInCalID;
370
371   $calID = strtr('sm_cal_' . $user . ($useDomainInCalID ? '__' . $domain : ''),
372                  '@|_-.:/\ ', '________');
373
374
375   // if calendar data file doesn't exist, create it
376   //
377   if (!$dontCheckExists && get_calendar($calID, TRUE) === FALSE)
378   {
379
380      bindtextdomain ('calendar', SM_PATH . 'locale');
381      textdomain ('calendar');
382
383      $calendar = new Calendar($calID, 0, $domain, '', SM_CAL_TYPE_PERSONAL,
384                               sprintf(_("Personal Calendar for %s"), $user),
385                               $user, gmdate('Ymd\THis\Z'), '', '', array($user));
386
387      bindtextdomain ('squirrelmail', SM_PATH . 'locale');
388      textdomain ('squirrelmail');
389
390      create_calendar($calendar);
391
392   }
393
394   return $calID;
395
396}
397
398
399
400/**
401  * Build event start/end time string for display
402  *
403  * For example, "[Oct 14, 2004 10:00 - Oct 24, 2004 12:00]"
404  * or "[All Day]"
405  *
406  * @param object $event The event object for which to build
407  *                      the start/end time string for display
408  * @param int $year The year where this event is being displayed
409  * @param int $month The month where this event is being displayed
410  * @param int $day The day where this event is being displayed
411  *
412  * @return string The start/end time string, ready for display
413  *
414  */
415function buildEventTimeDisplayText($event, $year, $month, $day)
416{
417
418   global $hour_format, $always_show_full_event_date_and_time;
419
420   $text = ' [';
421
422   $startsToday = $event->startsOnDay($year, $month, $day);
423   $endsToday = $event->endsOnDay($year, $month, $day);
424
425   // NOTE: should not need to switch gettext domain here, since
426   //       this function should be being called from within an
427   //       interface function that is already in the correct calendar
428   //       gettext domain.... right?
429
430   // all-day events render differently
431   //
432   if ($event->isAllDay())
433   {
434
435      if (!$startsToday && !$endsToday
436       || ($always_show_full_event_date_and_time &&
437        (!$startsToday || !$endsToday)))
438         $text .= $event->formattedStartDate('', $year, $month, $day) . ' - '
439                . $event->formattedEndDate('', $year, $month, $day);
440
441      else if (!$startsToday)
442         $text .= $event->formattedStartDate('', $year, $month, $day) . ' - '
443                . _("Today");
444
445      else if (!$endsToday)
446         $text .= _("Today") . ' - '
447                . $event->formattedEndDate('', $year, $month, $day);
448
449      else
450         $text .= _("All Day");
451
452      $text .= "]";
453
454   }
455
456
457   // regular non-all-day events
458   //
459   else
460   {
461
462      if (!$startsToday || $always_show_full_event_date_and_time)
463         $text .= $event->formattedStartDate('', $year, $month, $day) . ' ';
464
465      $text .= $event->formattedStartTime($hour_format == SMPREF_TIME_24HR ? 'H:i' : 'g:ia') . ' - ';
466
467      if (!$endsToday || $always_show_full_event_date_and_time)
468         $text .= $event->formattedEndDate('', $year, $month, $day) . ' ';
469
470      $text .= $event->formattedEndTime($hour_format == SMPREF_TIME_24HR ? 'H:i' : 'g:ia') . "]";
471
472   }
473
474   return $text;
475}
476
477
478
479/**
480  * Inserts link to calendar module in main menu bar
481  *
482  */
483function cal_menu_link_do()
484{
485
486   bindtextdomain ('calendar', SM_PATH . 'locale');
487   textdomain ('calendar');
488
489   displayInternalLink("plugins/calendar/list_calendars.php", _("Calendar"));
490   echo "&nbsp;&nbsp\n";
491
492   bindtextdomain ('squirrelmail', SM_PATH . 'locale');
493   textdomain ('squirrelmail');
494
495}
496
497
498
499/**
500  * Inserts link to calendar admin module and user settings
501  * page in options page
502  *
503  */
504function cal_options_block_do()
505{
506
507   global $username, $optpage_blocks, $cal_user_can_override_defaults;
508
509
510   bindtextdomain ('calendar', SM_PATH . 'locale');
511   textdomain ('calendar');
512
513   if ($cal_user_can_override_defaults)
514      $optpage_blocks[] = array(
515         'name' => _("Calendar Preferences"),
516         'url'  => SM_PATH . 'plugins/calendar/calendar_options.php',
517         'desc' => _("Change the way your calendars behave and are displayed, including the miniature calendar beneath the folder list."),
518         'js'   => false
519      );
520
521
522   $userType = check_cal_user_type($username);
523
524   if (!($userType == SM_CAL_SUPERUSER || $userType == SM_CAL_LIMITED_ADMIN))
525   {
526      bindtextdomain ('squirrelmail', SM_PATH . 'locale');
527      textdomain ('squirrelmail');
528      return;
529   }
530
531
532   $optpage_blocks[] = array(
533      'name' => _("Calendar Administration"),
534      'url'  => SM_PATH . 'plugins/calendar/admin_options.php',
535      'desc' => _("Create and maintain shared calendars. Edit holidays, assign calendar access by user, or create publicly available calendars."),
536      'js'   => false
537   );
538
539   bindtextdomain ('squirrelmail', SM_PATH . 'locale');
540   textdomain ('squirrelmail');
541
542}
543
544
545
546/**
547  * Sort events by (start) time
548  *
549  * @param object $a Event A
550  * @param object $b Event B
551  *
552  * @return -1 if event A comes before event B, 1 if event
553  *         B comes before event A, or zero if the two are equal.
554  *
555  */
556function sortEventsByTime($a, $b)
557{
558
559   return $a->compareTo($b);
560
561}
562
563
564
565//TODO -- do we need a version of this that doesn't work by reference???
566/**
567  * Fold text (by reference) for iCal data stream
568  *
569  * Text is folded after every 75 characters by using
570  * a CRLF and one space.  These can be overridden
571  * using the parameters for this function.
572  *
573  * @param string $text The text value to be folded
574  * @param int $maxLineLength The maximum allowable
575  *                           length of any one line
576  *                           (optional; default = 75)
577  * @param string $foldDelimiter The character sequence
578  *                              used to insert a fold
579  *                              (optional; default =
580  *                              CRLF + SPACE)
581  *
582  */
583function foldICalStreamByRef(&$text, $maxLineLength=75, $foldDelimiter='')
584{
585
586   if (empty($foldDelimiter)) $foldDelimiter = ICAL_LINE_DELIM . ' ';
587   $newText = '';
588   while (strlen($text) > 0)
589   {
590
591      $EOL = strpos($text, ICAL_LINE_DELIM);
592
593      if ($EOL <= $maxLineLength)
594         $cutoff = $EOL + strlen(ICAL_LINE_DELIM);
595      else
596         $cutoff = $maxLineLength;
597
598      $newText .= substr($text, 0, $cutoff);
599      $text = substr($text, $cutoff);
600
601      if ($EOL > $maxLineLength)
602         $newText .= $foldDelimiter;
603
604   }
605   $text = $newText;
606
607}
608
609
610
611//TODO -- do we need a version of this that doesn't work by reference???
612/**
613  * Unfold an iCal data stream (by reference)
614  *
615  * Text is unfolded based on the presence of CRLF followed
616  * by one whitespace character (space or tab).  These (but
617  * not the CRLF) can be overridden using this function's
618  * parameters.
619  *
620  * @param array $iCalData The text value to be unfolded,
621  *                        one text line in each array value.
622  *                        NOTE that it is assumed that the
623  *                        CRLF characters have already been
624  *                        stripped from each line!
625  * @param array $foldDelimiters An array of characters which,
626  *                              when immediately following a
627  *                              CRLF, indicate a text fold
628  *                              (optional; default = space
629  *                              and tab)
630  *
631  */
632function unfoldICalStreamByRef(&$iCalData, $foldDelimiters='')
633{
634
635   if (empty($foldDelimiters) || !is_array($foldDelimiters))
636      $foldDelimiters = array(' ', "\t");
637   $newData = array();
638   $x = -1;
639   foreach ($iCalData as $line)
640   {
641
642      $fold = FALSE;
643      foreach ($foldDelimiters as $delim)
644         if (strpos($line, $delim) === 0)
645         {
646            $fold = TRUE;
647            break;
648         }
649      if ($fold)
650         $newData[$x] .= substr($line, 1);
651      else
652         $newData[++$x] = $line;
653
654   }
655   $iCalData = $newData;
656
657}
658
659
660
661/**
662  * Utility function for sorting PERIOD iCal values
663  * given as two arrays, each array containing two
664  * elements: a starting timestamp and and ending
665  * timestamp.
666  *
667  * @param array $a First PERIOD array to compare
668  * @param array $b Second PERIOD array to compare
669  *
670  * @return -1 if the first PERIOD comes first, 1 if
671  *         the second PERIOD comes first, or 0 if
672  *         the two are equal
673  *
674  */
675function sortPeriods($a, $b)
676{
677
678   if ($a[0] < $b[0]) return -1;
679   if ($a[0] > $b[0]) return 1;
680   if ($a[1] < $b[1]) return -1;
681   if ($a[1] > $b[1]) return 1;
682   return 0;
683
684}
685
686
687
688/**
689  * Get timestamp in correct timezone
690  *
691  * @param int $time Original timestamp
692  * @param string $zone Requested timezone
693  *
694  * @return int Modified timestamp, or same timestamp
695  *             if timezone was unknown
696  *
697  */
698function convertTimestampToTimezone($time, $zone)
699{
700
701   global $tz_array, $color;
702
703   $isDST = date('I', $time);
704
705   // try to guess when dash was used instead of forward slash:
706   // US-Eastern --> US/Eastern
707   //
708   $dash = strpos($zone, '-');
709   if (!array_key_exists($zone, $tz_array) && $dash !== FALSE)
710      $zone = substr($zone, 0, $dash) . '/' . substr($zone, $dash + 1);
711
712   if (array_key_exists($zone, $tz_array))
713      $offset = $tz_array[$zone][$isDST];
714   else
715      $offset = '+0000';
716
717   return getGMTSeconds($time, $offset);
718
719}
720
721
722
723/**
724  * Determines what kind of calendar user the given username is; checks
725  * the user's privelege level.
726  *
727  * @param string $user The username to check for admin permission
728  *
729  * @return string Gives back one of the calendar user type constants:
730  *                SM_CAL_SUPERUSER (user can edit any and all calendars),
731  *                SM_CAL_LIMITED_ADMIN (user can edit calendars they own),
732  *                or SM_CAL_REGULAR_USER (no administrative permissions).
733  *
734  */
735function check_cal_user_type($user)
736{
737
738   global $cal_admins, $cal_debug;
739
740   foreach ($cal_admins as $admin => $perms)
741   {
742
743      if ($cal_debug)
744         echo "current username is \"$user\"; admin username is \"$admin\"; are they equal? "
745            . (strtolower($admin) == strtolower($user) ? 'YES' : 'NO') . '<br />';
746
747
748      if (preg_match('/^' . str_replace(array('?', '*'), array('\w{1}', '.*?'),
749                     strtoupper($admin)) . '$/', strtoupper($user)))
750      {
751         $perms = trim($perms);
752         if (strtolower($perms) == 'yes')
753            return SM_CAL_SUPERUSER;
754         else
755            return SM_CAL_LIMITED_ADMIN;
756      }
757
758   }
759
760   return SM_CAL_REGULAR_USER;
761
762}
763
764
765
766/**
767  * Determine the week of the month for a date
768  *
769  * The date in question may be given as a timestamp
770  * or as separate day, month and year numbers
771  *
772  * @param timestamp $timestamp The date in question
773  * @param int $year The year of the date in question
774  * @param int $month The month of the date in question
775  * @param int $day The day of the date in question
776  *
777  * @return int The week of the month that the given
778  *             date falls within, or zero if error
779  *
780  */
781function weekOfMonth($timestamp=0, $year=0, $month=0, $day=0)
782{
783
784   // bunch of junk to figure out parameters
785   // skip to last line to see what you really
786   // wanted...
787   //
788   if ($month == 0 || $year == 0 || $day == 0)
789   {
790      if ($timestamp == 0)
791         return 0;  // error
792      else
793         list($month, $year) = explode(',', date('m,Y', $timestamp));
794   }
795   else if ($month == 0 || $year == 0 || $day == 0)
796      return 0;  // error
797   else
798      $timestamp = mktime(0, 0, 0, $month, $day, $year);
799
800
801   // not much to it; just subtract week of year
802   // for the first of the month from the week of
803   // the year for the target timestamp
804   //
805   return date('W', $timestamp)
806        - date('W', mktime(0, 0, 0, $month, 1, $year));
807
808}
809
810
811
812// ==========================================================================
813// Handy function to get a timestamp when all you have
814// is a week number (of the year), a day (Monday - Sunday)
815// and a year number -- taken from php.net
816//
817// TODO: what is not clear is if Sunday is considered day 7 or day 0???
818//       if the former, we need to make some mods here, since PHP considers
819//       Sunday as 0
820//       After just a trivial look at the code, it looks like it considers
821//       Sunday as 7, so I inserted code at the beginning of the function
822//       to make the correct conversion.
823
824/**
825  * Get a date by providing a week number, day of week and a year.
826  *
827  * Be careful! There are different definitions for weeks. Here the European definition is used.
828  * In Europe a week starts on Monday.
829  * Also the start of the first week in a year is defined differently in different countries.
830  * Here the ISO 8601 definition is used. This is the standard in Europe.
831  *
832  * I got the information from http://home.t-online.de/home/PeterJHaas/delphi.htm
833  * There are many websites with information on week numbers.
834  * An excellent site on this subject is http://www.pjh2.de/datetime/weeknumber/index.php
835  *
836  * This PHP source was based on the Delphi source code by Peter J. Haas
837  *
838  *
839  * //give me the date of Friday week 20 of the year 2004 (Should result in Friday May 14 2004)
840  * $aWeek=20; $aDay=05; $aYear=2004;
841  * $adate=datefromweeknr($aYear, $aWeek, $aDay);
842  * echo 'The date (week='.$aWeek.' day='.$aDay.' year= '.$aYear.') is '.date('D d-m-Y',$adate).'<br>';
843  *
844  */
845 function datefromweeknr($aYear, $aWeek, $aDay)
846 {
847  // correct for PHP Sundays
848  if ($aDay == 0) $aDay = 7;
849
850  $FirstDayOfWeek=1; //First day of week is Monday
851  $BaseDate=4; //We calculate from 4/1 which is always in week 1
852  $CJDDelta=2415019; //Based on start of Chronological Julian Day
853  $StartDate = DelphiDate(mktime(1,0,0,01,$BaseDate,$aYear)); //The date to start with
854  $Offset = ($aWeek-1) * 7 - mod(floor($StartDate) + $CJDDelta + 8 - $FirstDayOfWeek,7) + $aDay - 1;
855  return PHPUnixTimeStamp($StartDate + $Offset);
856 }
857
858 #---------extra functions used----------
859
860 function DelphiDate($aPHPTime)
861 {
862   # The Unix Timestamp holds the number of seconds after January 1 1970 01:00:00
863   return div($aPHPTime,86400)+25569;
864 }
865
866 function PHPUnixTimeStamp($aDelphiDate)
867 {
868   # Delphi's TDate holds number of days after December 30 1899
869   return ($aDelphiDate-25569)*86400-3600;
870 }
871
872 function mod($number, $div)
873 {
874   return $number - floor($number/$div)*$div;
875 }
876
877 function div($number, $div)
878 {
879   return floor($number/$div);
880 }
881// ==========================================================================
882
883
884
885/**
886  * Determines what occurrence number the day of
887  * the week is in its month for a given date.
888  * That is, is this the 2nd Friday of the month?
889  * Or the 1st Wednesday?
890  *
891  * The date in question may be given as a timestamp
892  * or as separate day, month and year numbers
893  *
894  * @param timestamp $timestamp The date in question
895  * @param int $year The year of the date in question
896  * @param int $month The month of the date in question
897  * @param int $day The day of the date in question
898  *
899  * @return int The occurrence number of the day in
900  *             the month, or zero if error.
901  *
902  */
903function dayOcurrenceInMonth($timestamp=0, $year=0, $month=0, $day=0)
904{
905
906   // bunch of junk to figure out parameters
907   // skip to last line to see what you really
908   // wanted...
909   //
910   if ($month == 0 || $year == 0 || $day == 0)
911   {
912      if ($timestamp == 0)
913         return 0;  // error
914      else
915         list($day, $month, $year) = explode(',', date('j,m,Y', $timestamp));
916   }
917   else if ($month == 0 || $year == 0 || $day == 0)
918      return 0;  // error
919   else
920      $timestamp = mktime(0, 0, 0, $month, $day, $year);
921
922
923   // manually count day occurrences
924   //
925   $count = 0;
926   list($dayOfWeek, $daysThisMonth) = explode(',', date('w,t', $timestamp));
927   for ($i = 1; $i < $daysThisMonth + 1 && $i <= $day; $i++)
928   {
929      if (date('w', mktime(0, 0, 0, $month, $i, $year)) == $dayOfWeek)
930         $count++;
931   }
932
933   return $count;
934
935}
936
937
938
939/**
940  * Checks if the given date is within any two
941  * timestamps (inclusive).
942  *
943  * Note that granularity only goes to DAYS.  Hours
944  * and seconds of the start and end times are ignored.
945  *
946  * @param int $year The year of the day to check
947  * @param int $month The month of the day to check
948  * @param int $day The day to check
949  * @param timestamp $start Beginning of date range for check
950  * @param timestamp $end End of date range for check
951  * @param timestamp $theDay Timestamp corresponding to the
952  *                          $year, $month and $day parameters
953  *                          (optional, but gives performance
954  *                          boost if provided)
955  *
956  * @return boolean TRUE if the target date is between the
957  *                 given date range (inclusive), FALSE otherwise.
958  *
959  */
960function dayIsBetween($year, $month, $day, $start, $end, $theDay=0)
961{
962
963   // just verify that start day doesn't come after target day
964   // and that end day doesn't come before target day
965
966
967   // we need this to be as fast as possible, so check everything
968   // we can before we start making dates (make dates one at a time,
969   // as little as needed to just return outta here)
970   //
971   // the order below is important!  this is the fastest combination
972   //
973   $endYear = date('Y', $end);
974   if ($endYear < $year)
975      return FALSE;
976
977
978   $startYear = date('Y', $start);
979   if ($startYear > $year)
980      return FALSE;
981
982
983   if ($theDay == 0)
984      $yearDay = date('z', mktime(0, 0, 0, $month, $day, $year));
985   else
986      $yearDay = date('z', $theDay);
987
988
989   $endYearDay = date('z', $end);
990   if ($endYear == $year && $endYearDay < $yearDay)
991      return FALSE;
992
993
994   $startYearDay = date('z', $start);
995   if ($startYear == $year && $startYearDay > $yearDay)
996      return FALSE;
997
998
999   return TRUE;
1000
1001}
1002
1003
1004
1005/**
1006  * clean/encode a string for output in HTML pages by reference
1007  *
1008  * If the parameter is an array, any and all of the array's values that are
1009  * strings are cleaned/encoded, and if it contains any nested arrays, those
1010  * will be recursively scanned for string values to clean/encode.
1011  *
1012  * @param mixed $item The string (or array) to be encoded
1013  * @param boolean $convertNewlines If TRUE, all newlines are converted to <br /> tags;
1014  *                                 if FALSE, newlines are left as is (optional; default = TRUE)
1015  *
1016  */
1017function cal_encode_output_by_ref(&$item, $convertNewlines=TRUE)
1018{
1019   if (is_string($item)) $item = ($convertNewlines ? nl2br(htmlspecialchars($item, ENT_QUOTES))
1020                                                   : htmlspecialchars($item, ENT_QUOTES));
1021   else if (is_array($item))
1022      foreach ($item as $index => $value) cal_encode_output_by_ref($item[$index], $convertNewlines);
1023}
1024
1025
1026
1027/**
1028  * clean/encode and return a string for output in HTML pages
1029  *
1030  * If the parameter is an array, any and all of the array's values that are
1031  * strings are cleaned/encoded, and if it contains any nested arrays, those
1032  * will be recursively scanned for string values to clean/encode.
1033  *
1034  * @param mixed $item The string (or array) to be encoded
1035  * @param boolean $convertNewlines If TRUE, all newlines are converted to <br /> tags;
1036  *                                 if FALSE, newlines are left as is (optional; default = TRUE)
1037  *
1038  */
1039function cal_encode_output($item, $convertNewlines=TRUE)
1040{
1041   if (is_string($item)) return ($convertNewlines ? nl2br(htmlspecialchars($item, ENT_QUOTES))
1042                                                  : htmlspecialchars($item, ENT_QUOTES));
1043   else if (is_array($item))
1044   {
1045      $newItem = array();
1046      foreach ($item as $index => $value)
1047         $newItem[$index] = cal_encode_output($value, $convertNewlines);
1048      return $newItem;
1049   }
1050   else return $item;
1051}
1052
1053
1054
1055/**
1056  * Sorts a list of calendars
1057  *
1058  * Sorts by calendar name, using case insensitive comparisons
1059  *
1060  * @param object $a Calendar A
1061  * @param object $b Calendar B
1062  *
1063  * @return -1 if calendar A comes before calendar B, 1 if calendar
1064  *         B comes before calendar A, or zero if the two are equal.
1065  *
1066  */
1067function calendar_sort($a, $b)
1068{
1069
1070   return $a->compareTo($b);
1071
1072}
1073
1074
1075
1076/**
1077  * Sort and organize events for one day
1078  *
1079  * Takes a flat array of events and places them into
1080  * a nested array structure (based on start time) as such:
1081  *
1082  * Hour
1083  *  |
1084  *  |--- Quarter Hour
1085  *  |      |
1086  *  |      |--- Event
1087  *  |      |--- Event
1088  *  |
1089  *  |--- Quarter Hour
1090  *         |
1091  *         |--- Event
1092  *
1093  * For example, one event at 3:30pm and two at 8pm:
1094  *
1095  * Array
1096  * (
1097  *     [15] => Array
1098  *         (
1099  *             [30] => Array
1100  *                 (
1101  *                     [0] => event Object
1102  *                         (
1103  *                             ....
1104  *                         )
1105  *                 )
1106  *         )
1107  *     [20] => Array
1108  *         (
1109  *             [0] => Array
1110  *                 (
1111  *                     [0] => event Object
1112  *                         (
1113  *                             ....
1114  *                         )
1115  *                     [1] => event Object
1116  *                         (
1117  *                             ....
1118  *                         )
1119  *                 )
1120  *         )
1121  * )
1122  *
1123  * NOTE: events that start before this day are all
1124  *       placed at midnight ([0][0]) in the return array
1125  *       unless the $startHour parameter is given, in
1126  *       which case they are placed at minute zero for
1127  *       that hour.
1128  *
1129  * @param array $events The events that are to be organized
1130  * @param int $year The year of the day for which events are being organized.
1131  * @param int $month The month of the day for which events are being organized.
1132  * @param int $day The day for which events are being organized.
1133  * @param int $startHour The hour that the returned array should start
1134  *                       with - any events starting before that time are
1135  *                       pushed into the zero-minute slot for this hour
1136  *                       (optional; if not given, all hours of the day
1137  *                       (that have events) are returned)
1138  *
1139  * @return array Those same events, organized per above.
1140  *
1141  */
1142function organizeEvents($events, $year, $month, $day, $startHour=0)
1143{
1144
1145   if (!is_numeric($startHour) || $startHour < 0 || $startHour > 23)
1146   {
1147      global $color;
1148      plain_error_message('ERROR (organizeEvents): Bad start hour: ' . $startHour, $color);
1149      exit;
1150   }
1151
1152
1153   $returnArray = array();
1154
1155   foreach ($events as $event)
1156   {
1157
1158      $eventStartHour = $event->startHour($year, $month, $day);
1159
1160      if ($startHour && $eventStartHour < $startHour)
1161         $returnArray[$startHour][$event->startQuarterHour($year, $month, $day)][] = $event;
1162
1163      else
1164         $returnArray[$eventStartHour][$event->startQuarterHour($year, $month, $day)][] = $event;
1165
1166   }
1167
1168   return $returnArray;
1169
1170}
1171
1172
1173
1174/**
1175  * Generates generic set of <option> tags for inclusion
1176  * in a <select> tag for a list of years
1177  *
1178  * @param int $selected The year that should be preselected (optional)
1179  *
1180  * @return string The list of <option> tags, ready to be
1181  *                output to the HTML page
1182  *
1183  */
1184function select_option_year($selected='')
1185{
1186
1187   $html = '';
1188
1189   for ($i = 1970; $i < 2038; $i++)
1190   {
1191      if (!empty($selected) && $i == $selected)
1192      {
1193         $html .= "<option value=\"$i\" selected>$i</option>\n";
1194      }
1195      else
1196      {
1197         $html .= "<option value=\"$i\">$i</option>\n";
1198      }
1199   }
1200
1201   return $html;
1202
1203}
1204
1205
1206
1207/**
1208  * Generates generic set of <option> tags for inclusion
1209  * in a <select> tag for a list of months
1210  *
1211  * @param int $selected The month that should be preselected (optional)
1212  *
1213  * @return string The list of <option> tags, ready to be
1214  *                output to the HTML page
1215  *
1216  */
1217function select_option_month($selected='')
1218{
1219
1220   // NOTE: should not need to switch gettext domain here, since
1221   //       this function should be being called from within an
1222   //       interface function that is already in the correct calendar
1223   //       gettext domain.... right?
1224
1225   $html = '';
1226
1227   for ($i = 1; $i < 13; $i++)
1228   {
1229      $monthName = getMonthAbrv($i);
1230
1231      if (!empty($selected) && $i == $selected)
1232      {
1233         $html .= "<option value=\"$i\" SELECTED>$monthName</option>\n";
1234      }
1235      else
1236      {
1237         $html .= "<option value=\"$i\">$monthName</option>\n";
1238      }
1239   }
1240
1241   return $html;
1242
1243}
1244
1245
1246
1247/**
1248  * Generates generic set of <option> tags for inclusion
1249  * in a <select> tag for a list of days
1250  *
1251  * @param int $selected The day that should be preselected (optional)
1252  *
1253  * @return string The list of <option> tags, ready to be
1254  *                output to the HTML page
1255  *
1256  */
1257function select_option_day($selected='')
1258{
1259
1260   $html = '';
1261
1262   for ($i = 1; $i < 32; $i++)
1263   {
1264
1265      if (!empty($selected) && $i == $selected)
1266      {
1267         $html .= "<option value=\"$i\" SELECTED>$i</option>\n";
1268      }
1269      else
1270      {
1271         $html .= "<option value=\"$i\">$i</option>\n";
1272      }
1273
1274   }
1275
1276   return $html;
1277
1278}
1279
1280
1281
1282/**
1283  * Generates generic set of <option> tags for inclusion
1284  * in a <select> tag for a list of days of the week
1285  *
1286  * @param int $selected The day of the week that should
1287  * be preselected (optional)
1288  *
1289  * @return string The list of <option> tags, ready to be
1290  *                output to the HTML page
1291  *
1292  */
1293function select_option_day_of_week($selected=0)
1294{
1295
1296   // NOTE: should not need to switch gettext domain here, since
1297   //       this function should be being called from within an
1298   //       interface function that is already in the correct calendar
1299   //       gettext domain.... right?
1300
1301   $html = '';
1302
1303
1304   $html .= '<option value="0"' . ($selected == 0 ? ' SELECTED' : '')
1305         . '>' . _("Sunday") . "</option>\n";
1306   $html .= '<option value="1"' . ($selected == 1 ? ' SELECTED' : '')
1307         . '>' . _("Monday") . "</option>\n";
1308   $html .= '<option value="2"' . ($selected == 2 ? ' SELECTED' : '')
1309         . '>' . _("Tuesday") . "</option>\n";
1310   $html .= '<option value="3"' . ($selected == 3 ? ' SELECTED' : '')
1311         . '>' . _("Wednesday") . "</option>\n";
1312   $html .= '<option value="4"' . ($selected == 4 ? ' SELECTED' : '')
1313         . '>' . _("Thursday") . "</option>\n";
1314   $html .= '<option value="5"' . ($selected == 5 ? ' SELECTED' : '')
1315         . '>' . _("Friday") . "</option>\n";
1316   $html .= '<option value="6"' . ($selected == 6 ? ' SELECTED' : '')
1317         . '>' . _("Saturday") . "</option>\n";
1318
1319
1320   return $html;
1321
1322}
1323
1324
1325
1326/**
1327  * Generates generic set of <option> tags for inclusion
1328  * in a <select> tag for a list of integers with the given range
1329  *
1330  * @param int $startValue The integer from which option values should start
1331  * @param int $endValue The integer up to which option values should reach
1332  * @param int $selected The integer that should be preselected (optional)
1333  *
1334  * @return string The list of <option> tags, ready to be
1335  *                output to the HTML page
1336  *
1337  */
1338function select_option_integer($startValue, $endValue, $selected='')
1339{
1340
1341   $html = '';
1342
1343   for ($i = $startValue; $i < $endValue + 1; $i++)
1344   {
1345
1346      if (!empty($selected) && $i == $selected)
1347      {
1348         $html .= "<option value=\"$i\" SELECTED>$i</option>\n";
1349      }
1350      else
1351      {
1352         $html .= "<option value=\"$i\">$i</option>\n";
1353      }
1354
1355   }
1356
1357   return $html;
1358
1359}
1360
1361
1362
1363/**
1364  * Generates generic set of <option> tags for inclusion
1365  * in a <select> tag for a list of time lengths
1366  *
1367  * @param int $selected The length value that should be preselected (optional)
1368  *                      Note that this parameter need not match the exact
1369  *                      length minute value - the nearest length greater than
1370  *                      the given default value will be preselected if there
1371  *                      is not an exact match.
1372  *
1373  * @return string The list of <option> tags, ready to be
1374  *                output to the HTML page
1375  *
1376  */
1377function select_option_length($selected='')
1378{
1379
1380   // NOTE: should not need to switch gettext domain here, since
1381   //       this function should be being called from within an
1382   //       interface function that is already in the correct calendar
1383   //       gettext domain.... right?
1384
1385   $html = '';
1386
1387/* see below...
1388   $lengths = array(
1389      '0'     => '0 '   . _("min."),
1390      '15'    => '15 '  . _("min."),
1391      '30'    => '30 '  . _("min."),
1392      '45'    => '45 '  . _("min."),
1393      '60'    => '1 '   . _("hr."),
1394      '90'    => '1.5 ' . _("hr."),
1395      '120'   => '2 '   . _("hr."),
1396      '150'   => '2.5 ' . _("hr."),
1397      '180'   => '3 '   . _("hr."),
1398      '210'   => '3.5 ' . _("hr."),
1399      '240'   => '4 '   . _("hr."),
1400      '270'   => '4.5 ' . _("hr."),
1401      '300'   => '5 '   . _("hr."),
1402      '330'   => '5.5 ' . _("hr."),
1403      '360'   => '6 '   . _("hr."),
1404      '420'   => '7 '   . _("hr."),
1405      '480'   => '8 '   . _("hr."),
1406      '540'   => '9 '   . _("hr."),
1407      '600'   => '10 '  . _("hr."),
1408      '660'   => '11 '  . _("hr."),
1409      '720'   => '12 '  . _("hr."),
1410      '1440'  => '1 '   . _("day"),
1411      '2880'  => '2 '   . _("days"),
1412      '4320'  => '3 '   . _("days"),
1413      '5760'  => '4 '   . _("days"),
1414      '7200'  => '5 '   . _("days"),
1415      '8640'  => '6 '   . _("days"),
1416      '10080' => '1 '   . _("wk."),
1417      '20160' => '2 '   . _("wk."),
1418      '30240' => '3 '   . _("wk."),
1419      '40320' => '4 '   . _("wk."),
1420      '50400' => '5 '   . _("wk."),
1421      '60480' => '6 '   . _("wk."),
1422      '70560' => '7 '   . _("wk."),
1423      '80640' => '8 '   . _("wk."),
1424   );
1425*/
1426   $lengths=array();
1427   foreach (array(0,15,30,45,
1428                  60,90,120,150,180,210,240,270,300,330,360,420,480,540,
1429                  600,660,720,1440,2880,4320,5760,7200,8640,
1430                  10080,20160,30240,40320,50400,60480,70560,80640) as $length)
1431   {
1432      if ($length < 60)
1433      {
1434         // event length in minutes. Use abbreviation, that works with any numeric value
1435         //
1436         $lengths[$length] = sprintf(_("%d min."), $length);
1437      }
1438      else if ($length < 1440)
1439      {
1440         // event length in hours. Use abbreviation, that works with any numeric value
1441         // length is float number.
1442         // %s is used instead of %.1f in order to suppress zero padding.
1443         // if php implements strict variable format checking, all ($length/something) calls
1444         // used in sprintf, should be converted to strings.
1445         // floating point delimiter depends on used squirrelmail version. Some SM versions
1446         // revert to C float delimiter (.) in order to solve issues with other plugins
1447         //
1448         $lengths[$length] = sprintf(_("%s hr."), ($length / 60));
1449      }
1450      else if ($length < 10080)
1451      {
1452         // event length in days.
1453         //
1454//TODO: wait until SM (and/or compatibility plugin) has ngettext wrapper
1455//         $lengths[$length] = sprintf(ngettext("%s day", "%s days", ($length / 1440)), ($length / 1440));
1456         if ($length < 2880)
1457            $lengths[$length] = sprintf(_("%s day"), ($length / 1440));
1458         else
1459            $lengths[$length] = sprintf(_("%s days"), ($length / 1440));
1460      }
1461      else
1462      {
1463         // event length in weeks. Use abbreviation, that works with any numeric value
1464         //
1465         $lengths[$length] = sprintf(_("%s wk."), ($length / 10080));
1466      }
1467   }
1468
1469
1470   $noDefaultYet = TRUE;
1471   foreach ($lengths as $min => $text)
1472   {
1473
1474      if (!empty($selected) && ($min == $selected || ($min > $selected && $noDefaultYet)))
1475      {
1476         $html .= "<option value=\"$min\" selected>$text</option>\n";
1477         $noDefaultYet = FALSE;
1478      }
1479      else
1480      {
1481         $html .= "<option value=\"$min\">$text</option>\n";
1482      }
1483
1484   }
1485
1486   return $html;
1487
1488}
1489
1490
1491
1492/**
1493  * Generates generic set of <option> tags for inclusion
1494  * in a <select> tag for a list of hours
1495  *
1496  * @param int $selected The hour that should be preselected (optional)
1497  *
1498  * @return string The list of <option> tags, ready to be
1499  *                output to the HTML page
1500  *
1501  */
1502function select_option_hour($selected='')
1503{
1504
1505   // NOTE: should not need to switch gettext domain here, since
1506   //       this function should be being called from within an
1507   //       interface function that is already in the correct calendar
1508   //       gettext domain.... right?
1509
1510   global $hour_format;
1511
1512   $html = '';
1513
1514   if ($hour_format == SMPREF_TIME_24HR)
1515      $hours = array(
1516         '0' => 0,
1517         '1' => 1,
1518         '2' => 2,
1519         '3' => 3,
1520         '4' => 4,
1521         '5' => 5,
1522         '6' => 6,
1523         '7' => 7,
1524         '8' => 8,
1525         '9' => 9,
1526         '10' => 10,
1527         '11' => 11,
1528         '12' => 12,
1529         '13' => 13,
1530         '14' => 14,
1531         '15' => 15,
1532         '16' => 16,
1533         '17' => 17,
1534         '18' => 18,
1535         '19' => 19,
1536         '20' => 20,
1537         '21' => 21,
1538         '22' => 22,
1539         '23' => 23,
1540      );
1541
1542   else
1543      $hours = array(
1544         '0' => sprintf(_("%sam"), '12'),
1545         '1' => sprintf(_("%sam"), '1'),
1546         '2' => sprintf(_("%sam"), '2'),
1547         '3' => sprintf(_("%sam"), '3'),
1548         '4' => sprintf(_("%sam"), '4'),
1549         '5' => sprintf(_("%sam"), '5'),
1550         '6' => sprintf(_("%sam"), '6'),
1551         '7' => sprintf(_("%sam"), '7'),
1552         '8' => sprintf(_("%sam"), '8'),
1553         '9' => sprintf(_("%sam"), '9'),
1554         '10' => sprintf(_("%sam"), '10'),
1555         '11' => sprintf(_("%sam"), '11'),
1556         '12' => sprintf(_("%spm"), '12'),
1557         '13' => sprintf(_("%spm"), '1'),
1558         '14' => sprintf(_("%spm"), '2'),
1559         '15' => sprintf(_("%spm"), '3'),
1560         '16' => sprintf(_("%spm"), '4'),
1561         '17' => sprintf(_("%spm"), '5'),
1562         '18' => sprintf(_("%spm"), '6'),
1563         '19' => sprintf(_("%spm"), '7'),
1564         '20' => sprintf(_("%spm"), '8'),
1565         '21' => sprintf(_("%spm"), '9'),
1566         '22' => sprintf(_("%spm"), '10'),
1567         '23' => sprintf(_("%spm"), '11'),
1568      );
1569
1570
1571   foreach ($hours as $hour => $text)
1572   {
1573
1574      if (!empty($selected) && $hour == $selected)
1575      {
1576         $html .= "<option value=\"$hour\" SELECTED>$text</option>\n";
1577      }
1578      else
1579      {
1580         $html .= "<option value=\"$hour\">$text</option>\n";
1581      }
1582
1583   }
1584
1585   return $html;
1586
1587}
1588
1589
1590
1591/**
1592  * Generates generic set of <option> tags for inclusion
1593  * in a <select> tag for a list of minutes
1594  *
1595  * @param int $selected The minute that should be preselected (optional)
1596  *
1597  * @return string The list of <option> tags, ready to be
1598  *                output to the HTML page
1599  *
1600  */
1601function select_option_minute($selected='')
1602{
1603
1604   $html = '';
1605
1606   $minutes = array();
1607
1608   for ($i = 0; $i < 60; $i++)
1609      $minutes[$i] = ($i < 10 ? '0' . $i : $i);
1610
1611/*
1612   $minutes = array(
1613      '0'=>'00',
1614      '5'=>'05',
1615      '10'=>'10',
1616      '15'=>'15',
1617      '20'=>'20',
1618      '25'=>'25',
1619      '30'=>'30',
1620      '35'=>'35',
1621      '40'=>'40',
1622      '45'=>'45',
1623      '50'=>'50',
1624      '55'=>'55'
1625   );
1626*/
1627
1628
1629   foreach ($minutes as $min => $text)
1630   {
1631
1632      if (!empty($selected) && $min == $selected)
1633      {
1634         $html .= "<option value=\"$min\" SELECTED>$text</option>\n";
1635      }
1636      else
1637      {
1638         $html .= "<option value=\"$min\">$text</option>\n";
1639      }
1640
1641   }
1642
1643   return $html;
1644
1645}
1646
1647
1648
1649/**
1650  * Generates generic set of <option> tags for inclusion
1651  * in a <select> tag for a list of priorities
1652  *
1653  * @param int $selected The priority that should be
1654  *                      preselected (optional; default
1655  *                      is Normal priority)
1656  *
1657  * @return string The list of <option> tags, ready to be
1658  *                output to the HTML page
1659  *
1660  */
1661function select_option_priority($selected='')
1662{
1663
1664   // NOTE: should not need to switch gettext domain here, since
1665   //       this function should be being called from within an
1666   //       interface function that is already in the correct calendar
1667   //       gettext domain.... right?
1668
1669   $html = '';
1670
1671   global $EVENT_PRIORITIES;
1672
1673
1674   foreach ($EVENT_PRIORITIES as $num => $text)
1675   {
1676
1677      // skip unknown so we can put it at the end of the list,
1678      // where it is more intuiitive
1679      //
1680      if ($num == 0) continue;
1681
1682      if (($selected !== '' && $num == $selected)
1683       || ($selected === '' && $num == SM_CAL_EVENT_PRIORITY_NORMAL))
1684      {
1685         $html .= "<option value=\"$num\" SELECTED>$text</option>\n";
1686      }
1687      else
1688      {
1689         $html .= "<option value=\"$num\">$text</option>\n";
1690      }
1691
1692   }
1693   if ($selected === 0)
1694      $html .= "<option SELECTED value=\"0\">" . _("Unknown") . "</option>\n";
1695   else
1696      $html .= "<option value=\"0\">" . _("Unknown") . "</option>\n";
1697
1698   return $html;
1699
1700}
1701
1702
1703
1704/**
1705  * Generates generic set of <option> tags for inclusion
1706  * in a <select> tag for a list of recurrence types
1707  *
1708  * @param int $selected The recurrence type that should be
1709  *                      preselected
1710  *
1711  * @return string The list of <option> tags, ready to be
1712  *                output to the HTML page
1713  *
1714  */
1715function select_option_recurrence_type($selected='')
1716{
1717
1718   $html = '';
1719
1720   global $RECURRENCE_TYPES;
1721
1722
1723   foreach ($RECURRENCE_TYPES as $code => $text)
1724   {
1725
1726      if (!empty($selected) && $code == $selected)
1727      {
1728         $html .= "<option value=\"$code\" SELECTED>$text</option>\n";
1729      }
1730      else
1731      {
1732         $html .= "<option value=\"$code\">$text</option>\n";
1733      }
1734
1735   }
1736
1737   return $html;
1738
1739}
1740
1741
1742
1743/**
1744  * get_headers() implementation for php4
1745  *
1746  * Swiped from http://www.php.net/manual/function.get-headers.php
1747  *
1748  */
1749if (!function_exists('get_headers'))
1750{
1751
1752   /**
1753     * @return array
1754     * @param string $url
1755     * @param int $format
1756     * @desc Fetches all the headers
1757     * @author cpurruc fh-landshut de
1758     * @modified by dotpointer
1759     * @modified by aeontech
1760     */
1761   function get_headers($url,$format=0)
1762   {
1763       $url_info=parse_url($url);
1764       $port = isset($url_info['port']) ? $url_info['port'] : 80;
1765       $fp=fsockopen($url_info['host'], $port, $errno, $errstr, 30);
1766
1767       if($fp)
1768       {
1769           $head = "HEAD ".@$url_info['path']."?".@$url_info['query']." HTTP/1.0\r\nHost: ".@$url_info['host']."\r\n\r\n";
1770           fputs($fp, $head);
1771           while(!feof($fp))
1772           {
1773               if($header=trim(fgets($fp, 1024)))
1774               {
1775                   if($format == 1)
1776                   {
1777                       $key = array_shift(explode(':',$header));
1778                       // the first element is the http header type, such as HTTP 200 OK,
1779                       // it doesn't have a separate name, so we have to check for it.
1780                       if($key == $header)
1781                       {
1782                           $headers[] = $header;
1783                       }
1784                       else
1785                       {
1786                           $headers[$key]=substr($header,strlen($key)+2);
1787                       }
1788                       unset($key);
1789                   }
1790                   else
1791                   {
1792                       $headers[] = $header;
1793                   }
1794               }
1795           }
1796           return $headers;
1797       }
1798       else
1799       {
1800           return false;
1801       }
1802   }
1803
1804}
1805
1806
1807
1808?>
1809