aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mod/search/README.txt185
-rw-r--r--mod/search/index.php20
-rw-r--r--mod/search/start.php29
-rw-r--r--mod/search/views/default/search/comments/entity.php59
-rw-r--r--mod/search/views/default/search/comments/listing.php107
-rw-r--r--mod/search/views/default/search/entity.php41
-rw-r--r--mod/search/views/default/search/listing.php43
7 files changed, 320 insertions, 164 deletions
diff --git a/mod/search/README.txt b/mod/search/README.txt
new file mode 100644
index 000000000..047ba10c2
--- /dev/null
+++ b/mod/search/README.txt
@@ -0,0 +1,185 @@
+Full text search dev reference.
+
+1. OVERVIEW
+
+ * All entities are searched through title and description using
+ MySQL's native fulltext search when possible, and LIKE %...% when not.
+ This can be overriden on a type/subtype basis.
+
+ * Entities are displayed in a standard list view consisting of a
+ title, blurb, and icon of the owning entity. This can be overriden
+ on a type/subtype basis.
+
+ * Search is separated based upon types/subtypes pairs and any
+ registered custom search.
+
+ * METADATA, ANNOTATIONS, AND PRIVATE DATA ARE NOT SEARCHED.
+ These are used in a variety of ways by plugin authors and generally
+ should not be displayed. There are exceptions (profile fields and
+ comments) but if a plugin needs to match against metadata,
+ annotations, or private data it must register a search hook itself.
+
+
+2. SEARCH AND YOUR PLUGIN
+
+ * To appear in search you must register your entity type and subtype
+ by saying in your plugin's init function:
+
+ register_entity_type($type, $subtype);
+
+ If you are extending ElggObject with your own class, it is also advised
+ to add a subtype in your plugin's run_once function by saying:
+
+ add_subtype($type, $subtype, $class);
+
+ * If your plugin uses ElggEntity's standard title and description,
+ and you don't need a custom display, there is nothing else you need
+ to do for your results to appear in search. If you would like more
+ granular control of search, continue below.
+
+
+3.1 CONTROLLING SEARCH -- ENTITIES
+
+ * You can override the default search by responding to the search/type
+ or search/type:subtype hook. Generally, you will be replying to
+ search/object:subtype.
+
+ * Search will first trigger a hook for search/type:subtype. If no
+ results are returned (but not FALSE, see below) a hook for search/type
+ will be triggered.
+
+ * FALSE returned for any search hook will halt results for that type/subtype.
+
+ * Register plugin hooks like this:
+
+ register_plugin_hook('search', 'object:my_subtype', 'my_subtype_search_hook');
+
+ * The hooked function is provided with details about the search query in $param.
+ These include:
+ query
+ offset
+ limit
+ search_type
+ type - Entity type. (Not applicable for custom searches)
+ subtype - Entity subtype. (Not applicable for custom searches)
+ owner_guid
+ friends - Should only entities by friends of the logged in
+ user be searched? (@todo)
+ pagination - Show pagination?
+
+ * The hooked function should respond to search triggers with the
+ following:
+ array(
+ 'count' => A count of ALL entities found,
+ 'entities' => An array of entities.
+ )
+
+ This information is passed directly to the search view, so if you are
+ registering your own custom hook, you can provide more
+ information to display in your custom view.
+
+ * For each entity in the returned array, search expects two pieces of
+ volatile data: search_matched_title and search_matched_description.
+ Set these by saying:
+
+ $entity->setVolatileData('data_name', 'data_value');
+
+ Again, if you are customizing your search views, you can add anything
+ you need.
+
+
+3.2 CONTROLLING SEARCH - ENTITY VIEWS
+
+ * The default view for entities is search/entity.
+
+ * Search views are separate from the object/entity views because
+ view types might not match entity types.
+
+ * The default search listing view interates through each entity
+ found and passes to the entity view. See 3.3 for more information
+ about listing views.
+
+ * Views are discovered in the following order. The first search view
+ found is used.
+ search/type/subtype/entity (For entity-based searches only)
+ search/type/entity
+ search/entity
+
+ * The following parameters are passed in $vars to the entity view by
+ the default listing view:
+ entity => The current returned entity
+ params =>
+ results
+
+ * Example: To create an entity view for an ElggObject of subtype blog,
+ create a file called:
+ views/default/search/object/blog/entity.php
+
+ To create an entity view for a custom search mysearch, create a file
+ called:
+ views/default/search/mysearch/entity.php
+
+
+3.3 CONTROLLING SEARCH - LISTING VIEWS
+
+ * The default search view is search/listing.
+
+ * For each entity in the returned array, search expects two pieces of
+ volatile data: search_matched_title and search_matched_description.
+
+ * Views are discovered in the following order. The first search view
+ found is used.
+ search/type/subtype/listing (For entity-based searches only)
+ search/type/listing
+ search/listing
+
+ * The view is called with the following in $vars:
+ results => The results from the search/type:subtype hook
+ params => The params passed to the search/type:subtype hook
+
+ * Example: To create a listing view for ElggObjects with the subtype
+ of blog, create a file called:
+ views/default/search/object/blog/listing.php
+
+ To create a listing view for the custom search mysearch, create a file
+ called:
+ views/default/search/mysearch/listing.php
+
+
+3.4 CONTROLLING SEARCH - CUSTOM SEARCH
+
+ * Non-entities, including information from 3rd party applications,
+ can easily be included in search by registering a custom search hook
+ that responds to the search_types/get_types trigger:
+
+ register_plugin_hook('search_types', 'get_types', 'my_custom_search_hook_function');
+
+ In this function, append to the array sent in $value with the name of
+ your custom search:
+
+ function my_custom_search_hook_function($hook, $type, $value, $params) {
+ $value[] = 'my_custom_search';
+ return $value;
+ }
+
+ Search will trigger a hook for search/my_custom_search, which your
+ plugin should respond to as detailed in section 3.1 above.
+
+
+4. HINTS
+
+ * Use search_get_relevant_substring() to extract and highlight
+ relevant substrings for the search_match_title and description.
+
+ * If searching in 3rd party applications, create a temporary
+ ElggObject to hold the results. No need to save it since search
+ uses volatile data.
+ $entity = new ElggObject();
+ $entity->owner_guid = use_magic_to_match_to_a_real_user();
+ $entity->setVolatileData('search_matched_title', '3rd Party Integration');
+ $entity->setVolatileData('search_matched_description', 'Searching is fun!');
+
+ return array(
+ 'count' => $count,
+ 'entities' => array($entity)
+ ); \ No newline at end of file
diff --git a/mod/search/index.php b/mod/search/index.php
index e7081ecb5..adf514caf 100644
--- a/mod/search/index.php
+++ b/mod/search/index.php
@@ -152,7 +152,9 @@ if ($search_type == 'all' || $search_type == 'entities') {
}
if (is_array($results['entities']) && $results['count']) {
- $results_html .= search_get_listing_html($results['entities'], $results['count'], $params);
+ if ($view = search_get_search_view($params, 'listing')) {
+ $results_html .= elgg_view($view, array('results' => $results, 'params' => $params));
+ }
}
}
}
@@ -169,23 +171,23 @@ if ($search_type == 'all' || $search_type == 'entities') {
}
if (is_array($results['entities']) && $results['count']) {
- $results_html .= search_get_listing_html($results['entities'], $results['count'], $params);
+ if ($view = search_get_search_view($params, 'listing')) {
+ $results_html .= elgg_view($view, array('results' => $results, 'params' => $params));
+ }
}
}
}
// call custom searches
if ($search_type != 'entities' || $search_type == 'all') {
- // get custom search types
- $types = trigger_plugin_hook('search_types', 'get_types', $params, array());
-
- if (is_array($types)) {
- foreach ($types as $type) {
+ if (is_array($custom_types)) {
+ foreach ($custom_types as $type) {
if ($search_type != 'all' && $search_type != $type) {
continue;
}
$params['search_type'] = $type;
+ // custom search types have no subtype.
unset($params['subtype']);
$results = trigger_plugin_hook('search', $type, $params, array());
@@ -196,7 +198,9 @@ if ($search_type != 'entities' || $search_type == 'all') {
}
if (is_array($results['entities']) && $results['count']) {
- $results_html .= search_get_listing_html($results['entities'], $results['count'], $params);
+ if ($view = search_get_search_view($params, 'listing')) {
+ $results_html .= elgg_view($view, array('results' => $results, 'params' => $params));
+ }
}
}
}
diff --git a/mod/search/start.php b/mod/search/start.php
index 92d5c65b6..c54072b91 100644
--- a/mod/search/start.php
+++ b/mod/search/start.php
@@ -316,48 +316,41 @@ function search_remove_ignored_words($query, $format = 'array') {
/**
- * Passes entities, count, and original params to the view functions for
+ * Passes results, and original params to the view functions for
* search type.
*
- * @param array $entities
- * @param int $count
+ * @param array $results
* @param array $params
+ * @param string $view_type = listing || entity
* @return string
*/
-function search_get_listing_html($entities, $count, $params) {
- if (!is_array($entities) || !$count) {
+function search_get_search_view($params, $view_type) {
+ if ($view_type != 'listing' && $view_type != 'entity') {
return FALSE;
}
-
$view_order = array();
- // check if there's a special search view for this type:subtype
+ // check if there's a special search listing view for this type:subtype
if (isset($params['type']) && $params['type'] && isset($params['subtype']) && $params['subtype']) {
- $view_order[] = "search/{$params['type']}/{$params['subtype']}/listing";
+ $view_order[] = "search/{$params['type']}/{$params['subtype']}/$view_type";
}
// also check for the default type
if (isset($params['type']) && $params['type']) {
- $view_order[] = "search/{$params['type']}/listing";
+ $view_order[] = "search/{$params['type']}/$view_type";
}
// check search types
if (isset($params['search_type']) && $params['search_type']) {
- $view_order[] = "search/{$params['search_type']}/listing";
+ $view_order[] = "search/{$params['search_type']}/$view_type";
}
// finally default to a search listing default
- $view_order[] = "search/listing";
-
- $vars = array(
- 'entities' => $entities,
- 'count' => $count,
- 'params' => $params
- );
+ $view_order[] = "search/$view_type";
foreach ($view_order as $view) {
if (elgg_view_exists($view)) {
- return elgg_view($view, $vars);
+ return $view;
}
}
diff --git a/mod/search/views/default/search/comments/entity.php b/mod/search/views/default/search/comments/entity.php
new file mode 100644
index 000000000..8b4d286c9
--- /dev/null
+++ b/mod/search/views/default/search/comments/entity.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Elgg search entity
+ *
+ * @package Elgg
+ * @subpackage Core
+ * @author Curverider Ltd
+ * @link http://elgg.org/
+ */
+$entity = $vars['entity'];
+
+$owner = get_entity($entity->getVolatileData('search_matched_comment_owner_guid'));
+
+if ($owner instanceof ElggUser) {
+ $icon = elgg_view('profile/icon', array('entity' => $owner, 'size' => 'small'));
+} else {
+ $icon = '';
+}
+
+// @todo Sometimes we find comments on entities we can't display...
+if ($entity->getVolatileData('search_unavailable_entity')) {
+ $title = sprintf(elgg_echo('search:comment_on'), elgg_echo('search:unavailable_entity'));
+ // keep anchor for formatting.
+ $title = "<a>$title</a>";
+} else {
+ if ($entity->getType() == 'object') {
+ $title = $entity->title;
+ } else {
+ $title = $entity->name;
+ }
+
+ if (!$title) {
+ $title = elgg_echo('item:' . $entity->getType() . ':' . $entity->getSubtype());
+ }
+
+ if (!$title) {
+ $title = elgg_echo('item:' . $entity->getType());
+ }
+
+ $title = sprintf(elgg_echo('search:comment_on'), $title);
+ $url = $entity->getURL() . '#annotation-' . $entity->getVolatileData('search_match_annotation_id');
+ $title = "<a href=\"$url\">$title</a>";
+}
+
+$description = $entity->getVolatileData('search_matched_comment');
+$tc = $entity->getVolatileData('search_matched_comment_time_created');;
+$time = friendly_time($tc);
+
+echo <<<___END
+ <div class="search_listing">
+ <div class="search_listing_icon">$icon</div>
+ <div class="search_listing_info">
+ <p class="ItemTitle">$title</p>$description
+ <p class="ItemTimestamp">$time</p>
+ </div>
+ </div>
+___END;
+
+?> \ No newline at end of file
diff --git a/mod/search/views/default/search/comments/listing.php b/mod/search/views/default/search/comments/listing.php
deleted file mode 100644
index 34456bde8..000000000
--- a/mod/search/views/default/search/comments/listing.php
+++ /dev/null
@@ -1,107 +0,0 @@
-<?php
-/**
- * Elgg comments search listing
- *
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
- */
-
-if (!is_array($vars['entities']) || !count($vars['entities'])) {
- return FALSE;
-}
-
-$title_str = elgg_echo('comments');
-
-$query = htmlspecialchars(http_build_query(
- array(
- 'q' => $vars['params']['query'],
- 'entity_type' => $vars['params']['type'],
- 'entity_subtype' => $vars['params']['subtype'],
- 'limit' => get_input('limit', 10),
- 'offset' => get_input('offset', 0),
- 'search_type' => 'comments',
- )
-));
-
-$url = "{$vars['url']}pg/search?$query";
-
-// get pagination
-if (array_key_exists('pagination', $vars) && $vars['pagination']) {
- $nav .= elgg_view('navigation/pagination',array(
- 'baseurl' => $url,
- 'offset' => $vars['params']['offset'],
- 'count' => $vars['count'],
- 'limit' => $vars['params']['limit'],
- ));
-} else {
- $nav = '';
-}
-
-// get more links
-$more_check = $vars['count'] - ($vars['params']['offset'] + $vars['params']['limit']);
-$more = ($more_check > 0) ? $more_check : 0;
-
-if ($more) {
- $title_key = ($more == 1) ? 'comment' : 'comments';
- $more_str = sprintf(elgg_echo('search:more'), $vars['count'], elgg_echo($title_key));
- $more_link = "<div class='search_listing'><a href=\"$url\">$more_str</a></div>";
-} else {
- $more_link = '';
-}
-
-$body = elgg_view_title($title_str);
-
-foreach ($vars['entities'] as $entity) {
- $owner = get_entity($entity->getVolatileData('search_matched_comment_owner_guid'));
-
- if ($owner instanceof ElggUser) {
- $icon = elgg_view('profile/icon', array('entity' => $owner, 'size' => 'small'));
- } else {
- $icon = '';
- }
-
- // @todo Sometimes we find comments on entities we can't display...
- if ($entity->getVolatileData('search_unavailable_entity')) {
- $title = sprintf(elgg_echo('search:comment_on'), elgg_echo('search:unavailable_entity'));
- // keep anchor for formatting.
- $title = "<a>$title</a>";
- } else {
- if ($entity->getType() == 'object') {
- $title = $entity->title;
- } else {
- $title = $entity->name;
- }
-
- if (!$title) {
- $title = elgg_echo('item:' . $entity->getType() . ':' . $entity->getSubtype());
- }
-
- if (!$title) {
- $title = elgg_echo('item:' . $entity->getType());
- }
-
- $title = sprintf(elgg_echo('search:comment_on'), $title);
- $url = $entity->getURL() . '#annotation-' . $entity->getVolatileData('search_match_annotation_id');
- $title = "<a href=\"$url\">$title</a>";
- }
-
- $description = $entity->getVolatileData('search_matched_comment');
- $tc = $entity->getVolatileData('search_matched_comment_time_created');;
- $time = friendly_time($tc);
-
- $body .= <<<___END
- <div class="search_listing">
- <div class="search_listing_icon">$icon</div>
- <div class="search_listing_info">
- <p class="ItemTitle">$title</p>$description
- <p class="ItemTimestamp">$time</p>
- </div>
- </div>
-___END;
-}
-
-echo $body;
-echo $more_link;
-echo $nav;
diff --git a/mod/search/views/default/search/entity.php b/mod/search/views/default/search/entity.php
new file mode 100644
index 000000000..06dd54f39
--- /dev/null
+++ b/mod/search/views/default/search/entity.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Elgg search entity
+ *
+ * @package Elgg
+ * @subpackage Core
+ * @author Curverider Ltd
+ * @link http://elgg.org/
+ */
+
+$entity = $vars['entity'];
+
+if ($owner = $entity->getOwnerEntity()) {
+ $icon = elgg_view('profile/icon', array('entity' => $owner, 'size' => 'small'));
+} elseif ($entity instanceof ElggUser) {
+ $icon = elgg_view('profile/icon', array('entity' => $entity, 'size' => 'small'));
+} else {
+ $icon = '';
+}
+
+$title = $entity->getVolatileData('search_matched_title');
+$description = $entity->getVolatileData('search_matched_description');
+$extra_info = $entity->getVolatileData('search_matched_extra');
+$url = $entity->getURL();
+$title = "<a href=\"$url\">$title</a>";
+$tc = $entity->time_created;
+$tu = $entity->time_updated;
+$time = friendly_time(($tu > $tc) ? $tu : $tc);
+
+echo <<<___END
+ <div class="search_listing">
+ <div class="search_listing_icon">$icon</div>
+ <div class="search_listing_info">
+ <p class="ItemTitle">$title</p>$description
+ <p class="ItemTimestamp">$time $extra_info</p>
+ </div>
+ </div>
+___END;
+
+// php bug. must have close tag after heredocs
+?> \ No newline at end of file
diff --git a/mod/search/views/default/search/listing.php b/mod/search/views/default/search/listing.php
index 867523d65..36032940d 100644
--- a/mod/search/views/default/search/listing.php
+++ b/mod/search/views/default/search/listing.php
@@ -8,11 +8,10 @@
* @link http://elgg.org/
*/
+$entities = $vars['results']['entities'];
+$count = $vars['results']['count'] - count($entities);
-$entities = $vars['entities'];
-$count = $vars['count'] - count($vars['entities']);
-
-if (!is_array($vars['entities']) || !count($vars['entities'])) {
+if (!is_array($entities) || !count($entities)) {
return FALSE;
}
@@ -34,7 +33,7 @@ if (array_key_exists('pagination', $vars) && $vars['pagination']) {
$nav .= elgg_view('navigation/pagination',array(
'baseurl' => $url,
'offset' => $vars['params']['offset'],
- 'count' => $vars['count'],
+ 'count' => $vars['results']['count'],
'limit' => $vars['params']['limit'],
));
} else {
@@ -84,7 +83,7 @@ if (array_key_exists('pagination', $vars['params']) && $vars['params']['paginati
$nav .= elgg_view('navigation/pagination',array(
'baseurl' => $url,
'offset' => $vars['params']['offset'],
- 'count' => $vars['count'],
+ 'count' => $vars['results']['count'],
'limit' => $vars['params']['limit'],
));
} else {
@@ -92,7 +91,7 @@ if (array_key_exists('pagination', $vars['params']) && $vars['params']['paginati
}
// get any more links.
-$more_check = $vars['count'] - ($vars['params']['offset'] + $vars['params']['limit']);
+$more_check = $vars['results']['count'] - ($vars['params']['offset'] + $vars['params']['limit']);
$more = ($more_check > 0) ? $more_check : 0;
if ($more) {
@@ -106,31 +105,13 @@ if ($more) {
$body = elgg_view_title($type_str);
foreach ($entities as $entity) {
- if ($owner = $entity->getOwnerEntity()) {
- $icon = elgg_view('profile/icon', array('entity' => $owner, 'size' => 'small'));
- } elseif ($entity instanceof ElggUser) {
- $icon = elgg_view('profile/icon', array('entity' => $entity, 'size' => 'small'));
- } else {
- $icon = '';
+ if ($view = search_get_search_view($vars['params'], 'entity')) {
+ $body .= elgg_view($view, array(
+ 'entity' => $entity,
+ 'params' => $vars['params'],
+ 'results' => $vars['results']
+ ));
}
- $title = $entity->getVolatileData('search_matched_title');
- $description = $entity->getVolatileData('search_matched_description');
- $extra_info = $entity->getVolatileData('search_matched_extra');
- $url = $entity->getURL();
- $title = "<a href=\"$url\">$title</a>";
- $tc = $entity->time_created;
- $tu = $entity->time_updated;
- $time = friendly_time(($tu > $tc) ? $tu : $tc);
-
- $body .= <<<___END
- <div class="search_listing">
- <div class="search_listing_icon">$icon</div>
- <div class="search_listing_info">
- <p class="ItemTitle">$title</p>$description
- <p class="ItemTimestamp">$time $extra_info</p>
- </div>
- </div>
-___END;
}
echo $body;
echo $more_link;