Basic search API integration.
[sandbox/serialjaywalker/1195272.git] / modules / search / commerce_pos_search.module
1 <?php
2
3 /**
4 * @file
5 * Common functions for searches embedded in the POS interface.
6 */
7
8 /**
9 * Implements hook_commerce_pos_panel_info().
10 */
11 function commerce_pos_search_commerce_pos_panel_info() {
12 $search_info = commerce_pos_search_info();
13
14 foreach ($search_info as $name => $search) {
15 $panels['search_' . $name] = array(
16 'name' => 'search_' . $name,
17 'title' => $search['title'],
18 'content_callback' => 'commerce_pos_search_panel_content',
19 'content_arguments' => array($name),
20 'weight' => $search['weight'],
21 );
22 }
23
24 return $panels;
25 }
26
27 /**
28 * Get all POS searches defined by modules.
29 */
30 function commerce_pos_search_info($name = NULL) {
31 $search_info = drupal_static(__FUNCTION__);
32
33 if (empty($search_info)) {
34 $search_info = module_invoke_all('commerce_pos_search_info');
35
36 foreach ($search_info as $key => $search) {
37 $search_info[$key] = $search + array(
38 'weight' => 0,
39 'sort' => array(
40 'search_api_relevance' => 'DESC',
41 ),
42 );
43 }
44 drupal_alter('commerce_pos_search_info', $search_info);
45 }
46
47 if (empty($name)) {
48 return $search_info;
49 }
50 else {
51 return $search_info[$name];
52 }
53 }
54
55 /**
56 * Build the search panel content
57 */
58 function commerce_pos_search_panel_content($name) {
59 $search_info = commerce_pos_search_info($name);
60
61 commerce_pos_search_refresh_results($name);
62 $render = array(
63 'form' => drupal_get_form('commerce_pos_search_form', $name),
64 'facets' => array(
65 '#prefix' => "<div class='commerce-pos-search-facets'>",
66 '#suffix' => "</div>",
67 ),
68 'results' => array(
69 '#prefix' => "<div class='commerce-pos-search-results'>",
70 '#suffix' => "</div>",
71 ),
72 '#attributes' => array(
73 'class' => array(
74 'commerce-pos-search-panel-' . commerce_pos_search_name_to_class($name),
75 'commerce-pos-search-panel',
76 ),
77 ),
78 '#name' => 'commerce-pos-search-' . $name,
79 );
80 $render['#attached'] = array(
81 'js' => array(array('data' => drupal_get_path('module', 'commerce_pos_search') . '/commerce_pos_search.js')),
82 'css' => array(array('data' => drupal_get_path('module', 'commerce_pos_search') . '/theme/commerce_pos_search.css')),
83 );
84 if (!empty($search_info['attached'])) {
85 $render['#attached'] = drupal_array_merge_deep($render['#attached'], $search_info['attached']);
86 }
87
88 return $render;
89 }
90
91 /**
92 * Returns a path argument from a product type.
93 */
94 function commerce_pos_search_name_to_class($name) {
95 return strtr($name, '_', '-');
96 }
97
98 /**
99 * Refresh the search results for a POS search.
100 */
101 function commerce_pos_search_refresh_results($search_name) {
102 //Would this be better done with #attached?
103 drupal_add_js(drupal_get_path('module', 'commerce_pos_search') . '/commerce_pos_search.js');
104 drupal_add_js(array('commercePosSearch' => array('refresh' => array($search_name => TRUE))), 'setting');
105 }
106
107 /**
108 * Form callback for search_form.
109 */
110 function commerce_pos_search_form($form_state, $form_state, $search_name) {
111 $form['query'] = array(
112 '#type' => 'textfield',
113 '#size' => 20,
114 '#attributes' => array(
115 'class' => array('commerce-pos-search-form-query'),
116 ),
117 '#suffix' => "<a class = 'commerce-pos-search-query-clear'>" . t('Clear') . "</a>",
118 );
119
120 $form['search_name'] = array(
121 '#type' => 'hidden',
122 '#attributes' => array(
123 'class' => array('commerce-pos-search-form-search-name'),
124 ),
125 '#value' => $search_name
126 );
127
128 return $form;
129 }
130
131 /**
132 * Form callback for facet form.
133 */
134 function commerce_pos_search_facets_form($form_state, $form_state, $search_name) {
135 $form = facetapi_build_realm(commerce_pos_search_facet_api_searcher($search_name), 'commerce_pos_search');
136 $form['#facet_names'] = element_children($form);
137
138 return $form;
139 }
140
141 /**
142 * The name of a searcher as it is known to Facet API module.
143 * @param $search_name
144 */
145 function commerce_pos_search_facet_api_searcher($search_name) {
146 $search_info = commerce_pos_search_info($search_name);
147 return 'search_api@' . $search_info['index'];
148 }
149
150 /**
151 * Get facets for a search.
152 */
153 function commerce_pos_search_facets($search_name) {
154 return drupal_get_form('commerce_pos_search_facets_form', $search_name);
155 }
156
157 /**
158 * Implements hook_menu().
159 */
160 function commerce_pos_search_menu() {
161 $items = array();
162 $search_info = commerce_pos_search_info();
163
164 foreach ($search_info as $name => $search) {
165 $items['commerce_pos_search/' . $name] = array(
166 'title' => 'POS Search',
167 'type' => MENU_CALLBACK,
168 'description' => 'AJAX callback for POS search',
169 'access callback' => TRUE,
170 'page callback' => 'commerce_pos_search_results_ajax',
171 'page arguments' => array(1),
172 );
173 }
174
175 return $items;
176 }
177
178 /**
179 * Callback for search results.
180 */
181 function commerce_pos_search_results($search_name, $keys) {
182 $search = commerce_pos_search_info($search_name);
183 if (!$search) {
184 return MENU_NOT_FOUND;
185 }
186 else{
187 $index = search_api_index_load($search['index']);
188 if (!$index->enabled) {
189 return array(
190 'error' => t('Please make sure that a search server has been created and the index has been enabled.'),
191 );
192 }
193 }
194
195 if ($keys) {
196 $results = commerce_pos_search_execute($search_name, $keys);
197
198 $render['results']['#theme'] = 'commerce_pos_search_results';
199 $render['results']['#view_mode'] = $search['view_mode'];
200 $render['results']['#index'] = search_api_index_load($search['index']);
201 $render['results']['#keys'] = $keys;
202 $render['results']['#search_name'] = $search_name;
203 $render['results']['#results'] = $results;
204
205 if ($results['result count'] > $search['per_page']) {
206 pager_default_initialize($results['result count'], $search['per_page']);
207 $render['pager']['#theme'] = 'pager';
208 $render['pager']['#quantity'] = 5;
209 }
210
211 return $render;
212 }
213 }
214
215 /**
216 * AJAX callback for search results.
217 */
218 function commerce_pos_search_results_ajax($search_name, $keys='', $get_facets = 1) {
219 if (empty($keys)) {
220 $keys = ' ';
221 }
222 $results = commerce_pos_search_results($search_name, $keys);
223 if (!empty($results['error'])) {
224 $return = array(
225 'results' => $results['error'],
226 );
227 }
228 else {
229 $return = array(
230 'results' => drupal_render($results),
231 );
232 if (!empty($get_facets)) {
233 $facets = commerce_pos_search_facets($search_name);
234 $return += array(
235 'facets' => drupal_render($facets),
236 'facetNames' => $facets['#facet_names'],
237 );
238 }
239 }
240
241 return drupal_json_output($return);
242 }
243
244 /**
245 * Get search results.
246 */
247 function commerce_pos_search_execute($search_name, $keys) {
248 $search = commerce_pos_search_info($search_name);
249
250 $limit = $search['per_page'];
251 $offset = pager_find_page() * $limit;
252 $options = array(
253 'search id' => 'commerce_pos_search:' . $search_name,
254 );
255
256 $query = search_api_query($search['index'], $options)
257 ->keys($keys)
258 ->range($offset, $limit);
259
260 foreach($search['sort'] as $field => $direction) {
261 $query->sort($field, $direction);
262 }
263
264 return $query->execute();
265 }
266
267 /**
268 * Implements hook_theme().
269 */
270 function commerce_pos_search_theme() {
271 $themes['commerce_pos_search_results'] = array(
272 'variables' => array(
273 'index' => NULL,
274 'results' => array('result count' => 0),
275 'items' => array(),
276 'view_mode' => '',
277 'keys' => '',
278 'search_name' => '',
279 ),
280 );
281 $themes['commerce_pos_search_result'] = array(
282 'variables' => array(
283 'entity_type' => '',
284 'entity_id' => 0,
285 'view_mode' => ''
286 ),
287 );
288
289 return $themes;
290 }
291
292 /**
293 * Function for preprocessing the variables for the search_api_page_results
294 * theme.
295 *
296 * @param array $variables
297 * An associative array containing:
298 * - index: The index this search was executed on.
299 * - results: An array of search results, as returned by
300 * SearchApiQueryInterface::execute().
301 * - keys: The keywords of the executed search.
302 */
303 function template_preprocess_commerce_pos_search_results(&$variables) {
304
305 $index = $variables['index'];
306 $results = $variables['results'];
307 $items = $variables['items'];
308 $view_mode = $variables['view_mode'];
309
310 $variables['results']['render'] = array();
311
312 foreach ($variables['results']['results'] as $entity_id => $item) {
313 $variables['results']['render'][] = array(
314 '#theme' => 'commerce_pos_search_result',
315 '#cache' => 'commerce_pos_search_result:' . $variables['search_name'] . ':' . $entity_id,
316 '#entity_type' => $index->item_type,
317 '#entity_id' => $entity_id,
318 '#view_mode' => $view_mode,
319 );
320 }
321 }
322
323 /**
324 * Theme function for displaying search results.
325 *
326 * @param array $variables
327 * An associative array containing:
328 * - index: The index this search was executed on.
329 * - results: An array of search results, as returned by
330 * SearchApiQueryInterface::execute().
331 * - items: The loaded items for all results, in an array keyed by ID.
332 * - view_mode: The view mode to use for displaying the individual results,
333 * or the special mode "search_api_page_result" to use the theme function
334 * of the same name.
335 * - keys: The keywords of the executed search.
336 */
337 function theme_commerce_pos_search_results(array $variables) {
338 drupal_add_css(drupal_get_path('module', 'commerce_pos_search') . '/theme/commerce_pos_search.css');
339
340 $index = $variables['index'];
341 $results = $variables['results'];
342 $items = $variables['items'];
343 $keys = $variables['keys'];
344 $class = 'commerce-pos-search-results-container-' . commerce_pos_search_name_to_class($variables['search_name']);
345
346 $render = array(
347 '#prefix' => '<div class="commerce-pos-search-results-container ' . $class . '">',
348 'content' => $results['render'],
349 '#suffix' => '</div>',
350 );
351
352 if (!$results['result count']) {
353 $render['results'] = array(
354 '#markup' => t('No results.'),
355 );
356 }
357
358 return drupal_render($render);
359 }
360
361 /**
362 * Theme function for displaying a single result.
363 *
364 * @param array $variables
365 * An associative array with the following keys:
366 * --entity_type
367 * --entity_id
368 * --view_mode
369 */
370 function theme_commerce_pos_search_result(array $variables) {
371 $entity_type = $variables['entity_type'];
372 $entity_id = $variables['entity_id'];
373 $view_mode = $variables['view_mode'];
374 $render = entity_view($entity_type, entity_load($entity_type, array($entity_id)), $view_mode);
375 return drupal_render($render);
376 }
377
378 /**
379 * Implements hook_search_api_alter_callback_info().
380 */
381 function commerce_pos_search_search_api_alter_callback_info() {
382 $callbacks['commerce_pos_search_status_filter'] = array(
383 'name' => t('Status filter'),
384 'description' => t("Exclude inactive Drupal Commerce entities."),
385 'class' => 'CommercePosSearchStatusFilter',
386 // Filters should be executed first.
387 'weight' => -10,
388 );
389 return $callbacks;
390 }
391
392 /**
393 * Implements hook_entity_update().
394 */
395 function commerce_pos_search_entity_update($entity, $type) {
396 $search_info = commerce_pos_search_info();
397 $entity_info = entity_get_info();
398 foreach($search_info as $name => $search) {
399 if ($search['entity_type'] == $type) {
400 $key = $entity_info[$type]['entity keys']['id'];
401 cache_clear_all('commerce_pos_search_result:' . $name . ':' . $key, 'cache');
402 }
403 }
404 }