plugins/calendar plugins/libkolab

Thomas Brüderli bruederli at kolabsys.com
Thu Feb 5 19:37:26 CET 2015


 plugins/calendar/drivers/kolab/kolab_calendar.php |   66 ++++++++++++++++------
 plugins/libkolab/lib/kolab_format_xcal.php        |   19 +++++-
 2 files changed, 65 insertions(+), 20 deletions(-)

New commits:
commit 754a4d51bfa6f08ee7aadff2c3b270e9ca185e28
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 5 19:37:03 2015 +0100

    Improve search in calendar: consider recurrence exceptions for indexing and matching (#4279); ignore order of search terms (boolean and matching)

diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index 9e3ecdf..9d573ee 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -271,27 +271,19 @@ class kolab_calendar extends kolab_storage_folder_api
       // remember seen categories
       if ($event['categories'])
         $this->categories[$event['categories']]++;
-      
+
       // filter events by search query
       if (!empty($search)) {
-        $hit = false;
-        foreach ($this->search_fields as $col) {
-          $sval = is_array($event[$col]) ? self::_complex2string($event[$col]) : $event[$col];
-          if (empty($sval))
-            continue;
-          
-          // do a simple substring matching (to be improved)
-          $val = mb_strtolower($sval);
-          if (strpos($val, $search) !== false) {
-            $hit = true;
-            break;
-          }
+        $hits = 0;
+        $words = rcube_utils::tokenize_string($search, 1);
+        foreach ($words as $word) {
+          $hits += $this->_fulltext_match($event, $word);
         }
-        
-        if (!$hit)  // skip this event if not match with search term
+
+        if ($hits < count($words))  // skip this event if not match with search term
           continue;
       }
-      
+
       // list events in requested time window
       if ($event['start'] <= $end && $event['end'] >= $start) {
         unset($event['_attendees']);
@@ -324,6 +316,18 @@ class kolab_calendar extends kolab_storage_folder_api
       // resolve recurring events
       if ($record['recurrence'] && $virtual == 1) {
         $events = array_merge($events, $this->get_recurring_events($record, $start, $end));
+
+        // when searching, only recurrence exceptions may match the query so post-filter the results again
+        if (!empty($search) && $record['recurrence']['EXCEPTIONS']) {
+          $me = $this;
+          $events = array_filter($events, function($event) use ($words, $me) {
+            $hits = 0;
+            foreach ($words as $word) {
+              $hits += $me->_fulltext_match($event, $word, false);
+            }
+            return $hits >= count($words);
+          });
+        }
       }
     }
 
@@ -756,6 +760,36 @@ class kolab_calendar extends kolab_storage_folder_api
   }
 
   /**
+   * Match the given word in the event contents
+   */
+  private function _fulltext_match($event, $word, $recursive = true)
+  {
+    $hits = 0;
+    foreach ($this->search_fields as $col) {
+      $sval = is_array($event[$col]) ? self::_complex2string($event[$col]) : $event[$col];
+      if (empty($sval))
+        continue;
+
+      // do a simple substring matching (to be improved)
+      $val = mb_strtolower($sval);
+      if (strpos($val, $word) !== false) {
+        $hits++;
+        break;
+      }
+    }
+
+    // search in recurrence exceptions
+    if (!$hits && $recursive && !empty($event['recurrence']['EXCEPTIONS'])) {
+      foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
+        $hits = $this->_fulltext_match($exception, $word, false);
+        if ($hits) break;
+      }
+    }
+
+    return $hits;
+  }
+
+  /**
    * Convert a complex event attribute to a string value
    */
   private static function _complex2string($prop)
diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
index ad54505..33ada93 100644
--- a/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -581,27 +581,38 @@ abstract class kolab_format_xcal extends kolab_format
      *
      * @return array List of words to save in cache
      */
-    public function get_words()
+    public function get_words($obj = null)
     {
         $data = '';
+        $object = $obj ?: $this->data;
+
         foreach (self::$fulltext_cols as $colname) {
             list($col, $field) = explode(':', $colname);
 
             if ($field) {
                 $a = array();
-                foreach ((array)$this->data[$col] as $attr)
+                foreach ((array)$object[$col] as $attr)
                     $a[] = $attr[$field];
                 $val = join(' ', $a);
             }
             else {
-                $val = is_array($this->data[$col]) ? join(' ', $this->data[$col]) : $this->data[$col];
+                $val = is_array($object[$col]) ? join(' ', $object[$col]) : $object[$col];
             }
 
             if (strlen($val))
                 $data .= $val . ' ';
         }
 
-        return array_unique(rcube_utils::normalize_string($data, true));
+        $words = rcube_utils::normalize_string($data, true);
+
+        // collect words from recurrence exceptions
+        if (is_array($object['recurrence']) && $object['recurrence']['EXCEPTIONS']) {
+            foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) {
+                $words = array_merge($words, $this->get_words($exception));
+            }
+        }
+
+        return array_unique($words);
     }
 
     /**




More information about the commits mailing list