[ Index ]
 

Code source de WebCalendar 1.0.5

Accédez au Source d'autres logiciels libres

Classes | Fonctions | Variables | Constantes | Tables | Statistiques

title

Body

[fermer]

/includes/ -> functions.php (source)

   1  <?php
   2  /**
   3   * All of WebCalendar's functions
   4   *
   5   * @author Craig Knudsen <cknudsen@cknudsen.com>
   6   * @copyright Craig Knudsen, <cknudsen@cknudsen.com>, http://www.k5n.us/cknudsen
   7   * @license http://www.gnu.org/licenses/gpl.html GNU GPL
   8   * @package WebCalendar
   9   */
  10  
  11  if ( empty ( $PHP_SELF ) && ! empty ( $_SERVER ) &&
  12    ! empty ( $_SERVER['PHP_SELF'] ) ) {
  13    $PHP_SELF = $_SERVER['PHP_SELF'];
  14  }
  15  if ( ! empty ( $PHP_SELF ) && preg_match ( "/\/includes\//", $PHP_SELF ) ) {
  16      die ( "You can't access this file directly!" );
  17  }
  18  
  19  /**#@+
  20   * Used for activity log
  21   * @global string
  22   */
  23  $LOG_CREATE = "C";
  24  $LOG_APPROVE = "A";
  25  $LOG_REJECT = "X";
  26  $LOG_UPDATE = "U";
  27  $LOG_DELETE = "D";
  28  $LOG_NOTIFICATION = "N";
  29  $LOG_REMINDER = "R";
  30  /**#@-*/
  31  
  32  /**
  33   * Number of seconds in a day
  34   *
  35   * @global int $ONE_DAY
  36   */
  37  $ONE_DAY = 86400;
  38  
  39  /**
  40   * Array containing the number of days in each month in a non-leap year
  41   *
  42   * @global array $days_per_month
  43   */
  44  $days_per_month = array ( 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
  45  
  46  /**
  47   * Array containing the number of days in each month in a leap year
  48   *
  49   * @global array $ldays_per_month
  50   */
  51  $ldays_per_month = array ( 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
  52  
  53  /**
  54   * Array of global variables which are not allowed to by set via HTTP GET/POST
  55   *
  56   * This is a security precaution to prevent users from overriding any global
  57   * variables
  58   *
  59   * @global array $noSet
  60   */
  61  $noSet = array (
  62    "is_admin" => 1,
  63    "db_type" => 1,
  64    "db_host" => 1,
  65    "db_login" => 1,
  66    "db_password" => 1,
  67    "db_persistent" => 1,
  68    "PROGRAM_NAME" => 1,
  69    "PROGRAM_URL" => 1,
  70    "readonly" => 1,
  71    "single_user" => 1,
  72    "single_user_login" => 1,
  73    "use_http_auth" => 1,
  74    "user_inc" => 1,
  75    "includedir" => 1,
  76    "NONUSER_PREFIX" => 1,
  77    "languages" => 1,
  78    "browser_languages" => 1,
  79    "pub_acc_enabled" => 1,
  80    "user_can_update_password" => 1,
  81    "admin_can_add_user" => 1,
  82    "admin_can_delete_user" => 1,
  83    "noSet" => 1,
  84  );
  85  
  86  // This code is a temporary hack to make the application work when
  87  // register_globals is set to Off in php.ini (the default setting in
  88  // PHP 4.2.0 and after).
  89  if ( empty ( $HTTP_GET_VARS ) ) $HTTP_GET_VARS = $_GET;
  90  if ( ! empty ( $HTTP_GET_VARS ) ) {
  91    while (list($key, $val) = @each($HTTP_GET_VARS)) {
  92      // don't allow anything to have <script> in it...
  93      if ( ! is_array ( $val ) ) {
  94        if ( preg_match ( "/<\s*script/i", $val ) ) {
  95          echo "Security violation!"; exit;
  96        }
  97      }
  98      if ( $key == "login" ) {
  99        if ( strstr ( $PHP_SELF, "login.php" ) ) {
 100          //$GLOBALS[$key] = $val;
 101          $GLOBALS[$key] = $val;
 102        }
 103      } else {
 104        if ( empty ( $noSet[$key] ) ) {
 105          $GLOBALS[$key] = $val;
 106          //echo "XXX $key<br />\n";
 107        }
 108      }
 109      //echo "GET var '$key' = '$val' <br />\n";
 110    }
 111    reset ( $HTTP_GET_VARS );
 112  }
 113  
 114  if ( empty ( $HTTP_POST_VARS ) ) $HTTP_POST_VARS = $_POST;
 115  if ( ! empty ( $HTTP_POST_VARS ) ) {
 116    while (list($key, $val) = @each($HTTP_POST_VARS)) {
 117      // don't allow anything to have <script> in it... except 'template'
 118      if ( ! is_array ( $val ) && $key != 'template' ) {
 119        if ( preg_match ( "/<\s*script/i", $val ) ) {
 120          echo "Security violation!"; exit;
 121        }
 122      }
 123      if ( empty ( $noSet[$key] ) ) {
 124        $GLOBALS[$key] = $val;
 125      }
 126    }
 127    reset ( $HTTP_POST_VARS );
 128  }
 129  //while (list($key, $val) = @each($HTTP_POST_FILES)) {
 130  //       $GLOBALS[$key] = $val;
 131  //}
 132  //while (list($key, $val) = @each($HTTP_SESSION_VARS)) {
 133  //       $GLOBALS[$key] = $val;
 134  //}
 135  if ( empty ( $HTTP_COOKIE_VARS ) ) $HTTP_COOKIE_VARS = $_COOKIE;
 136  if ( ! empty ( $HTTP_COOKIE_VARS ) ) {
 137    while (list($key, $val) = @each($HTTP_COOKIE_VARS)) {
 138      if ( empty ( $noSet[$key] ) && substr($key,0,12) == "webcalendar_" ) {
 139        $GLOBALS[$key] = $val;
 140      }
 141      //echo "COOKIE var '$key' = '$val' <br />\n";
 142    }
 143    reset ( $HTTP_COOKIE_VARS );
 144  }
 145  
 146  // Don't allow a user to put "login=XXX" in the URL if they are not
 147  // coming from the login.php page.
 148  if ( empty ( $PHP_SELF ) && ! empty ( $_SERVER['PHP_SELF'] ) )
 149    $PHP_SELF = $_SERVER['PHP_SELF']; // backward compatibility
 150  if ( empty ( $PHP_SELF ) )
 151    $PHP_SELF = ''; // this happens when running send_reminders.php from CL
 152  if ( ! strstr ( $PHP_SELF, "login.php" ) && ! empty ( $GLOBALS["login"] ) ) {
 153    $GLOBALS["login"] = "";
 154  }
 155  
 156  // Define an array to use to jumble up the key: $offsets
 157  // We define a unique key to scramble the cookie we generate.
 158  // We use the admin install password that the user set to make
 159  // the salt unique for each WebCalendar install.
 160  if ( ! empty ( $settings ) && ! empty ( $settings['install_password'] ) ) {
 161    $salt = $settings['install_password'];
 162  } else {
 163    $salt = md5 ( $db_login );
 164  }
 165  $salt_len = strlen ( $salt );
 166  
 167  if ( ! empty ( $db_password ) ) {
 168    $salt2 = md5 ( $db_password );
 169  } else {
 170    $salt2 = md5 ( "oogabooga" );
 171  }
 172  $salt2_len = strlen ( $salt2 );
 173  
 174  $offsets = array ();
 175  for ( $i = 0; $i < $salt_len || $i < $salt2_len; $i++ ) {
 176    $offsets[$i] = 0;
 177    if ( $i < $salt_len )
 178      $offsets[$i] += ord ( substr ( $salt, $i, 1 ) );
 179    if ( $i < $salt2_len )
 180      $offsets[$i] += ord ( substr ( $salt2, $i, 1 ) );
 181    $offsets[$i] %= 128;
 182  }
 183  /* debugging code...
 184  for ( $i = 0; $i < count ( $offsets ); $i++ ) {
 185    echo "offset $i: $offsets[$i] <br />\n";
 186  }
 187  */
 188  
 189  /*
 190   * Functions start here.  All non-function code should be above this
 191   *
 192   * Note to developers:
 193   *  Documentation is generated from the function comments below.
 194   *  When adding/updating functions, please follow the following conventions
 195   *  seen below.  Your cooperation in this matter is appreciated :-)
 196   *
 197   *  If you want your documentation to link to the db documentation,
 198   *  just make sure you mention the db table name followed by "table"
 199   *  on the same line.  Here's an example:
 200   *    Retrieve preferences from the webcal_user_pref table.
 201   *
 202   */
 203  
 204  /**
 205   * Gets the value resulting from an HTTP POST method.
 206   * 
 207   * <b>Note:</b> The return value will be affected by the value of
 208   * <var>magic_quotes_gpc</var> in the php.ini file.
 209   * 
 210   * @param string $name Name used in the HTML form
 211   *
 212   * @return string The value used in the HTML form
 213   *
 214   * @see getGetValue
 215   */
 216  function getPostValue ( $name ) {
 217    global $HTTP_POST_VARS;
 218  
 219    if ( isset ( $_POST ) && is_array ( $_POST ) && ! empty ( $_POST[$name] ) ) {
 220        $HTTP_POST_VARS[$name] = $_POST[$name];
 221      return $_POST[$name];
 222     } else if ( ! isset ( $HTTP_POST_VARS ) ) {
 223      return null;
 224    } else if ( ! isset ( $HTTP_POST_VARS[$name] ) ) {
 225      return null;
 226      }
 227    return ( $HTTP_POST_VARS[$name] );
 228  }
 229  
 230  /**
 231   * Gets the value resulting from an HTTP GET method.
 232   *
 233   * <b>Note:</b> The return value will be affected by the value of
 234   * <var>magic_quotes_gpc</var> in the php.ini file.
 235   *
 236   * If you need to enforce a specific input format (such as numeric input), then
 237   * use the {@link getValue()} function.
 238   *
 239   * @param string $name Name used in the HTML form or found in the URL
 240   *
 241   * @return string The value used in the HTML form (or URL)
 242   *
 243   * @see getPostValue
 244   */
 245  function getGetValue ( $name ) {
 246    global $HTTP_GET_VARS;
 247  
 248    if ( isset ( $_GET ) && is_array ( $_GET ) && ! empty ( $_GET[$name] ) ) {
 249        $HTTP_GET_VARS[$name] = $_GET[$name];
 250      return $_GET[$name];
 251    } else if ( ! isset ( $HTTP_GET_VARS ) )  {
 252      return null;
 253     } else if ( ! isset ( $HTTP_GET_VARS[$name] ) ) {
 254      return null;
 255      }
 256    return ( $HTTP_GET_VARS[$name] );
 257  }
 258  
 259  /**
 260   * Gets the value resulting from either HTTP GET method or HTTP POST method.
 261   *
 262   * <b>Note:</b> The return value will be affected by the value of
 263   * <var>magic_quotes_gpc</var> in the php.ini file.
 264   *
 265   * <b>Note:</b> If you need to get an integer value, yuou can use the
 266   * getIntValue function.
 267   *
 268   * @param string $name   Name used in the HTML form or found in the URL
 269   * @param string $format A regular expression format that the input must match.
 270   *                       If the input does not match, an empty string is
 271   *                       returned and a warning is sent to the browser.  If The
 272   *                       <var>$fatal</var> parameter is true, then execution
 273   *                       will also stop when the input does not match the
 274   *                       format.
 275   * @param bool   $fatal  Is it considered a fatal error requiring execution to
 276   *                       stop if the value retrieved does not match the format
 277   *                       regular expression?
 278   *
 279   * @return string The value used in the HTML form (or URL)
 280   *
 281   * @uses getGetValue
 282   * @uses getPostValue
 283   */
 284  function getValue ( $name, $format="", $fatal=false ) {
 285    $val = getPostValue ( $name );
 286    if ( ! isset ( $val ) )
 287      $val = getGetValue ( $name );
 288    // for older PHP versions...
 289    if ( ! isset ( $val  ) && get_magic_quotes_gpc () == 1 &&
 290      ! empty ( $GLOBALS[$name] ) )
 291      $val = $GLOBALS[$name];
 292    if ( ! isset ( $val  ) )
 293      return "";
 294    if ( ! empty ( $format ) && ! preg_match ( "/^" . $format . "$/", $val ) ) {
 295      // does not match
 296      if ( $fatal ) {
 297        die_miserable_death ( "Fatal Error: Invalid data format for $name" );
 298      }
 299      // ignore value
 300      return "";
 301    }
 302    return $val;
 303  }
 304  
 305  /**
 306   * Gets an integer value resulting from an HTTP GET or HTTP POST method.
 307   *
 308   * <b>Note:</b> The return value will be affected by the value of
 309   * <var>magic_quotes_gpc</var> in the php.ini file.
 310   *
 311   * @param string $name  Name used in the HTML form or found in the URL
 312   * @param bool   $fatal Is it considered a fatal error requiring execution to
 313   *                      stop if the value retrieved does not match the format
 314   *                      regular expression?
 315   *
 316   * @return string The value used in the HTML form (or URL)
 317   *
 318   * @uses getValue
 319   */
 320  function getIntValue ( $name, $fatal=false ) {
 321    $val = getValue ( $name, "-?[0-9]+", $fatal );
 322    return $val;
 323  }
 324  
 325  /**
 326   * Loads default system settings (which can be updated via admin.php).
 327   *
 328   * System settings are stored in the webcal_config table.
 329   *
 330   * <b>Note:</b> If the setting for <var>server_url</var> is not set, the value
 331   * will be calculated and stored in the database.
 332   *
 333   * @global string User's login name
 334   * @global bool   Readonly
 335   * @global string HTTP hostname
 336   * @global int    Server's port number
 337   * @global string Request string
 338   * @global array  Server variables
 339   */
 340  function load_global_settings () {
 341    global $login, $readonly, $HTTP_HOST, $SERVER_PORT, $REQUEST_URI, $_SERVER;
 342  
 343    // Note: when running from the command line (send_reminders.php),
 344    // these variables are (obviously) not set.
 345    // TODO: This type of checking should be moved to a central locationm
 346    // like init.php.
 347    if ( isset ( $_SERVER ) && is_array ( $_SERVER ) ) {
 348      if ( empty ( $HTTP_HOST ) && isset ( $_SERVER["HTTP_POST"] ) )
 349        $HTTP_HOST = $_SERVER["HTTP_HOST"];
 350      if ( empty ( $SERVER_PORT ) && isset ( $_SERVER["SERVER_PORT"] ) )
 351        $SERVER_PORT = $_SERVER["SERVER_PORT"];
 352      if ( empty ( $REQUEST_URI ) && isset ( $_SERVER["REQUEST_URI"] ) )
 353        $REQUEST_URI = $_SERVER["REQUEST_URI"];
 354    }
 355  
 356    $res = dbi_query ( "SELECT cal_setting, cal_value FROM webcal_config" );
 357    if ( $res ) {
 358      while ( $row = dbi_fetch_row ( $res ) ) {
 359        $setting = $row[0];
 360        $value = $row[1];
 361        //echo "Setting '$setting' to '$value' <br />\n";
 362        $GLOBALS[$setting] = $value;
 363      }
 364      dbi_free_result ( $res );
 365    }
 366  
 367    // If app name not set.... default to "Title".  This gets translated
 368    // later since this function is typically called before translate.php
 369    // is included.
 370    // Note: We usually use translate($application_name) instead of
 371    // translate("Title").
 372    if ( empty ( $GLOBALS["application_name"] ) )
 373      $GLOBALS["application_name"] = "Title";
 374  
 375    // If $server_url not set, then calculate one for them, then store it
 376    // in the database.
 377    if ( empty ( $GLOBALS["server_url"] ) ) {
 378      if ( ! empty ( $HTTP_HOST ) && ! empty ( $REQUEST_URI ) ) {
 379        $ptr = strrpos ( $REQUEST_URI, "/" );
 380        if ( $ptr > 0 ) {
 381          $uri = substr ( $REQUEST_URI, 0, $ptr + 1 );
 382          $server_url = "http://" . $HTTP_HOST;
 383          if ( ! empty ( $SERVER_PORT ) && $SERVER_PORT != 80 )
 384            $server_url .= ":" . $SERVER_PORT;
 385          $server_url .= $uri;
 386  
 387          dbi_query ( "INSERT INTO webcal_config ( cal_setting, cal_value ) ".
 388            "VALUES ( 'server_url', '$server_url' )" );
 389          $GLOBALS["server_url"] = $server_url;
 390        }
 391      }
 392    }
 393  
 394    // If no font settings, then set some
 395    if ( empty ( $GLOBALS["FONTS"] ) ) {
 396      if ( $GLOBALS["LANGUAGE"] == "Japanese" )
 397        $GLOBALS["FONTS"] = "Osaka, Arial, Helvetica, sans-serif";
 398      else
 399        $GLOBALS["FONTS"] = "Arial, Helvetica, sans-serif";
 400    }
 401  }
 402  
 403  /**
 404   * Gets the list of active plugins.
 405   *
 406   * Should be called after {@link load_global_settings()} and {@link load_user_preferences()}.
 407   *
 408   * @internal cek: ignored since I am not sure this will ever be used...
 409   *
 410   * @return array Active plugins
 411   *
 412   * @ignore
 413   */
 414  function get_plugin_list ( $include_disabled=false ) {
 415    // first get list of available plugins
 416    $sql = "SELECT cal_setting FROM webcal_config " .
 417      "WHERE cal_setting LIKE '%.plugin_status'";
 418    if ( ! $include_disabled )
 419      $sql .= " AND cal_value = 'Y'";
 420    $sql .= " ORDER BY cal_setting";
 421    $res = dbi_query ( $sql );
 422    $plugins = array ();
 423    if ( $res ) {
 424      while ( $row = dbi_fetch_row ( $res ) ) {
 425        $e = explode ( ".", $row[0] );
 426        if ( $e[0] != "" ) {
 427          $plugins[] = $e[0];
 428        }
 429      }
 430      dbi_free_result ( $res );
 431    } else {
 432      echo translate("Database error") . ": " . dbi_error (); exit;
 433    }
 434    if ( count ( $plugins ) == 0 ) {
 435      $plugins[] = "webcalendar";
 436    }
 437    return $plugins;
 438  }
 439  
 440  /**
 441   * Get plugins available to the current user.
 442   *
 443   * Do this by getting a list of all plugins that are not disabled by the
 444   * administrator and make sure this user has not disabled any of them.
 445   * 
 446   * It's done this was so that when an admin adds a new plugin, it shows up on
 447   * each users system automatically (until they disable it).
 448   *
 449   * @return array Plugins available to current user
 450   *
 451   * @ignore
 452   */
 453  function get_user_plugin_list () {
 454    $ret = array ();
 455    $all_plugins = get_plugin_list ();
 456    for ( $i = 0; $i < count ( $all_plugins ); $i++ ) {
 457      if ( $GLOBALS[$all_plugins[$i] . ".disabled"] != "N" )
 458        $ret[] = $all_plugins[$i];
 459    }
 460    return $ret;
 461  }
 462  
 463  /**
 464   * Identify user's browser.
 465   *
 466   * Returned value will be one of:
 467   * - "Mozilla/5" = Mozilla (open source Mozilla 5.0)
 468   * - "Mozilla/[3,4]" = Netscape (3.X, 4.X)
 469   * - "MSIE 4" = MSIE (4.X)
 470   *
 471   * @return string String identifying browser
 472   *
 473   * @ignore
 474   */
 475  function get_web_browser () {
 476    if ( ereg ( "MSIE [0-9]", getenv ( "HTTP_USER_AGENT" ) ) )
 477      return "MSIE";
 478    if ( ereg ( "Mozilla/[234]", getenv ( "HTTP_USER_AGENT" ) ) )
 479      return "Netscape";
 480    if ( ereg ( "Mozilla/[5678]", getenv ( "HTTP_USER_AGENT" ) ) )
 481      return "Mozilla";
 482    return "Unknown";
 483  }
 484  
 485  
 486  /**
 487   * Logs a debug message.
 488   *
 489   * Generally, we do not leave calls to this function in the code.  It is used
 490   * for debugging only.
 491   *
 492   * @param string $msg Text to be logged
 493   */
 494  function do_debug ( $msg ) {
 495    // log to /tmp/webcal-debug.log
 496    //error_log ( date ( "Y-m-d H:i:s" ) .  "> $msg\n",
 497    //  3, "/tmp/webcal-debug.log" );
 498    //error_log ( date ( "Y-m-d H:i:s" ) .  "> $msg\n",
 499    //  2, "sockieman:2000" );
 500  }
 501  
 502  /**
 503   * Gets user's preferred view.
 504   *
 505   * The user's preferred view is stored in the $STARTVIEW global variable.  This
 506   * is loaded from the user preferences (or system settings if there are no user
 507   * prefererences.)
 508   *
 509   * @param string $indate Date to pass to preferred view in YYYYMMDD format
 510   * @param string $args   Arguments to include in the URL (such as "user=joe")
 511   *
 512   * @return string URL of the user's preferred view
 513   */
 514  function get_preferred_view ( $indate="", $args="" ) {
 515    global $STARTVIEW, $thisdate;
 516  
 517    $url = empty ( $STARTVIEW ) ? "month.php" : $STARTVIEW;
 518    // We used to just store "month" in $STARTVIEW without the ".php"
 519    // This is just to prevent users from getting a "404 not found" if
 520    // they have not updated their preferences.
 521    if ( $url == "month" || $url == "day" || $url == "week" || $url == "year" )
 522      $url .= ".php";
 523  
 524    $url = str_replace ( '&amp;', '&', $url );
 525    $url = str_replace ( '&', '&amp;', $url );
 526  
 527    $xdate = empty ( $indate ) ? $thisdate : $indate;
 528    if ( ! empty ( $xdate ) ) {
 529      if ( strstr ( $url, "?" ) )
 530        $url .= '&amp;' . "date=$xdate";
 531      else
 532        $url .= '?' . "date=$xdate";
 533    }
 534  
 535    if ( ! empty ( $args ) ) {
 536      if ( strstr ( $url, "?" ) )
 537        $url .= '&amp;' . $args;
 538      else
 539        $url .= '?' . $args;
 540    }
 541  
 542    return $url;
 543  }
 544  
 545  /**
 546   * Sends a redirect to the user's preferred view.
 547   *
 548   * The user's preferred view is stored in the $STARTVIEW global variable.  This
 549   * is loaded from the user preferences (or system settings if there are no user
 550   * prefererences.)
 551   *
 552   * @param string $indate Date to pass to preferred view in YYYYMMDD format
 553   * @param string $args   Arguments to include in the URL (such as "user=joe")
 554   */
 555  function send_to_preferred_view ( $indate="", $args="" ) {
 556    $url = get_preferred_view ( $indate, $args );
 557    do_redirect ( $url );
 558  }
 559  
 560  /** Sends a redirect to the specified page.
 561   *
 562   * The database connection is closed and execution terminates in this function.
 563   *
 564   * <b>Note:</b> MS IIS/PWS has a bug in which it does not allow us to send a
 565   * cookie and a redirect in the same HTTP header.  When we detect that the web
 566   * server is IIS, we accomplish the redirect using meta-refresh.  See the
 567   * following for more info on the IIS bug:
 568   *
 569   * {@link http://www.faqts.com/knowledge_base/view.phtml/aid/9316/fid/4}
 570   *
 571   * @param string $url The page to redirect to.  In theory, this should be an
 572   *                    absolute URL, but all browsers accept relative URLs (like
 573   *                    "month.php").
 574   *
 575   * @global string   Type of webserver
 576   * @global array    Server variables
 577   * @global resource Database connection
 578   */
 579  function do_redirect ( $url ) {
 580    global $SERVER_SOFTWARE, $_SERVER, $c;
 581  
 582    // Replace any '&amp;' with '&' since we don't want that in the HTTP
 583    // header.
 584    $url = str_replace ( '&amp;', '&', $url );
 585  
 586    if ( empty ( $SERVER_SOFTWARE ) )
 587      $SERVER_SOFTWARE = $_SERVER["SERVER_SOFTWARE"];
 588    //echo "SERVER_SOFTWARE = $SERVER_SOFTWARE <br />\n"; exit;
 589    if ( ( substr ( $SERVER_SOFTWARE, 0, 5 ) == "Micro" ) ||
 590      ( substr ( $SERVER_SOFTWARE, 0, 3 ) == "WN/" ) ) {
 591      echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html
 592      PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"
 593      \"DTD/xhtml1-transitional.dtd\">
 594  <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">
 595  <head>\n<title>Redirect</title>\n" .
 596        "<meta http-equiv=\"refresh\" content=\"0; url=$url\" />\n</head>\n<body>\n" .
 597        "Redirecting to.. <a href=\"" . $url . "\">here</a>.</body>\n</html>";
 598    } else {
 599      Header ( "Location: $url" );
 600      echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html
 601      PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"
 602      \"DTD/xhtml1-transitional.dtd\">
 603  <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">
 604  <head>\n<title>Redirect</title>\n</head>\n<body>\n" .
 605        "Redirecting to ... <a href=\"" . $url . "\">here</a>.</body>\n</html>";
 606    }
 607    dbi_close ( $c );
 608    exit;
 609  }
 610  
 611  /**
 612   * Sends an HTTP login request to the browser and stops execution.
 613   */
 614  function send_http_login () {
 615    global $lang_file, $application_name;
 616  
 617    if ( strlen ( $lang_file ) ) {
 618      Header ( "WWW-Authenticate: Basic realm=\"" . translate("Title") . "\"");
 619      Header ( "HTTP/1.0 401 Unauthorized" );
 620      echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html
 621      PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"
 622      \"DTD/xhtml1-transitional.dtd\">
 623  <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">
 624  <head>\n<title>Unauthorized</title>\n</head>\n<body>\n" .
 625        "<h2>" . translate("Title") . "</h2>\n" .
 626        translate("You are not authorized") .
 627        "\n</body>\n</html>";
 628    } else {
 629      Header ( "WWW-Authenticate: Basic realm=\"WebCalendar\"");
 630      Header ( "HTTP/1.0 401 Unauthorized" );
 631      echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html
 632      PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"
 633      \"DTD/xhtml1-transitional.dtd\">
 634  <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">
 635  <head>\n<title>Unauthorized</title>\n</head>\n<body>\n" .
 636        "<h2>WebCalendar</h2>\n" .
 637        "You are not authorized" .
 638        "\n</body>\n</html>";
 639    }
 640    exit;
 641  }
 642  
 643  /**
 644   * Generates a cookie that saves the last calendar view.
 645   *
 646   * Cookie is based on the current <var>$REQUEST_URI</var>.
 647   *
 648   * We save this cookie so we can return to this same page after a user
 649   * edits/deletes/etc an event.
 650   *
 651   * @global string Request string
 652   */
 653  function remember_this_view () {
 654    global $REQUEST_URI;
 655    if ( empty ( $REQUEST_URI ) )
 656      $REQUEST_URI = $_SERVER["REQUEST_URI"];
 657  
 658    // do not use anything with friendly in the URI
 659    if ( strstr ( $REQUEST_URI, "friendly=" ) )
 660      return;
 661  
 662    SetCookie ( "webcalendar_last_view", $REQUEST_URI );
 663  }
 664  
 665  /**
 666   * Gets the last page stored using {@link remember_this_view()}.
 667   *
 668   * @return string The URL of the last view or an empty string if it cannot be
 669   *                determined.
 670   *
 671   * @global array Cookies
 672   */
 673  function get_last_view () {
 674    global $HTTP_COOKIE_VARS;
 675    $val = '';
 676  
 677    if ( isset ( $_COOKIE["webcalendar_last_view"] ) ) {
 678        $HTTP_COOKIE_VARS["webcalendar_last_view"] = $_COOKIE["webcalendar_last_view"];
 679      $val = $_COOKIE["webcalendar_last_view"];
 680    } else if ( isset ( $HTTP_COOKIE_VARS["webcalendar_last_view"] ) ) {
 681      $val = $HTTP_COOKIE_VARS["webcalendar_last_view"];
 682      }
 683    $val =   str_replace ( "&", "&amp;", $val );
 684    return $val;
 685  }
 686  
 687  /**
 688   * Sends HTTP headers that tell the browser not to cache this page.
 689   *
 690   * Different browser use different mechanisms for this, so a series of HTTP
 691   * header directives are sent.
 692   *
 693   * <b>Note:</b> This function needs to be called before any HTML output is sent
 694   * to the browser.
 695   */
 696  function send_no_cache_header () {
 697    header ( "Expires: Mon, 26 Jul 1997 05:00:00 GMT" );
 698    header ( "Last-Modified: " . gmdate ( "D, d M Y H:i:s" ) . " GMT" );
 699    header ( "Cache-Control: no-store, no-cache, must-revalidate" );
 700    header ( "Cache-Control: post-check=0, pre-check=0", false );
 701    header ( "Pragma: no-cache" );
 702  }
 703  
 704  /**
 705   * Loads the current user's preferences as global variables from the webcal_user_pref table.
 706   *
 707   * Also loads the list of views for this user (not really a preference, but
 708   * this is a convenient place to put this...)
 709   *
 710   * <b>Notes:</b>
 711   * - If <var>$allow_color_customization</var> is set to 'N', then we ignore any
 712   *   color preferences.
 713   * - Other default values will also be set if the user has not saved a
 714   *   preference and no global value has been set by the administrator in the
 715   *   system settings.
 716   */
 717  function load_user_preferences () {
 718    global $login, $browser, $views, $prefarray, $is_assistant,
 719      $has_boss, $user, $is_nonuser_admin, $allow_color_customization;
 720    $lang_found = false;
 721    $colors = array (
 722      "BGCOLOR" => 1,
 723      "H2COLOR" => 1,
 724      "THBG" => 1,
 725      "THFG" => 1,
 726      "CELLBG" => 1,
 727      "TODAYCELLBG" => 1,
 728      "WEEKENDBG" => 1,
 729      "POPUP_BG" => 1,
 730      "POPUP_FG" => 1,
 731    );
 732  
 733    $browser = get_web_browser ();
 734    $browser_lang = get_browser_language ();
 735    $prefarray = array ();
 736  
 737    // Note: default values are set in config.php
 738    $res = dbi_query (
 739      "SELECT cal_setting, cal_value FROM webcal_user_pref " .
 740      "WHERE cal_login = '$login'" );
 741    if ( $res ) {
 742      while ( $row = dbi_fetch_row ( $res ) ) {
 743        $setting = $row[0];
 744        $value = $row[1];
 745        if ( $allow_color_customization == 'N' ) {
 746          if ( isset ( $colors[$setting] ) )
 747            continue;
 748        }
 749        $sys_setting = "sys_" . $setting;
 750        // save system defaults
 751        if ( ! empty ( $GLOBALS[$setting] ) )
 752          $GLOBALS["sys_" . $setting] = $GLOBALS[$setting];
 753        $GLOBALS[$setting] = $value;
 754        $prefarray[$setting] = $value;
 755        if ( $setting == "LANGUAGE" )
 756          $lang_found = true;
 757      }
 758      dbi_free_result ( $res );
 759    }
 760    // get views for this user and global views
 761    $res = dbi_query (
 762      "SELECT cal_view_id, cal_name, cal_view_type, cal_is_global " .
 763      "FROM webcal_view " .
 764      "WHERE cal_owner = '$login' OR cal_is_global = 'Y' " .
 765      "ORDER BY cal_name" );
 766    if ( $res ) {
 767      $views = array ();
 768      while ( $row = dbi_fetch_row ( $res ) ) {
 769        if ( $row[2] == 'S' )
 770          $url = "view_t.php?timeb=1&amp;id=$row[0]";
 771        else if ( $row[2] == 'T' )
 772          $url = "view_t.php?timeb=0&amp;id=$row[0]";
 773        else
 774          $url = "view_" . strtolower ( $row[2] ) . ".php?id=$row[0]";
 775        $v = array (
 776          "cal_view_id" => $row[0],
 777          "cal_name" => $row[1],
 778          "cal_view_type" => $row[2],
 779          "cal_is_global" => $row[3],
 780          "url" => $url
 781          );
 782        $views[] = $v;
 783      }
 784      dbi_free_result ( $res );
 785    }
 786  
 787    // If user has not set a language preference, then use their browser
 788    // settings to figure it out, and save it in the database for future
 789    // use (email reminders).
 790    if ( ! $lang_found && strlen ( $login ) && $login != "__public__" ) {
 791      $LANGUAGE = $browser_lang;
 792      dbi_query ( "INSERT INTO webcal_user_pref " .
 793        "( cal_login, cal_setting, cal_value ) VALUES " .
 794        "( '$login', 'LANGUAGE', '$LANGUAGE' )" );
 795    }
 796  
 797    if ( empty ( $GLOBALS["DATE_FORMAT_MY"] ) )
 798      $GLOBALS["DATE_FORMAT_MY"] = "__month__ __yyyy__";
 799    if ( empty ( $GLOBALS["DATE_FORMAT_MD"] ) )
 800      $GLOBALS["DATE_FORMAT_MD"] = "__month__ __dd__";
 801    $is_assistant = empty ( $user ) ? false :
 802      user_is_assistant ( $login, $user );
 803    $has_boss = user_has_boss ( $login );
 804    $is_nonuser_admin = ($user) ? user_is_nonuser_admin ( $login, $user ) : false;
 805    if ( $is_nonuser_admin ) load_nonuser_preferences ($user);
 806  }
 807  
 808  /**
 809   * Gets the list of external users for an event from the webcal_entry_ext_user table in an HTML format.
 810   *
 811   * @param int $event_id   Event ID
 812   * @param int $use_mailto When set to 1, email address will contain an href
 813   *                        link with a mailto URL.
 814   *
 815   * @return string The list of external users for an event formatte in HTML.
 816   */
 817  function event_get_external_users ( $event_id, $use_mailto=0 ) {
 818    global $error;
 819    $ret = "";
 820  
 821    $res = dbi_query ( "SELECT cal_fullname, cal_email " .
 822      "FROM webcal_entry_ext_user " .
 823      "WHERE cal_id = $event_id " .
 824      "ORDER by cal_fullname" );
 825    if ( $res ) {
 826      while ( $row = dbi_fetch_row ( $res ) ) {
 827        if ( strlen ( $ret ) )
 828          $ret .= "\n";
 829        // Remove [\d] if duplicate name
 830        $trow = trim( preg_replace( '/\[[\d]]/' , "", $row[0] ) );
 831        $ret .= $trow;
 832        if ( strlen ( $row[1] ) ) {
 833          if ( $use_mailto ) {
 834            $ret .= " <a href=\"mailto:$row[1]\">&lt;" .
 835              htmlentities ( $row[1] ) . "&gt;</a>";
 836          } else {
 837            $ret .= " &lt;". htmlentities ( $row[1] ) . "&gt;";
 838          }
 839        }
 840      }
 841      dbi_free_result ( $res );
 842    } else {
 843      echo translate("Database error") .": " . dbi_error ();
 844      echo "<br />\nSQL:<br />\n$sql";
 845      exit;
 846    }
 847    return $ret;
 848  }
 849  
 850  /**
 851   * Adds something to the activity log for an event.
 852   *
 853   * The information will be saved to the webcal_entry_log table.
 854   *
 855   * @param int    $event_id Event ID
 856   * @param string $user     Username of user doing this
 857   * @param string $user_cal Username of user whose calendar is affected
 858   * @param string $type     Type of activity we are logging:
 859   *   - $LOG_CREATE
 860   *   - $LOG_APPROVE
 861   *   - $LOG_REJECT
 862   *   - $LOG_UPDATE
 863   *   - $LOG_DELETE
 864   *   - $LOG_NOTIFICATION
 865   *   - $LOG_REMINDER
 866   * @param string $text     Text comment to add with activity log entry
 867   */
 868  function activity_log ( $event_id, $user, $user_cal, $type, $text ) {
 869    $next_id = 1;
 870  
 871    if ( empty ( $type ) ) {
 872      echo "Error: type not set for activity log!";
 873      // but don't exit since we may be in mid-transaction
 874      return;
 875    }
 876  
 877    $res = dbi_query ( "SELECT MAX(cal_log_id) FROM webcal_entry_log" );
 878    if ( $res ) {
 879      if ( $row = dbi_fetch_row ( $res ) ) {
 880        $next_id = $row[0] + 1;
 881      }
 882      dbi_free_result ( $res );
 883    }
 884  
 885    $date = date ( "Ymd" );
 886    $time = date ( "Gis" );
 887    $sql_text = empty ( $text ) ? "NULL" : "'$text'";
 888    $sql_user_cal = empty ( $user_cal ) ? "NULL" : "'$user_cal'";
 889  
 890    $sql = "INSERT INTO webcal_entry_log ( " .
 891      "cal_log_id, cal_entry_id, cal_login, cal_user_cal, cal_type, " .
 892      "cal_date, cal_time, cal_text ) VALUES ( $next_id, $event_id, " .
 893      "'$user', $sql_user_cal, '$type', $date, $time, $sql_text )";
 894    if ( ! dbi_query ( $sql ) ) {
 895      echo "Database error: " . dbi_error ();
 896      echo "<br />\nSQL:<br />\n$sql";
 897      exit;
 898    }
 899  }
 900  
 901  /**
 902   * Gets a list of users.
 903   *
 904   * If groups are enabled, this will restrict the list of users to only those
 905   * users who are in the same group(s) as the user (unless the user is an admin
 906   * user).  We allow admin users to see all users because they can also edit
 907   * someone else's events (so they may need access to users who are not in the
 908   * same groups that they are in).
 909   *
 910   * @return array Array of users, where each element in the array is an array
 911   *               with the following keys:
 912   *    - cal_login
 913   *    - cal_lastname
 914   *    - cal_firstname
 915   *    - cal_is_admin
 916   *    - cal_is_admin
 917   *    - cal_email
 918   *    - cal_password
 919   *    - cal_fullname
 920   */
 921  function get_my_users () {
 922    global $login, $is_admin, $groups_enabled, $user_sees_only_his_groups;
 923  
 924    if ( $groups_enabled == "Y" && $user_sees_only_his_groups == "Y" &&
 925      ! $is_admin ) {
 926      // get groups that current user is in
 927      $res = dbi_query ( "SELECT cal_group_id FROM webcal_group_user " .
 928        "WHERE cal_login = '$login'" );
 929      $groups = array ();
 930      if ( $res ) {
 931        while ( $row = dbi_fetch_row ( $res ) ) {
 932          $groups[] = $row[0];
 933        }
 934        dbi_fetch_row ( $res );
 935      }
 936      $u = user_get_users ();
 937      $u_byname = array ();
 938      for ( $i = 0; $i < count ( $u ); $i++ ) {
 939        $name = $u[$i]['cal_login'];
 940        $u_byname[$name] = $u[$i];
 941      }
 942      $ret = array ();
 943      if ( count ( $groups ) == 0 ) {
 944        // Eek.  User is in no groups... Return only themselves
 945        $ret[] = $u_byname[$login];
 946        return $ret;
 947      }
 948      // get list of users in the same groups as current user
 949      $sql = "SELECT DISTINCT(webcal_group_user.cal_login), cal_lastname, cal_firstname from webcal_group_user " .
 950        "LEFT JOIN webcal_user ON webcal_group_user.cal_login = webcal_user.cal_login " .
 951        "WHERE cal_group_id ";
 952      if ( count ( $groups ) == 1 )
 953        $sql .= "= " . $groups[0];
 954      else {
 955        $sql .= "IN ( " . implode ( ", ", $groups ) . " )";
 956      }
 957      $sql .= " ORDER BY cal_lastname, cal_firstname, webcal_group_user.cal_login";
 958      //echo "SQL: $sql <br />\n";
 959      $res = dbi_query ( $sql );
 960      if ( $res ) {
 961        while ( $row = dbi_fetch_row ( $res ) ) {
 962          $ret[] = $u_byname[$row[0]];
 963        }
 964        dbi_free_result ( $res );
 965      }
 966      return $ret;
 967    } else {
 968      // groups not enabled... return all users
 969      //echo "No groups. ";
 970      return user_get_users ();
 971    }
 972  }
 973  
 974  /**
 975   * Gets a preference setting for the specified user.
 976   *
 977   * If no value is found in the database, then the system default setting will
 978   * be returned.
 979   *
 980   * @param string $user    User login we are getting preference for
 981   * @param string $setting Name of the setting
 982   *
 983   * @return string The value found in the webcal_user_pref table for the
 984   *                specified setting or the sytem default if no user settings
 985   *                was found.
 986   */
 987  function get_pref_setting ( $user, $setting ) {
 988    $ret = '';
 989    // set default
 990    if ( ! isset ( $GLOBALS["sys_" .$setting] ) ) {
 991      // this could happen if the current user has not saved any pref. yet
 992      if ( ! empty ( $GLOBALS[$setting] ) )
 993        $ret = $GLOBALS[$setting];
 994    } else {
 995      $ret = $GLOBALS["sys_" .$setting];
 996    }
 997  
 998    $sql = "SELECT cal_value FROM webcal_user_pref " .
 999      "WHERE cal_login = '" . $user . "' AND " .
1000      "cal_setting = '" . $setting . "'";
1001    //echo "SQL: $sql <br />\n";
1002    $res = dbi_query ( $sql );
1003    if ( $res ) {
1004      if ( $row = dbi_fetch_row ( $res ) )
1005        $ret = $row[0];
1006      dbi_free_result ( $res );
1007    }
1008    return $ret;
1009  }
1010  
1011  /**
1012   * Gets browser-specified language preference.
1013   *
1014   * @return string Preferred language
1015   *
1016   * @ignore
1017   */
1018  function get_browser_language () {
1019    global $HTTP_ACCEPT_LANGUAGE, $browser_languages;
1020    $ret = "";
1021    if ( empty ( $HTTP_ACCEPT_LANGUAGE ) &&
1022      isset ( $_SERVER["HTTP_ACCEPT_LANGUAGE"] ) )
1023      $HTTP_ACCEPT_LANGUAGE = $_SERVER["HTTP_ACCEPT_LANGUAGE"];
1024    if (  empty ( $HTTP_ACCEPT_LANGUAGE ) ) {
1025      return "none";
1026    } else {
1027      $langs = explode ( ",", $HTTP_ACCEPT_LANGUAGE );
1028      for ( $i = 0; $i < count ( $langs ); $i++ ) {
1029       $l = strtolower ( trim ( ereg_replace(';.*', '', $langs[$i] ) ) );
1030        $ret .= "\"$l\" ";
1031        if ( ! empty ( $browser_languages[$l] ) ) {
1032          return $browser_languages[$l];
1033        }
1034      }
1035    }
1036    //if ( strlen ( $HTTP_ACCEPT_LANGUAGE ) )
1037    //  return "none ($HTTP_ACCEPT_LANGUAGE not supported)";
1038    //else
1039      return "none";
1040  }
1041  
1042  /**
1043   * Loads current user's layer info into layer global variable.
1044   *
1045   * If the system setting <var>$allow_view_other</var> is not set to 'Y', then
1046   * we ignore all layer functionality.  If <var>$force</var> is 0, we only load
1047   * layers if the current user preferences have layers turned on.
1048   *
1049   * @param string $user  Username of user to load layers for
1050   * @param int    $force If set to 1, then load layers for this user even if
1051   *                      user preferences have layers turned off.
1052   */
1053  function load_user_layers ($user="",$force=0) {
1054    global $login;
1055    global $layers;
1056    global $LAYERS_STATUS, $allow_view_other;
1057  
1058    if ( $user == "" )
1059      $user = $login;
1060  
1061    $layers = array ();
1062  
1063    if ( empty ( $allow_view_other ) || $allow_view_other != 'Y' )
1064      return; // not allowed to view others' calendars, so cannot use layers
1065  
1066    if ( $force || ( ! empty ( $LAYERS_STATUS ) && $LAYERS_STATUS != "N" ) ) {
1067      $res = dbi_query (
1068        "SELECT cal_layerid, cal_layeruser, cal_color, cal_dups " .
1069        "FROM webcal_user_layers " .
1070        "WHERE cal_login = '$user' ORDER BY cal_layerid" );
1071      if ( $res ) {
1072        $count = 1;
1073        while ( $row = dbi_fetch_row ( $res ) ) {
1074          $layers[$row[0]] = array (
1075            "cal_layerid" => $row[0],
1076            "cal_layeruser" => $row[1],
1077            "cal_color" => $row[2],
1078            "cal_dups" => $row[3]
1079          );
1080          $count++;
1081        }
1082        dbi_free_result ( $res );
1083      }
1084    } else {
1085      //echo "Not loading!";
1086    }
1087  }
1088  
1089  /**
1090   * Generates the HTML used in an event popup for the site_extras fields of an event.
1091   *
1092   * @param int $id Event ID
1093   *
1094   * @return string The HTML to be used within the event popup for any site_extra
1095   *                fields found for the specified event
1096   */
1097  function site_extras_for_popup ( $id ) {
1098    global $site_extras_in_popup, $site_extras;
1099    // These are needed in case the site_extras.php file was already
1100    // included.
1101    global $EXTRA_TEXT, $EXTRA_MULTILINETEXT, $EXTRA_URL, $EXTRA_DATE,
1102      $EXTRA_EMAIL, $EXTRA_USER, $EXTRA_REMINDER, $EXTRA_SELECTLIST;
1103    global $EXTRA_REMINDER_WITH_DATE, $EXTRA_REMINDER_WITH_OFFSET,
1104      $EXTRA_REMINDER_DEFAULT_YES;
1105  
1106    $ret = '';
1107  
1108    if ( $site_extras_in_popup != 'Y' )
1109      return '';
1110  
1111    include_once  'includes/site_extras.php';
1112  
1113    $extras = get_site_extra_fields ( $id );
1114    for ( $i = 0; $i < count ( $site_extras ); $i++ ) {
1115      $extra_name = $site_extras[$i][0];
1116      $extra_type = $site_extras[$i][2];
1117      $extra_arg1 = $site_extras[$i][3];
1118      $extra_arg2 = $site_extras[$i][4];
1119      if ( ! empty ( $extras[$extra_name]['cal_name'] ) ) {
1120        $ret .= "<dt>" .  translate ( $site_extras[$i][1] ) . ":</dt>\n<dd>";
1121        if ( $extra_type == $EXTRA_DATE ) {
1122          if ( $extras[$extra_name]['cal_date'] > 0 )
1123            $ret .= date_to_str ( $extras[$extra_name]['cal_date'] );
1124        } else if ( $extra_type == $EXTRA_TEXT ||
1125          $extra_type == $EXTRA_MULTILINETEXT ) {
1126          $ret .= nl2br ( $extras[$extra_name]['cal_data'] );
1127        } else if ( $extra_type == $EXTRA_REMINDER ) {
1128          if ( $extras[$extra_name]['cal_remind'] <= 0 )
1129            $ret .= translate ( "No" );
1130          else {
1131            $ret .= translate ( "Yes" );
1132            if ( ( $extra_arg2 & $EXTRA_REMINDER_WITH_DATE ) > 0 ) {
1133              $ret .= "&nbsp;&nbsp;-&nbsp;&nbsp;";
1134              $ret .= date_to_str ( $extras[$extra_name]['cal_date'] );
1135            } else if ( ( $extra_arg2 & $EXTRA_REMINDER_WITH_OFFSET ) > 0 ) {
1136              $ret .= "&nbsp;&nbsp;-&nbsp;&nbsp;";
1137              $minutes = $extras[$extra_name]['cal_data'];
1138              $d = (int) ( $minutes / ( 24 * 60 ) );
1139              $minutes -= ( $d * 24 * 60 );
1140              $h = (int) ( $minutes / 60 );
1141              $minutes -= ( $h * 60 );
1142              if ( $d > 0 )
1143                $ret .= $d . "&nbsp;" . translate("days") . "&nbsp;";
1144              if ( $h > 0 )
1145                $ret .= $h . "&nbsp;" . translate("hours") . "&nbsp;";
1146              if ( $minutes > 0 )
1147                $ret .= $minutes . "&nbsp;" . translate("minutes");
1148              $ret .= "&nbsp;" . translate("before event" );
1149            }
1150          }
1151        } else {
1152          $ret .= $extras[$extra_name]['cal_data'];
1153        }
1154        $ret .= "</dd>\n";
1155      }
1156    }
1157    return $ret;
1158  }
1159  
1160  /**
1161   * Builds the HTML for the event popup.
1162   *
1163   * @param string $popupid     CSS id to use for event popup
1164   * @param string $user        Username of user the event pertains to
1165   * @param string $description Event description
1166   * @param string $time        Time of the event (already formatted in a display format)
1167   * @param string $site_extras HTML for any site_extras for this event
1168   *
1169   * @return string The HTML for the event popup
1170   */
1171  function build_event_popup ( $popupid, $user, $description, $time, $site_extras='' ) {
1172    global $login, $popup_fullnames, $popuptemp_fullname;
1173    $ret = "<dl id=\"$popupid\" class=\"popup\">\n";
1174  
1175    if ( empty ( $popup_fullnames ) )
1176      $popup_fullnames = array ();
1177    
1178    if ( $user != $login ) {
1179      if ( empty ( $popup_fullnames[$user] ) ) {
1180        user_load_variables ( $user, "popuptemp_" );
1181        $popup_fullnames[$user] = $popuptemp_fullname;
1182      }
1183      $ret .= "<dt>" . translate ("User") .
1184        ":</dt>\n<dd>$popup_fullnames[$user]</dd>\n";
1185    }
1186    if ( strlen ( $time ) )
1187      $ret .= "<dt>" . translate ("Time") . ":</dt>\n<dd>$time</dd>\n";
1188    $ret .= "<dt>" . translate ("Description") . ":</dt>\n<dd>";
1189    if ( ! empty ( $GLOBALS['allow_html_description'] ) &&
1190      $GLOBALS['allow_html_description'] == 'Y' ) {
1191      $str = str_replace ( "&", "&amp;", $description );
1192      $str = str_replace ( "&amp;amp;", "&amp;", $str );
1193      // If there is no html found, then go ahead and replace
1194      // the line breaks ("\n") with the html break.
1195      if ( strstr ( $str, "<" ) && strstr ( $str, ">" ) ) {
1196        // found some html...
1197        $ret .= $str;
1198      } else {
1199        // no html, replace line breaks
1200        $ret .= nl2br ( $str );
1201      }
1202    } else {
1203      // html not allowed in description, escape everything
1204      $ret .= nl2br ( htmlspecialchars ( $description ) );
1205    }
1206    $ret .= "</dd>\n";
1207    if ( ! empty ( $site_extras ) )
1208      $ret .= $site_extras;
1209    $ret .= "</dl>\n";
1210    return $ret;
1211  }
1212  
1213  /**
1214   * Prints out a date selection box for use in a form.
1215   *
1216   * @param string $prefix Prefix to use in front of form element names
1217   * @param int    $date   Currently selected date (in YYYYMMDD format)
1218   *
1219   * @uses date_selection_html
1220   */
1221  function print_date_selection ( $prefix, $date ) {
1222    print date_selection_html ( $prefix, $date );
1223  }
1224  
1225  /**
1226   * Generate HTML for a date selection for use in a form.
1227   *
1228   * @param string $prefix Prefix to use in front of form element names
1229   * @param int    $date   Currently selected date (in YYYYMMDD format)
1230   *
1231   * @return string HTML for the selection box
1232   */
1233  function date_selection_html ( $prefix, $date ) {
1234    $ret = "";
1235    $num_years = 20;
1236    if ( strlen ( $date ) != 8 )
1237      $date = date ( "Ymd" );
1238    $thisyear = $year = substr ( $date, 0, 4 );
1239    $thismonth = $month = substr ( $date, 4, 2 );
1240    $thisday = $day = substr ( $date, 6, 2 );
1241    if ( $thisyear - date ( "Y" ) >= ( $num_years - 1 ) )
1242      $num_years = $thisyear - date ( "Y" ) + 2;
1243    $ret .= "<select name=\"" . $prefix . "day\">\n";
1244    for ( $i = 1; $i <= 31; $i++ )
1245      $ret .= "<option value=\"$i\"" .
1246        ( $i == $thisday ? " selected=\"selected\"" : "" ) . ">$i</option>\n";
1247    $ret .= "</select>\n<select name=\"" . $prefix . "month\">\n";
1248    for ( $i = 1; $i <= 12; $i++ ) {
1249      $m = month_short_name ( $i - 1 );
1250      $ret .= "<option value=\"$i\"" .
1251        ( $i == $thismonth ? " selected=\"selected\"" : "" ) . ">$m</option>\n";
1252    }
1253    $ret .= "</select>\n<select name=\"" . $prefix . "year\">\n";
1254    for ( $i = -10; $i < $num_years; $i++ ) {
1255      $y = $thisyear + $i;
1256      $ret .= "<option value=\"$y\"" .
1257        ( $y == $thisyear ? " selected=\"selected\"" : "" ) . ">$y</option>\n";
1258    }
1259    $ret .= "</select>\n";
1260    $ret .= "<input type=\"button\" onclick=\"selectDate( '" .
1261      $prefix . "day','" . $prefix . "month','" . $prefix . "year',$date, event)\" value=\"" .
1262      translate("Select") . "...\" />\n";
1263  
1264    return $ret;
1265  }
1266  
1267  /**
1268   * Prints out a minicalendar for a month.
1269   *
1270   * @todo Make day.php NOT be a special case
1271   *
1272   * @param int    $thismonth     Number of the month to print
1273   * @param int    $thisyear      Number of the year
1274   * @param bool   $showyear      Show the year in the calendar's title?
1275   * @param bool   $show_weeknums Show week numbers to the left of each row?
1276   * @param string $minical_id    id attribute for the minical table
1277   * @param string $month_link    URL and query string for month link that should
1278   *                              come before the date specification (e.g.
1279   *                              month.php?  or  view_l.php?id=7&amp;)
1280   */
1281  function display_small_month ( $thismonth, $thisyear, $showyear,
1282    $show_weeknums=false, $minical_id='', $month_link='month.php?' ) {
1283    global $WEEK_START, $user, $login, $boldDays, $get_unapproved;
1284    global $DISPLAY_WEEKNUMBER;
1285    global $SCRIPT, $thisday; // Needed for day.php
1286    global $caturl, $today;
1287  
1288    if ( $user != $login && ! empty ( $user ) ) {
1289      $u_url = "user=$user" . "&amp;";
1290    } else {
1291      $u_url = '';
1292    }
1293  
1294    //start the minical table for each month
1295    echo "\n<table class=\"minical\"";
1296    if ( $minical_id != '' ) {
1297      echo " id=\"$minical_id\"";
1298    }
1299    echo ">\n";
1300  
1301    $monthstart = mktime(2,0,0,$thismonth,1,$thisyear);
1302    $monthend = mktime(2,0,0,$thismonth + 1,0,$thisyear);
1303  
1304    if ( $SCRIPT == 'day.php' ) {
1305      $month_ago = date ( "Ymd",
1306        mktime ( 3, 0, 0, $thismonth - 1, $thisday, $thisyear ) );
1307      $month_ahead = date ( "Ymd",
1308        mktime ( 3, 0, 0, $thismonth + 1, $thisday, $thisyear ) );
1309  
1310      echo "<caption>$thisday</caption>\n";
1311      echo "<thead>\n";
1312      echo "<tr class=\"monthnav\"><th colspan=\"7\">\n";
1313      echo "<a title=\"" . 
1314   translate("Previous") . "\" class=\"prev\" href=\"day.php?" . $u_url  .
1315   "date=$month_ago$caturl\"><img src=\"leftarrowsmall.gif\" alt=\"" .
1316   translate("Previous") . "\" /></a>\n";
1317      echo "<a title=\"" . 
1318   translate("Next") . "\" class=\"next\" href=\"day.php?" . $u_url .
1319   "date=$month_ahead$caturl\"><img src=\"rightarrowsmall.gif\" alt=\"" .
1320   translate("Next") . "\" /></a>\n";
1321      echo month_name ( $thismonth - 1 );
1322      if ( $showyear != '' ) {
1323        echo " $thisyear";
1324      }
1325      echo "</th></tr>\n<tr>\n";
1326    } else {  //not day script
1327      //print the month name
1328      echo "<caption><a href=\"{$month_link}{$u_url}year=$thisyear&amp;month=$thismonth\">";
1329   echo month_name ( $thismonth - 1 ) .
1330    ( $showyear ? " $thisyear" : "" );
1331      echo "</a></caption>\n";
1332  
1333      echo "<thead>\n<tr>\n";
1334    }
1335  
1336    //determine if the week starts on sunday or monday
1337    if ( $WEEK_START == "1" ) {
1338      $wkstart = get_monday_before ( $thisyear, $thismonth, 1 );
1339    } else {
1340      $wkstart = get_sunday_before ( $thisyear, $thismonth, 1 );
1341    }
1342    //print the headers to display the day of the week (sun, mon, tues, etc.)
1343  
1344    // if we're showing week numbers we need an extra column
1345    if ( $show_weeknums && $DISPLAY_WEEKNUMBER == 'Y' )
1346      echo "<th class=\"empty\">&nbsp;</th>\n";
1347    //if the week doesn't start on monday, print the day
1348    if ( $WEEK_START == 0 ) echo "<th>" .
1349      weekday_short_name ( 0 ) . "</th>\n";
1350    //cycle through each day of the week until gone
1351    for ( $i = 1; $i < 7; $i++ ) {
1352      echo "<th>" .  weekday_short_name ( $i ) .  "</th>\n";
1353    }
1354    //if the week DOES start on monday, print sunday
1355    if ( $WEEK_START == 1 )
1356      echo "<th>" .  weekday_short_name ( 0 ) .  "</th>\n";
1357    //end the header row
1358    echo "</tr>\n</thead>\n<tbody>\n";
1359    for ($i = $wkstart; date("Ymd",$i) <= date ("Ymd",$monthend);
1360      $i += (24 * 3600 * 7) ) {
1361      echo "<tr>\n";
1362      if ( $show_weeknums && $DISPLAY_WEEKNUMBER == 'Y' ) {
1363        echo "<td class=\"weeknumber\"><a href=\"week.php?" . $u_url .
1364          "date=".date("Ymd", $i)."\">(" . week_number($i) . ")</a></td>\n";
1365      }
1366      for ($j = 0; $j < 7; $j++) {
1367        $date = $i + ($j * 24 * 3600);
1368        $dateYmd = date ( "Ymd", $date );
1369        $hasEvents = false;
1370        if ( $boldDays ) {
1371          $ev = get_entries ( $user, $dateYmd, $get_unapproved );
1372          if ( count ( $ev ) > 0 ) {
1373            $hasEvents = true;
1374          } else {
1375            $rep = get_repeating_entries ( $user, $dateYmd, $get_unapproved );
1376            if ( count ( $rep ) > 0 )
1377              $hasEvents = true;
1378          }
1379        }
1380        if ( $dateYmd >= date ("Ymd",$monthstart) &&
1381          $dateYmd <= date ("Ymd",$monthend) ) {
1382          echo "<td";
1383          $wday = date ( 'w', $date );
1384          $class = '';
1385    //add class="weekend" if it's saturday or sunday
1386          if ( $wday == 0 || $wday == 6 ) {
1387            $class = "weekend";
1388          }
1389    //if the day being viewed is today's date AND script = day.php
1390          if ( $dateYmd == $thisyear . $thismonth . $thisday &&
1391            $SCRIPT == 'day.php'  ) {
1392      //if it's also a weekend, add a space between class names to combine styles
1393      if ( $class != '' ) {
1394              $class .= ' ';
1395            }
1396            $class .= "selectedday";
1397          }
1398          if ( $hasEvents ) {
1399            if ( $class != '' ) {
1400              $class .= ' ';
1401            }
1402            $class .= "hasevents";
1403          }
1404          if ( $class != '' ) {
1405            echo " class=\"$class\"";
1406          }
1407          if ( date ( "Ymd", $date  ) == date ( "Ymd", $today ) ){
1408            echo " id=\"today\"";
1409          }
1410          echo "><a href=\"day.php?" .$u_url  . "date=" .  $dateYmd . 
1411            "\">";
1412          echo date ( "d", $date ) . "</a></td>\n";
1413          } else {
1414            echo "<td class=\"empty\">&nbsp;</td>\n";
1415          }
1416        }                 // end for $j
1417        echo "</tr>\n";
1418      }                         // end for $i
1419    echo "</tbody>\n</table>\n";
1420  }
1421  
1422  /**
1423   * Prints the HTML for one day's events in the month view.
1424   *
1425   * @param int    $id          Event ID
1426   * @param int    $date        Date of event (relevant in repeating events) in
1427   *                            YYYYMMDD format
1428   * @param int    $time        Time (in HHMMSS format)
1429   * @param int    $duration    Event duration in minutes
1430   * @param string $name        Event name
1431   * @param string $description Long description of event
1432   * @param string $status      Event status
1433   * @param int    $pri         Event priority
1434   * @param string $access      Event access
1435   * @param string $event_owner Username of user associated with this event
1436   * @param int    $event_cat   Category of event for <var>$event_owner</var>
1437   *
1438   * @staticvar int Used to ensure all event popups have a unique id
1439   *
1440   * @uses build_event_popup
1441   */
1442  function print_entry ( $id, $date, $time, $duration,
1443    $name, $description, $status,
1444    $pri, $access, $event_owner, $event_cat=-1 ) {
1445    global $eventinfo, $login, $user, $PHP_SELF, $TZ_OFFSET;
1446    static $key = 0;
1447    
1448    global $layers;
1449  
1450    if ( $login != $event_owner && strlen ( $event_owner ) ) {
1451      $class = "layerentry";
1452    } else {
1453      $class = "entry";
1454      if ( $status == "W" ) $class = "unapprovedentry";
1455    }
1456    // if we are looking at a view, then always use "entry"
1457    if ( strstr ( $PHP_SELF, "view_m.php" ) ||
1458      strstr ( $PHP_SELF, "view_w.php" ) ||
1459      strstr ( $PHP_SELF, "view_v.php" ) ||
1460      strstr ( $PHP_SELF, "view_t.php" ) )
1461      $class = "entry";
1462  
1463    if ( $pri == 3 ) echo "<strong>";
1464    $popupid = "eventinfo-$id-$key";
1465    $key++;
1466    echo "<a title=\"" . 
1467      translate("View this entry") . "\" class=\"$class\" href=\"view_entry.php?id=$id&amp;date=$date";
1468    if ( strlen ( $user ) > 0 )
1469      echo "&amp;user=" . $user;
1470    echo "\" onmouseover=\"window.status='" . 
1471      translate("View this entry") .
1472      "'; show(event, '$popupid'); return true;\" onmouseout=\"window.status=''; hide('$popupid'); return true;\">";
1473    $icon = "circle.gif";
1474    $catIcon = '';
1475    if ( $event_cat > 0 ) {
1476      $catIcon = "icons/cat-" . $event_cat . ".gif";
1477      if ( ! file_exists ( $catIcon ) )
1478        $catIcon = '';
1479    }
1480  
1481    if ( empty ( $catIcon ) ) {
1482      echo "<img src=\"$icon\" class=\"bullet\" alt=\"" . 
1483        translate("View this entry") . "\" />";
1484    } else {
1485      // Use category icon
1486      echo "<img src=\"$catIcon\" alt=\"" . 
1487        translate("View this entry") . "\" /><br />";
1488    }
1489  
1490    if ( $login != $event_owner && strlen ( $event_owner ) ) {
1491      if ($layers) foreach ($layers as $layer) {
1492        if ($layer['cal_layeruser'] == $event_owner) {
1493          echo("<span style=\"color:" . $layer['cal_color'] . ";\">");
1494        }
1495      }
1496    }
1497  
1498  
1499    $timestr = "";
1500    if ( $duration == ( 24 * 60 ) ) {
1501      $timestr = translate("All day event");
1502    } else if ( $time != -1 ) {
1503      $timestr = display_time ( $time );
1504      $time_short = preg_replace ("/(:00)/", '', $timestr);
1505      echo $time_short . "&raquo;&nbsp;";
1506      if ( $duration > 0 ) {
1507          // calc end time
1508          $h = (int) ( $time / 10000 );
1509          $m = ( $time / 100 ) % 100;
1510          $m += $duration;
1511          $d = $duration;
1512          while ( $m >= 60 ) {
1513            $h++;
1514            $m -= 60;
1515          }
1516          $end_time = sprintf ( "%02d%02d00", $h, $m );
1517          $timestr .= " - " . display_time ( $end_time );
1518      }
1519    }
1520    if ( $login != $user && $access == 'R' && strlen ( $user ) ) {
1521      echo "(" . translate("Private") . ")";
1522    } else if ( $login != $event_owner && $access == 'R' &&
1523      strlen ( $event_owner ) ) {
1524      echo "(" . translate("Private") . ")";
1525    } else {
1526      echo htmlspecialchars ( $name );
1527    }
1528  
1529    if ( $login != $event_owner && strlen ( $event_owner ) ) {
1530      if ($layers) foreach ($layers as $layer) {
1531          if($layer['cal_layeruser'] == $event_owner) {
1532              echo "</span>";
1533          }
1534      }
1535    }
1536    echo "</a>\n";
1537    if ( $pri == 3 ) echo "</strong>\n"; //end font-weight span
1538    echo "<br />";
1539    if ( $login != $user && $access == 'R' && strlen ( $user ) )
1540      $eventinfo .= build_event_popup ( $popupid, $event_owner,
1541        translate("This event is confidential"), "" );
1542    else
1543    if ( $login != $event_owner && $access == 'R' && strlen ( $event_owner ) )
1544      $eventinfo .= build_event_popup ( $popupid, $event_owner,
1545        translate("This event is confidential"), "" );
1546    else
1547      $eventinfo .= build_event_popup ( $popupid, $event_owner,
1548        $description, $timestr, site_extras_for_popup ( $id ) );
1549  }
1550  
1551  /** 
1552   * Gets any site-specific fields for an entry that are stored in the database in the webcal_site_extras table.
1553   *
1554   * @param int $eventid Event ID
1555   *
1556   * @return array Array with the keys as follows:
1557   *    - <var>cal_name</var>
1558   *    - <var>cal_type</var>
1559   *    - <var>cal_date</var>
1560   *    - <var>cal_remind</var>
1561   *    - <var>cal_data</var>
1562   */
1563  function get_site_extra_fields ( $eventid ) {
1564    $sql = "SELECT cal_name, cal_type, cal_date, cal_remind, cal_data " .
1565      "FROM webcal_site_extras " .
1566      "WHERE cal_id = $eventid";
1567    $res = dbi_query ( $sql );
1568    $extras = array ();
1569    if ( $res ) {
1570      while ( $row = dbi_fetch_row ( $res ) ) {
1571        // save by cal_name (e.g. "URL")
1572        $extras[$row[0]] = array (
1573          "cal_name" => $row[0],
1574          "cal_type" => $row[1],
1575          "cal_date" => $row[2],
1576          "cal_remind" => $row[3],
1577          "cal_data" => $row[4]
1578        );
1579      }
1580      dbi_free_result ( $res );
1581    }
1582    return $extras;
1583  }
1584  
1585  /**
1586   * Reads all the events for a user for the specified range of dates.
1587   *
1588   * This is only called once per page request to improve performance.  All the
1589   * events get loaded into the array <var>$events</var> sorted by time of day
1590   * (not date).
1591   *
1592   * @param string $user      Username
1593   * @param string $startdate Start date range, inclusive (in YYYYMMDD format)
1594   * @param string $enddate   End date range, inclusive (in YYYYMMDD format)
1595   * @param int    $cat_id    Category ID to filter on
1596   *
1597   * @return array Array of events
1598   *
1599   * @uses query_events
1600   */
1601  function read_events ( $user, $startdate, $enddate, $cat_id = ''  ) {
1602    global $login;
1603    global $layers;
1604    global $TZ_OFFSET;
1605  
1606    $sy = substr ( $startdate, 0, 4 );
1607    $sm = substr ( $startdate, 4, 2 );
1608    $sd = substr ( $startdate, 6, 2 );
1609    $ey = substr ( $enddate, 0, 4 );
1610    $em = substr ( $enddate, 4, 2 );
1611    $ed = substr ( $enddate, 6, 2 );
1612    if ( $startdate == $enddate ) {
1613      if ( $TZ_OFFSET == 0 ) {
1614        $date_filter = " AND webcal_entry.cal_date = $startdate";
1615      } else if ( $TZ_OFFSET > 0 ) {
1616        $prev_day = mktime ( 3, 0, 0, $sm, $sd - 1, $sy );
1617        $cutoff = 24 - $TZ_OFFSET .  "0000";
1618        $date_filter = " AND ( ( webcal_entry.cal_date = $startdate AND " .
1619          "( webcal_entry.cal_time <= $cutoff OR " .
1620          "webcal_entry.cal_time = -1 ) ) OR " .
1621          "( webcal_entry.cal_date = " . date("Ymd", $prev_day ) .
1622          " AND webcal_entry.cal_time >= $cutoff ) )";
1623      } else {
1624        $next_day = mktime ( 3, 0, 0, $sm, $sd + 1, $sy );
1625        $cutoff = ( 0 - $TZ_OFFSET ) * 10000;
1626        $date_filter = " AND ( ( webcal_entry.cal_date = $startdate AND " .
1627          "( webcal_entry.cal_time > $cutoff OR " .
1628          "webcal_entry.cal_time = -1 ) ) OR " .
1629          "( webcal_entry.cal_date = " . date("Ymd", $next_day ) .
1630          " AND webcal_entry.cal_time <= $cutoff ) )";
1631      }
1632    } else {
1633      if ( $TZ_OFFSET == 0 ) {
1634        $date_filter = " AND webcal_entry.cal_date >= $startdate " .
1635          "AND webcal_entry.cal_date <= $enddate";
1636      } else if ( $TZ_OFFSET > 0 ) {
1637        $prev_day = date ( ( "Ymd" ), mktime ( 3, 0, 0, $sm, $sd - 1, $sy ) );
1638        $enddate_minus1 = date ( ( "Ymd" ), mktime ( 3, 0, 0, $em, $ed - 1, $ey ) );
1639        $cutoff = 24 - $TZ_OFFSET . "0000";
1640        $date_filter = " AND ( ( webcal_entry.cal_date >= $startdate " .
1641          "AND webcal_entry.cal_date <= $enddate AND " .
1642          "webcal_entry.cal_time = -1 ) OR " .
1643          "( webcal_entry.cal_date = $prev_day AND " .
1644          "webcal_entry.cal_time >= $cutoff ) OR " .
1645          "( webcal_entry.cal_date = $enddate AND " .
1646          "webcal_entry.cal_time < $cutoff ) OR " .
1647          "( webcal_entry.cal_date >= $startdate AND " .
1648          "webcal_entry.cal_date <= $enddate_minus1 ) )";
1649      } else {
1650        // TZ_OFFSET < 0
1651        $next_day = date ( ( "Ymd" ), mktime ( 3, 0, 0, $sm, $sd + 1, $sy ) );
1652        $enddate_plus1 =
1653          date ( ( "Ymd" ), mktime ( 3, 0, 0, $em, $ed + 1, $ey ) );
1654        $cutoff = ( 0 - $TZ_OFFSET ) * 10000;
1655        $date_filter = " AND ( ( webcal_entry.cal_date >= $startdate " .
1656          "AND webcal_entry.cal_date <= $enddate AND " .
1657          "webcal_entry.cal_time = -1 ) OR " .
1658          "( webcal_entry.cal_date = $startdate AND " .
1659          "webcal_entry.cal_time > $cutoff ) OR " .
1660          "( webcal_entry.cal_date = $enddate_plus1 AND " .
1661          "webcal_entry.cal_time <= $cutoff ) OR " .
1662          "( webcal_entry.cal_date > $startdate AND " .
1663          "webcal_entry.cal_date < $enddate_plus1 ) )";
1664      }
1665    }
1666    return query_events ( $user, false, $date_filter, $cat_id  );
1667  }
1668  
1669  /**
1670   * Gets all the events for a specific date.
1671   *
1672   * Events are retreived from the array of pre-loaded events (which was loaded
1673   * all at once to improve performance).
1674   *
1675   * The returned events will be sorted by time of day.
1676   *
1677   * @param string $user           Username
1678   * @param string $date           Date to get events for in YYYYMMDD format
1679   * @param bool   $get_unapproved Load unapproved events?
1680   *
1681   * @return array Array of events
1682   */
1683  function get_entries ( $user, $date, $get_unapproved=true ) {
1684    global $events, $TZ_OFFSET;
1685    $n = 0;
1686    $ret = array ();
1687  
1688    //echo "<br />\nChecking " . count ( $events ) . " events.  TZ_OFFSET = $TZ_OFFSET, get_unapproved=" . $get_unapproved . "<br />\n";
1689  
1690    //print_r ( $events );
1691  
1692    for ( $i = 0; $i < count ( $events ); $i++ ) {
1693      // In case of data corruption (or some other bug...)
1694      if ( empty ( $events[$i] ) || empty ( $events[$i]['cal_id'] ) )
1695        continue;
1696      if ( ( ! $get_unapproved ) && $events[$i]['cal_status'] == 'W' ) {
1697        // ignore this event
1698      //don't adjust anything  if  no TZ offset or ALL Day Event or Untimed
1699      } else if ( empty ( $TZ_OFFSET) ||  ( $events[$i]['cal_time'] <= 0 ) ) {
1700        if ( $events[$i]['cal_date'] == $date )
1701          $ret[$n++] = $events[$i];
1702      } else if ( $TZ_OFFSET > 0 ) {
1703        $cutoff = ( 24 - $TZ_OFFSET ) * 10000;
1704        //echo "<br /> cal_time " . $events[$i]['cal_time'] . "<br />\n";
1705        $sy = substr ( $date, 0, 4 );
1706        $sm = substr ( $date, 4, 2 );
1707        $sd = substr ( $date, 6, 2 );
1708        $prev_day = date ( ( "Ymd" ), mktime ( 3, 0, 0, $sm, $sd - 1, $sy ) );
1709          //echo "prev_date = $prev_day <br />\n";
1710        if ( $events[$i]['cal_date'] == $date &&
1711          $events[$i]['cal_time'] == -1 ) {
1712          $ret[$n++] = $events[$i];
1713          //echo "added event $events[$i][cal_id] <br />\n";
1714        } else if ( $events[$i]['cal_date'] == $date &&
1715          $events[$i]['cal_time'] < $cutoff ) {
1716          $ret[$n++] = $events[$i];
1717          //echo "added event {$events[$i][cal_id]} <br />\n";
1718        } else if ( $events[$i]['cal_date'] == $prev_day &&
1719          $events[$i]['cal_time'] >= $cutoff ) {
1720          $ret[$n++] = $events[$i];
1721          //echo "added event {$events[$i][cal_id]} <br />\n";
1722        }
1723      } else {
1724        //TZ < 0
1725        $cutoff = ( 0 - $TZ_OFFSET ) * 10000;
1726        //echo "<br />\ncal_time " . $events[$i]['cal_time'] . "<br />\n";
1727        $sy = substr ( $date, 0, 4 );
1728        $sm = substr ( $date, 4, 2 );
1729        $sd = substr ( $date, 6, 2 );
1730        $next_day = date ( ( "Ymd" ), mktime ( 3, 0, 0, $sm, $sd + 1, $sy ) );
1731        //echo "next_date = $next_day <br />\n";
1732        if ( $events[$i]['cal_time'] == -1 ) {
1733    if ( $events[$i]['cal_date'] == $date ) {
1734            $ret[$n++] = $events[$i];
1735            //echo "added event $events[$i][cal_id] <br />\n";
1736          }
1737        } else {
1738    if ( $events[$i]['cal_date'] == $date &&
1739            $events[$i]['cal_time'] > $cutoff ) {
1740            $ret[$n++] = $events[$i];
1741            //echo "added event $events[$i][cal_id] <br />\n";
1742          } else if ( $events[$i]['cal_date'] == $next_day &&
1743            $events[$i]['cal_time'] <= $cutoff ) {
1744            $ret[$n++] = $events[$i];
1745            //echo "added event $events[$i][cal_id] <br />\n";
1746          }
1747        }
1748      }
1749    }
1750    return $ret;
1751  }
1752  
1753  /**
1754   * Reads events visible to a user.
1755   *
1756   * Includes layers and possibly public access if enabled
1757   *
1758   * @param string $user          Username
1759   * @param bool   $want_repeated Get repeating events?
1760   * @param string $date_filter   SQL phrase starting with AND, to be appended to
1761   *                              the WHERE clause.  May be empty string.
1762   * @param int    $cat_id        Category ID to filter on.  May be empty.
1763   *
1764   * @return array Array of events sorted by time of day
1765   */
1766  function query_events ( $user, $want_repeated, $date_filter, $cat_id = '' ) {
1767    global $login;
1768    global $layers, $public_access_default_visible;
1769    $result = array ();
1770    $layers_byuser = array ();
1771  
1772    $sql = "SELECT webcal_entry.cal_name, webcal_entry.cal_description, "
1773      . "webcal_entry.cal_date, webcal_entry.cal_time, "
1774      . "webcal_entry.cal_id, webcal_entry.cal_ext_for_id, "
1775      . "webcal_entry.cal_priority, "
1776      . "webcal_entry.cal_access, webcal_entry.cal_duration, "
1777      . "webcal_entry_user.cal_status, "
1778      . "webcal_entry_user.cal_category, "
1779      . "webcal_entry_user.cal_login ";
1780    if ( $want_repeated ) {
1781      $sql .= ", "
1782        . "webcal_entry_repeats.cal_type, webcal_entry_repeats.cal_end, "
1783        . "webcal_entry_repeats.cal_frequency, webcal_entry_repeats.cal_days "
1784        . "FROM webcal_entry, webcal_entry_repeats, webcal_entry_user "
1785        . "WHERE webcal_entry.cal_id = webcal_entry_repeats.cal_id AND ";
1786    } else {
1787      $sql .= "FROM webcal_entry, webcal_entry_user WHERE ";
1788    }
1789    $sql .= "webcal_entry.cal_id = webcal_entry_user.cal_id " .
1790      "AND webcal_entry_user.cal_status IN ('A','W') ";
1791  
1792    if ( $cat_id != '' ) $sql .= "AND webcal_entry_user.cal_category LIKE '$cat_id' ";
1793  
1794    if ( strlen ( $user ) > 0 )
1795      $sql .= "AND (webcal_entry_user.cal_login = '" . $user . "' ";
1796  
1797    if ( $user == $login && strlen ( $user ) > 0 ) {
1798      if ($layers) foreach ($layers as $layer) {
1799        $layeruser = $layer['cal_layeruser'];
1800  
1801        $sql .= "OR webcal_entry_user.cal_login = '" . $layeruser . "' ";
1802  
1803        // while we are parsing the whole layers array, build ourselves
1804        // a new array that will help when we have to check for dups
1805        $layers_byuser["$layeruser"] = $layer['cal_dups'];
1806      }
1807    }
1808    if ( $user == $login && strlen ( $user ) &&
1809      $public_access_default_visible == 'Y' ) {
1810      $sql .= "OR webcal_entry_user.cal_login = '__public__' ";
1811    }
1812    if ( strlen ( $user ) > 0 )
1813      $sql .= ") ";
1814    $sql .= $date_filter;
1815  
1816    // now order the results by time and by entry id.
1817    $sql .= " ORDER BY webcal_entry.cal_time, webcal_entry.cal_id";
1818  
1819    //echo "<strong>SQL:</strong> $sql<br />\n";
1820    
1821    $res = dbi_query ( $sql );
1822    if ( $res ) {
1823      $i = 0;
1824      $checkdup_id = -1;
1825      $first_i_this_id = -1;
1826  
1827      while ( $row = dbi_fetch_row ( $res ) ) {
1828  
1829        if ($row[9] == 'R' || $row[9] == 'D') {
1830          continue;  // don't show rejected/deleted ones
1831        }
1832        $item = array (
1833          "cal_name" => $row[0],
1834          "cal_description" => $row[1],
1835          "cal_date" => $row[2],
1836          "cal_time" => $row[3],
1837          "cal_id"   => $row[4],
1838          "cal_ext_for_id"   => $row[5],
1839          "cal_priority" => $row[6],
1840          "cal_access" => $row[7],
1841          "cal_duration" => $row[8],
1842          "cal_status" => $row[9],
1843          "cal_category" => $row[10],
1844          "cal_login" => $row[11],
1845    "cal_exceptions" => array()
1846          );
1847        if ( $want_repeated && ! empty ( $row[12] ) ) {
1848          $item['cal_type'] = empty ( $row[12] ) ? "" : $row[12];
1849          $item['cal_end'] = empty ( $row[13] ) ? "" : $row[13];
1850          $item['cal_frequency'] = empty ( $row[14] ) ? "" : $row[14];
1851          $item['cal_days'] = empty ( $row[15] ) ? "" : $row[15];
1852        }
1853  
1854        if ( $item['cal_id'] != $checkdup_id ) {
1855          $checkdup_id = $item['cal_id'];
1856          $first_i_this_id = $i;
1857        }
1858  
1859        if ( $item['cal_login'] == $user ) {
1860          // Insert this one before all other ones with this ID.
1861          my_array_splice ( $result, $first_i_this_id, 0, array($item) );
1862          $i++;
1863  
1864          if ($first_i_this_id + 1 < $i) {
1865            // There's another one with the same ID as the one we inserted.
1866            // Check for dup and if so, delete it.
1867            $other_item = $result[$first_i_this_id + 1];
1868            if ($layers_byuser[$other_item['cal_login']] == 'N') {
1869              // NOTE: array_splice requires PHP4
1870              my_array_splice ( $result, $first_i_this_id + 1, 1, "" );
1871              $i--;
1872            }
1873          }
1874        } else {
1875          if ($i == $first_i_this_id
1876            || ( ! empty ( $layers_byuser[$item['cal_login']] ) &&
1877            $layers_byuser[$item['cal_login']] != 'N' ) ) {
1878            // This item either is the first one with its ID, or allows dups.
1879            // Add it to the end of the array.
1880            $result [$i++] = $item;
1881          }
1882        }
1883      }
1884      dbi_free_result ( $res );
1885    }
1886  
1887    // Now load event exceptions and store as array in 'cal_exceptions' field
1888    if ( $want_repeated ) {
1889      for ( $i = 0; $i < count ( $result ); $i++ ) {
1890        if ( ! empty ( $result[$i]['cal_id'] ) ) {
1891          $res = dbi_query ( "SELECT cal_date FROM webcal_entry_repeats_not " .
1892              "WHERE cal_id = " . $result[$i]['cal_id'] );
1893          while ( $row = dbi_fetch_row ( $res ) ) {
1894            $result[$i]['cal_exceptions'][] = $row[0];
1895          }
1896        }
1897      }
1898    }
1899  
1900    return $result;
1901  }
1902  
1903  /**
1904   * Reads all the repeated events for a user.
1905   *
1906   * This is only called once per page request to improve performance. All the
1907   * events get loaded into the array <var>$repeated_events</var> sorted by time of day (not
1908   * date).
1909   *
1910   * This will load all the repeated events into memory.
1911   *
1912   * <b>Notes:</b>
1913   * - To get which events repeat on a specific date, use
1914   *   {@link get_repeating_entries()}.
1915   * - To get all the dates that one specific event repeats on, call
1916   *   {@link get_all_dates()}.
1917   *
1918   * @param string $user   Username
1919   * @param int    $cat_id Category ID to filter on  (May be empty)
1920   * @param string $date   Cutoff date for repeating event endtimes in YYYYMMDD
1921   *                       format (may be empty)
1922   *
1923   * @return Array of repeating events sorted by time of day
1924   *
1925   * @uses query_events
1926   */
1927  function read_repeated_events ( $user, $cat_id = '', $date = ''  ) {
1928    global $login;
1929    global $layers;
1930  
1931    $filter = ($date != '') ? "AND (webcal_entry_repeats.cal_end >= $date OR webcal_entry_repeats.cal_end IS NULL) " : '';
1932    return query_events ( $user, true, $filter, $cat_id );
1933  }
1934  
1935  /**
1936   * Returns all the dates a specific event will fall on accounting for the repeating.
1937   *
1938   * Any event with no end will be assigned one.
1939   *
1940   * @param string $date     Initial date in raw format
1941   * @param string $rpt_type Repeating type as stored in the database
1942   * @param string $end      End date
1943   * @param string $days     Days events occurs on (for weekly)
1944   * @param array  $ex_dates Array of exception dates for this event in YYYYMMDD format
1945   * @param int    $freq     Frequency of repetition
1946   *
1947   * @return array Array of dates (in UNIX time format)
1948   */
1949  function get_all_dates ( $date, $rpt_type, $end, $days, $ex_days, $freq=1 ) {
1950    global $conflict_repeat_months, $days_per_month, $ldays_per_month;
1951    global $ONE_DAY;
1952    //echo "get_all_dates ( $date, '$rpt_type', $end, '$days', [array], $freq ) <br>\n";
1953    $currentdate = floor($date/$ONE_DAY)*$ONE_DAY;
1954    $realend = floor($end/$ONE_DAY)*$ONE_DAY;
1955    $dateYmd = date ( "Ymd", $date );
1956    if ($end=='NULL') {
1957      // Check for $conflict_repeat_months months into future for conflicts
1958      $thismonth = substr($dateYmd, 4, 2);
1959      $thisyear = substr($dateYmd, 0, 4);
1960      $thisday = substr($dateYmd, 6, 2);
1961      $thismonth += $conflict_repeat_months;
1962      if ($thismonth > 12) {
1963        $thisyear++;
1964        $thismonth -= 12;
1965      }
1966      $realend = mktime(3,0,0,$thismonth,$thisday,$thisyear);
1967    }
1968    $ret = array();
1969    $ret[0] = $date;
1970    //do iterative checking here.
1971    //I floored the $realend so I check it against the floored date
1972    if ($rpt_type && $currentdate < $realend) {
1973      $cdate = $date;
1974      if (!$freq) $freq = 1;
1975      $n = 1;
1976      if ($rpt_type == 'daily') {
1977        //we do inclusive counting on end dates.
1978        $cdate += $ONE_DAY * $freq;
1979        while ($cdate <= $realend+$ONE_DAY) {
1980          if ( ! is_exception ( $cdate, $ex_days ) )
1981            $ret[$n++]=$cdate;
1982          $cdate += $ONE_DAY * $freq;
1983        }
1984      } else if ($rpt_type == 'weekly') {
1985        $daysarray = array();
1986        $r=0;
1987        $dow = date("w",$date);
1988        $cdate = $date - ($dow * $ONE_DAY);
1989        for ($i = 0; $i < 7; $i++) {
1990          $isDay = substr($days, $i, 1);
1991          if (strcmp($isDay,"y")==0) {
1992            $daysarray[$r++]=$i * $ONE_DAY;
1993          }
1994        }
1995        //we do inclusive counting on end dates.
1996        while ($cdate <= $realend+$ONE_DAY) {
1997          //add all of the days of the week.
1998          for ($j=0; $j<$r;$j++) {
1999            $td = $cdate + $daysarray[$j];
2000            if ($td >= $date) {
2001              if ( ! is_exception ( $td, $ex_days ) )
2002                $ret[$n++] = $td;
2003            }
2004          }
2005          //skip to the next week in question.
2006          $cdate += ( $ONE_DAY * 7 ) * $freq;
2007        }
2008      } else if ($rpt_type == 'monthlyByDay') {
2009        $dow  = date('w', $date);
2010        $thismonth = substr($dateYmd, 4, 2);
2011        $thisyear  = substr($dateYmd, 0, 4);
2012        $week  = floor(date("d", $date)/7);
2013        $thismonth+=$freq;
2014        //dow1 is the weekday that the 1st of the month falls on
2015        $dow1 = date('w',mktime (3,0,0,$thismonth,1,$thisyear));
2016        $t = $dow - $dow1;
2017        if ($t < 0) $t += 7;
2018        $day = 7*$week + $t + 1;
2019        $cdate = mktime (3,0,0,$thismonth,$day,$thisyear);
2020        while ($cdate <= $realend+$ONE_DAY) {
2021          if ( ! is_exception ( $cdate, $ex_days ) )
2022            $ret[$n++] = $cdate;
2023          $thismonth+=$freq;
2024          //dow1 is the weekday that the 1st of the month falls on
2025          $dow1time = mktime ( 3, 0, 0, $thismonth, 1, $thisyear );
2026          $dow1 = date ( 'w', $dow1time );
2027          $t = $dow - $dow1;
2028          if ($t < 0) $t += 7;
2029          $day = 7*$week + $t + 1;
2030          $cdate = mktime (3,0,0,$thismonth,$day,$thisyear);
2031        }
2032      } else if ($rpt_type == 'monthlyByDayR') {
2033        // by weekday of month reversed (i.e., last Monday of month)
2034        $dow  = date('w', $date);
2035        $thisday = substr($dateYmd, 6, 2);
2036        $thismonth = substr($dateYmd, 4, 2);
2037        $thisyear  = substr($dateYmd, 0, 4);
2038        // get number of days in this month
2039        $daysthismonth = $thisyear % 4 == 0 ? $ldays_per_month[$thismonth] :
2040          $days_per_month[$thismonth];
2041        // how many weekdays like this one remain in the month?
2042        // 0=last one, 1=one more after this one, etc.
2043        $whichWeek = floor ( ( $daysthismonth - $thisday ) / 7 );
2044        // find first repeat date
2045        $thismonth += $freq;
2046        if ( $thismonth > 12 ) {
2047          $thisyear++;
2048          $thismonth -= 12;
2049        }
2050        // get weekday for last day of month
2051        $dowLast += date('w',mktime (3,0,0,$thismonth + 1, -1,$thisyear));
2052        if ( $dowLast >= $dow ) {
2053          // last weekday is in last week of this month
2054          $day = $daysthismonth - ( $dowLast - $dow ) -
2055            ( 7 * $whichWeek );
2056        } else {
2057          // last weekday is NOT in last week of this month
2058          $day = $daysthismonth - ( $dowLast - $dow ) -
2059            ( 7 * ( $whichWeek + 1 ) );
2060        }
2061        $cdate = mktime (3,0,0,$thismonth,$day,$thisyear);
2062        while ($cdate <= $realend+$ONE_DAY) {
2063          if ( ! is_exception ( $cdate, $ex_days ) )
2064            $ret[$n++] = $cdate;
2065          $thismonth += $freq;
2066          if ( $thismonth > 12 ) {
2067            $thisyear++;
2068            $thismonth -= 12;
2069          }
2070          // get weekday for last day of month
2071          $dowLast += date('w',mktime (3,0,0,$thismonth + 1, -1,$thisyear));
2072          if ( $dowLast >= $dow ) {
2073            // last weekday is in last week of this month
2074            $day = $daysthismonth - ( $dowLast - $dow ) -
2075              ( 7 * $whichWeek );
2076          } else {
2077            // last weekday is NOT in last week of this month
2078            $day = $daysthismonth - ( $dowLast - $dow ) -
2079              ( 7 * ( $whichWeek + 1 ) );
2080          }
2081          $cdate = mktime (3,0,0,$thismonth,$day,$thisyear);
2082        }
2083      } else if ($rpt_type == 'monthlyByDate') {
2084        $thismonth = substr($dateYmd, 4, 2);
2085        $thisyear  = substr($dateYmd, 0, 4);
2086        $thisday   = substr($dateYmd, 6, 2);
2087        $hour      = date('H',$date);
2088        $minute    = date('i',$date);
2089  
2090        $thismonth += $freq;
2091        $cdate = mktime (3,0,0,$thismonth,$thisday,$thisyear);
2092        while ($cdate <= $realend+$ONE_DAY) {
2093          if ( ! is_exception ( $cdate, $ex_days ) )
2094            $ret[$n++] = $cdate;
2095          $thismonth += $freq;
2096          $cdate = mktime (3,0,0,$thismonth,$thisday,$thisyear);
2097        }
2098      } else if ($rpt_type == 'yearly') {
2099        $thismonth = substr($dateYmd, 4, 2);
2100        $thisyear  = substr($dateYmd, 0, 4);
2101        $thisday   = substr($dateYmd, 6, 2);
2102        $hour      = date('H',$date);
2103        $minute    = date('i',$date);
2104  
2105        $thisyear += $freq;
2106        $cdate = mktime (3,0,0,$thismonth,$thisday,$thisyear);
2107        while ($cdate <= $realend+$ONE_DAY) {
2108          if ( ! is_exception ( $cdate, $ex_days ) )
2109            $ret[$n++] = $cdate;
2110          $thisyear += $freq;
2111          $cdate = mktime (3,0,0,$thismonth,$thisday,$thisyear);
2112        }
2113      }
2114    }
2115    return $ret;
2116  }
2117  
2118  /**
2119   * Gets all the repeating events for the specified date.
2120   *
2121   * <b>Note:</b>
2122   * The global variable <var>$repeated_events</var> needs to be
2123   * set by calling {@link read_repeated_events()} first.
2124   *
2125   * @param string $user           Username
2126   * @param string $date           Date to get events for in YYYYMMDD format
2127   * @param bool   $get_unapproved Include unapproved events in results?
2128   *
2129   * @return mixed The query result resource on queries (which can then be
2130   *               passed to {@link dbi_fetch_row()} to obtain the results), or
2131   *               true/false on insert or delete queries.
2132   *
2133   * @global array Array of repeating events retreived using {@link read_repeated_events()}
2134   */
2135  function get_repeating_entries ( $user, $dateYmd, $get_unapproved=true ) {
2136    global $repeated_events;
2137    $n = 0;
2138    $ret = array ();
2139    //echo count($repeated_events)."<br />\n";
2140    for ( $i = 0; $i < count ( $repeated_events ); $i++ ) {
2141      if ( $repeated_events[$i]['cal_status'] == 'A' || $get_unapproved ) {
2142        if ( repeated_event_matches_date ( $repeated_events[$i], $dateYmd ) ) {
2143          // make sure this is not an exception date...
2144          $unixtime = date_to_epoch ( $dateYmd );
2145          if ( ! is_exception ( $unixtime, $repeated_events[$i]['cal_exceptions'] ) )
2146            $ret[$n++] = $repeated_events[$i];
2147        }
2148      }
2149    }
2150    return $ret;
2151  }
2152  
2153  /**
2154   * Determines whether the event passed in will fall on the date passed.
2155   *
2156   * @param array  $event   The event as an array
2157   * @param string $dateYmd Date to check in YYYYMMDD format
2158   *
2159   * @return bool Does <var>$event</var> occur on <var>$dateYmd</var>?
2160   */
2161  function repeated_event_matches_date($event,$dateYmd) {
2162    global $days_per_month, $ldays_per_month, $ONE_DAY;
2163    // only repeat after the beginning, and if there is an end
2164    // before the end
2165    $date = date_to_epoch ( $dateYmd );
2166    $thisyear = substr($dateYmd, 0, 4);
2167    $start = date_to_epoch ( $event['cal_date'] );
2168    $end   = date_to_epoch ( $event['cal_end'] );
2169    $freq = $event['cal_frequency'];
2170    $thismonth = substr($dateYmd, 4, 2);
2171    if ($event['cal_end'] && $dateYmd > date("Ymd",$end) )
2172      return false;
2173    if ( $dateYmd <= date("Ymd",$start) )
2174      return false;
2175    $id = $event['cal_id'];
2176  
2177    if ($event['cal_type'] == 'daily') {
2178      if ( (floor(($date - $start)/$ONE_DAY)%$freq) )
2179        return false;
2180      return true;
2181    } else if ($event['cal_type'] == 'weekly') {
2182      $dow  = date("w", $date);
2183      $dow1 = date("w", $start);
2184      $isDay = substr($event['cal_days'], $dow, 1);
2185      $wstart = $start - ($dow1 * $ONE_DAY);
2186      if (floor(($date - $wstart)/604800)%$freq)
2187        return false;
2188      return (strcmp($isDay,"y") == 0);
2189    } else if ($event['cal_type'] == 'monthlyByDay') {
2190      $dowS = date("w", $start);
2191      $dow  = date("w", $date);
2192      // do this comparison first in hopes of best performance
2193      if ( $dowS != $dow )
2194        return false;
2195      $mthS = date("m", $start);
2196      $yrS  = date("Y", $start);
2197      $dayS  = floor(date("d", $start));
2198      $dowS1 = ( date ( "w", $start - ( $ONE_DAY * ( $dayS - 1 ) ) ) + 35 ) % 7;
2199      $days_in_first_weekS = ( 7 - $dowS1 ) % 7;
2200      $whichWeekS = floor ( ( $dayS - $days_in_first_weekS ) / 7 );
2201      if ( $dowS >= $dowS1 && $days_in_first_weekS )
2202        $whichWeekS++;
2203      //echo "dayS=$dayS;dowS=$dowS;dowS1=$dowS1;wWS=$whichWeekS<br />\n";
2204      $mth  = date("m", $date);
2205      $yr   = date("Y", $date);
2206      $day  = date("d", $date);
2207      $dow1 = ( date ( "w", $date - ( $ONE_DAY * ( $day - 1 ) ) ) + 35 ) % 7;
2208      $days_in_first_week = ( 7 - $dow1 ) % 7;
2209      $whichWeek = floor ( ( $day - $days_in_first_week ) / 7 );
2210      if ( $dow >= $dow1 && $days_in_first_week )
2211        $whichWeek++;
2212      //echo "day=$day;dow=$dow;dow1=$dow1;wW=$whichWeek<br />\n";
2213  
2214      if ((($yr - $yrS)*12 + $mth - $mthS) % $freq)
2215        return false;
2216  
2217      return ( $whichWeek == $whichWeekS );
2218    } else if ($event['cal_type'] == 'monthlyByDayR') {
2219      $dowS = date("w", $start);
2220      $dow  = date("w", $date);
2221      // do this comparison first in hopes of best performance
2222      if ( $dowS != $dow )
2223        return false;
2224  
2225      $dayS = ceil(date("d", $start));
2226      $mthS = ceil(date("m", $start));
2227      $yrS  = date("Y", $start);
2228      $daysthismonthS = $mthS % 4 == 0 ? $ldays_per_month[$mthS] :
2229        $days_per_month[$mthS];
2230      $whichWeekS = floor ( ( $daysthismonthS - $dayS ) / 7 );
2231  
2232      $day = ceil(date("d", $date));
2233      $mth = ceil(date("m", $date));
2234      $yr  = date("Y", $date);
2235      $daysthismonth = $mth % 4 == 0 ? $ldays_per_month[$mth] :
2236        $days_per_month[$mth];
2237      $whichWeek = floor ( ( $daysthismonth - $day ) / 7 );
2238  
2239      if ((($yr - $yrS)*12 + $mth - $mthS) % $freq)
2240        return false;
2241  
2242      return ( $whichWeekS == $whichWeek );
2243    } else if ($event['cal_type'] == 'monthlyByDate') {
2244      $mthS = date("m", $start);
2245      $yrS  = date("Y", $start);
2246  
2247      $mth  = date("m", $date);
2248      $yr   = date("Y", $date);
2249  
2250      if ((($yr - $yrS)*12 + $mth - $mthS) % $freq)
2251        return false;
2252  
2253      return (date("d", $date) == date("d", $start));
2254    }
2255    else if ($event['cal_type'] == 'yearly') {
2256      $yrS = date("Y", $start);
2257      $yr  = date("Y", $date);
2258  
2259      if (($yr - $yrS)%$freq)
2260        return false;
2261  
2262      return (date("dm", $date) == date("dm", $start));
2263    } else {
2264      // unknown repeat type
2265      return false;
2266    }
2267    return false;
2268  }
2269  
2270  /**
2271   * Converts a date to a timestamp.
2272   * 
2273   * @param string $d Date in YYYYMMDD format
2274   *
2275   * @return int Timestamp representing 3:00 (or 4:00 if during Daylight Saving
2276   *             Time) in the morning on that day
2277   */
2278  function date_to_epoch ( $d ) {
2279    if ( $d == 0 )
2280      return 0;
2281    $T = mktime ( 3, 0, 0, substr ( $d, 4, 2 ), substr ( $d, 6, 2 ), substr ( $d, 0, 4 ) );
2282    $lt = localtime($T);
2283    if ($lt[8]) {
2284      return mktime ( 4, 0, 0, substr ( $d, 4, 2 ), substr ( $d, 6, 2 ), substr ( $d, 0, 4 ) );
2285    } else {
2286      return $T;
2287    }
2288  }
2289  
2290  /**
2291   * Checks if a date is an exception for an event.
2292   *
2293   * @param string $date   Date in YYYYMMDD format
2294   * @param array  $exdays Array of dates in YYYYMMDD format
2295   *
2296   * @ignore
2297   */
2298  function is_exception ( $date, $ex_days ) {
2299    $size = count ( $ex_days );
2300    $count = 0;
2301    $date = date ( "Ymd", $date );
2302    //echo "Exception $date check.. count is $size <br />\n";
2303    while ( $count < $size ) {
2304      //echo "Exception date: $ex_days[$count] <br />\n";
2305      if ( $date == $ex_days[$count++] )
2306        return true;
2307    }
2308    return false;
2309  }
2310  
2311  /**
2312   * Gets the Sunday of the week that the specified date is in.
2313   *
2314   * If the date specified is a Sunday, then that date is returned.
2315   *
2316   * @param int $year  Year
2317   * @param int $month Month (1-12)
2318   * @param int $day   Day of the month
2319   *
2320   * @return int The date (in UNIX timestamp format)
2321   *
2322   * @see get_monday_before
2323   */
2324  function get_sunday_before ( $year, $month, $day ) {
2325    $weekday = date ( "w", mktime ( 3, 0, 0, $month, $day, $year ) );
2326    $newdate = mktime ( 3, 0, 0, $month, $day - $weekday, $year );
2327    return $newdate;
2328  }
2329  
2330  /** 
2331   * Gets the Monday of the week that the specified date is in.
2332   *
2333   * If the date specified is a Monday, then that date is returned.
2334   *
2335   * @param int $year  Year
2336   * @param int $month Month (1-12)
2337   * @param int $day   Day of the month
2338   *
2339   * @return int The date (in UNIX timestamp format)
2340   *
2341   * @see get_sunday_before
2342   */
2343  function get_monday_before ( $year, $month, $day ) {
2344    $weekday = date ( "w", mktime ( 3, 0, 0, $month, $day, $year ) );
2345    if ( $weekday == 0 )
2346      return mktime ( 3, 0, 0, $month, $day - 6, $year );
2347    if ( $weekday == 1 )
2348      return mktime ( 3, 0, 0, $month, $day, $year );
2349    return mktime ( 3, 0, 0, $month, $day - ( $weekday - 1 ), $year );
2350  }
2351  
2352  /**
2353   * Returns the week number for specified date.
2354   * 
2355   * Depends on week numbering settings.
2356   *
2357   * @param int $date Date in UNIX timestamp format
2358   *
2359   * @return string The week number of the specified date
2360   */
2361  function week_number ( $date ) {
2362    $tmp = getdate($date);
2363    $iso = gregorianToISO($tmp['mday'], $tmp['mon'], $tmp['year']);
2364    $parts = explode('-',$iso);
2365    $week_number = intval($parts[1]);
2366    return sprintf("%02d",$week_number);
2367  }
2368  
2369  /**
2370   * Generates the HTML for an add/edit/delete icon.
2371   *
2372   * This function is not yet used.  Some of the places that will call it have to
2373   * be updated to also get the event owner so we know if the current user has
2374   * access to edit and delete.
2375   *
2376   * @param int  $id         Event ID
2377   * @param bool $can_edit   Can this user edit this event?
2378   * @param bool $can_delete Can this user delete this event?
2379   *
2380   * @return HTML for add/edit/delete icon.
2381   *
2382   * @ignore
2383   */
2384  function icon_text ( $id, $can_edit, $can_delete ) {
2385    global $readonly, $is_admin;
2386    $ret = "<a title=\"" . 
2387    translate("View this entry") . "\" href=\"view_entry.php?id=$id\"><img src=\"view.gif\" alt=\"" . 
2388    translate("View this entry") . "\" style=\"border-width:0px; width:10px; height:10px;\" /></a>";
2389    if ( $can_edit && $readonly == "N" )
2390      $ret .= "<a title=\"" . 
2391    translate("Edit entry") . "\" href=\"edit_entry.php?id=$id\"><img src=\"edit.gif\" alt=\"" . 
2392    translate("Edit entry") . "\" style=\"border-width:0px; width:10px; height:10px;\" /></a>";
2393    if ( $can_delete && ( $readonly == "N" || $is_admin ) )
2394      $ret .= "<a title=\"" . 
2395        translate("Delete entry") . "\" href=\"del_entry.php?id=$id\" onclick=\"return confirm('" .
2396    translate("Are you sure you want to delete this entry?") . "\\n\\n" . 
2397    translate("This will delete this entry for all users.") . "');\"><img src=\"delete.gif\" alt=\"" . 
2398    translate("Delete entry") . "\" style=\"border-width:0px; width:10px; height:10px;\" /></a>";
2399    return $ret;
2400  }
2401  
2402  /**
2403   * Prints all the calendar entries for the specified user for the specified date.
2404   *
2405   * If we are displaying data from someone other than
2406   * the logged in user, then check the access permission of the entry.
2407   *
2408   * @param string $date Date in YYYYMMDD format
2409   * @param string $user Username
2410   * @param bool   $ssi  Is this being called from week_ssi.php?
2411   */
2412  function print_date_entries ( $date, $user, $ssi ) {
2413    global $events, $readonly, $is_admin, $login,
2414      $public_access, $public_access_can_add, $cat_id;
2415    $cnt = 0;
2416    $get_unapproved = ( $GLOBALS["DISPLAY_UNAPPROVED"] == "Y" );
2417    // public access events always must be approved before being displayed
2418    if ( $user == "__public__" )
2419      $get_unapproved = false;
2420  
2421    $year = substr ( $date, 0, 4 );
2422    $month = substr ( $date, 4, 2 );
2423    $day = substr ( $date, 6, 2 );
2424    $dateu = mktime ( 3, 0, 0, $month, $day, $year );
2425    $can_add = ( $readonly == "N" || $is_admin );
2426    if ( $public_access == "Y" && $public_access_can_add != "Y" &&
2427      $login == "__public__" )
2428      $can_add = false;
2429    if ( $readonly == 'Y' )
2430      $can_add = false;
2431    if ( ! $ssi && $can_add ) {
2432      print "<a title=\"" .
2433        translate("New Entry") . "\" href=\"edit_entry.php?";
2434      if ( strcmp ( $user, $GLOBALS["login"] ) )
2435        print "user=$user&amp;";
2436      if ( ! empty ( $cat_id ) )
2437        print "cat_id=$cat_id&amp;";
2438      print "date=$date\"><img src=\"new.gif\" alt=\"" .
2439        translate("New Entry") . "\" class=\"new\" /></a>";
2440      $cnt++;
2441    }
2442    if ( ! $ssi ) {
2443      echo "<a class=\"dayofmonth\" href=\"day.php?";
2444      if ( strcmp ( $user, $GLOBALS["login"] ) )
2445        echo "user=$user&amp;";
2446      if ( ! empty ( $cat_id ) )
2447        echo "cat_id=$cat_id&amp;";
2448      echo "date=$date\">$day</a>";
2449      if ( $GLOBALS["DISPLAY_WEEKNUMBER"] == "Y" &&
2450        date ( "w", $dateu ) == $GLOBALS["WEEK_START"] ) {
2451        echo "&nbsp;<a title=\"" .
2452          translate("Week") . "&nbsp;" . week_number ( $dateu ) . "\" href=\"week.php?date=$date";
2453        if ( strcmp ( $user, $GLOBALS["login"] ) )
2454          echo "&amp;user=$user";
2455        if ( ! empty ( $cat_id ) )
2456        echo "&amp;cat_id=$cat_id";
2457         echo "\" class=\"weeknumber\">";
2458        echo "(" .
2459          translate("Week") . "&nbsp;" . week_number ( $dateu ) . ")</a>";
2460      }
2461      print "<br />\n";
2462      $cnt++;
2463    }
2464    
2465    // get all the repeating events for this date and store in array $rep
2466    $rep = get_repeating_entries ( $user, $date, $get_unapproved );
2467    $cur_rep = 0;
2468  
2469    // get all the non-repeating events for this date and store in $ev
2470    $ev = get_entries ( $user, $date, $get_unapproved );
2471  
2472    for ( $i = 0; $i < count ( $ev ); $i++ ) {
2473      // print out any repeating events that are before this one...
2474      while ( $cur_rep < count ( $rep ) &&
2475        $rep[$cur_rep]['cal_time'] < $ev[$i]['cal_time'] ) {
2476        if ( $get_unapproved || $rep[$cur_rep]['cal_status'] == 'A' ) {
2477          if ( ! empty ( $rep[$cur_rep]['cal_ext_for_id'] ) ) {
2478            $viewid = $rep[$cur_rep]['cal_ext_for_id'];
2479            $viewname = $rep[$cur_rep]['cal_name'] . " (" .
2480              translate("cont.") . ")";
2481          } else {
2482            $viewid = $rep[$cur_rep]['cal_id'];
2483            $viewname = $rep[$cur_rep]['cal_name'];
2484          }
2485          print_entry ( $viewid,
2486            $date, $rep[$cur_rep]['cal_time'], $rep[$cur_rep]['cal_duration'],
2487            $viewname, $rep[$cur_rep]['cal_description'],
2488            $rep[$cur_rep]['cal_status'], $rep[$cur_rep]['cal_priority'],
2489            $rep[$cur_rep]['cal_access'], $rep[$cur_rep]['cal_login'],
2490            $rep[$cur_rep]['cal_category'] );
2491          $cnt++;
2492        }
2493        $cur_rep++;
2494      }
2495      if ( $get_unapproved || $ev[$i]['cal_status'] == 'A' ) {
2496        if ( ! empty ( $ev[$i]['cal_ext_for_id'] ) ) {
2497          $viewid = $ev[$i]['cal_ext_for_id'];
2498          $viewname = $ev[$i]['cal_name'] . " (" .
2499            translate("cont.") . ")";
2500        } else {
2501          $viewid = $ev[$i]['cal_id'];
2502          $viewname = $ev[$i]['cal_name'];
2503        }
2504        print_entry ( $viewid,
2505          $date, $ev[$i]['cal_time'], $ev[$i]['cal_duration'],
2506          $viewname, $ev[$i]['cal_description'],
2507          $ev[$i]['cal_status'], $ev[$i]['cal_priority'],
2508          $ev[$i]['cal_access'], $ev[$i]['cal_login'],
2509          $ev[$i]['cal_category'] );
2510        $cnt++;
2511      }
2512    }
2513    // print out any remaining repeating events
2514    while ( $cur_rep < count ( $rep ) ) {
2515      if ( $get_unapproved || $rep[$cur_rep]['cal_status'] == 'A' ) {
2516        if ( ! empty ( $rep[$cur_rep]['cal_ext_for_id'] ) ) {
2517          $viewid = $rep[$cur_rep]['cal_ext_for_id'];
2518          $viewname = $rep[$cur_rep]['cal_name'] . " (" .
2519            translate("cont.") . ")";
2520        } else {
2521          $viewid = $rep[$cur_rep]['cal_id'];
2522          $viewname = $rep[$cur_rep]['cal_name'];
2523        }
2524        print_entry ( $viewid,
2525          $date, $rep[$cur_rep]['cal_time'], $rep[$cur_rep]['cal_duration'],
2526          $viewname, $rep[$cur_rep]['cal_description'],
2527          $rep[$cur_rep]['cal_status'], $rep[$cur_rep]['cal_priority'],
2528          $rep[$cur_rep]['cal_access'], $rep[$cur_rep]['cal_login'],
2529          $rep[$cur_rep]['cal_category'] );
2530        $cnt++;
2531      }
2532      $cur_rep++;
2533    }
2534    if ( $cnt == 0 )
2535      echo "&nbsp;"; // so the table cell has at least something
2536  }
2537  
2538  /**
2539   * Checks to see if two events overlap.
2540   *
2541   * @param string $time1 Time 1 in HHMMSS format
2542   * @param int    $duration1 Duration 1 in minutes
2543   * @param string $time2 Time 2 in HHMMSS format
2544   * @param int    $duration2 Duration 2 in minutes
2545   *
2546   * @return bool True if the two times overlap, false if they do not
2547   */
2548  function times_overlap ( $time1, $duration1, $time2, $duration2 ) {
2549    //echo "times_overlap ( $time1, $duration1, $time2, $duration2 )<br />\n";
2550    $hour1 = (int) ( $time1 / 10000 );
2551    $min1 = ( $time1 / 100 ) % 100;
2552    $hour2 = (int) ( $time2 / 10000 );
2553    $min2 = ( $time2 / 100 ) % 100;
2554    // convert to minutes since midnight
2555    // remove 1 minute from duration so 9AM-10AM will not conflict with 10AM-11AM
2556    if ( $duration1 > 0 )
2557      $duration1 -= 1;
2558    if ( $duration2 > 0 )
2559      $duration2 -= 1;
2560    $tmins1start = $hour1 * 60 + $min1;
2561    $tmins1end = $tmins1start + $duration1;
2562    $tmins2start = $hour2 * 60 + $min2;
2563    $tmins2end = $tmins2start + $duration2;
2564    //echo "tmins1start=$tmins1start, tmins1end=$tmins1end, tmins2start=$tmins2start, tmins2end=$tmins2end<br />\n";
2565    if ( ( $tmins1start >= $tmins2end ) || ( $tmins2start >= $tmins1end ) )
2566      return false;
2567    return true;
2568  }
2569  
2570  /**
2571   * Checks for conflicts.
2572   *
2573   * Find overlaps between an array of dates and the other dates in the database.
2574   *
2575   * Limits on number of appointments: if enabled in System Settings
2576   * (<var>$limit_appts</var> global variable), too many appointments can also
2577   * generate a scheduling conflict.
2578   * 
2579   * @todo Update this to handle exceptions to repeating events
2580   *
2581   * @param array  $dates        Array of dates in YYYYMMDD format that is
2582   *                             checked for overlaps.
2583   * @param int    $duration     Event duration in minutes
2584   * @param int    $hour         Hour of event (0-23)
2585   * @param int    $minute       Minute of the event (0-59)
2586   * @param array  $participants Array of users whose calendars are to be checked
2587   * @param string $login        The current user name
2588   * @param int    $id           Current event id (this keeps overlaps from
2589   *                             wrongly checking an event against itself)
2590   *
2591   * @return Empty string for no conflicts or return the HTML of the
2592   *         conflicts when one or more are found.
2593   */
2594  function check_for_conflicts ( $dates, $duration, $hour, $minute,
2595    $participants, $login, $id ) {
2596    global $single_user_login, $single_user;
2597    global $repeated_events, $limit_appts, $limit_appts_number;
2598    if (!count($dates)) return false;
2599  
2600    $evtcnt = array ();
2601  
2602    $sql = "SELECT distinct webcal_entry_user.cal_login, webcal_entry.cal_time," .
2603      "webcal_entry.cal_duration, webcal_entry.cal_name, " .
2604      "webcal_entry.cal_id, webcal_entry.cal_ext_for_id, " .
2605      "webcal_entry.cal_access, " .
2606      "webcal_entry_user.cal_status, webcal_entry.cal_date " .
2607      "FROM webcal_entry, webcal_entry_user " .
2608      "WHERE webcal_entry.cal_id = webcal_entry_user.cal_id " .
2609      "AND (";
2610    for ($x = 0; $x < count($dates); $x++) {
2611      if ($x != 0) $sql .= " OR ";
2612      $sql.="webcal_entry.cal_date = " . date ( "Ymd", $dates[$x] );
2613    }
2614    $sql .=  ") AND webcal_entry.cal_time >= 0 " .
2615      "AND webcal_entry_user.cal_status IN ('A','W') AND ( ";
2616    if ( $single_user == "Y" ) {
2617       $participants[0] = $single_user_login;
2618    } else if ( strlen ( $participants[0] ) == 0 ) {
2619       // likely called from a form with 1 user
2620       $participants[0] = $login;
2621    }
2622    for ( $i = 0; $i < count ( $participants ); $i++ ) {
2623      if ( $i > 0 )
2624        $sql .= " OR ";
2625      $sql .= " webcal_entry_user.cal_login = '" . $participants[$i] . "'";
2626    }
2627    $sql .= " )";
2628    // make sure we don't get something past the end date of the
2629    // event we are saving.
2630    //echo "SQL: $sql<br />\n";
2631    $conflicts = "";
2632    $res = dbi_query ( $sql );
2633    $found = array();
2634    $count = 0;
2635    if ( $res ) {
2636      $time1 = sprintf ( "%d%02d00", $hour, $minute );
2637      $duration1 = sprintf ( "%d", $duration );
2638      while ( $row = dbi_fetch_row ( $res ) ) {
2639        //Add to an array to see if it has been found already for the next part.
2640        $found[$count++] = $row[4];
2641        // see if either event overlaps one another
2642        if ( $row[4] != $id && ( empty ( $row[5] ) || $row[5] != $id ) ) {
2643          $time2 = $row[1];
2644          $duration2 = $row[2];
2645          $cntkey = $row[0] . "-" . $row[8];
2646          if ( empty ( $evtcnt[$cntkey] ) )
2647            $evtcnt[$cntkey] = 0;
2648          else
2649            $evtcnt[$cntkey]++;
2650          $over_limit = 0;
2651          if ( $limit_appts == "Y" && $limit_appts_number > 0
2652            && $evtcnt[$cntkey] >= $limit_appts_number ) {
2653            $over_limit = 1;
2654          }
2655          if ( $over_limit ||
2656            times_overlap ( $time1, $duration1, $time2, $duration2 ) ) {
2657            $conflicts .= "<li>";
2658            if ( $single_user != "Y" )
2659              $conflicts .= "$row[0]: ";
2660            if ( $row[6] == 'R' && $row[0] != $login )
2661              $conflicts .=  "(" . translate("Private") . ")";
2662            else {
2663              $conflicts .=  "<a href=\"view_entry.php?id=$row[4]";
2664              if ( $row[0] != $login )
2665                $conflicts .= "&amp;user=$row[0]";
2666              $conflicts .= "\">$row[3]</a>";
2667            }
2668            if ( $duration2 == ( 24 * 60 ) ) {
2669              $conflicts .= " (" . translate("All day event") . ")";
2670            } else {
2671              $conflicts .= " (" . display_time ( $time2 );
2672              if ( $duration2 > 0 )
2673                $conflicts .= "-" .
2674                  display_time ( add_duration ( $time2, $duration2 ) );
2675              $conflicts .= ")";
2676            }
2677            $conflicts .= " on " . date_to_str( $row[8] );
2678            if ( $over_limit ) {
2679              $tmp = translate ( "exceeds limit of XXX events per day" );
2680              $tmp = str_replace ( "XXX", $limit_appts_number, $tmp );
2681              $conflicts .= " (" . $tmp . ")";
2682            }
2683            $conflicts .= "</li>\n";
2684          }
2685        }
2686      }
2687      dbi_free_result ( $res );
2688    } else {
2689      echo translate("Database error") . ": " . dbi_error (); exit;
2690    }
2691    
2692    //echo "<br />\nhello";
2693    for ($q=0;$q<count($participants);$q++) {
2694      $time1 = sprintf ( "%d%02d00", $hour, $minute );
2695      $duration1 = sprintf ( "%d", $duration );
2696      //This date filter is not necessary for functional reasons, but it eliminates some of the
2697      //events that couldn't possibly match.  This could be made much more complex to put more
2698      //of the searching work onto the database server, or it could be dropped all together to put
2699      //the searching work onto the client.
2700      $date_filter  = "AND (webcal_entry.cal_date <= " . date("Ymd",$dates[count($dates)-1]);
2701      $date_filter .= " AND (webcal_entry_repeats.cal_end IS NULL OR webcal_entry_repeats.cal_end >= " . date("Ymd",$dates[0]) . "))";
2702      //Read repeated events for the participants only once for a participant for
2703      //for performance reasons.
2704      $repeated_events=query_events($participants[$q],true,$date_filter);
2705      //for ($dd=0; $dd<count($repeated_events); $dd++) {
2706      //  echo $repeated_events[$dd]['cal_id'] . "<br />";
2707      //}
2708      for ($i=0; $i < count($dates); $i++) {
2709        $dateYmd = date ( "Ymd", $dates[$i] );
2710        $list = get_repeating_entries($participants[$q],$dateYmd);
2711        $thisyear = substr($dateYmd, 0, 4);
2712        $thismonth = substr($dateYmd, 4, 2);
2713        for ($j=0; $j < count($list);$j++) {
2714          //okay we've narrowed it down to a day, now I just gotta check the time...
2715          //I hope this is right...
2716          $row = $list[$j];
2717          if ( $row['cal_id'] != $id && ( empty ( $row['cal_ext_for_id'] ) || 
2718            $row['cal_ext_for_id'] != $id ) ) {
2719            $time2 = $row['cal_time'];
2720            $duration2 = $row['cal_duration'];
2721            if ( times_overlap ( $time1, $duration1, $time2, $duration2 ) ) {
2722              $conflicts .= "<li>";
2723              if ( $single_user != "Y" )
2724                $conflicts .= $row['cal_login'] . ": ";
2725              if ( $row['cal_access'] == 'R' && $row['cal_login'] != $login )
2726                $conflicts .=  "(" . translate("Private") . ")";
2727              else {
2728                $conflicts .=  "<a href=\"view_entry.php?id=" . $row['cal_id'];
2729                if ( ! empty ( $user ) && $user != $login )
2730                  $conflicts .= "&amp;user=$user";
2731                $conflicts .= "\">" . $row['cal_name'] . "</a>";
2732              }
2733              $conflicts .= " (" . display_time ( $time2 );
2734              if ( $duration2 > 0 )
2735                $conflicts .= "-" .
2736                  display_time ( add_duration ( $time2, $duration2 ) );
2737              $conflicts .= ")";
2738              $conflicts .= " on " . date("l, F j, Y", $dates[$i]);
2739              $conflicts .= "</li>\n";
2740            }
2741          }
2742        }
2743      }
2744    }
2745     
2746    return $conflicts;
2747  }
2748  
2749  /**
2750   * Converts a time format HHMMSS (like 130000 for 1PM) into number of minutes past midnight.
2751   *
2752   * @param string $time Input time in HHMMSS format
2753   *
2754   * @return int The number of minutes since midnight
2755   */
2756  function time_to_minutes ( $time ) {
2757    $h = (int) ( $time / 10000 );
2758    $m = (int) ( $time / 100 ) % 100;
2759    $num = $h * 60 + $m;
2760    return $num;
2761  }
2762  
2763  /**
2764   * Calculates which row/slot this time represents.
2765   *
2766   * This is used in day and week views where hours of the time are separeted
2767   * into different cells in a table.
2768   *
2769   * <b>Note:</b> the global variable <var>$TIME_SLOTS</var> is used to determine
2770   * how many time slots there are and how many minutes each is.  This variable
2771   * is defined user preferences (or defaulted to admin system settings).
2772   *
2773   * @param string $time       Input time in HHMMSS format
2774   * @param bool   $round_down Should we change 1100 to 1059?
2775   *                           (This will make sure a 10AM-100AM appointment just
2776   *                           shows up in the 10AM slow and not in the 11AM slot
2777   *                           also.)
2778   *
2779   * @return int The time slot index
2780   */
2781  function calc_time_slot ( $time, $round_down = false ) {
2782    global $TIME_SLOTS, $TZ_OFFSET;
2783  
2784    $interval = ( 24 * 60 ) / $TIME_SLOTS;
2785    $mins_since_midnight = time_to_minutes ( $time );
2786    $ret = (int) ( $mins_since_midnight / $interval );
2787    if ( $round_down ) {
2788      if ( $ret * $interval == $mins_since_midnight )
2789        $ret--;
2790    }
2791    //echo "$mins_since_midnight / $interval = $ret <br />\n";
2792    if ( $ret > $TIME_SLOTS )
2793      $ret = $TIME_SLOTS;
2794  
2795    //echo "<br />\ncalc_time_slot($time) = $ret <br />\nTIME_SLOTS = $TIME_SLOTS<br />\n";
2796    return $ret;
2797  }
2798  
2799  /**
2800   * Generates the HTML for an icon to add a new event.
2801   *
2802   * @param string $date   Date for new event in YYYYMMDD format
2803   * @param int    $hour   Hour of day (0-23)
2804   * @param int    $minute Minute of the hour (0-59)
2805   * @param string $user   Participant to initially select for new event
2806   *
2807   * @return string The HTML for the add event icon
2808   */
2809  function html_for_add_icon ( $date=0,$hour="", $minute="", $user="" ) {
2810    global $TZ_OFFSET;
2811    global $login, $readonly, $cat_id;
2812    $u_url = '';
2813  
2814    if ( $readonly == 'Y' )
2815      return '';
2816  
2817    if ( $minute < 0 ) {
2818     $minute = abs($minute);
2819     $hour = $hour -1;
2820    }
2821    if ( ! empty ( $user ) && $user != $login )
2822      $u_url = "user=$user&amp;";
2823    if ( isset ( $hour ) && $hour != NULL )
2824      $hour += $TZ_OFFSET;
2825    return "<a title=\"" . 
2826   translate("New Entry") . "\" href=\"edit_entry.php?" . $u_url .
2827      "date=$date" . ( isset ( $hour ) && $hour != NULL && $hour >= 0 ? "&amp;hour=$hour" : ""  ) .
2828      ( $minute > 0 ? "&amp;minute=$minute" : "" ) .
2829      ( empty ( $user ) ? "" :  "&amp;defusers=$user" ) .
2830      ( empty ( $cat_id ) ? "" :  "&amp;cat_id=$cat_id" ) .
2831      "\"><img src=\"new.gif\" class=\"new\" alt=\"" . 
2832   translate("New Entry") . "\" /></a>\n";
2833  }
2834  
2835  /**
2836   * Generates the HTML for an event to be viewed in the week-at-glance (week.php).
2837   *
2838   * The HTML will be stored in an array (global variable $hour_arr)
2839   * indexed on the event's starting hour.
2840   *
2841   * @param int    $id             Event id
2842   * @param string $date           Date of event in YYYYMMDD format
2843   * @param string $time           Time of event in HHMM format
2844   * @param string $name           Brief description of event
2845   * @param string $description    Full description of event
2846   * @param string $status         Status of event ('A', 'W')
2847   * @param int    $pri            Priority of event
2848   * @param string $access         Access to event by others ('P', 'R')
2849   * @param int    $duration       Duration of event in minutes
2850   * @param string $event_owner    User who created event
2851   * @param int    $event_category Category id for event
2852   */
2853  function html_for_event_week_at_a_glance ( $id, $date, $time,
2854    $name, $description, $status, $pri, $access, $duration, $event_owner,
2855    $event_category=-1 ) {
2856    global $first_slot, $last_slot, $hour_arr, $rowspan_arr, $rowspan,
2857      $eventinfo, $login, $user;
2858    static $key = 0;
2859    global $DISPLAY_ICONS, $PHP_SELF, $TIME_SLOTS;
2860    global $layers;
2861  
2862    $popupid = "eventinfo-day-$id-$key";
2863    $key++;
2864    
2865    // Figure out which time slot it goes in.
2866    if ( $time >= 0 && $duration != ( 24 * 60 ) ) {
2867      $ind = calc_time_slot ( $time );
2868      if ( $ind < $first_slot )
2869        $first_slot = $ind;
2870      if ( $ind > $last_slot )
2871        $last_slot = $ind;
2872    } else
2873      $ind = 9999;
2874  
2875    if ( $login != $event_owner && strlen ( $event_owner ) ) {
2876      $class = "layerentry";
2877    } else {
2878      $class = "entry";
2879      if ( $status == "W" ) $class = "unapprovedentry";
2880    }
2881    // if we are looking at a view, then always use "entry"
2882    if ( strstr ( $PHP_SELF, "view_m.php" ) ||
2883      strstr ( $PHP_SELF, "view_w.php" ) ||
2884      strstr ( $PHP_SELF, "view_v.php" ) ||
2885      strstr ( $PHP_SELF, "view_t.php" ) )
2886      $class = "entry";
2887  
2888    // avoid php warning for undefined array index
2889    if ( empty ( $hour_arr[$ind] ) )
2890      $hour_arr[$ind] = "";
2891  
2892    $catIcon = "icons/cat-" . $event_category . ".gif";
2893    if ( $event_category > 0 && file_exists ( $catIcon ) ) {
2894      $hour_arr[$ind] .= "<img src=\"$catIcon\" alt=\"$catIcon\" />";
2895    }
2896  
2897    $hour_arr[$ind] .= "<a title=\"" . 
2898    translate("View this entry") . "\" class=\"$class\" href=\"view_entry.php?id=$id&amp;date=$date";
2899    if ( strlen ( $GLOBALS["user"] ) > 0 )
2900      $hour_arr[$ind] .= "&amp;user=" . $GLOBALS["user"];
2901    $hour_arr[$ind] .= "\" onmouseover=\"window.status='" .
2902      translate("View this entry") . "'; show(event, '$popupid'); return true;\" onmouseout=\"hide('$popupid'); return true;\">";
2903    if ( $pri == 3 )
2904      $hour_arr[$ind] .= "<strong>";
2905  
2906    if ( $login != $event_owner && strlen ( $event_owner ) ) {
2907      if ($layers) foreach ($layers as $layer) {
2908        if ( $layer['cal_layeruser'] == $event_owner ) {
2909          $in_span = true;
2910          $hour_arr[$ind] .= "<span style=\"color:" . $layer['cal_color'] . ";\">";
2911        }
2912      }
2913    }
2914    if ( $duration == ( 24 * 60 ) ) {
2915      $timestr = translate("All day event");
2916    } else if ( $time >= 0 ) {
2917      $hour_arr[$ind] .= display_time ( $time ) . "&raquo;&nbsp;";
2918      $timestr = display_time ( $time );
2919      if ( $duration > 0 ) {
2920        // calc end time
2921        $h = (int) ( $time / 10000 );
2922        $m = ( $time / 100 ) % 100;
2923        $m += $duration;
2924        $d = $duration;
2925        while ( $m >= 60 ) {
2926          $h++;
2927          $m -= 60;
2928        }
2929        $end_time = sprintf ( "%02d%02d00", $h, $m );
2930        $timestr .= "-" . display_time ( $end_time );
2931      } else {
2932        $end_time = 0;
2933      }
2934      if ( empty ( $rowspan_arr[$ind] ) )
2935        $rowspan_arr[$ind] = 0; // avoid warning below
2936      // which slot is end time in? take one off so we don't
2937      // show 11:00-12:00 as taking up both 11 and 12 slots.
2938      $endind = calc_time_slot ( $end_time, true );
2939      if ( $endind == $ind )
2940        $rowspan = 0;
2941      else
2942        $rowspan = $endind - $ind + 1;
2943      if ( $rowspan > $rowspan_arr[$ind] && $rowspan > 1 )
2944        $rowspan_arr[$ind] = $rowspan;
2945    } else {
2946      $timestr = "";
2947    }
2948  
2949    // avoid php warning of undefined index when using .= below
2950    if ( empty ( $hour_arr[$ind] ) )
2951      $hour_arr[$ind] = "";
2952  
2953    if ( $login != $user && $access == 'R' && strlen ( $user ) ) {
2954      $hour_arr[$ind] .= "(" . translate("Private") . ")";
2955    } else if ( $login != $event_owner && $access == 'R' &&
2956      strlen ( $event_owner ) ) {
2957      $hour_arr[$ind] .= "(" . translate("Private") . ")";
2958    } else if ( $login != $event_owner && strlen ( $event_owner ) ) {
2959      $hour_arr[$ind] .= htmlspecialchars ( $name );
2960      if ( ! empty ( $in_span ) )
2961        $hour_arr[$ind] .= "</span>"; //end color span
2962    } else {
2963      $hour_arr[$ind] .= htmlspecialchars ( $name );
2964    }
2965  
2966    if ( $pri == 3 ) $hour_arr[$ind] .= "</strong>"; //end font-weight span
2967      $hour_arr[$ind] .= "</a>";
2968    //if ( $DISPLAY_ICONS == "Y" ) {
2969    //  $hour_arr[$ind] .= icon_text ( $id, true, true );
2970    //}
2971    $hour_arr[$ind] .= "<br />\n";
2972    if ( $login != $user && $access == 'R' && strlen ( $user ) ) {
2973      $eventinfo .= build_event_popup ( $popupid, $event_owner,
2974        translate("This event is confidential"), "" );
2975    } else if ( $login != $event_owner && $access == 'R' &&
2976      strlen ( $event_owner ) ) {
2977      $eventinfo .= build_event_popup ( $popupid, $event_owner,
2978        translate("This event is confidential"), "" );
2979    } else {
2980      $eventinfo .= build_event_popup ( $popupid, $event_owner,
2981        $description, $timestr, site_extras_for_popup ( $id ) );
2982    }
2983  }
2984  
2985  /**
2986   * Generates the HTML for an event to be viewed in the day-at-glance (day.php).
2987   *
2988   * The HTML will be stored in an array (global variable $hour_arr)
2989   * indexed on the event's starting hour.
2990   *
2991   * @param int    $id             Event id
2992   * @param string $date           Date of event in YYYYMMDD format
2993   * @param string $time           Time of event in HHMM format
2994   * @param string $name           Brief description of event
2995   * @param string $description    Full description of event
2996   * @param string $status         Status of event ('A', 'W')
2997   * @param int    $pri            Priority of event
2998   * @param string $access         Access to event by others ('P', 'R')
2999   * @param int    $duration       Duration of event in minutes
3000   * @param string $event_owner    User who created event
3001   * @param int    $event_category Category id for event
3002   */
3003  function html_for_event_day_at_a_glance ( $id, $date, $time,
3004    $name, $description, $status, $pri, $access, $duration, $event_owner,
3005    $event_category=-1 ) {
3006    global $first_slot, $last_slot, $hour_arr, $rowspan_arr, $rowspan,
3007      $eventinfo, $login, $user;
3008    static $key = 0;
3009    global $layers, $PHP_SELF, $TIME_SLOTS, $TZ_OFFSET;
3010  
3011    $popupid = "eventinfo-day-$id-$key";
3012    $key++;
3013  
3014    if ( $login != $user && $access == 'R' && strlen ( $user ) )
3015      $eventinfo .= build_event_popup ( $popupid, $event_owner,
3016        translate("This event is confidential"), "" );
3017    else if ( $login != $event_owner && $access == 'R' &&
3018      strlen ( $event_owner ) )
3019      $eventinfo .= build_event_popup ( $popupid, $event_owner,
3020        translate("This event is confidential"), "" );
3021    else
3022      $eventinfo .= build_event_popup ( $popupid, $event_owner, $description,
3023        "", site_extras_for_popup ( $id ) );
3024  
3025    // calculate slot length in minutes
3026    $interval = ( 60 * 24 ) / $TIME_SLOTS;
3027  
3028    // If TZ_OFFSET make this event before the start of the day or
3029    // after the end of the day, adjust the time slot accordingly.
3030    if ( $time >= 0 && $duration != ( 24 * 60 ) ) {
3031      if ( $time + ( $TZ_OFFSET * 10000 ) > 240000 )
3032        $time -= 240000;
3033      else if ( $time + ( $TZ_OFFSET * 10000 ) < 0 )
3034        $time += 240000;
3035      $ind = calc_time_slot ( $time );
3036      if ( $ind < $first_slot )
3037        $first_slot = $ind;
3038      if ( $ind > $last_slot )
3039        $last_slot = $ind;
3040    } else
3041      $ind = 9999;
3042    //echo "time = $time <br />\nind = $ind <br />\nfirst_slot = $first_slot<br />\n";
3043  
3044    if ( empty ( $hour_arr[$ind] ) )
3045      $hour_arr[$ind] = "";
3046  
3047    if ( $login != $event_owner && strlen ( $event_owner ) ) {
3048      $class = "layerentry";
3049    } else {
3050      $class = "entry";
3051      if ( $status == "W" )
3052        $class = "unapprovedentry";
3053    }
3054    // if we are looking at a view, then always use "entry"
3055    if ( strstr ( $PHP_SELF, "view_m.php" ) ||
3056      strstr ( $PHP_SELF, "view_w.php" )  || 
3057      strstr ( $PHP_SELF, "view_v.php" ) ||
3058      strstr ( $PHP_SELF, "view_t.php" ) )
3059      $class = "entry";
3060  
3061    $catIcon = "icons/cat-" . $event_category . ".gif";
3062    if ( $event_category > 0 && file_exists ( $catIcon ) ) {
3063      $hour_arr[$ind] .= "<img src=\"$catIcon\" alt=\"$catIcon\" />";
3064    }
3065  
3066    $hour_arr[$ind] .= "<a title=\"" .
3067      translate("View this entry") . "\" class=\"$class\" href=\"view_entry.php?id=$id&amp;date=$date";
3068    if ( strlen ( $GLOBALS["user"] ) > 0 )
3069      $hour_arr[$ind] .= "&amp;user=" . $GLOBALS["user"];
3070    $hour_arr[$ind] .= "\" onmouseover=\"window.status='" .
3071      translate("View this entry") . "'; show(event, '$popupid'); return true;\" onmouseout=\"hide('$popupid'); return true;\">";
3072    if ( $pri == 3 ) $hour_arr[$ind] .= "<strong>";
3073  
3074    if ( $login != $event_owner && strlen ( $event_owner ) ) {
3075      if ($layers) foreach ($layers as $layer) {
3076        if ( $layer['cal_layeruser'] == $event_owner) {
3077          $in_span = true;
3078          $hour_arr[$ind] .= "<span style=\"color:" . $layer['cal_color'] . ";\">";
3079        }
3080      }
3081    }
3082  
3083    if ( $duration == ( 24 * 60 ) ) {
3084      $hour_arr[$ind] .= "[" . translate("All day event") . "] ";
3085    } else if ( $time >= 0 ) {
3086      $hour_arr[$ind] .= "[" . display_time ( $time );
3087      if ( $duration > 0 ) {
3088        // calc end time
3089        $h = (int) ( $time / 10000 );
3090        $m = ( $time / 100 ) % 100;
3091        $m += $duration;
3092        $d = $duration;
3093        while ( $m >= 60 ) {
3094          $h++;
3095          $m -= 60;
3096        }
3097        $end_time = sprintf ( "%02d%02d00", $h, $m );
3098        $hour_arr[$ind] .= "-" . display_time ( $end_time );
3099        // which slot is end time in? take one off so we don't
3100        // show 11:00-12:00 as taking up both 11 and 12 slots.
3101        $endind = calc_time_slot ( $end_time, true );
3102        if ( $endind == $ind )
3103          $rowspan = 0;
3104        else
3105          $rowspan = $endind - $ind + 1;
3106        if ( ! isset ( $rowspan_arr[$ind] ) )
3107          $rowspan_arr[$ind] = 0;
3108        if ( $rowspan > $rowspan_arr[$ind] && $rowspan > 1 )
3109          $rowspan_arr[$ind] = $rowspan;
3110      }
3111      $hour_arr[$ind] .= "] ";
3112    }
3113    if ( $login != $user && $access == 'R' && strlen ( $user ) )
3114      $hour_arr[$ind] .= "(" . translate("Private") . ")";
3115    else
3116    if ( $login != $event_owner && $access == 'R' && strlen ( $event_owner ) )
3117      $hour_arr[$ind] .= "(" . translate("Private") . ")";
3118    else
3119    if ( $login != $event_owner && strlen ( $event_owner ) )
3120    {
3121      $hour_arr[$ind] .= htmlspecialchars ( $name );
3122      if ( ! empty ( $in_span ) )
3123        $hour_arr[$ind] .= "</span>"; //end color span
3124    }
3125  
3126    else
3127      $hour_arr[$ind] .= htmlspecialchars ( $name );
3128    if ( $pri == 3 ) $hour_arr[$ind] .= "</strong>"; //end font-weight span
3129  
3130    $hour_arr[$ind] .= "</a>";
3131    if ( $GLOBALS["DISPLAY_DESC_PRINT_DAY"] == "Y" ) {
3132      $hour_arr[$ind] .= "\n<dl class=\"desc\">\n";
3133      $hour_arr[$ind] .= "<dt>" . translate("Description") . ":</dt>\n<dd>";
3134      if ( ! empty ( $GLOBALS['allow_html_description'] ) &&
3135        $GLOBALS['allow_html_description'] == 'Y' ) {
3136        $str = str_replace ( "&", "&amp;", $description );
3137        $str = str_replace ( "&amp;amp;", "&amp;", $str );
3138        // If there is no html found, then go ahead and replace
3139        // the line breaks ("\n") with the html break.
3140        if ( strstr ( $str, "<" ) && strstr ( $str, ">" ) ) {
3141          // found some html...
3142          $hour_arr[$ind] .= $str;
3143        } else {
3144          // no html, replace line breaks
3145          $hour_arr[$ind] .= nl2br ( $str );
3146        }
3147      } else {
3148        // html not allowed in description, escape everything
3149        $hour_arr[$ind] .= nl2br ( htmlspecialchars ( $description ) );
3150      }
3151      $hour_arr[$ind] .= "</dd>\n</dl>\n";
3152    }
3153  
3154    $hour_arr[$ind] .= "<br />\n";
3155  }
3156  
3157  /**
3158   * Prints all the calendar entries for the specified user for the specified date in day-at-a-glance format.
3159   *
3160   * If we are displaying data from someone other than
3161   * the logged in user, then check the access permission of the entry.
3162   *
3163   * @param string $date Date in YYYYMMDD format
3164   * @param string $user Username of calendar
3165   */
3166  function print_day_at_a_glance ( $date, $user, $can_add=0 ) {
3167    global $first_slot, $last_slot, $hour_arr, $rowspan_arr, $rowspan;
3168    global $TABLEBG, $CELLBG, $TODAYCELLBG, $THFG, $THBG, $TIME_SLOTS, $TZ_OFFSET;
3169    global $WORK_DAY_START_HOUR, $WORK_DAY_END_HOUR;
3170    global $repeated_events;
3171    $get_unapproved = ( $GLOBALS["DISPLAY_UNAPPROVED"] == "Y" );
3172    if ( $user == "__public__" )
3173      $get_unapproved = false;
3174    if ( empty ( $TIME_SLOTS ) ) {
3175      echo "Error: TIME_SLOTS undefined!<br />\n";
3176      return;
3177    }
3178  
3179    // $interval is number of minutes per slot
3180    $interval = ( 24 * 60 ) / $TIME_SLOTS;
3181      
3182    $rowspan_arr = array ();
3183    for ( $i = 0; $i < $TIME_SLOTS; $i++ ) {
3184      $rowspan_arr[$i] = 0;
3185    }
3186  
3187    // get all the repeating events for this date and store in array $rep
3188    $rep = get_repeating_entries ( $user, $date );
3189    $cur_rep = 0;
3190  
3191    // Get static non-repeating events
3192    $ev = get_entries ( $user, $date, $get_unapproved );
3193    $hour_arr = array ();
3194    $interval = ( 24 * 60 ) / $TIME_SLOTS;
3195    $first_slot = (int) ( ( ( $WORK_DAY_START_HOUR - $TZ_OFFSET ) * 60 ) / $interval );
3196    $last_slot = (int) ( ( ( $WORK_DAY_END_HOUR - $TZ_OFFSET ) * 60 ) / $interval);
3197    //echo "first_slot = $first_slot<br />\nlast_slot = $last_slot<br />\ninterval = $interval<br />\nTIME_SLOTS = $TIME_SLOTS<br />\n";
3198    $rowspan_arr = array ();
3199    $all_day = 0;
3200    for ( $i = 0; $i < count ( $ev ); $i++ ) {
3201      // print out any repeating events that are before this one...
3202      while ( $cur_rep < count ( $rep ) &&
3203        $rep[$cur_rep]['cal_time'] < $ev[$i]['cal_time'] ) {
3204        if ( $get_unapproved || $rep[$cur_rep]['cal_status'] == 'A' ) {
3205          if ( ! empty ( $rep[$cur_rep]['cal_ext_for_id'] ) ) {
3206            $viewid = $rep[$cur_rep]['cal_ext_for_id'];
3207            $viewname = $rep[$cur_rep]['cal_name'] . " (" .
3208              translate("cont.") . ")";
3209          } else {
3210            $viewid = $rep[$cur_rep]['cal_id'];
3211            $viewname = $rep[$cur_rep]['cal_name'];
3212          }
3213          if ( $rep[$cur_rep]['cal_duration'] == ( 24 * 60 ) )
3214            $all_day = 1;
3215          html_for_event_day_at_a_glance ( $viewid,
3216            $date, $rep[$cur_rep]['cal_time'],
3217            $viewname, $rep[$cur_rep]['cal_description'],
3218            $rep[$cur_rep]['cal_status'], $rep[$cur_rep]['cal_priority'],
3219            $rep[$cur_rep]['cal_access'], $rep[$cur_rep]['cal_duration'],
3220            $rep[$cur_rep]['cal_login'], $rep[$cur_rep]['cal_category'] );
3221        }
3222        $cur_rep++;
3223      }
3224      if ( $get_unapproved || $ev[$i]['cal_status'] == 'A' ) {
3225        if ( ! empty ( $ev[$i]['cal_ext_for_id'] ) ) {
3226          $viewid = $ev[$i]['cal_ext_for_id'];
3227          $viewname = $ev[$i]['cal_name'] . " (" .
3228            translate("cont.") . ")";
3229        } else {
3230          $viewid = $ev[$i]['cal_id'];
3231          $viewname = $ev[$i]['cal_name'];
3232        }
3233        if ( $ev[$i]['cal_duration'] == ( 24 * 60 ) )
3234          $all_day = 1;
3235        html_for_event_day_at_a_glance ( $viewid,
3236          $date, $ev[$i]['cal_time'],
3237          $viewname, $ev[$i]['cal_description'],
3238          $ev[$i]['cal_status'], $ev[$i]['cal_priority'],
3239          $ev[$i]['cal_access'], $ev[$i]['cal_duration'],
3240          $ev[$i]['cal_login'], $ev[$i]['cal_category'] );
3241      }
3242    }
3243    // print out any remaining repeating events
3244    while ( $cur_rep < count ( $rep ) ) {
3245      if ( $get_unapproved || $rep[$cur_rep]['cal_status'] == 'A' ) {
3246        if ( ! empty ( $rep[$cur_rep]['cal_ext_for_id'] ) ) {
3247          $viewid = $rep[$cur_rep]['cal_ext_for_id'];
3248          $viewname = $rep[$cur_rep]['cal_name'] . " (" .
3249            translate("cont.") . ")";
3250        } else {
3251          $viewid = $rep[$cur_rep]['cal_id'];
3252          $viewname = $rep[$cur_rep]['cal_name'];
3253        }
3254        if ( $rep[$cur_rep]['cal_duration'] == ( 24 * 60 ) )
3255          $all_day = 1;
3256        html_for_event_day_at_a_glance ( $viewid,
3257          $date, $rep[$cur_rep]['cal_time'],
3258          $viewname, $rep[$cur_rep]['cal_description'],
3259          $rep[$cur_rep]['cal_status'], $rep[$cur_rep]['cal_priority'],
3260          $rep[$cur_rep]['cal_access'], $rep[$cur_rep]['cal_duration'],
3261          $rep[$cur_rep]['cal_login'], $rep[$cur_rep]['cal_category'] );
3262      }
3263      $cur_rep++;
3264    }
3265  
3266    // squish events that use the same cell into the same cell.
3267    // For example, an event from 8:00-9:15 and another from 9:30-9:45 both
3268    // want to show up in the 8:00-9:59 cell.
3269    $rowspan = 0;
3270    $last_row = -1;
3271    //echo "First SLot: $first_slot; Last Slot: $last_slot<br />\n";
3272    $i = 0;
3273    if ( $first_slot < 0 )
3274      $i = $first_slot;
3275    for ( ; $i < $TIME_SLOTS; $i++ ) {
3276      if ( $rowspan > 1 ) {
3277        if ( ! empty ( $hour_arr[$i] ) ) {
3278          $diff_start_time = $i - $last_row;
3279          if ( $rowspan_arr[$i] > 1 ) {
3280            if (  $rowspan_arr[$i] + ( $diff_start_time ) >  $rowspan_arr[$last_row]  ) {
3281              $rowspan_arr[$last_row] = ( $rowspan_arr[$i] + ( $diff_start_time ) );
3282            }
3283            $rowspan += ( $rowspan_arr[$i] - 1 );
3284          } else {
3285            $rowspan_arr[$last_row] += $rowspan_arr[$i];
3286          }
3287          // this will move entries apart that appear in one field,
3288          // yet start on different hours
3289          for ( $u = $diff_start_time ; $u > 0 ; $u-- ) {
3290            $hour_arr[$last_row] .= "<br />\n"; 
3291          }
3292          $hour_arr[$last_row] .= $hour_arr[$i];
3293          $hour_arr[$i] = "";
3294          $rowspan_arr[$i] = 0;
3295        }
3296        $rowspan--;
3297      } else if ( ! empty ( $rowspan_arr[$i] ) && $rowspan_arr[$i] > 1 ) {
3298        $rowspan = $rowspan_arr[$i];
3299        $last_row = $i;
3300      }
3301    }
3302    if ( ! empty ( $hour_arr[9999] ) ) {
3303      echo "<tr><th class=\"empty\">&nbsp;</th>\n" .
3304        "<td class=\"hasevents\">$hour_arr[9999]</td></tr>\n";
3305    }
3306    $rowspan = 0;
3307    //echo "first_slot = $first_slot<br />\nlast_slot = $last_slot<br />\ninterval = $interval<br />\n";
3308    for ( $i = $first_slot; $i <= $last_slot; $i++ ) {
3309      $time_h = (int) ( ( $i * $interval ) / 60 );
3310      $time_m = ( $i * $interval ) % 60;
3311      $time = display_time ( ( $time_h * 100 + $time_m ) * 100 );
3312      echo "<tr>\n<th class=\"row\">" . $time . "</th>\n";
3313      if ( $rowspan > 1 ) {
3314        // this might mean there's an overlap, or it could mean one event
3315        // ends at 11:15 and another starts at 11:30.
3316        if ( ! empty ( $hour_arr[$i] ) ) {
3317          echo "<td class=\"hasevents\">";
3318          if ( $can_add )
3319            echo html_for_add_icon ( $date, $time_h, $time_m, $user );
3320          echo "$hour_arr[$i]</td>\n";
3321        }
3322        $rowspan--;
3323      } else {
3324        if ( empty ( $hour_arr[$i] ) ) {
3325          echo "<td>";
3326          if ( $can_add ) {
3327            echo html_for_add_icon ( $date, $time_h, $time_m, $user ) . "</td>";
3328    } else {
3329      echo "&nbsp;</td>";
3330    }
3331          echo "</tr>\n";
3332        } else {
3333          if ( empty ( $rowspan_arr[$i] ) )
3334            $rowspan = '';
3335          else
3336            $rowspan = $rowspan_arr[$i];
3337          if ( $rowspan > 1 ) {
3338            echo "<td rowspan=\"$rowspan\" class=\"hasevents\">";
3339            if ( $can_add )
3340              echo html_for_add_icon ( $date, $time_h, $time_m, $user );
3341            echo "$hour_arr[$i]</td></tr>\n";
3342          } else {
3343            echo "<td class=\"hasevents\">";
3344            if ( $can_add )
3345              echo html_for_add_icon ( $date, $time_h, $time_m, $user );
3346            echo "$hour_arr[$i]</td></tr>\n";
3347          }
3348        }
3349      }
3350    }
3351  }
3352  
3353  /**
3354   * Checks for any unnaproved events.
3355   *
3356   * If any are found, display a link to the unapproved events (where they can be
3357   * approved).
3358   *
3359   * If the user is an admin user, also count up any public events.
3360   * If the user is a nonuser admin, count up events on the nonuser calendar.
3361   *
3362   * @param string $user Current user login
3363   */
3364  function display_unapproved_events ( $user ) {
3365    global $public_access, $is_admin, $nonuser_enabled, $login;
3366  
3367    // Don't do this for public access login, admin user must approve public
3368    // events
3369    if ( $user == "__public__" )
3370      return;
3371  
3372    $sql = "SELECT COUNT(webcal_entry_user.cal_id) " .
3373      "FROM webcal_entry_user, webcal_entry " .
3374      "WHERE webcal_entry_user.cal_id = webcal_entry.cal_id " .
3375      "AND webcal_entry_user.cal_status = 'W' " .
3376      "AND ( webcal_entry.cal_ext_for_id IS NULL " .
3377      "OR webcal_entry.cal_ext_for_id = 0 ) " .
3378      "AND ( webcal_entry_user.cal_login = '$user'";
3379    if ( $public_access == "Y" && $is_admin ) {
3380      $sql .= " OR webcal_entry_user.cal_login = '__public__'";
3381    }
3382    if ( $nonuser_enabled == 'Y' ) {
3383      $admincals = get_nonuser_cals ( $login );
3384      for ( $i = 0; $i < count ( $admincals ); $i++ ) {
3385        $sql .= " OR webcal_entry_user.cal_login = '" .
3386          $admincals[$i]['cal_login'] . "'";
3387      }
3388    }
3389    $sql .= " )";
3390    //print "SQL: $sql<br />\n";
3391    $res = dbi_query ( $sql );
3392    if ( $res ) {
3393      if ( $row = dbi_fetch_row ( $res ) ) {
3394        if ( $row[0] > 0 ) {
3395   $str = translate ("You have XXX unapproved events");
3396   $str = str_replace ( "XXX", $row[0], $str );
3397          echo "<a class=\"nav\" href=\"list_unapproved.php";
3398          if ( $user != $login )
3399            echo "?user=$user\"";
3400          echo "\">" . $str .  "</a><br />\n";
3401        }
3402      }
3403      dbi_free_result ( $res );
3404    }
3405  }
3406  
3407  /**
3408   * Looks for URLs in the given text, and makes them into links.
3409   *
3410   * @param string $text Input text
3411   *
3412   * @return string The text altered to have HTML links for any web links
3413   *                (http or https)
3414   */
3415  function activate_urls ( $text ) {
3416    $str = eregi_replace ( "(http://[^[:space:]$]+)",
3417      "<a href=\"\\1\">\\1</a>", $text );
3418    $str = eregi_replace ( "(https://[^[:space:]$]+)",
3419      "<a href=\"\\1\">\\1</a>", $str );
3420    return $str;
3421  }
3422  
3423  /**
3424   * Displays a time in either 12 or 24 hour format.
3425   *
3426   * The global variable $TZ_OFFSET is used to adjust the time.  Note that this
3427   * is somewhat of a kludge for timezone support.  If an event is set for 11PM
3428   * server time and the user is 2 hours ahead, it will show up as 1AM, but the
3429   * date will not be adjusted to the next day.
3430   *
3431   * @param string $time          Input time in HHMMSS format
3432   * @param bool   $ignore_offset If true, then do not use the timezone offset
3433   *
3434   * @return string The time in the user's timezone and preferred format
3435   *
3436   * @global int The user's timezone offset from the server
3437   */
3438  function display_time ( $time, $ignore_offset=0 ) {
3439    global $TZ_OFFSET;
3440    $hour = (int) ( $time / 10000 );
3441    if ( ! $ignore_offset )
3442      $hour += $TZ_OFFSET;
3443    $min = abs( ( $time / 100 ) % 100 );
3444    //Prevent goofy times like 8:00 9:30 9:00 10:30 10:00 
3445    if ( $time < 0 && $min > 0 ) $hour = $hour - 1;
3446    while ( $hour < 0 )
3447      $hour += 24;
3448    while ( $hour > 23 )
3449      $hour -= 24;
3450    if ( $GLOBALS["TIME_FORMAT"] == "12" ) {
3451      $ampm = ( $hour >= 12 ) ? translate("pm") : translate("am");
3452      $hour %= 12;
3453      if ( $hour == 0 )
3454        $hour = 12;
3455      $ret = sprintf ( "%d:%02d%s", $hour, $min, $ampm );
3456    } else {
3457      $ret = sprintf ( "%d:%02d", $hour, $min );
3458    }
3459    return $ret;
3460  }
3461  
3462  /**
3463   * Returns the full name of the specified month.
3464   *
3465   * Use {@link month_short_name()} to get the abbreviated name of the month.
3466   *
3467   * @param int $m Number of the month (0-11)
3468   *
3469   * @return string The full name of the specified month
3470   *
3471   * @see month_short_name
3472   */
3473  function month_name ( $m ) {
3474    switch ( $m ) {
3475      case 0: return translate("January");
3476      case 1: return translate("February");
3477      case 2: return translate("March");
3478      case 3: return translate("April");
3479      case 4: return translate("May_"); // needs to be different than "May"
3480      case 5: return translate("June");
3481      case 6: return translate("July");
3482      case 7: return translate("August");
3483      case 8: return translate("September");
3484      case 9: return translate("October");
3485      case 10: return translate("November");
3486      case 11: return translate("December");
3487    }
3488    return "unknown-month($m)";
3489  }
3490  
3491  /**
3492   * Returns the abbreviated name of the specified month (such as "Jan").
3493   *
3494   * Use {@link month_name()} to get the full name of the month.
3495   *
3496   * @param int $m Number of the month (0-11)
3497   *
3498   * @return string The abbreviated name of the specified month (example: "Jan")
3499   *
3500   * @see month_name
3501   */
3502  function month_short_name ( $m ) {
3503    switch ( $m ) {
3504      case 0: return translate("Jan");
3505      case 1: return translate("Feb");
3506      case 2: return translate("Mar");
3507      case 3: return translate("Apr");
3508      case 4: return translate("May");
3509      case 5: return translate("Jun");
3510      case 6: return translate("Jul");
3511      case 7: return translate("Aug");
3512      case 8: return translate("Sep");
3513      case 9: return translate("Oct");
3514      case 10: return translate("Nov");
3515      case 11: return translate("Dec");
3516    }
3517    return "unknown-month($m)";
3518  }
3519  
3520  /**
3521   * Returns the full weekday name.
3522   *
3523   * Use {@link weekday_short_name()} to get the abbreviated weekday name.
3524   *
3525   * @param int $w Number of the day in the week (0=Sunday,...,6=Saturday)
3526   *
3527   * @return string The full weekday name ("Sunday")
3528   *
3529   * @see weekday_short_name
3530   */
3531  function weekday_name ( $w ) {
3532    switch ( $w ) {
3533      case 0: return translate("Sunday");
3534      case 1: return translate("Monday");
3535      case 2: return translate("Tuesday");
3536      case 3: return translate("Wednesday");
3537      case 4: return translate("Thursday");
3538      case 5: return translate("Friday");
3539      case 6: return translate("Saturday");
3540    }
3541    return "unknown-weekday($w)";
3542  }
3543  
3544  /**
3545   * Returns the abbreviated weekday name.
3546   *
3547   * Use {@link weekday_name()} to get the full weekday name.
3548   *
3549   * @param int $w Number of the day in the week (0=Sunday,...,6=Saturday)
3550   *
3551   * @return string The abbreviated weekday name ("Sun")
3552   */
3553  function weekday_short_name ( $w ) {
3554    switch ( $w ) {
3555      case 0: return translate("Sun");
3556      case 1: return translate("Mon");
3557      case 2: return translate("Tue");
3558      case 3: return translate("Wed");
3559      case 4: return translate("Thu");
3560      case 5: return translate("Fri");
3561      case 6: return translate("Sat");
3562    }
3563    return "unknown-weekday($w)";
3564  }
3565  
3566  /**
3567   * Converts a date in YYYYMMDD format into "Friday, December 31, 1999",
3568   * "Friday, 12-31-1999" or whatever format the user prefers.
3569   *
3570   * @param string $indate       Date in YYYYMMDD format
3571   * @param string $format       Format to use for date (default is "__month__
3572   *                             __dd__, __yyyy__")
3573   * @param bool   $show_weekday Should the day of week also be included?
3574   * @param bool   $short_months Should the abbreviated month names be used
3575   *                             instead of the full month names?
3576   * @param int    $server_time ???
3577   *
3578   * @return string Date in the specified format
3579   *
3580   * @global string Preferred date format
3581   * @global int    User's timezone offset from the server
3582   */
3583  function date_to_str ( $indate, $format="", $show_weekday=true, $short_months=false, $server_time="" ) {
3584    global $DATE_FORMAT, $TZ_OFFSET;
3585  
3586    if ( strlen ( $indate ) == 0 ) {
3587      $indate = date ( "Ymd" );
3588    }
3589  
3590    $newdate = $indate;
3591    if ( $server_time != "" && $server_time >= 0 ) {
3592      $y = substr ( $indate, 0, 4 );
3593      $m = substr ( $indate, 4, 2 );
3594      $d = substr ( $indate, 6, 2 );
3595      if ( $server_time + $TZ_OFFSET * 10000 > 240000 ) {
3596         $newdate = date ( "Ymd", mktime ( 3, 0, 0, $m, $d + 1, $y ) );
3597      } else if ( $server_time + $TZ_OFFSET * 10000 < 0 ) {
3598         $newdate = date ( "Ymd", mktime ( 3, 0, 0, $m, $d - 1, $y ) );
3599      }
3600    }
3601  
3602    // if they have not set a preference yet...
3603    if ( $DATE_FORMAT == "" )
3604      $DATE_FORMAT = "__month__ __dd__, __yyyy__";
3605  
3606    if ( empty ( $format ) )
3607      $format = $DATE_FORMAT;
3608  
3609    $y = (int) ( $newdate / 10000 );
3610    $m = (int) ( $newdate / 100 ) % 100;
3611    $d = $newdate % 100;
3612    $date = mktime ( 3, 0, 0, $m, $d, $y );
3613    $wday = strftime ( "%w", $date );
3614  
3615    if ( $short_months ) {
3616      $weekday = weekday_short_name ( $wday );
3617      $month = month_short_name ( $m - 1 );
3618    } else {
3619      $weekday = weekday_name ( $wday );
3620      $month = month_name ( $m - 1 );
3621    }
3622    $yyyy = $y;
3623    $yy = sprintf ( "%02d", $y %= 100 );
3624  
3625    $ret = $format;
3626    $ret = str_replace ( "__yyyy__", $yyyy, $ret );
3627    $ret = str_replace ( "__yy__", $yy, $ret );
3628    $ret = str_replace ( "__month__", $month, $ret );
3629    $ret = str_replace ( "__mon__", $month, $ret );
3630    $ret = str_replace ( "__dd__", $d, $ret );
3631    $ret = str_replace ( "__mm__", $m, $ret );
3632  
3633    if ( $show_weekday )
3634      return "$weekday, $ret";
3635    else
3636      return $ret;
3637  }
3638  
3639  
3640  /**
3641   * Converts a hexadecimal digit to an integer.
3642   *
3643   * @param string $val Hexadecimal digit
3644   *
3645   * @return int Equivalent integer in base-10
3646   *
3647   * @ignore
3648   */
3649  function hextoint ( $val ) {
3650    if ( empty ( $val ) )
3651      return 0;
3652    switch ( strtoupper ( $val ) ) {
3653      case "0": return 0;
3654      case "1": return 1;
3655      case "2": return 2;
3656      case "3": return 3;
3657      case "4": return 4;
3658      case "5": return 5;
3659      case "6": return 6;
3660      case "7": return 7;
3661      case "8": return 8;
3662      case "9": return 9;
3663      case "A": return 10;
3664      case "B": return 11;
3665      case "C": return 12;
3666      case "D": return 13;
3667      case "E": return 14;
3668      case "F": return 15;
3669    }
3670    return 0;
3671  }
3672  
3673  /**
3674   * Extracts a user's name from a session id.
3675   *
3676   * This prevents users from begin able to edit their cookies.txt file and set
3677   * the username in plain text.
3678   *
3679   * @param string $instr A hex-encoded string. "Hello" would be "678ea786a5".
3680   * 
3681   * @return string The decoded string
3682   *
3683   * @global array Array of offsets
3684   *
3685   * @see encode_string
3686   */
3687  function decode_string ( $instr ) {
3688    global $offsets;
3689    //echo "<br />\nDECODE<br />\n";
3690    $orig = "";
3691    for ( $i = 0; $i < strlen ( $instr ); $i += 2 ) {
3692      //echo "<br />\n";
3693      $ch1 = substr ( $instr, $i, 1 );
3694      $ch2 = substr ( $instr, $i + 1, 1 );
3695      $val = hextoint ( $ch1 ) * 16 + hextoint ( $ch2 );
3696      //echo "decoding \"" . $ch1 . $ch2 . "\" = $val<br />\n";
3697      $j = ( $i / 2 ) % count ( $offsets );
3698      //echo "Using offsets $j = " . $offsets[$j] . "<br />\n";
3699      $newval = $val - $offsets[$j] + 256;
3700      $newval %= 256;
3701      //echo " neval \"$newval\"<br />\n";
3702      $dec_ch = chr ( $newval );
3703      //echo " which is \"$dec_ch\"<br />\n";
3704      $orig .= $dec_ch;
3705    }
3706    //echo "Decode string: '$orig' <br/>\n";
3707    return $orig;
3708  }
3709  
3710  /**
3711   * Takes an input string and encode it into a slightly encoded hexval that we
3712   * can use as a session cookie.
3713   *
3714   * @param string $instr Text to encode
3715   *
3716   * @return string The encoded text
3717   *
3718   * @global array Array of offsets
3719   *
3720   * @see decode_string
3721   */
3722  function encode_string ( $instr ) {
3723    global $offsets;
3724    //echo "<br />\nENCODE<br />\n";
3725    $ret = "";
3726    for ( $i = 0; $i < strlen ( $instr ); $i++ ) {
3727      //echo "<br />\n";
3728      $ch1 = substr ( $instr, $i, 1 );
3729      $val = ord ( $ch1 );
3730      //echo "val = $val for \"$ch1\"<br />\n";
3731      $j = $i % count ( $offsets );
3732      //echo "Using offsets $j = $offsets[$j]<br />\n";
3733      $newval = $val + $offsets[$j];
3734      $newval %= 256;
3735      //echo "newval = $newval for \"$ch1\"<br />\n";
3736      $ret .= bin2hex ( chr ( $newval ) );
3737    }
3738    return $ret;
3739  }
3740  
3741  /**
3742   * An implementatin of array_splice() for PHP3.
3743   *
3744   * @param array $input       Array to be spliced into
3745   * @param int   $offset      Where to begin the splice
3746   * @param int   $length      How long the splice should be
3747   * @param array $replacement What to splice in
3748   *
3749   * @ignore
3750   */
3751  function my_array_splice(&$input,$offset,$length,$replacement) {
3752    if ( floor(phpversion()) < 4 ) {
3753      // if offset is negative, then it starts at the end of array
3754      if ( $offset < 0 )
3755        $offset = count($input) + $offset;
3756  
3757      for ($i=0;$i<$offset;$i++) {
3758        $new_array[] = $input[$i];
3759      }
3760  
3761      // if we have a replacement, insert it
3762      for ($i=0;$i<count($replacement);$i++) {
3763        $new_array[] = $replacement[$i];
3764      }
3765  
3766      // now tack on the rest of the original array
3767      for ($i=$offset+$length;$i<count($input);$i++) {
3768        $new_array[] = $input[$i];
3769      }
3770  
3771      $input = $new_array;
3772    } else {
3773      array_splice($input,$offset,$length,$replacement);
3774    }
3775  }
3776  
3777  /**
3778   * Loads current user's category info and stuff it into category global
3779   * variable.
3780   *
3781   * @param string $ex_global Don't include global categories ('' or '1')
3782   */
3783  function load_user_categories ($ex_global = '') {
3784    global $login, $user, $is_assistant;
3785    global $categories, $category_owners;
3786    global $categories_enabled, $is_admin;
3787  
3788    $cat_owner =  ( ( ! empty ( $user ) && strlen ( $user ) ) &&  ( $is_assistant  ||
3789      $is_admin ) ) ? $user : $login;  
3790    $categories = array ();
3791    $category_owners = array ();
3792    if ( $categories_enabled == "Y" ) {
3793      $sql = "SELECT cat_id, cat_name, cat_owner FROM webcal_categories WHERE ";
3794      $sql .=  ($ex_global == '') ? " (cat_owner = '$cat_owner') OR  (cat_owner IS NULL) ORDER BY cat_owner, cat_name" : " cat_owner = '$cat_owner' ORDER BY cat_name";
3795  
3796      $res = dbi_query ( $sql );
3797      if ( $res ) {
3798        while ( $row = dbi_fetch_row ( $res ) ) {
3799          $cat_id = $row[0];
3800          $categories[$cat_id] = $row[1];
3801          $category_owners[$cat_id] = $row[2];
3802        }
3803        dbi_free_result ( $res );
3804      }
3805    } else {
3806      //echo "Categories disabled.";
3807    }
3808  }
3809  
3810  /**
3811   * Prints dropdown HTML for categories.
3812   *
3813   * @param string $form   The page to submit data to (without .php)
3814   * @param string $date   Date in YYYYMMDD format
3815   * @param int    $cat_id Category id that should be pre-selected
3816   */
3817  function print_category_menu ( $form, $date = '', $cat_id = '' ) {
3818    global $categories, $category_owners, $user, $login;
3819    echo "<form action=\"{$form}.php\" method=\"get\" name=\"SelectCategory\" class=\"categories\">\n";
3820    if ( ! empty($date) ) echo "<input type=\"hidden\" name=\"date\" value=\"$date\" />\n";
3821    if ( ! empty ( $user ) && $user != $login )
3822      echo "<input type=\"hidden\" name=\"user\" value=\"$user\" />\n";
3823    echo translate ("Category") . ": <select name=\"cat_id\" onchange=\"document.SelectCategory.submit()\">\n";
3824    echo "<option value=\"\"";
3825    if ( $cat_id == '' ) echo " selected=\"selected\"";
3826    echo ">" . translate("All") . "</option>\n";
3827    $cat_owner =  ( ! empty ( $user ) && strlen ( $user ) ) ? $user : $login;
3828    if (  is_array ( $categories ) ) {
3829      foreach ( $categories as $K => $V ){
3830        if ( $cat_owner ||
3831          empty ( $category_owners[$K] ) ) {
3832          echo "<option value=\"$K\"";
3833          if ( $cat_id == $K ) echo " selected=\"selected\"";
3834          echo ">$V</option>\n";
3835        }
3836      }
3837    }
3838    echo "</select>\n";
3839    echo "</form>\n";
3840    echo "<span id=\"cat\">" . translate ("Category") . ": ";
3841    echo ( strlen ( $cat_id ) ? $categories[$cat_id] : translate ('All') ) . "</span>\n";
3842  }
3843  
3844  /**
3845   * Converts HTML entities in 8bit.
3846   *
3847   * <b>Note:</b> Only supported for PHP4 (not PHP3).
3848   *
3849   * @param string $html HTML text
3850   *
3851   * @return string The converted text
3852   */
3853  function html_to_8bits ( $html ) {
3854    if ( floor(phpversion()) < 4 ) {
3855      return $html;
3856    } else {
3857      return strtr ( $html, array_flip (
3858        get_html_translation_table (HTML_ENTITIES) ) );
3859    }
3860  }
3861  
3862  // ***********************************************************************
3863  // Functions for getting information about boss and their assistant.
3864  // ***********************************************************************
3865  
3866  /**
3867   * Gets a list of an assistant's boss from the webcal_asst table.
3868   *
3869   * @param string $assistant Login of assistant
3870   *
3871   * @return array Array of bosses, where each boss is an array with the following
3872   *               fields:
3873   * - <var>cal_login</var>
3874   * - <var>cal_fullname</var>
3875   */
3876  function user_get_boss_list ( $assistant ) {
3877    global $bosstemp_fullname;
3878  
3879    $res = dbi_query (
3880      "SELECT cal_boss " .
3881      "FROM webcal_asst " .
3882      "WHERE cal_assistant = '$assistant'" );
3883    $count = 0;
3884    $ret = array ();
3885    if ( $res ) {
3886      while ( $row = dbi_fetch_row ( $res ) ) {
3887        user_load_variables ( $row[0], "bosstemp_" );
3888        $ret[$count++] = array (
3889          "cal_login" => $row[0],
3890          "cal_fullname" => $bosstemp_fullname
3891        );
3892      }
3893      dbi_free_result ( $res );
3894    }
3895    return $ret;
3896  }
3897  
3898  /**
3899   * Is this user an assistant of this boss?
3900   *
3901   * @param string $assistant Login of potential assistant
3902   * @param string $boss      Login of potential boss
3903   * 
3904   * @return bool True or false
3905   */
3906  function user_is_assistant ( $assistant, $boss ) {
3907    $ret = false;
3908  
3909    if ( empty ( $boss ) )
3910      return false;
3911    $res = dbi_query ( "SELECT * FROM webcal_asst " . 
3912       "WHERE cal_assistant = '$assistant' AND cal_boss = '$boss'" );
3913    if ( $res ) {
3914      if ( dbi_fetch_row ( $res ) )
3915        $ret = true;
3916      dbi_free_result ( $res );
3917    }
3918    return $ret;
3919  }
3920  
3921  /**
3922   * Is this user an assistant?
3923   *
3924   * @param string $assistant Login for user
3925   *
3926   * @return bool true if the user is an assistant to one or more bosses
3927   */
3928  function user_has_boss ( $assistant ) {
3929    $ret = false;
3930    $res = dbi_query ( "SELECT * FROM webcal_asst " .
3931      "WHERE cal_assistant = '$assistant'" );
3932    if ( $res ) {
3933      if ( dbi_fetch_row ( $res ) )
3934        $ret = true;
3935      dbi_free_result ( $res );
3936    }
3937    return $ret;
3938  }
3939  
3940  /**
3941   * Checks the boss user preferences to see if the boss wants to be notified via
3942   * email on changes to their calendar.
3943   *
3944   * @param string $assistant Assistant login
3945   * @param string $boss      Boss login
3946   *
3947   * @return bool True if the boss wants email notifications
3948   */
3949  function boss_must_be_notified ( $assistant, $boss ) {
3950    if (user_is_assistant ( $assistant, $boss ) )
3951      return ( get_pref_setting ( $boss, "EMAIL_ASSISTANT_EVENTS" )=="Y" ? true : false );
3952    return true;
3953  }
3954  
3955  /**
3956   * Checks the boss user preferences to see if the boss must approve events
3957   * added to their calendar.
3958   *
3959   * @param string $assistant Assistant login
3960   * @param string $boss      Boss login
3961   *
3962   * @return bool True if the boss must approve new events
3963   */
3964  function boss_must_approve_event ( $assistant, $boss ) {
3965    if (user_is_assistant ( $assistant, $boss ) )
3966      return ( get_pref_setting ( $boss, "APPROVE_ASSISTANT_EVENT" )=="Y" ? true : false );
3967    return true;
3968  }
3969  
3970  /**
3971   * Fakes an email for testing purposes.
3972   *
3973   * @param string $mailto Email address to send mail to
3974   * @param string $subj   Subject of email
3975   * @param string $text   Email body
3976   * @param string $hdrs   Other email headers
3977   *
3978   * @ignore
3979   */
3980  function fake_mail ( $mailto, $subj, $text, $hdrs ) { 
3981    echo "To: $mailto <br />\n" .
3982      "Subject: $subj <br />\n" .
3983      nl2br ( $hdrs ) . "<br />\n" .
3984      nl2br ( $text );
3985  }
3986  
3987  /**
3988   * Prints all the entries in a time bar format for the specified user for the
3989   * specified date.
3990   *
3991   * If we are displaying data from someone other than the logged in user, then
3992   * check the access permission of the entry.
3993   *
3994   * @param string $date Date in YYYYMMDD format
3995   * @param string $user Username
3996   * @param bool   $ssi  Should we not include links to add new events?
3997   */
3998  function print_date_entries_timebar ( $date, $user, $ssi ) {
3999    global $events, $readonly, $is_admin,
4000      $public_access, $public_access_can_add;
4001    $cnt = 0;
4002    $get_unapproved = ( $GLOBALS["DISPLAY_UNAPPROVED"] == "Y" );
4003    // public access events always must be approved before being displayed
4004    if ( $GLOBALS["login"] == "__public__" )
4005      $get_unapproved = false;
4006  
4007    $year = substr ( $date, 0, 4 );
4008    $month = substr ( $date, 4, 2 );
4009    $day = substr ( $date, 6, 2 );
4010   
4011    $dateu = mktime ( 3, 0, 0, $month, $day, $year );
4012  
4013    $can_add = ( $readonly == "N" || $is_admin );
4014    if ( $public_access == "Y" && $public_access_can_add != "Y" &&
4015      $GLOBALS["login"] == "__public__" )
4016      $can_add = false;
4017  
4018    // get all the repeating events for this date and store in array $rep
4019    $rep = get_repeating_entries ( $user, $date ) ;
4020    $cur_rep = 0;
4021  
4022    // get all the non-repeating events for this date and store in $ev
4023    $ev = get_entries ( $user, $date, $get_unapproved );
4024  
4025    for ( $i = 0; $i < count ( $ev ); $i++ ) {
4026      // print out any repeating events that are before this one...
4027      while ( $cur_rep < count ( $rep ) &&
4028        $rep[$cur_rep]['cal_time'] < $ev[$i]['cal_time'] ) {
4029        if ( $get_unapproved || $rep[$cur_rep]['cal_status'] == 'A' ) {
4030          print_entry_timebar ( $rep[$cur_rep]['cal_id'],
4031            $date, $rep[$cur_rep]['cal_time'], $rep[$cur_rep]['cal_duration'],
4032            $rep[$cur_rep]['cal_name'], $rep[$cur_rep]['cal_description'],
4033            $rep[$cur_rep]['cal_status'], $rep[$cur_rep]['cal_priority'],
4034            $rep[$cur_rep]['cal_access'], $rep[$cur_rep]['cal_login'],
4035            $rep[$cur_rep]['cal_category'] );
4036          $cnt++;
4037        }
4038        $cur_rep++;
4039      }
4040      if ( $get_unapproved || $ev[$i]['cal_status'] == 'A' ) {
4041        print_entry_timebar ( $ev[$i]['cal_id'],
4042          $date, $ev[$i]['cal_time'], $ev[$i]['cal_duration'],
4043          $ev[$i]['cal_name'], $ev[$i]['cal_description'],
4044          $ev[$i]['cal_status'], $ev[$i]['cal_priority'],
4045          $ev[$i]['cal_access'], $ev[$i]['cal_login'],
4046          $ev[$i]['cal_category'] );
4047        $cnt++;
4048      }
4049    }
4050    // print out any remaining repeating events
4051    while ( $cur_rep < count ( $rep ) ) {
4052      if ( $get_unapproved || $rep[$cur_rep]['cal_status'] == 'A' ) {
4053        print_entry_timebar ( $rep[$cur_rep]['cal_id'],
4054          $date, $rep[$cur_rep]['cal_time'], $rep[$cur_rep]['cal_duration'],
4055          $rep[$cur_rep]['cal_name'], $rep[$cur_rep]['cal_description'],
4056          $rep[$cur_rep]['cal_status'], $rep[$cur_rep]['cal_priority'],
4057          $rep[$cur_rep]['cal_access'], $rep[$cur_rep]['cal_login'],
4058          $rep[$cur_rep]['cal_category'] );
4059        $cnt++;
4060      }
4061      $cur_rep++;
4062    }
4063    if ( $cnt == 0 )
4064      echo "&nbsp;"; // so the table cell has at least something
4065  }
4066  
4067  /**
4068   * Prints the HTML for an events with a timebar.
4069   *
4070   * @param int    $id             Event id
4071   * @param string $date           Date of event in YYYYMMDD format
4072   * @param string $time           Time of event in HHMM format
4073   * @param int    $duration       Duration of event in minutes
4074   * @param string $name           Brief description of event
4075   * @param string $description    Full description of event
4076   * @param string $status         Status of event ('A', 'W')
4077   * @param int    $pri            Priority of event
4078   * @param string $access         Access to event by others ('P', 'R')
4079   * @param string $event_owner    User who created event
4080   * @param int    $event_category Category id for event
4081   *
4082   * @staticvar int Used to ensure all event popups have a unique id
4083   */
4084  function print_entry_timebar ( $id, $date, $time, $duration,
4085    $name, $description, $status,
4086    $pri, $access, $event_owner, $event_category=-1 ) {
4087    global $eventinfo, $login, $user, $PHP_SELF, $prefarray;
4088    static $key = 0;
4089    $insidespan = false;
4090    global $layers;
4091  
4092    // compute time offsets in % of total table width
4093    $day_start=$prefarray["WORK_DAY_START_HOUR"] * 60;
4094    if ( $day_start == 0 ) $day_start = 9*60;
4095    $day_end=$prefarray["WORK_DAY_END_HOUR"] * 60;
4096    if ( $day_end == 0 ) $day_end = 19*60;
4097    if ( $day_end <= $day_start ) $day_end = $day_start + 60; //avoid exceptions
4098  
4099    if ($time >= 0) {
4100    $bar_units= 100/(($day_end - $day_start)/60) ; // Percentage each hour occupies
4101    $ev_start = round((floor(($time/10000) - ($day_start/60)) + (($time/100)%100)/60) * $bar_units);
4102    }else{
4103      $ev_start= 0;
4104    }
4105    if ($ev_start < 0) $ev_start = 0;
4106    if ($duration > 0) {
4107      $ev_duration = round(100 * $duration / ($day_end - $day_start)) ;
4108      if ($ev_start + $ev_duration > 100 ) {
4109        $ev_duration = 100 - $ev_start;
4110      }
4111    } else {
4112      if ($time >= 0) {
4113        $ev_duration = 1;
4114      } else {
4115        $ev_duration=100-$ev_start;
4116      }
4117    }
4118    $ev_padding = 100 - $ev_start - $ev_duration;
4119    // choose where to position the text (pos=0->before,pos=1->on,pos=2->after)
4120    if ($ev_duration > 20)   { $pos = 1; }
4121     elseif ($ev_padding > 20)   { $pos = 2; }
4122     else        { $pos = 0; }
4123   
4124    echo "\n<!-- ENTRY BAR -->\n<table class=\"entrycont\" cellpadding=\"0\" cellspacing=\"0\">\n";
4125     echo "<tr>\n";
4126     echo ($ev_start > 0 ?  "<td style=\"text-align:right;  width:$ev_start%;\">" : "" );
4127     if ( $pos > 0 ) {
4128       echo ($ev_start > 0 ?  "&nbsp;</td>\n": "" ) ;
4129      echo "<td style=\"width:$ev_duration%;\">\n<table class=\"entrybar\">\n<tr>\n<td class=\"entry\">";
4130       if ( $pos > 1 ) {
4131         echo ($ev_padding > 0 ?  "&nbsp;</td>\n": "" ) . "</tr>\n</table></td>\n";
4132         echo ($ev_padding > 0 ?  "<td style=\"text-align:left; width:$ev_padding%;\">" : "");
4133      }
4134    };
4135  
4136    if ( $login != $event_owner && strlen ( $event_owner ) ) {
4137      $class = "layerentry";
4138    } else {
4139      $class = "entry";
4140      if ( $status == "W" ) $class = "unapprovedentry";
4141    }
4142    // if we are looking at a view, then always use "entry"
4143    if ( strstr ( $PHP_SELF, "view_m.php" ) ||
4144      strstr ( $PHP_SELF, "view_w.php" ) ||
4145      strstr ( $PHP_SELF, "view_v.php" ) ||
4146      strstr ( $PHP_SELF, "view_t.php" ) )
4147      $class = "entry";
4148  
4149    if ( $pri == 3 ) echo "<strong>";
4150    $popupid = "eventinfo-$id-$key";
4151    $key++;
4152    echo "<a class=\"$class\" href=\"view_entry.php?id=$id&amp;date=$date";
4153    if ( strlen ( $user ) > 0 )
4154      echo "&amp;user=" . $user;
4155    echo "\" onmouseover=\"window.status='" . 
4156      translate("View this entry") . "'; show(event, '$popupid'); return true;\" onmouseout=\"hide('$popupid'); return true;\">";
4157  
4158    if ( $login != $event_owner && strlen ( $event_owner ) ) {
4159      if ($layers) foreach ($layers as $layer) {
4160          if($layer['cal_layeruser'] == $event_owner) {
4161              $insidespan = true;
4162              echo("<span style=\"color:" . $layer['cal_color'] . ";\">");
4163          }
4164      }
4165    }
4166  
4167    echo "[$event_owner]&nbsp;";
4168    $timestr = "";
4169    if ( $duration == ( 24 * 60 ) ) {
4170      $timestr = translate("All day event");
4171    } else if ( $time >= 0 ) {
4172      $timestr = display_time ( $time );
4173      if ( $duration > 0 ) {
4174        // calc end time
4175        $h = (int) ( $time / 10000 );
4176        $m = ( $time / 100 ) % 100;
4177        $m += $duration;
4178        $d = $duration;
4179        while ( $m >= 60 ) {
4180          $h++;
4181          $m -= 60;
4182        }
4183        $end_time = sprintf ( "%02d%02d00", $h, $m );
4184        $timestr .= " - " . display_time ( $end_time );
4185      }
4186    }
4187    if ( $login != $user && $access == 'R' && strlen ( $user ) )
4188      echo "(" . translate("Private") . ")";
4189    else
4190    if ( $login != $event_owner && $access == 'R' && strlen ( $event_owner ) )
4191      echo "(" . translate("Private") . ")";
4192    else
4193    if ( $login != $event_owner && strlen ( $event_owner ) )
4194    {
4195      echo htmlspecialchars ( $name );
4196      if ( $insidespan ) { echo ("</span>"); } //end color span
4197    }
4198    else
4199      echo htmlspecialchars ( $name );
4200    echo "</a>";
4201    if ( $pri == 3 ) echo "</strong>"; //end font-weight span
4202    echo "</td>\n";
4203    if ( $pos < 2 ) {
4204      if ( $pos < 1 ) {
4205        echo "<td style=\"width:$ev_duration%;\"><table  class=\"entrybar\">\n<tr>\n<td class=\"entry\">&nbsp;</td>\n";
4206      }
4207      echo "</tr>\n</table></td>\n";
4208      echo ($ev_padding > 0 ? "<td style=\"text-align:left; width:$ev_padding%;\">&nbsp;</td>\n" : "" );
4209    }
4210    echo "</tr>\n</table>\n";
4211    if ( $login != $user && $access == 'R' && strlen ( $user ) )
4212      $eventinfo .= build_event_popup ( $popupid, $event_owner,
4213        translate("This event is confidential"), "" );
4214    else
4215    if ( $login != $event_owner && $access == 'R' && strlen ( $event_owner ) )
4216      $eventinfo .= build_event_popup ( $popupid, $event_owner,
4217        translate("This event is confidential"), "" );
4218    else
4219      $eventinfo .= build_event_popup ( $popupid, $event_owner,
4220        $description, $timestr, site_extras_for_popup ( $id ) );
4221  }
4222  
4223  /**
4224   * Prints the header for the timebar.
4225   *
4226   * @param int $start_hour Start hour
4227   * @param int $end_hour   End hour
4228   */
4229  function print_header_timebar($start_hour, $end_hour) {
4230    //      sh+1   ...   eh-1
4231    // +------+----....----+------+
4232    // |      |            |      |
4233  
4234    // print hours
4235    if ( ($end_hour - $start_hour) == 0 )
4236      $offset = 0;
4237    else
4238      $offset = round(100/($end_hour - $start_hour));
4239      echo "\n<!-- TIMEBAR -->\n<table class=\"timebar\">\n<tr><td style=\"width:$offset%;\">&nbsp;</td>\n";
4240     for ($i = $start_hour+1; $i < $end_hour; $i++) {
4241  //     $prev_offset = $offset;
4242  //     $offset = round(100/($end_hour - $start_hour)*($i - $start_hour + .5));
4243       $offset = round(100/($end_hour - $start_hour));
4244       $width = $offset;
4245      echo "<td style=\"width:$width%;text-align:left;\">$i</td>\n";
4246     }
4247  //   $width = 100 - $offset;
4248  //   echo "<td style=\"width:$width%;\">&nbsp;</td>\n";
4249     echo "</tr>\n</table>\n<!-- /TIMEBAR -->\n";
4250   
4251     // print yardstick
4252    echo "\n<!-- YARDSTICK -->\n<table class=\"yardstick\">\n<tr>\n";
4253    $width = round(100/($end_hour - $start_hour));
4254    for ($i = $start_hour; $i < $end_hour; $i++) {
4255      echo "<td style=\"width:$width%;\">&nbsp;</td>\n";
4256     }
4257     echo "</tr>\n</table>\n<!-- /YARDSTICK -->\n";
4258   }
4259  
4260  /**
4261   * Gets a list of nonuser calendars and return info in an array.
4262   *
4263   * @param string $user Login of admin of the nonuser calendars
4264   *
4265   * @return array Array of nonuser cals, where each is an array with the
4266   *               following fields:
4267   * - <var>cal_login</var>
4268   * - <var>cal_lastname</var>
4269   * - <var>cal_firstname</var>
4270   * - <var>cal_admin</var>
4271   * - <var>cal_fullname</var>
4272   */
4273  function get_nonuser_cals ($user = '') {
4274    $count = 0;
4275    $ret = array ();
4276    $sql = "SELECT cal_login, cal_lastname, cal_firstname, " .
4277      "cal_admin FROM webcal_nonuser_cals ";
4278    if ($user != '') $sql .= "WHERE cal_admin = '$user' ";
4279    $sql .= "ORDER BY cal_lastname, cal_firstname, cal_login";
4280    $res = dbi_query ( $sql );
4281    if ( $res ) {
4282      while ( $row = dbi_fetch_row ( $res ) ) {
4283        if ( strlen ( $row[1] ) || strlen ( $row[2] ) )
4284          $fullname = "$row[2] $row[1]";
4285        else
4286          $fullname = $row[0];
4287        $ret[$count++] = array (
4288          "cal_login" => $row[0],
4289          "cal_lastname" => $row[1],
4290          "cal_firstname" => $row[2],
4291          "cal_admin" => $row[3],
4292          "cal_fullname" => $fullname
4293        );
4294      }
4295      dbi_free_result ( $res );
4296    }
4297    return $ret;
4298  }
4299  
4300  /**
4301   * Loads nonuser variables (login, firstname, etc.).
4302   *
4303   * The following variables will be set:
4304   * - <var>login</var>
4305   * - <var>firstname</var>
4306   * - <var>lastname</var>
4307   * - <var>fullname</var>
4308   * - <var>admin</var>
4309   * - <var>email</var>
4310   *
4311   * @param string $login  Login name of nonuser calendar
4312   * @param string $prefix Prefix to use for variables that will be set.
4313   *                       For example, if prefix is "temp", then the login will
4314   *                       be stored in the <var>$templogin</var> global variable.
4315   */
4316  function nonuser_load_variables ( $login, $prefix ) {
4317    global $error,$nuloadtmp_email;
4318    $ret =  false;
4319    $res = dbi_query ( "SELECT cal_login, cal_lastname, cal_firstname, " .
4320      "cal_admin FROM webcal_nonuser_cals WHERE cal_login = '$login'" );
4321    if ( $res ) {
4322      while ( $row = dbi_fetch_row ( $res ) ) {
4323        if ( strlen ( $row[1] ) || strlen ( $row[2] ) )
4324          $fullname = "$row[2] $row[1]";
4325        else
4326          $fullname = $row[0];
4327  
4328          // We need the email address for the admin
4329          user_load_variables ( $row[3], 'nuloadtmp_' );
4330  
4331          $GLOBALS[$prefix . "login"] = $row[0];
4332          $GLOBALS[$prefix . "firstname"] = $row[2];
4333          $GLOBALS[$prefix . "lastname"] = $row[1];
4334          $GLOBALS[$prefix . "fullname"] = $fullname;
4335          $GLOBALS[$prefix . "admin"] = $row[3];
4336          $GLOBALS[$prefix . "email"] = $nuloadtmp_email;
4337          $ret = true;
4338      }
4339      dbi_free_result ( $res );
4340    }
4341    return $ret;
4342  }
4343  
4344  /**
4345    * Checks the webcal_nonuser_cals table to determine if the user is the
4346    * administrator for the nonuser calendar.
4347    *
4348    * @param string $login   Login of user that is the potential administrator
4349    * @param string $nonuser Login name for nonuser calendar
4350    *
4351    * @return bool True if the user is the administrator for the nonuser calendar
4352    */
4353  function user_is_nonuser_admin ( $login, $nonuser ) {
4354    $ret = false;
4355  
4356    $res = dbi_query ( "SELECT * FROM webcal_nonuser_cals " .
4357      "WHERE cal_login = '$nonuser' AND cal_admin = '$login'" );
4358    if ( $res ) {
4359      if ( dbi_fetch_row ( $res ) )
4360        $ret = true;
4361      dbi_free_result ( $res );
4362    }
4363    return $ret;
4364  }
4365  
4366  /**
4367   * Loads nonuser preferences from the webcal_user_pref table if on a nonuser
4368   * admin page.
4369   *
4370   * @param string $nonuser Login name for nonuser calendar
4371   */
4372  function load_nonuser_preferences ($nonuser) {
4373    global $prefarray;
4374    $res = dbi_query (
4375      "SELECT cal_setting, cal_value FROM webcal_user_pref " .
4376      "WHERE cal_login = '$nonuser'" );
4377    if ( $res ) {
4378      while ( $row = dbi_fetch_row ( $res ) ) {
4379        $setting = $row[0];
4380        $value = $row[1];
4381        $sys_setting = "sys_" . $setting;
4382        // save system defaults
4383        // ** don't override ones set by load_user_prefs
4384        if ( ! empty ( $GLOBALS[$setting] ) && empty ( $GLOBALS["sys_" . $setting] ))
4385          $GLOBALS["sys_" . $setting] = $GLOBALS[$setting];
4386        $GLOBALS[$setting] = $value;
4387        $prefarray[$setting] = $value;
4388      }
4389      dbi_free_result ( $res );
4390    }
4391  }
4392  
4393  /**
4394   * Determines what the day is after the <var>$TZ_OFFSET</var> and sets it globally.
4395   *
4396   * The following global variables will be set:
4397   * - <var>$thisyear</var>
4398   * - <var>$thismonth</var>
4399   * - <var>$thisday</var>
4400   * - <var>$thisdate</var>
4401   * - <var>$today</var>
4402   *
4403   * @param string $date The date in YYYYMMDD format
4404   */
4405  function set_today($date) {
4406    global $thisyear, $thisday, $thismonth, $thisdate, $today;
4407    global $TZ_OFFSET, $month, $day, $year, $thisday;
4408  
4409    // Adjust for TimeZone
4410    $today = time() + ($TZ_OFFSET * 60 * 60);
4411  
4412    if ( ! empty ( $date ) && ! empty ( $date ) ) {
4413      $thisyear = substr ( $date, 0, 4 );
4414      $thismonth = substr ( $date, 4, 2 );
4415      $thisday = substr ( $date, 6, 2 );
4416    } else {
4417      if ( empty ( $month ) || $month == 0 )
4418        $thismonth = date("m", $today);
4419      else
4420        $thismonth = $month;
4421      if ( empty ( $year ) || $year == 0 )
4422        $thisyear = date("Y", $today);
4423      else
4424        $thisyear = $year;
4425      if ( empty ( $day ) || $day == 0 )
4426        $thisday = date("d", $today);
4427      else
4428        $thisday = $day;
4429    }
4430    $thisdate = sprintf ( "%04d%02d%02d", $thisyear, $thismonth, $thisday );
4431  }
4432  
4433  /**
4434   * Converts from Gregorian Year-Month-Day to ISO YearNumber-WeekNumber-WeekDay.
4435   *
4436   * @internal JGH borrowed gregorianToISO from PEAR Date_Calc Class and added
4437   * $GLOBALS["WEEK_START"] (change noted)
4438   *
4439   * @param int $day   Day of month
4440   * @param int $month Number of month
4441   * @param int $year  Year
4442   *
4443   * @return string Date in ISO YearNumber-WeekNumber-WeekDay format
4444   *
4445   * @ignore
4446   */
4447  function gregorianToISO($day,$month,$year) {
4448      $mnth = array (0,31,59,90,120,151,181,212,243,273,304,334);
4449      $y_isleap = isLeapYear($year);
4450      $y_1_isleap = isLeapYear($year - 1);
4451      $day_of_year_number = $day + $mnth[$month - 1];
4452      if ($y_isleap && $month > 2) {
4453          $day_of_year_number++;
4454      }
4455      // find Jan 1 weekday (monday = 1, sunday = 7)
4456      $yy = ($year - 1) % 100;
4457      $c = ($year - 1) - $yy;
4458      $g = $yy + intval($yy/4);
4459      $jan1_weekday = 1 + intval((((($c / 100) % 4) * 5) + $g) % 7);
4460  
4461  
4462      // JGH added next if/else to compensate for week begins on Sunday
4463      if (! $GLOBALS["WEEK_START"] && $jan1_weekday < 7) {
4464        $jan1_weekday++;
4465      } elseif (! $GLOBALS["WEEK_START"] && $jan1_weekday == 7) {
4466        $jan1_weekday=1;
4467      }
4468  
4469      // weekday for year-month-day
4470      $h = $day_of_year_number + ($jan1_weekday - 1);
4471      $weekday = 1 + intval(($h - 1) % 7);
4472      // find if Y M D falls in YearNumber Y-1, WeekNumber 52 or
4473      if ($day_of_year_number <= (8 - $jan1_weekday) && $jan1_weekday > 4){
4474          $yearnumber = $year - 1;
4475          if ($jan1_weekday == 5 || ($jan1_weekday == 6 && $y_1_isleap)) {
4476              $weeknumber = 53;
4477          } else {
4478              $weeknumber = 52;
4479          }
4480      } else {
4481          $yearnumber = $year;
4482      }
4483      // find if Y M D falls in YearNumber Y+1, WeekNumber 1
4484      if ($yearnumber == $year) {
4485          if ($y_isleap) {
4486              $i = 366;
4487          } else {
4488              $i = 365;
4489          }
4490          if (($i - $day_of_year_number) < (4 - $weekday)) {
4491              $yearnumber++;
4492              $weeknumber = 1;
4493          }
4494      }
4495      // find if Y M D falls in YearNumber Y, WeekNumber 1 through 53
4496      if ($yearnumber == $year) {
4497          $j = $day_of_year_number + (7 - $weekday) + ($jan1_weekday - 1);
4498          $weeknumber = intval($j / 7);
4499          if ($jan1_weekday > 4) {
4500              $weeknumber--;
4501          }
4502      }
4503      // put it all together
4504      if ($weeknumber < 10)
4505          $weeknumber = '0'.$weeknumber;
4506      return "{$yearnumber}-{$weeknumber}-{$weekday}";
4507  }
4508  
4509  /**
4510   * Is this a leap year?
4511   *
4512   * @internal JGH Borrowed isLeapYear from PEAR Date_Calc Class
4513   *
4514   * @param int $year Year
4515   *
4516   * @return bool True for a leap year, else false
4517   *
4518   * @ignore
4519   */
4520  function isLeapYear($year='') {
4521    if (empty($year)) $year = strftime("%Y",time());
4522    if (strlen($year) != 4) return false;
4523    if (preg_match('/\D/',$year)) return false;
4524    return (($year % 4 == 0 && $year % 100 != 0) || $year % 400 == 0);
4525  }
4526  
4527  /**
4528   * Replaces unsafe characters with HTML encoded equivalents.
4529   *
4530   * @param string $value Input text
4531   *
4532   * @return string The cleaned text
4533   */
4534  function clean_html($value){
4535    $value = htmlspecialchars($value, ENT_QUOTES);
4536    $value = strtr($value, array(
4537      '('   => '&#40;',
4538      ')'   => '&#41;'
4539    ));
4540    return $value;
4541  }
4542  
4543  /**
4544   * Removes non-word characters from the specified text.
4545   *
4546   * @param string $data Input text
4547   *
4548   * @return string The converted text
4549   */
4550  function clean_word($data) { 
4551    return preg_replace("/\W/", '', $data);
4552  }
4553  
4554  /**
4555   * Removes non-digits from the specified text.
4556   *
4557   * @param string $data Input text
4558   *
4559   * @return string The converted text
4560   */
4561  function clean_int($data) { 
4562    return preg_replace("/\D/", '', $data);
4563  }
4564  
4565  /**
4566   * Removes whitespace from the specified text.
4567   *
4568   * @param string $data Input text
4569   * 
4570   * @return string The converted text
4571   */
4572  function clean_whitespace($data) { 
4573    return preg_replace("/\s/", '', $data);
4574  }
4575  
4576  /**
4577   * Converts language names to their abbreviation.
4578   *
4579   * @param string $name Name of the language (such as "French")
4580   *
4581   * @return string The abbreviation ("fr" for "French")
4582   */
4583  function languageToAbbrev ( $name ) {
4584    global $browser_languages;
4585    foreach ( $browser_languages as $abbrev => $langname ) {
4586      if ( $langname == $name )
4587        return $abbrev;
4588    }
4589    return false;
4590  }
4591  
4592  /**
4593   * Creates the CSS for using gradient.php, if the appropriate GD functions are
4594   * available.
4595   *
4596   * A one-pixel wide image will be used for the background image.
4597   *
4598   * <b>Note:</b> The gd library module needs to be available to use gradient
4599   * images.  If it is not available, a single background color will be used
4600   * instead.
4601   *
4602   * @param string $color   Base color
4603   * @param int    $height  Height of gradient image
4604   * @param int    $percent How many percent lighter the top color should be
4605   *                        than the base color at the bottom of the image
4606   *
4607   * @return string The style sheet text to use
4608   */
4609  function background_css ( $color, $height = '', $percent = '' ) {
4610    $ret = '';
4611  
4612    if ( ( function_exists ( 'imagepng' ) || function_exists ( 'imagegif' ) )
4613      && ( empty ( $GLOBALS['enable_gradients'] ) ||
4614      $GLOBALS['enable_gradients'] == 'Y' ) ) {
4615      $ret = "background: $color url(\"gradient.php?base=" . substr ( $color, 1 );
4616  
4617      if ( $height != '' ) {
4618        $ret .= "&height=$height";
4619      }
4620  
4621      if ( $percent != '' ) {
4622        $ret .= "&percent=$percent";
4623      }
4624  
4625      $ret .= "\") repeat-x;\n";
4626    } else {
4627      $ret = "background-color: $color;\n";
4628    }
4629  
4630    return $ret;
4631  }
4632  
4633  /**
4634   * Draws a daily outlook style availability grid showing events that are
4635   * approved and awaiting approval.
4636   *
4637   * @param string $date         Date to show the grid for
4638   * @param array  $participants Which users should be included in the grid
4639   * @param string $popup        Not used
4640   */
4641  function daily_matrix ( $date, $participants, $popup = '' ) {
4642    global $CELLBG, $TODAYCELLBG, $THFG, $THBG, $TABLEBG;
4643    global $user_fullname, $repeated_events, $events;
4644    global $WORK_DAY_START_HOUR, $WORK_DAY_END_HOUR, $TZ_OFFSET,$ignore_offset;
4645  
4646    $increment = 15;
4647    $interval = 4;
4648    $participant_pct = '20%'; //use percentage
4649  
4650    $first_hour = $WORK_DAY_START_HOUR;
4651    $last_hour = $WORK_DAY_END_HOUR;
4652    $hours = $last_hour - $first_hour;
4653    $cols = (($hours * $interval) + 1);
4654    $total_pct = '80%';
4655    $cell_pct =  80 /($hours * $interval);
4656    $master = array();
4657  
4658    // Build a master array containing all events for $participants
4659    for ( $i = 0; $i < count ( $participants ); $i++ ) {
4660  
4661      /* Pre-Load the repeated events for quckier access */
4662      $repeated_events = read_repeated_events ( $participants[$i], "", $date );
4663      /* Pre-load the non-repeating events for quicker access */
4664      $events = read_events ( $participants[$i], $date, $date );
4665  
4666      // get all the repeating events for this date and store in array $rep
4667      $rep = get_repeating_entries ( $participants[$i], $date );
4668      // get all the non-repeating events for this date and store in $ev
4669      $ev = get_entries ( $participants[$i], $date );
4670  
4671      // combine into a single array for easy processing
4672      $ALL = array_merge ( $rep, $ev );
4673  
4674      foreach ( $ALL as $E ) {
4675        if ($E['cal_time'] == 0) {
4676          $E['cal_time'] = $first_hour."0000";
4677          $E['cal_duration'] = 60 * ( $last_hour - $first_hour );
4678        } else {
4679          $E['cal_time'] = sprintf ( "%06d", $E['cal_time']);
4680        }
4681  
4682        $hour = substr($E['cal_time'], 0, 2 );
4683        $mins = substr($E['cal_time'], 2, 2 );
4684         
4685        // Timezone Offset
4686        if ( ! $ignore_offset ) $hour += $TZ_OFFSET;
4687        while ( $hour < 0 ) $hour += 24;
4688        while ( $hour > 23 ) $hour -= 24;
4689  
4690        // Make sure hour is 2 digits
4691        $hour = sprintf ( "%02d",$hour);
4692  
4693        // convert cal_time to slot
4694        if ($mins < 15) {
4695          $slot = $hour.'';
4696        } elseif ($mins >= 15 && $mins < 30) {
4697          $slot = $hour.'.25';
4698        } elseif ($mins >= 30 && $mins < 45) {
4699          $slot = $hour.'.5';
4700        } elseif ($mins >= 45) {
4701          $slot = $hour.'.75';
4702        }
4703  
4704        // convert cal_duration to bars
4705        $bars = $E['cal_duration'] / $increment;
4706  
4707        // never replace 'A' with 'W'
4708        for ($q = 0; $bars > $q; $q++) {
4709          $slot = sprintf ("%02.2f",$slot);
4710          if (strlen($slot) == 4) $slot = '0'.$slot; // add leading zeros
4711          $slot = $slot.''; // convert to a string
4712          if ( empty ( $master['_all_'][$slot] ) ||
4713            $master['_all_'][$slot]['stat'] != 'A') {
4714            $master['_all_'][$slot]['stat'] = $E['cal_status'];
4715          }
4716          if ( empty ( $master[$participants[$i]][$slot] ) ||
4717            $master[$participants[$i]][$slot]['stat'] != 'A' ) {
4718            $master[$participants[$i]][$slot]['stat'] = $E['cal_status'];
4719            $master[$participants[$i]][$slot]['ID'] = $E['cal_id'];
4720          }
4721          $slot = $slot + '0.25';
4722        }
4723  
4724      }
4725    }
4726  ?>
4727    <br />
4728    <table  align="center" class="matrixd" style="width:<?php echo $total_pct;?>;" cellspacing="0" cellpadding="0">
4729    <tr><td class="matrix" colspan="<?php echo $cols;?>"></td></tr>
4730    <tr><th style="width:<?php echo $participant_pct;?>;">
4731      <?php etranslate("Participants");?></th>
4732  <?php
4733    $str = '';
4734    $MouseOut = "onmouseout=\"window.status=''; this.style.backgroundColor='".$THBG."';\"";
4735    $CC = 1;
4736    for($i=$first_hour;$i<$last_hour;$i++) {
4737      $hour = $i;
4738      if ( $GLOBALS["TIME_FORMAT"] == "12" ) {
4739        $hour %= 12;
4740        if ( $hour == 0 ) $hour = 12;
4741      }
4742  
4743       for($j=0;$j<$interval;$j++) {
4744          $str .= ' <td  id="C'.$CC.'" class="dailymatrix" ';
4745          $MouseDown = 'onmousedown="schedule_event('.$i.','.sprintf ("%02d",($increment * $j)).');"';
4746          switch($j) {
4747            case 1:
4748                    if($interval == 4) { $k = ($hour<=9?'0':substr($hour,0,1)); }
4749      $str .= 'style="width:'.$cell_pct.'%; text-align:right;"  '.$MouseDown." onmouseover=\"window.status='Schedule a ".$hour.':'.($increment * $j<=9?'0':'').($increment * $j)." appointment.'; this.style.backgroundColor='#CCFFCC'; return true;\" ".$MouseOut." title=\"Schedule an appointment for ".$hour.':'.($increment * $j<=9?'0':'').($increment * $j).".\">";
4750                    $str .= $k."</td>\n";
4751                    break;
4752            case 2:
4753                    if($interval == 4) { $k = ($hour<=9?substr($hour,0,1):substr($hour,1,2)); }
4754      $str .= 'style="width:'.$cell_pct.'%; text-align:left;" '.$MouseDown." onmouseover=\"window.status='Schedule a ".$hour.':'.($increment * $j)." appointment.'; this.style.backgroundColor='#CCFFCC'; return true;\" ".$MouseOut." title=\"Schedule an appointment for ".$hour.':'.($increment * $j<=9?'0':'').($increment * $j).".\">";
4755                    $str .= $k."</td>\n";
4756                    break;
4757            default:
4758      $str .= 'style="width:'.$cell_pct.'%;" '.$MouseDown." onmouseover=\"window.status='Schedule a ".$hour.':'.($increment * $j<=9?'0':'').($increment * $j)." appointment.'; this.style.backgroundColor='#CCFFCC'; return true;\" ".$MouseOut." title=\"Schedule an appointment for ".$hour.':'.($increment * $j<=9?'0':'').($increment * $j).".\">";
4759                    $str .= "&nbsp;&nbsp;</td>\n";
4760                    break;
4761          }
4762         $CC++;
4763       }
4764    }
4765    echo $str .
4766      "</tr>\n<tr><td class=\"matrix\" colspan=\"$cols\"></td></tr>\n";
4767  
4768    // Add user _all_ to beginning of $participants array
4769    array_unshift($participants, '_all_');
4770  
4771    // Javascript for cells
4772    $MouseOver = "onmouseover=\"this.style.backgroundColor='#CCFFCC';\"";
4773    $MouseOut = "onmouseout=\"this.style.backgroundColor='".$CELLBG."';\"";
4774  
4775    // Display each participant
4776    for ( $i = 0; $i < count ( $participants ); $i++ ) {
4777      if ($participants[$i] != '_all_') {
4778        // Load full name of user
4779        user_load_variables ( $participants[$i], "user_" );
4780    
4781        // exchange space for &nbsp; to keep from breaking
4782        $user_nospace = preg_replace ( '/\s/', '&nbsp;', $user_fullname );
4783      } else {
4784        $user_nospace = translate("All Attendees");
4785        $user_nospace = preg_replace ( '/\s/', '&nbsp;', $user_nospace );
4786      }
4787  
4788      echo "<tr>\n<th class=\"row\" style=\"width:{$participant_pct};\">".$user_nospace."</th>\n";
4789      $col = 1;
4790      $viewMsg = translate ( "View this entry" );
4791  
4792      // check each timebar
4793      for ( $j = $first_hour; $j < $last_hour; $j++ ) {
4794         for ( $k = 0; $k < $interval; $k++ ) {
4795           $border = ($k == '0') ? ' border-left: 1px solid #000000;' : "";
4796           $MouseDown = 'onmousedown="schedule_event('.$j.','.sprintf ("%02d",($increment * $k)).');"';
4797          $RC = $CELLBG;
4798           //$space = '';
4799           $space = "&nbsp;";
4800  
4801           $r = sprintf ("%02d",$j) . '.' . sprintf ("%02d", (25 * $k)).'';
4802           if ( empty ( $master[$participants[$i]][$r] ) ) {
4803             // ignore this..
4804           } else if ( empty ( $master[$participants[$i]][$r]['ID'] ) ) {
4805             // This is the first line for 'all' users.  No event here.
4806             $space = "<span class=\"matrix\"><img src=\"pix.gif\" alt=\"\" style=\"height: 8px\" /></span>";
4807           } else if ($master[$participants[$i]][$r]['stat'] == "A") {
4808             $space = "<a class=\"matrix\" href=\"view_entry.php?id={$master[$participants[$i]][$r]['ID']}\"><img src=\"pix.gif\" title=\"$viewMsg\" alt=\"$viewMsg\" /></a>";
4809           } else if ($master[$participants[$i]][$r]['stat'] == "W") {
4810             $space = "<a class=\"matrix\" href=\"view_entry.php?id={$master[$participants[$i]][$r]['ID']}\"><img src=\"pixb.gif\" title=\"$viewMsg\" alt=\"$viewMsg\" /></a>";
4811           }
4812  
4813           echo "<td class=\"matrixappts\" style=\"width:{$cell_pct}%;$border\" ";
4814           if ($space == "&nbsp;") echo "$MouseDown $MouseOver $MouseOut";
4815           echo ">$space</td>\n";
4816           $col++;
4817        }
4818      }
4819      
4820      echo "</tr><tr>\n<td class=\"matrix\" colspan=\"$cols\">" .
4821        "<img src=\"pix.gif\" alt=\"-\" /></td></tr>\n";
4822    } // End foreach participant
4823    
4824    echo "</table><br />\n";
4825    $busy = translate ("Busy");
4826    $tentative = translate ("Tentative");
4827    echo "<table align=\"center\"><tr><td class=\"matrixlegend\" >\n";
4828    echo "<img src=\"pix.gif\" title=\"$busy\" alt=\"$busy\" /> $busy &nbsp; &nbsp; &nbsp;\n";
4829    echo "<img src=\"pixb.gif\" title=\"$tentative\" alt=\"$tentative\" /> $tentative\n";
4830    echo "</td></tr></table>\n";
4831  } 
4832  
4833  /**
4834   * Return the time in HHMMSS format of input time + duration
4835   *
4836   *
4837   * <b>Note:</b> The gd library module needs to be available to use gradient
4838   * images.  If it is not available, a single background color will be used
4839   * instead.
4840   *
4841   * @param string $time   format "235900"
4842   * @param int $duration  number of minutes
4843   *
4844   * @return string The time in HHMMSS format
4845   */
4846  function add_duration ( $time, $duration ) {
4847    $hour = (int) ( $time / 10000 );
4848    $min = ( $time / 100 ) % 100;
4849    $minutes = $hour * 60 + $min + $duration;
4850    $h = $minutes / 60;
4851    $m = $minutes % 60;
4852    $ret = sprintf ( "%d%02d00", $h, $m );
4853    //echo "add_duration ( $time, $duration ) = $ret <br />\n";
4854    return $ret;
4855  }
4856  ?>


Généré le : Fri Nov 30 19:09:19 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics