I will jump right into this application by first discussing how to generate a month-view calendar. Afterwards I will cover the personal calendar application as a whole and discuss the day-view calendar, form and events list. Then I will discuss the reminder and export convenience features.
The code to generate a calendar that displays days within a month consists really of nothing more than a couple loops once information about the desired month has been established.
A unix timestamp is retrieved first either from a URL parameter or from the time() function, which returns the current timestamp. When the page is initially requested there is little likelihood that a timestamp will be sent in the URL so the current date will be the default. Afterwards a specific timestamp can be passed from links as other dates are navigated. The date() function can then use the timestamp value to identify important aspects of the date. It is a pretty powerful function that can do more than just format a timestamp for display; it can be used to determine the starting day of a month, the number of days in a month, whether the year is a leap year or not, and so on.
$timestamp = (isset($_GET['t'])) ? $_GET['t'] : time(); list($month, $day, $year) = explode('/', date('m/d/Y', $timestamp)); $first_day_of_month = date('w', mktime(0, 0, 0, $month, 1, $year)); $total_days = date('t', $timestamp);
Tables 5-1 and 5-2 show the available format specifiers the date() function accepts and their meaning.
A timestamp represents the date as an integer. More accurately, it represents the date in terms of the number of seconds since midnight UTC of January 1, 1970. To modify the timestamp for a future or previous date you can add/subtract the appropriate number of seconds or use the function strtotime(), which understands various string constructs.
// link to preceding day. 60 × 60 × 24 = 86400 seconds in a day echo '<a href="'. htmlspecialchars($_SERVER['PHP_SELF']) . '?t=' . ($timestamp - 86400) . '"><</a> '; // link to preceding day using strtotime() function echo '<a href="'. htmlspecialchars($_SERVER['PHP_SELF']) . '?t=' . strtotime('−1 day', $timestamp) . '"><</a> ';
A tracking variable is initialized to keep track of the current day's cell being generated by the loop. An outer while loop can use the variable to iterate until it has exceeded the allotted number of days for the month. Within the while loop, an inner for loop outputs a row of seven cells, one for each day of the week.
<table> <tr> <th>Sun</th><th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th> <th>Fri</th><th>Sat</th> </tr> <?php $current = 1; while ($current <= $total_days)
{ echo '<tr>'; for ($i = 0; $i < 7; $i++) { if (($current == 1 && $i < $first_day_of_month) || ($current > $total_days)) { echo '<td> </td>'; continue; } echo '<td>' . $current . '</td>'; $current++; } echo '</tr>'; } ?> </table>
The following code demonstrates how by pulling all these pieces together—accepting an incoming timestamp, determining information about the represented month, adjusting the timestamp to formulate new links and using loops to display a grid—you are able to generate a month-view calendar. public_files/month.php serves as an example of a basic calendar which you can later modify to suit your own needs. The output is shown in Figure 5-2.
<?php include '../lib/common.php'; // accept incoming URL parameter $timestamp = (isset($_GET['t'])) ? $_GET['t'] : time(); // determine useful aspects of the requested month list($month, $day, $year) = explode('/', date('m/d/Y', $timestamp)); $first_day_of_month = date('w', mktime(0, 0, 0, $month, 1, $year)); $total_days = date('t', $timestamp); // output table header ob_start(); echo '<table id="calendar">'; echo '<tr id="calendar_header"><th colspan="7">'; echo '<a href="' . htmlspecialchars($_SERVER['PHP_SELF']) . '?t=' . strtotime('−1 month', $timestamp) . '"><</a> '; echo date('F', $timestamp) . ' ' . $year; echo ' <a href="' . htmlspecialchars($_SERVER['PHP_SELF']) . '?t=' . strtotime('+1 month', $timestamp) . '"><</a>'; echo '</th></tr>'; echo '<tr><th>Sun</th><th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th>' . '<th>Fri</th><th>Sat</th></tr>'; // output date cells $current = 1; while ($current <= $total_days)
{ echo '<tr class="calendar_dates">'; for ($i = 0; $i < 7; $i++) { if (($current == 1 && $i < $first_day_of_month) || ($current > $total_days)) { echo '<td class="empty"> </td>'; continue; } echo '<td>' . $current . '</td>'; $current++; } echo '</tr>'; } echo '</table>'; $GLOBALS['TEMPLATE']['content'] = ob_get_clean(); // assign styles for calendar $GLOBALS['TEMPLATE']['extra_head'] = '<link rel="stylesheet" type="text/css" ' . 'href="css/monthly_calendar.css"/>'; // display page include '../templates/template-page.php'; ?>
The overall logic of generating a day-view calendar is similar to that of the month-view calendar. However, instead of outputting a cell for each day, the calendar shows a single day broken up into quarter-hour increments. An outer for loop iterates from a starting hour to an ending hour whereas an inner for loop increments through intervals of 15 (60 minutes in an hour divided into quarter segments is 15 minutes) and outputs the table's cells.
When working with time values it is often easier to work with a 24-hour clock or military time. If you're not familiar with the concept, the 24-hour clock is a convention of identifying hours sequentially from 0 (midnight) to 23 (11:00 pm) instead of repeating 1 to 12 and using am to denote morning hours and pm for evening hours. Most parts of the world use a 24-hour clock as well as a lot of computer software (MySQL denotes time using the 24-hour format), whereas the United States, Canada, and Mexico use a 12-hour format. Even though the for loop iterates through the day using a 24-hour clock, the code converts the time and displays it using am and pm designators. Table 5-3 compares the two time formats.
define('DAY_HR_START', 9); define('DAY_HR_END', 17); echo '<table>'; for ($i = DAY_HR_START; $i <= DAY_HR_END; $i++) { for ($j = 0; $j < 60; $j += 15) { $hour = $i; $minutes = $j; $meridian = 'AM'; if ($hour > 12) { $meridian = 'PM'; $hour -= 12; } else if ($hour == 12) { $meridian = 'PM'; } echo '<tr>'; printf('<td>%02d:%02d %s</td>', $hour, $minutes, $meridian); echo '<td> </td>'; echo '</tr>'; } } echo '</table>';
An HTML form is needed to collect information about the event from the user. The date will be determined automatically based on the page viewed, so the form posts back to the page with the timestamp in the action URL. The form will gather the name of the event, the starting time and whether or not a reminder should be sent via e-mail.
<h2>Add Event</h2> <form action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']) . '?t=' . $timestamp; ?>"method="post"> <table> <tr> <td class="label"><label for="evt_name">Event:</label></td> <td><input type="text" id="evt_name" name="evt_name"></td> </tr><tr> <td class="label"><label for="evt_hour">Time:</label></td> <td> <select name="evt_hour" id="evt_hour"> <option value="12">12</option> <?php for ($i = 1; $i < 12; $i++) { printf('<option value="%d">%02d</option>', $i, $i); } ?> </select> : <select name="evt_min"> <?php for ($i = 0; $i < 59; $i += 15) { printf('<option value="%d">%02d</option>', $i, $i); } ?> </select> <select name="evt_pm"> <option value="no">AM</option> <option value="yes">PM</option>
</select> </td> </tr><tr> <td class="label">Notify</td> <td> <input type="radio" name="evt_notify" id="evt_notify_yes" value="yes" checked="checked"/> <label for="evt_notify_yes">Yes</label> <input type="radio" name="evt_notify" id="evt_notify_no" value="no"/> <label for="evt_notify_no">No</label> </td> </tr><tr> <td></td> <td> <input type="hidden" name="submitted" value="true"/> <input type="submit" value="Add Event"/></td> </tr> </table> </form>
Code can be positioned at the beginning of the script immediately after a timestamp has been gleaned and the needed aspects of the month have been determined with date() to validate the incoming values and add the event to the database.
if (isset($_POST['submitted'])) { // validate incoming values $evt_name = (isset($_POST['evt_name'])) ? $_POST['evt_name'] : ''; $evt_name = trim($evt_name); if (!$evt_name) { $evt_name = 'Unknown'; } $evt_pm = (isset($_POST['evt_pm']) && $_POST['evt_pm'] == 'yes'), $evt_hour = (isset($_POST['evt_hour'])) ? (int)$_POST['evt_hour'] : 0; if ($evt_pm) { $evt_hour += 12; } if ($evt_hour == 24) { $evt_hour = 12; } else if ($evt_hour == 12) { $evt_hour = 0; } $evt_min = (isset($_POST['evt_min'])) ? (int)$_POST['evt_min'] : 0; $evt_notify = (isset($_POST['evt_notify']) && $_POST['evt_notify'] == 'yes'), // add to database $query = sprintf('INSERT INTO %sCALENDAR (EVENT_NAME, EVENT_TSTAMP, ' . 'NOTIFY) VALUES ("%s", "%04d-%02d-%02d %02d:%02d:00", %d)', DB_TBL_PREFIX,
mysql_real_escape_string($evt_name, $GLOBALS['DB']), $year, $month, $day, $evt_hour, $evt_min, $evt_notify); mysql_query($query, $GLOBALS['DB']); }
The logic to display the events can be incorporated into the loop that generates the day-view calendar. Within the inner for loop a query is sent to the database to retrieve any events for the current timeslot. Personally, I prefer to use MySQL's UNIX_TIMESTAMP() function when retrieving dates from a database table. It returns the date and time as a unix timestamp as opposed to MySQL's Y-m-d h:m:s format, which is easier to manipulate in PHP using the date() function.
for ($hour = DAY_HR_START; $hour <= DAY_HR_END; $hour++) { for ($minute = 0; $minute < 60; $minute += 15) { echo '<tr>'; $d_meridian = 'AM'; $d_hour = $hour; if ($hour >= 12) { $d_meridian = 'PM'; $d_hour = ($hour > 12)?$hour − 12:$hour; } printf('<td>%d: %02d %s</td>', $d_hour, $minutes, $d_meridian); echo '<td>'; $query = sprintf('SELECT EVENT_NAME FROM %sCALENDAR WHERE ' . 'EVENT_TSTAMP = "%04d-%02d-%02d %02d:%02d:00"', DB_TBL_PREFIX, $year, $month, $day, $i, $j); $result = mysql_query($query, $GLOBALS['DB']); if (mysql_num_rows($result)) { while ($row = mysql_fetch_assoc($result)) { echo '<div>' . htmlspecialchars($row['EVENT_NAME']) . '</div>'; } } else { echo ' '; } mysql_free_result($result); echo '</td>'; echo '</tr>'; } } echo '</table>';
It is possible for some events to be scheduled outside the timeframe displayed by the daily-view calendar, but it is just as important that they be displayed as well. It is possible to submit a query to select the events for the given day that are outside the time period and displayed in a bulleted list.
<h2>Also Scheduled</h2> <?php // retrieve and display events that fall outside the daily-view hours $query = sprintf('SELECT EVENT_NAME, UNIX_TIMESTAMP(EVENT_TSTAMP) AS ' . 'EVENT_TSTAMP FROM %sCALENDAR WHERE EVENT_TSTAMP NOT BETWEEN ' . '"%4d-%02d-%02d %02d:00:00" AND "%4d-%02d-%02d %02d:59:59" ORDER BY ' . 'EVENT_TSTAMP ASC, EVENT_NAME ASC', DB_TBL_PREFIX, $year, $month, $day, DAY_HR_START, $year, $month, $day, DAY_HR_END); $result = mysql_query($query, $GLOBALS['DB']); echo '<ul>'; if (mysql_num_rows($result)) { while ($row = mysql_fetch_assoc($result)) { echo '<li>' . date('h:i A - ', $row['EVENT_TSTAMP']) . htmlspecialchars($row['EVENT_NAME']) . '</li>'; } } else { echo '<p><i>No other events scheduled</i></p>'; } mysql_free_result($result); echo '</ul>';
The following code listing for public_files/calendar.php incorporates all of the previously discussed concepts to create the main functionality of the personal calendar application. One line not discussed yet adds a link to export.php so the user can download the calendar information. Figure 5-1 (shown earlier) shows the interface in a web browser.
<?php include '../lib/common.php'; include '../lib/db.php'; print_r($_GET); print_r($_POST); // view definitions define('DAY_HR_START', 9); define('DAY_HR_END', 17); // accept incoming URL parameter $timestamp = (isset($_GET['t'])) ? $_GET['t'] : time(); // determine useful aspects of the requested month list($month, $day, $year) = explode('/', date('m/d/Y', $timestamp)); $first_day_of_month = date('w', mktime(0, 0, 0, $month, 1, $year));
$total_days = date('t', $timestamp); // add new event if (isset($_POST['submitted'])) { // validate incoming values $evt_name = (isset($_POST['evt_name'])) ? $_POST['evt_name'] : ''; $evt_name = trim($evt_name); if (!$evt_name) { $evt_name = 'Unknown'; } $evt_pm = (isset($_POST['evt_pm']) && $_POST['evt_pm'] == 'yes'), $evt_hour = (isset($_POST['evt_hour'])) ? (int)$_POST['evt_hour'] : 0; if ($evt_pm) { $evt_hour += 12; } if ($evt_hour == 24) { $evt_hour = 12; } else if ($evt_hour == 12) { $evt_hour = 0; } $evt_min = (isset($_POST['evt_min'])) ? (int)$_POST['evt_min'] : 0; $evt_notify = (isset($_POST['evt_notify']) && $_POST['evt_notify'] == 'yes'), // add to database $query = sprintf('INSERT INTO %sCALENDAR (EVENT_NAME, EVENT_TSTAMP, ' . 'NOTIFY) VALUES ("%s", "%04d-%02d-%02d %02d:%02d:00", %d)', DB_TBL_PREFIX, mysql_real_escape_string($evt_name, $GLOBALS['DB']), $year, $month, $day, $evt_hour, $evt_min, $evt_notify); mysql_query($query, $GLOBALS['DB']); } // output table header ob_start(); echo '<table id="day_calendar">'; echo '<tr id="day_calendar_header"><th colspan="2">'; echo '<a href="'. htmlspecialchars($_SERVER['PHP_SELF']) . '?t=' . strtotime('−1 day', $timestamp) . '"><</a> '; echo date('l F d, Y', $timestamp); echo ' <a href="'. htmlspecialchars($_SERVER['PHP_SELF']) . '?t=' . strtotime('+1 day', $timestamp) . '">></a>'; echo '</th></tr>'; // output cells
for ($i = DAY_HR_START; $i <= DAY_HR_END; $i++) { for ($j = 0; $j < 60; $j += 15) { echo '<tr>'; if ($i < 12) { printf('<td class="time">%d:%02d %s</td>', $i, $j, 'AM'), } else if ($i > 12) { printf('<td class="time">%d:%02d %s</td>', $i − 12, $j, 'PM'), } else { printf('<td class="time">%d:%02d %s</td>', $i, $j, 'PM'), } echo '<td class="event">'; $query = sprintf('SELECT EVENT_NAME FROM %sCALENDAR WHERE ' . 'EVENT_TSTAMP = "%04d-%02d-%02d %02d:%02d:00"', DB_TBL_PREFIX, $year, $month, $day, $i, $j); $result = mysql_query($query, $GLOBALS['DB']); if (mysql_num_rows($result)) { while ($row = mysql_fetch_assoc($result)) { echo '<div>' . htmlspecialchars($row['EVENT_NAME']) . '</div>'; } } else { echo ' '; } mysql_free_result($result); echo '</td>'; echo '</tr>'; } } echo '</table>'; // display month calendar echo '<table id="calendar">'; echo '<tr id="calendar_header"><th colspan="7">'; echo '<a href="' . htmlspecialchars($_SERVER['PHP_SELF']) . '?t=' . strtotime('−1 month', $timestamp) . '"><</a> '; echo date('F', $timestamp) . ' ' . $year;
echo ' <a href="' . htmlspecialchars($_SERVER['PHP_SELF']) . '?t=' . strtotime('+1 month', $timestamp) . '">></a>'; echo '</th></tr>'; echo '<tr><th>Sun</th><th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th>' . '<th>Fri</th><th>Sat</th></tr>'; $current = 1; while ($current <= $total_days) { echo '<tr class="calendar_dates">'; for ($i = 0; $i < 7; $i++) { if (($current == 1 && $i < $first_day_of_month) || ($current > $total_days)) { echo '<td class="empty"> </td>'; continue; } echo '<td><a href="' . htmlspecialchars($_SERVER['PHP_SELF']) . '?t=' . mktime(0, 0, 0, $month, $current, $year) . '">' . $current . '</a></td>'; $current++; } echo '</tr>'; } echo '</table>'; // Form to add event ?> <h2>Add Event</h2> <form action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']) . '?t=' . $timestamp; ?>" method="post"> <table> <tr> <td class="label"><label for="evt_name">Event:</label></td> <td><input type="text" id="evt_name" name="evt_name"></td> </tr><tr> <td class="label"><label for="evt_hour">Time:</label></td> <td> <select name="evt_hour" id="evt_hour"> <option value="12">12</option> <?php for ($i = 1; $i < 12; $i++) { printf('<option value="%d">%02d</option>', $i, $i); } ?> </select> : <select name="evt_min"> <?php for ($i = 0; $i < 59; $i += 15) { printf('<option value="%d">%02d</option>', $i, $i); } ?>
</select> <select name="evt_pm"> <option value="no">AM</option> <option value="yes">PM</option> </select> </td> </tr><tr> <td class="label">Notify</td> <td> <input type="radio" name="evt_notify" id="evt_notify_yes" value="yes" checked="checked"/> <label for="evt_notify_yes">Yes</label> <input type="radio" name="evt_notify" id="evt_notify_no" value="no"/> <label for="evt_notify_no">No</label> </td> </tr><tr> <td></td> <td> <input type="hidden" name="submitted" value="true"/> <input type="submit" value="Add Event"/></td> </tr> </table> </form> <h2>Also Scheduled</h2> <?php // retrieve and display events that fall outside the daily-view hours $query = sprintf('SELECT EVENT_NAME, UNIX_TIMESTAMP(EVENT_TSTAMP) AS ' . 'EVENT_TSTAMP FROM %sCALENDAR WHERE EVENT_TSTAMP NOT BETWEEN ' . '"%4d-%02d-%02d %02d:00:00" AND "%4d-%02d-%02d %02d:59:59" ORDER BY ' . 'EVENT_TSTAMP ASC, EVENT_NAME ASC', DB_TBL_PREFIX, $year, $month, $day, DAY_HR_START, $year, $month, $day, DAY_HR_END); $result = mysql_query($query, $GLOBALS['DB']); echo '<ul>'; if (mysql_num_rows($result)) { while ($row = mysql_fetch_assoc($result)) { echo '<li>' . date('h:i A - ', $row['EVENT_TSTAMP']) . htmlspecialchars($row['EVENT_NAME']) . '</li>'; } } else { echo '<p><i>No other events scheduled</i></p>'; } mysql_free_result($result); echo '</ul>'; // link to download iCal file
echo '<p><a href="export.php">Export as iCalendar file</a></p>'; $GLOBALS['TEMPLATE']['content'] = ob_get_clean(); $GLOBALS['TEMPLATE']['extra_head'] = '<link rel="stylesheet"' . 'type="text/css" href="css/daily.css"/>'; include '../templates/template-page.php'; ?>
The project requirements also call for the ability for reminders to be sent when an event approaches. The first part of this is already in place with the form — selecting the option to be reminded or not. The preference is stored in the database for later examination. A separate script must be written to run periodically to check the database for upcoming events with the preference set. The script can then be scheduled the same way the mailing list scripts in chapter 3 were.
The script must know where to send the reminders so the e-mail address is provided as a constant. It must also determine the current date and time values to be able to construct the desired WHERE clause in the query. The query retrieves any events from the database that are scheduled to take place in the next hour from the current 15-minute timeframe, constructs an e-mail message, and mails it out.
Here is the complete code listing for public_files/notify.php. Because it will be run as a shell script instead of in response to a user request, the first line must be #! /usr/bin/php (or whichever path your installation's PHP interpreter resides at) and the file's execute permissions must be set correctly.
#! /usr/bin/php <?php include '../lib/common.php'; include '../lib/db.php'; // the e-mail address that will receive reminders define('E-mail_ADDR', '[email protected]); // determine the current date and time values list($month, $day, $year, $hour, $minute, $am) = explode('/', date('m/d/Y/G/i/A')); // retrieve upcoming events $query = sprintf('SELECT EVENT_NAME, UNIX_TIMESTAMP(EVENT_TSTAMP) AS ' . 'EVENT_TSTAMP FROM %sCALENDAR WHERE NOTIFY = 1 AND EVENT_TSTAMP BETWEEN ' . '"%4d-%02d-%02d %02d:%02d:00" AND "%4d-%02d-%02d %02d:%02d:00" ORDER BY ' . 'EVENT_TSTAMP ASC, EVENT_NAME ASC', DB_TBL_PREFIX, $year, $month, $day, $hour, $minute, $year, $month, $day, $hour, $minute + 15); $result = mysql_query($query, $GLOBALS['DB']); if (mysql_num_rows($result)) { // construct the reminder message
$msg = 'Don't forget! You have the following events scheduled:' . " "; while ($row = mysql_fetch_assoc($result)) { $msg .= ' * ' . date('h:i A - ', $row['EVENT_TSTAMP']) . $row['EVENT_NAME'] . " "; } // send the message mail(E-mail_ADDR, "Reminders for $month/$day/$year $hour:$minute $am", $mgs); } mysql_free_result($result); mysql_close($GLOBALS['DB']); ?>
The script is written to look at events scheduled in the next hour from the current 15-minute timeframe, so it should be scheduled to run once every 15 minutes. You may refer back to chapter 3 if you need to review scheduling a script with cron or the Windows Scheduled Tasks applet.
The final feature of the personal calendar application is the ability to export the saved entries as an iCalendar file so they can easily be shared with others. iCalendar is a text-based format to exchange calendar information and is standardized as RFC 2445 (available online at http://rfc.net/rfc2445.txt). The iCalendar format defines various components such as a calendar, an event, an alarm, busy times, and to-do lists. However, the only ones used in this application are the calendar, event, and alarm.
Some components may nest within other components and each component follows the schema BEGIN:component, properties, or other components then END:component. Each property is listed as an identifier and its value separated by a colon, one on each line. Each line terminates with a carriage return and a new line character ( ).
The iCalendar file begins with BEGIN:VCALENDAR and ends with END:VCALENDAR. Between the two delimiters are contained a few properties necessary to indicate which version of iCalendar is being adhered to and the collection of other components which populates the calendar.
BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Wrox//PHP Reuse//EN ... END:VCALENDAR
The component that collects several properties to define an event is VEVENT. In particular, the properties I'm interested in are DTSTART and DTEND, which define the starting and ending time of an event and SUMMARY, which provides a brief textual description. Here's an example of an event listing:
BEGIN:VEVENT DTSTART:20071228T103000 DTEND:20071228T110000 SUMMARY:Dentist Appointment END:VEVENT
The alarm component VALARM may be nested within a VEVENT and contains information an iCalendar application uses to trigger reminders. The properties I'm interested in are ACTION and TRIGGER. The TRIGGER property defines when to trigger the alert and the ACTION property defines how the alarm is issued. The application can be instructed to display the alert (DISPLAY), e-mail it (E-mail), play a sound (AUDIO), or trigger some other program (PROCEDURE). Here's the sample event again, but this time with a defined alert component to issue an on-screen reminder an hour beforehand.
BEGIN:VEVENT DTSTART:20071228T103000 DTEND:20071228T110000 SUMMARY:Dentist Appointment BEGIN:VALARM TRIGGER:-PT60M SUMMARY:You have a dentist appointment in 1 hour ACTION:DISPLAY END:VALARM END:VEVENT
Here is the complete code for public_files/export.php, which queries the database and constructs an iCalendar file. A listing of all the properties for the calendar, event, and alarm components is provided in Table 5-4.
<?php include '../lib/common.php'; include '../lib/db.php'; define('CRLF', " "); // retrieve all events $query = sprintf('SELECT EVENT_NAME, UNIX_TIMESTAMP(EVENT_TSTAMP) AS ' . 'EVENT_TSTAMP, NOTIFY FROM %sCALENDAR ORDER BY EVENT_TSTAMP ASC, ' . 'EVENT_NAME ASC', DB_TBL_PREFIX); $result = mysql_query($query, $GLOBALS['DB']); // generate iCalendar ob_start(); echo 'BEGIN:VCALENDAR' . CRLF; echo 'PRODID:-//Wrox//PHP Reuse//EN' . CRLF; echo 'VERSION:2.0' . CRLF; while ($row = mysql_fetch_assoc($result)) { echo 'BEGIN:VEVENT' . CRLF; echo 'DTSTART:' . date('YmdTHis', $row['EVENT_TSTAMP']) . CRLF; echo 'DTEND:' . date('YmdTHis', strtotime('+30 minutes', $row['EVENT_TSTAMP'])) . CRLF; echo 'SUMMARY:' . htmlspecialchars($row['EVENT_NAME']) . CRLF; if ($row['NOTIFY']) { echo 'BEGIN:VALARM' . CRLF; echo 'ACTION:DISPLAY' . CRLF;
echo 'SUMMARY:' . date('m/d/Y H:i A - ', $row['EVENT_TSTAMP']) . htmlspecialchars($row['EVENT_NAME']) . CRLF; echo 'TRIGGER:-PT60M' . CRLF; echo 'END:VALARM' . CRLF; } echo 'END:VEVENT' . CRLF; } mysql_free_result($result); echo 'END:VCALENDAR' . CRLF; $ics = ob_get_clean(); // send iCalendar file to browser header('Content-Type: text/calendar'), header('Content-Disposition: attachment; filename="export.ics";'), header('Content-Transfer-Encoding: binary'), header('Content-Length: ' . strlen($ics)); echo $ics; mysql_close($GLOBALS['DB']); ?>
After the calendar information has been exported as an iCalendar file, it can be shared with others. Figures 5-3 and 5-4 show the information after it has been imported into a couple different desktop calendar programs. Figure 5-3 shows it in Microsoft Windows Calendar, which ships with its new Vista operating system; Figure 5-4 shows it in the Mozilla Thunderbird program running the Lightning integrated calendar extension.
3.139.86.56