of recurring events has been much improved.
If searching in a time range, all recurring instances are returned, if
searching without end, only the next recurrence is returned, if searching
without start, the first recurrence is returned.
v3.0-git
--------
+[jan] Allow searching of any type of calendar and improve searching of
+ recurring events.
[cjh] With only SHOW permissions, display event titles as "busy".
[mjr] Replace categories and keywords by tags.
[jan] Set colors per calendar (Request #7480).
/**
* Searches a calendar.
*
- * @param object Kronolith_Event $query A Kronolith_Event object with the
- * criteria to search for.
+ * @param object $query An object with the criteria to search for.
+ * @param boolean $json Store the results of the events' toJson() method?
*
* @return mixed An array of Kronolith_Events or a PEAR_Error.
*/
- public function search($query)
+ public function search($query, $json = false)
{
/* Our default implementation first gets <em>all</em> events in a
* specific period, and then filters based on the actual values that
return $events;
}
- foreach ($events as $eventid) {
- $event = $this->getEvent($eventid);
- if (is_a($event, 'PEAR_Error')) {
- return $event;
- }
-
- if ((((!isset($query->start) ||
- $event->end->compareDateTime($query->start) > 0) &&
- (!isset($query->end) ||
- $event->end->compareDateTime($query->end) < 0)) ||
- ($event->recurs() &&
- $event->end->compareDateTime($query->start) >= 0 &&
- $event->start->compareDateTime($query->end) <= 0)) &&
- (empty($query->title) ||
- stristr($event->getTitle(), $query->title)) &&
- (empty($query->location) ||
- stristr($event->getLocation(), $query->location)) &&
- (empty($query->description) ||
- stristr($event->getDescription(), $query->description)) &&
- (empty($query->creatorID) ||
- stristr($event->getCreatorID(), $query->creatorID)) &&
- (!isset($query->status) ||
- $event->getStatus() == $query->status)) {
- $results[] = $event;
+ foreach ($events as $day => $day_events) {
+ foreach ($day_events as $event) {
+ if ((((!isset($query->start) ||
+ $event->end->compareDateTime($query->start) > 0) &&
+ (!isset($query->end) ||
+ $event->end->compareDateTime($query->end) < 0)) ||
+ ($event->recurs() &&
+ $event->end->compareDateTime($query->start) >= 0 &&
+ $event->start->compareDateTime($query->end) <= 0)) &&
+ (empty($query->title) ||
+ stristr($event->getTitle(), $query->title)) &&
+ (empty($query->location) ||
+ stristr($event->getLocation(), $query->location)) &&
+ (empty($query->description) ||
+ stristr($event->getDescription(), $query->description)) &&
+ (empty($query->creatorID) ||
+ stristr($event->getCreatorID(), $query->creatorID)) &&
+ (!isset($query->status) ||
+ $event->getStatus() == $query->status)) {
+ Kronolith::addEvents($results, $event, $event->start, $event->end, false, $json, false);
+ }
}
}
return $events;
}
- public function search($query)
+ /**
+ * Searches a calendar.
+ *
+ * @param object $query An object with the criteria to search for.
+ * @param boolean $json Store the results of the events' toJson() method?
+ *
+ * @return mixed An array of Kronolith_Events or a PEAR_Error.
+ */
+ public function search($query, $json = false)
{
/* Build SQL conditions based on the query string. */
$cond = '((';
return $eventIds;
}
+ $now = new Horde_Date($_SERVER['REQUEST_TIME']);
$events = array();
foreach ($eventIds as $eventId) {
$event = $this->getEvent($eventId);
if (is_a($event, 'PEAR_Error')) {
return $event;
}
- $events[] = $event;
+ $showRecurrence = true;
+ if ($event->recurs()) {
+ if (empty($query->end)) {
+ $eventEnd = $event->recurrence->nextRecurrence($now);
+ if (!$eventEnd) {
+ continue;
+ }
+ } else {
+ $eventEnd = $query->end;
+ }
+ if (empty($query->start)) {
+ $eventStart = $event->start;
+ $showRecurrence = false;
+ } else {
+ $eventStart = $query->start;
+ }
+ } else {
+ $eventStart = $event->start;
+ $eventEnd = $event->end;
+ }
+ Kronolith::addEvents($events, $event, $eventStart, $eventEnd, $showRecurrence, $json, false);
}
return $events;
/**
* Searches for events with the given properties.
*
- * @param object $query The search query.
+ * @param object $query The search query.
+ * @param string $calendar The calendar to search in the form
+ * "Driver|calendar_id".
*
* @return array The events.
*/
- public static function search($query)
+ public static function search($query, $calendar = null)
{
- $kronolith_driver = Kronolith::getDriver();
-
- if (!isset($query->calendars)) {
- $calendars = $GLOBALS['display_calendars'];
+ if ($calendar) {
+ $driver = explode('|', $calendar, 2);
+ $calendars = array($driver[0] => array($driver[1]));
} else {
- $calendars = $query->calendars;
+ $calendars = array(
+ String::ucfirst($GLOBALS['conf']['calendar']['driver']) => $GLOBALS['display_calendars'],
+ 'Horde' => $GLOBALS['display_external_calendars'],
+ 'Ical' => $GLOBALS['display_remote_calendars']);
+ if (!empty($GLOBALS['conf']['holidays']['enable'])) {
+ $calendars['Holidays'] = unserialize($GLOBALS['prefs']->getValue('holiday_drivers'));
+ }
}
$events = array();
- foreach ($calendars as $cal) {
- $kronolith_driver->open($cal);
- $retevents = $kronolith_driver->search($query);
- foreach ($retevents as $event) {
- $events[] = $event;
+ foreach ($calendars as $type => $list) {
+ $kronolith_driver = Kronolith::getDriver($type);
+ foreach ($list as $cal) {
+ $kronolith_driver->open($cal);
+ $retevents = $kronolith_driver->search($query);
+ Kronolith::mergeEvents($events, $retevents);
}
}
$results[$day] = $day_events;
}
}
+ ksort($results);
}
/**
* @access private
*/
public static function addEvents(&$results, &$event, $startDate, $endDate,
- $showRecurrence, $json)
+ $showRecurrence, $json,
+ $coverDates = false)
{
if ($event->recurs() && $showRecurrence) {
/* Recurring Event. */
if (!$event->recurrence->hasException($event->start->year,
$event->start->month,
$event->start->mday)) {
- Kronolith::addCoverDates($results, $event, $event->start, $event->end, $json);
+ if ($coverDates) {
+ Kronolith::addCoverDates($results, $event, $event->start, $event->end, $json);
+ } else {
+ $results[$event->start->dateString()][$event->getId()] = $json ? $event->toJson() : $event;
+ }
}
/* Start searching for recurrences from the day after it
$nextEnd->mday += $diff[2];
$nextEnd->hour += $diff[3];
$nextEnd->min += $diff[4];
- Kronolith::addCoverDates($results, $event, $next, $nextEnd, $json);
+ if ($coverDates) {
+ Kronolith::addCoverDates($results, $event, $next, $nextEnd, $json);
+ } else {
+ $addEvent = clone $event;
+ $addEvent->start = $next;
+ $addEvent->end = $nextEnd;
+ $results[$addEvent->start->dateString()][$addEvent->getId()] = $json ? $addEvent->toJson() : $addEvent;
+
+ }
}
$next = $event->recurrence->nextRecurrence(
array('year' => $next->year,
'sec' => $next->sec));
}
} else {
- /* Event only occurs once. */
- $allDay = $event->isAllDay();
-
- /* Work out what day it starts on. */
- if ($event->start->compareDateTime($startDate) < 0) {
- /* It started before the beginning of the period. */
- $eventStart = clone $startDate;
+ if (!$coverDates) {
+ $results[$event->start->dateString()][$event->getId()] = $json ? $event->toJson() : $event;
} else {
- $eventStart = clone $event->start;
- }
+ /* Event only occurs once. */
+ $allDay = $event->isAllDay();
- /* Work out what day it ends on. */
- if ($event->end->compareDateTime($endDate) > 0) {
- /* Ends after the end of the period. */
- $eventEnd = clone $endDate;
- } else {
- /* If the event doesn't end at 12am set the end date to the
- * current end date. If it ends at 12am and does not end at
- * the same time that it starts (0 duration), set the end date
- * to the previous day's end date. */
- if ($event->end->hour != 0 ||
- $event->end->min != 0 ||
- $event->end->sec != 0 ||
- $event->start->compareDateTime($event->end) == 0 ||
- $allDay) {
- $eventEnd = clone $event->end;
+ /* Work out what day it starts on. */
+ if ($event->start->compareDateTime($startDate) < 0) {
+ /* It started before the beginning of the period. */
+ $eventStart = clone $startDate;
} else {
- $eventEnd = new Horde_Date(
- array('hour' => 23,
- 'min' => 59,
- 'sec' => 59,
- 'month' => $event->end->month,
- 'mday' => $event->end->mday - 1,
- 'year' => $event->end->year));
+ $eventStart = clone $event->start;
}
- }
- /* Add the event to all the days it covers. This is
- * similar to Kronolith::addCoverDates(), but for days in
- * between the start and end day, the range is midnight to
- * midnight, and for the edge days it's start to midnight,
- * and midnight to end. */
- $i = $eventStart->mday;
- $loopDate = new Horde_Date(array('month' => $eventStart->month,
- 'mday' => $i,
- 'year' => $eventStart->year));
- while ($loopDate->compareDateTime($eventEnd) <= 0) {
- if (!$allDay ||
- $loopDate->compareDateTime($eventEnd) != 0) {
- $addEvent = clone $event;
-
- /* If this is the start day, set the start time to
- * the real start time, otherwise set it to
- * 00:00 */
- if ($loopDate->compareDate($eventStart) == 0) {
- $addEvent->start = $eventStart;
+ /* Work out what day it ends on. */
+ if ($event->end->compareDateTime($endDate) > 0) {
+ /* Ends after the end of the period. */
+ $eventEnd = clone $endDate;
+ } else {
+ /* If the event doesn't end at 12am set the end date to the
+ * current end date. If it ends at 12am and does not end at
+ * the same time that it starts (0 duration), set the end date
+ * to the previous day's end date. */
+ if ($event->end->hour != 0 ||
+ $event->end->min != 0 ||
+ $event->end->sec != 0 ||
+ $event->start->compareDateTime($event->end) == 0 ||
+ $allDay) {
+ $eventEnd = clone $event->end;
} else {
- $addEvent->start = new Horde_Date(array(
- 'hour' => 0, 'min' => 0, 'sec' => 0,
- 'month' => $loopDate->month, 'mday' => $loopDate->mday, 'year' => $loopDate->year));
+ $eventEnd = new Horde_Date(
+ array('hour' => 23,
+ 'min' => 59,
+ 'sec' => 59,
+ 'month' => $event->end->month,
+ 'mday' => $event->end->mday - 1,
+ 'year' => $event->end->year));
}
+ }
- /* If this is the end day, set the end time to the
- * real event end, otherwise set it to 23:59. */
- if ($loopDate->compareDate($eventEnd) == 0) {
- $addEvent->end = $eventEnd;
- } else {
- $addEvent->end = new Horde_Date(array(
- 'hour' => 23, 'min' => 59, 'sec' => 59,
- 'month' => $loopDate->month, 'mday' => $loopDate->mday, 'year' => $loopDate->year));
+ /* Add the event to all the days it covers. This is similar to
+ * Kronolith::addCoverDates(), but for days in between the
+ * start and end day, the range is midnight to midnight, and
+ * for the edge days it's start to midnight, and midnight to
+ * end. */
+ $i = $eventStart->mday;
+ $loopDate = new Horde_Date(array('month' => $eventStart->month,
+ 'mday' => $i,
+ 'year' => $eventStart->year));
+ while ($loopDate->compareDateTime($eventEnd) <= 0) {
+ if (!$allDay ||
+ $loopDate->compareDateTime($eventEnd) != 0) {
+ $addEvent = clone $event;
+
+ /* If this is the start day, set the start time to
+ * the real start time, otherwise set it to
+ * 00:00 */
+ if ($loopDate->compareDate($eventStart) == 0) {
+ $addEvent->start = $eventStart;
+ } else {
+ $addEvent->start = new Horde_Date(array(
+ 'hour' => 0, 'min' => 0, 'sec' => 0,
+ 'month' => $loopDate->month, 'mday' => $loopDate->mday, 'year' => $loopDate->year));
+ }
+
+ /* If this is the end day, set the end time to the
+ * real event end, otherwise set it to 23:59. */
+ if ($loopDate->compareDate($eventEnd) == 0) {
+ $addEvent->end = $eventEnd;
+ } else {
+ $addEvent->end = new Horde_Date(array(
+ 'hour' => 23, 'min' => 59, 'sec' => 59,
+ 'month' => $loopDate->month, 'mday' => $loopDate->mday, 'year' => $loopDate->year));
+ }
+
+ $results[$loopDate->dateString()][$addEvent->getId()] = $json ? $addEvent->toJson($allDay) : $addEvent;
}
- $results[$loopDate->dateString()][$addEvent->getId()] = $json ? $addEvent->toJson($allDay) : $addEvent;
+ $loopDate = new Horde_Date(
+ array('month' => $eventStart->month,
+ 'mday' => ++$i,
+ 'year' => $eventStart->year));
}
-
- $loopDate = new Horde_Date(
- array('month' => $eventStart->month,
- 'mday' => ++$i,
- 'year' => $eventStart->year));
}
}
ksort($results);
public static function addCoverDates(&$results, $event, $eventStart,
$eventEnd, $json)
{
- $i = $eventStart->mday;
- $loopDate = new Horde_Date(array('month' => $eventStart->month,
- 'mday' => $i,
- 'year' => $eventStart->year));
+ $loopDate = new Horde_Date($eventStart->year, $eventStart->month, $eventStart->mday);
$allDay = $event->isAllDay();
while ($loopDate->compareDateTime($eventEnd) <= 0) {
if (!$allDay ||
$addEvent->end = $eventEnd;
$results[$loopDate->dateString()][$addEvent->getId()] = $json ? $addEvent->toJson($allDay) : $addEvent;
}
- $loopDate = new Horde_Date(
- array('month' => $eventStart->month,
- 'mday' => ++$i,
- 'year' => $eventStart->year));
+ $loopDate->mday++;
}
}
* @author Meilof Veeningen <meilof@gmail.com>
*/
-/**
- * Used with usort() to sort events based on their start times.
- */
-function _sortEvents($a, $b)
-{
- $start_a = $a->recurs() ? $a->recurrence->nextRecurrence($GLOBALS['event']->start) : $a->start;
- $start_b = $b->recurs() ? $b->recurrence->nextRecurrence($GLOBALS['event']->start) : $b->start;
- $diff = $start_a->compareDateTime($start_b);
- if ($diff == 0) {
- return strcoll($a->title, $b->title);
- } else {
- return $diff;
- }
-}
-
@define('KRONOLITH_BASE', dirname(__FILE__));
require_once KRONOLITH_BASE . '/lib/base.php';
/* Get search parameters. */
$search_mode = Util::getFormData('search_mode', 'basic');
+$search_calendar = explode('|', Util::getFormData('calendar', '|__any'), 2);
+$events = null;
-if ($search_mode != 'basic') {
+if ($search_mode == 'basic') {
+ $desc = Util::getFormData('pattern_desc');
+ $title = Util::getFormData('pattern_title');
+ if (strlen($desc) || strlen($title)) {
+ $event = Kronolith::getDriver()->getEvent();
+ $event->setDescription($desc);
+ $event->setTitle($title);
+ $event->status = null;
+
+ $time1 = $_SERVER['REQUEST_TIME'];
+ $range = Util::getFormData('range');
+ if ($range == '+') {
+ $event->start = new Horde_Date($time1);
+ $event->end = null;
+ } elseif ($range == '-') {
+ $event->start = null;
+ $event->end = new Horde_Date($time1);
+ } else {
+ $time2 = $time1 + $range;
+ $event->start = new Horde_Date(min($time1, $time2));
+ $event->end = new Horde_Date(max($time1, $time2));
+ }
+ $events = Kronolith::search($event);
+ }
+} else {
/* Make a new empty event object with default values. */
- $event = Kronolith::getDriver()->getEvent();
- $event->title = $event->calendars = $event->location =
- $event->status = $event->description = null;
+ $event = Kronolith::getDriver($search_calendar[0], $search_calendar[1])->getEvent();
+ $event->title = $event->location = $event->status = $event->description = null;
/* Set start on today, stop on tomorrow. */
$event->start = new Horde_Date(mktime(0, 0, 0));
$event->initialized = true;
$q_title = Util::getFormData('title');
-
- if (isset($q_title)) {
- /* We're returning from a previous search. */
+ if (strlen($q_title)) {
$event->readForm();
if (Util::getFormData('status') == Kronolith::STATUS_NONE) {
$event->status = null;
}
- }
-}
-$desc = Util::getFormData('pattern_desc');
-$title = Util::getFormData('pattern_title');
-if ($desc || $title) {
- /* We're doing a simple search. */
- $event = Kronolith::getDriver()->getEvent();
- $event->setDescription($desc);
- $event->setTitle($title);
- $event->status = null;
+ $events = Kronolith::search($event, $search_calendar[1] == '__any' ? null : $search_calendar[0] . '|' . $search_calendar[1]);
+ }
- $time1 = $_SERVER['REQUEST_TIME'];
- $range = Util::getFormData('range');
- if ($range == '+') {
- $event->start = new Horde_Date($time1);
- $event->end = null;
- } elseif ($range == '-') {
- $event->start = null;
- $event->end = new Horde_Date($time1);
- } else {
- $time2 = $time1 + $range;
- $event->start = new Horde_Date(min($time1, $time2));
- $event->end = new Horde_Date(max($time1, $time2));
+ $optgroup = $GLOBALS['browser']->hasFeature('optgroup');
+ $current_user = Auth::getAuth();
+ $calendars = array();
+ foreach (Kronolith::listCalendars(false, PERMS_READ) as $id => $cal) {
+ if ($cal->get('owner') == $current_user) {
+ $calendars[_("My Calendars:")]['|' . $id] = $cal->get('name');
+ } else {
+ $calendars[_("Shared Calendars:")]['|' . $id] = $cal->get('name');
+ }
+ }
+ foreach ($GLOBALS['all_external_calendars'] as $api => $categories) {
+ $app = $GLOBALS['registry']->get('name', $GLOBALS['registry']->hasInterface($api));
+ foreach ($categories as $id => $name) {
+ $calendars[$app . ':']['Horde|external_' . $api . '/' . $id] = $name;
+ }
+ }
+ foreach ($GLOBALS['all_remote_calendars'] as $cal) {
+ $calendars[_("Remote Calendars:")]['Ical|' . $cal['url']] = $cal['name'];
+ }
+ if (!empty($GLOBALS['conf']['holidays']['enable'])) {
+ foreach (unserialize($GLOBALS['prefs']->getValue('holiday_drivers')) as $holiday) {
+ $calendars[_("Holidays:")]['Holidays|' . $holiday] = $holiday;
+ }
}
- $events = Kronolith::search($event);
-} elseif (isset($q_title)) {
- /* Advanced search. */
- $events = Kronolith::search($event);
}
$title = _("Search");
}
/* Display search results. */
-if (isset($events)) {
+if (!is_null($events)) {
if (count($events)) {
- usort($events, '_sortEvents');
-
require KRONOLITH_TEMPLATES . '/search/header.inc';
require KRONOLITH_TEMPLATES . '/search/event_headers.inc';
-
- foreach ($events as $found) {
- $start = $found->recurs() ? $found->recurrence->nextRecurrence($event->start) : $found->start;
- $end = new Horde_Date($start);
- $end->min += $found->durMin;
- require KRONOLITH_TEMPLATES . '/search/event_summaries.inc';
+ foreach ($events as $day => $day_events) {
+ foreach ($day_events as $event) {
+ require KRONOLITH_TEMPLATES . '/search/event_summaries.inc';
+ }
}
require KRONOLITH_TEMPLATES . '/search/event_footers.inc';
} else {
<tr class="text">
<td nowrap="nowrap">
- <?php echo $found->getLink() ?>
+ <?php echo $event->getLink() ?>
</td>
- <td class="nowrap"><?php echo htmlspecialchars($found->getLocation()) ?></td>
- <td class="nowrap"><?php echo Kronolith::statusToString($found->getStatus()) ?></td>
- <td class="nowrap"><?php echo $start->strftime($prefs->getValue('date_format')) . $start->strftime($prefs->getValue('twentyFour') ? ' %H:%M' : ' %I:%M %p') ?></td>
- <td class="nowrap"><?php echo $end->strftime($prefs->getValue('date_format')) . $end->strftime($prefs->getValue('twentyFour') ? ' %H:%M' : ' %I:%M %p') ?></td>
+ <td class="nowrap"><?php echo htmlspecialchars($event->getLocation()) ?></td>
+ <td class="nowrap"><?php echo Kronolith::statusToString($event->getStatus()) ?></td>
+ <td class="nowrap"><?php echo $event->start->strftime($prefs->getValue('date_format')) . $event->start->strftime($prefs->getValue('twentyFour') ? ' %H:%M' : ' %I:%M %p') ?></td>
+ <td class="nowrap"><?php echo $event->end->strftime($prefs->getValue('date_format')) . $event->end->strftime($prefs->getValue('twentyFour') ? ' %H:%M' : ' %I:%M %p') ?></td>
</tr>
-<?php require KRONOLITH_TEMPLATES . '/edit/javascript.inc'; ?>
+<?php
+$issearch = true;
+require KRONOLITH_TEMPLATES . '/edit/javascript.inc';
+?>
<form method="post" name="eventform" action="search.php">
<?php Util::pformInput() ?>
<input type="hidden" name="actionID" value="search_calendar" />
<strong><?php echo _("General") ?></strong>
</td></tr>
-<?php
-$calendars = Kronolith::listCalendars(false, PERMS_READ);
-$issearch = true;
-?>
<!-- title -->
<tr>
<td class="rightAlign"><strong><?php echo Horde::label('title', _("Tit_le")) ?></strong></td>
</td>
<td colspan="4">
<select id="calendar" name="calendar">
- <?php
- $eCalendar = $event->getCalendar();
- echo '<option value="__any"' .
- (!$eCalendar ? ' selected="selected"' : '') . '>' . _("Any") .
- '</option>';
- foreach ($calendars as $id => $cal) {
- $sel = ($id == $eCalendar) ? ' selected="selected"' : '';
- printf('<option value="%s"%s>%s</option>',
- htmlspecialchars($id), $sel, htmlspecialchars($cal->get('name'))) . "\n";
- } ?>
+ <option value="|__any"<?php if ($search_calendar[1] == '__any') echo ' selected="selected"' ?>><?php echo _("Any") ?></option>
+ <?php foreach ($calendars as $type => $list): ?>
+ <?php if ($optgroup): ?>
+ <optgroup label="<?php echo htmlspecialchars($type) ?>">
+ <?php endif; ?>
+ <?php foreach ($list as $id => $name): ?>
+ <option value="<?php echo htmlspecialchars($id) ?>"<?php if ($search_calendar[0] . '|' . $search_calendar[1] == $id) echo ' selected="selected"' ?>><?php echo ($optgroup ? '' : htmlspecialchars($type) . ' ') . htmlspecialchars($name) ?></option>
+ <?php endforeach; ?>
+ <?php if ($optgroup): ?>
+ </optgroup>
+ <?php endif; ?>
+ <?php endforeach; ?>
</select>
</td>
</tr>