updated search_api modules to last version
i will try to dont patch them anymore
This commit is contained in:
@@ -1,28 +0,0 @@
|
||||
From 6402fc7ab8f6343defbee7111dee7dd16a5082fc Mon Sep 17 00:00:00 2001
|
||||
From: Bachir Soussi Chiadmi <bachir@g-u-i.net>
|
||||
Date: Fri, 7 Feb 2014 10:10:15 +0100
|
||||
Subject: [PATCH 1/4] re-added own boosts
|
||||
|
||||
---
|
||||
search_api.admin.inc | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/search_api.admin.inc b/search_api.admin.inc
|
||||
index f4210f4..b2269d6 100644
|
||||
--- a/search_api.admin.inc
|
||||
+++ b/search_api.admin.inc
|
||||
@@ -1658,8 +1658,9 @@ function search_api_admin_index_fields(array $form, array &$form_state, SearchAp
|
||||
// An array of option arrays for types, keyed by nesting level.
|
||||
$types = array(0 => search_api_field_types());
|
||||
$entity_types = entity_get_info();
|
||||
- $boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0'));
|
||||
-
|
||||
+ //$boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0'));
|
||||
+ $boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0', '100', '1000', '1010', '1020', '1030', '1040', '1050', '1060'));
|
||||
+
|
||||
$fulltext_types = array(0 => array('text'));
|
||||
// Add all custom data types with fallback "text" to fulltext types as well.
|
||||
foreach (search_api_get_data_type_info() as $id => $type) {
|
||||
--
|
||||
2.3.5
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
From 54ee5c7b3a05850e15067d77a182cb8fe723d8e0 Mon Sep 17 00:00:00 2001
|
||||
From: Bachir Soussi Chiadmi <bachir@g-u-i.net>
|
||||
Date: Fri, 7 Feb 2014 10:29:08 +0100
|
||||
Subject: [PATCH 2/4] taxo term translation bug : added reference to bug fix in
|
||||
comment
|
||||
|
||||
---
|
||||
search_api.module | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
diff --git a/search_api.module b/search_api.module
|
||||
index 000cadb..55bd54a 100644
|
||||
--- a/search_api.module
|
||||
+++ b/search_api.module
|
||||
@@ -2221,6 +2221,10 @@ function search_api_extract_fields(EntityMetadataWrapper $wrapper, array $fields
|
||||
foreach ($nested as $prefix => $nested_fields) {
|
||||
if (isset($wrapper->$prefix)) {
|
||||
$nested_fields = search_api_extract_fields($wrapper->$prefix, $nested_fields, $value_options);
|
||||
+ # http://drupal.org/node/1873910#comment-6876200
|
||||
+ // $subwrapper = $wrapper->$prefix;
|
||||
+ // $subwrapper->language( $wrapper->language->value() );
|
||||
+ // $nested_fields = search_api_extract_fields($subwrapper, $nested_fields, $value_options);
|
||||
foreach ($nested_fields as $field => $info) {
|
||||
$fields["$prefix:$field"] = $info;
|
||||
}
|
||||
--
|
||||
2.3.5
|
||||
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
From 85251183d6ad5fadaa65154e33e1e8ac8ca7f9b0 Mon Sep 17 00:00:00 2001
|
||||
From: Bachir Soussi Chiadmi <bachir@g-u-i.net>
|
||||
Date: Fri, 7 Feb 2014 10:32:26 +0100
|
||||
Subject: [PATCH 3/4] NODE_PUBLISED : in previous patches i commented the next
|
||||
line, why ? maybe will have to do it again
|
||||
|
||||
---
|
||||
search_api.module | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/search_api.module b/search_api.module
|
||||
index 55bd54a..17f611a 100644
|
||||
--- a/search_api.module
|
||||
+++ b/search_api.module
|
||||
@@ -1994,6 +1994,7 @@ function _search_api_query_add_node_access($account, SearchApiQueryInterface $qu
|
||||
$query->filter($filter);
|
||||
}
|
||||
else {
|
||||
+ // /!\ in previous patches i commented the next line, why ? maybe will have to do it again
|
||||
$query->condition('status', $published);
|
||||
}
|
||||
|
||||
--
|
||||
2.3.5
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
From c06be9a44ed0be31859a1800cf7f0ae6e8ae492a Mon Sep 17 00:00:00 2001
|
||||
From: Bachir Soussi Chiadmi <bachir@g-u-i.net>
|
||||
Date: Fri, 21 Feb 2014 19:49:45 +0100
|
||||
Subject: [PATCH 4/4] Icons re-added icons, why they didn't be here, i don't
|
||||
know ...
|
||||
|
||||
---
|
||||
disabled.png | Bin 0 -> 384 bytes
|
||||
enabled.png | Bin 0 -> 383 bytes
|
||||
2 files changed, 0 insertions(+), 0 deletions(-)
|
||||
create mode 100644 disabled.png
|
||||
create mode 100644 enabled.png
|
||||
|
||||
diff --git a/disabled.png b/disabled.png
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..224776502046765ef7c083ffa3229fd206b9c975
|
||||
GIT binary patch
|
||||
literal 384
|
||||
zcmV-`0e}99P)<h;3K|Lk000e1NJLTq000aC000aK1^@s6R&`wG0000PbVXQnQ*UN;
|
||||
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzB1uF+RCwBik-tg<K@i4gmNYiGco6Uz
|
||||
z1Rp_+BwY$iC2}^tfuaNqN)E0PY=wY@f3Z~1&O-&?K=37m)3r_F_{}jFE*3iQyZL7J
|
||||
zn_c#nMT9h-L*7E#2LVlo2k}xSM_RBBJcfJ*9ns%$zMRR1dkA@F3^O3`V!2Gwi`45z
|
||||
zLR~;$(8^>Hxo5S~v);h!F5lHi?8tY}Y=6k>{VeZk13H0TfJ{L>zr#&187PJtE1&YF
|
||||
z#chq}k)8^(h8y8iq7K@{qH60+Je8qL{fT(Z%i(p9?@Xp=Ap7MLyiFg&aBu-LbgHOE
|
||||
zFV;2lcsCYG0D;xhDo4mEm@`uJI=W__B!9S*DqmuQ&OZ-#wfQCMPL+ypp>5y+{X%=Y
|
||||
e>QV2H00RI@_ptZRUTXUQ0000<MNUMnLSTXuN2F!|
|
||||
|
||||
literal 0
|
||||
HcmV?d00001
|
||||
|
||||
diff --git a/enabled.png b/enabled.png
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..95f8730e6955f1de7d244817db5ed7678bce0f72
|
||||
GIT binary patch
|
||||
literal 383
|
||||
zcmV-_0f7FAP)<h;3K|Lk000e1NJLTq000aC000aK1^@s6R&`wG0000PbVXQnQ*UN;
|
||||
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzAxT6*RCwBA{Qv(y0|*Fj@h<{WbwJF|
|
||||
zfC@eWanoC$jeQ^vBS?eLCf`Lsb}R#au=t(d<~T-yb)Ka_P8S1lpp21kmFrs|LkN$e
|
||||
zvp{SB#LPhaj_LoOKSDsv0L0&c_%)Ob!<&HE3Wy_s_%BE;(?6gD5Pt+>Hz0Nf;@42I
|
||||
zO+Xy_DRSR0DE}{rX8QO0Hv<r}0WtHJ*h80rv@;M-2jWm5{}<Oh%P1gw1yl_KBl}E~
|
||||
z|4_Gn2V&13X{Qgu9M3V!GyzD>fw~_IKsJ1Y+QJFM+u5cX*n=d1bS#iR2V^r;9)v$K
|
||||
zvP{rM4_1&(vw%1sYp{YMj=5K3DUcIIAP$!OExr-W1Y&_0|Ns6i2I7xE%z%bLVr3vT
|
||||
dAhiGi1_1Nf-q(XP%8~#8002ovPDHLkV1gxRmaYH*
|
||||
|
||||
literal 0
|
||||
HcmV?d00001
|
||||
|
||||
--
|
||||
2.3.5
|
||||
|
||||
@@ -1,4 +1,41 @@
|
||||
Search API 1.14 (12/26/2014):
|
||||
Search API 1.16 (2015-08-30):
|
||||
-----------------------------
|
||||
- #2502819: Fixed example code for hook_search_api_query_alter().
|
||||
- #2491175 by ptmkenny, drunken monkey: Added a data alteration for filtering
|
||||
out blocked users.
|
||||
- #1197538 by thePanz, k4v, drunken monkey, ayalon, nadavoid, timodwhit, becw,
|
||||
Elvar: Added support for the "Global: Random" sort in Views.
|
||||
- #2520934 by drunken monkey: Added an item type for indexing several types of
|
||||
entities in one index.
|
||||
- #2533096 by drunken monkey: Fixed uncaught exception when deleting a server.
|
||||
- #2479453 by prics, drunken monkey: Added a Drush command to
|
||||
list/enable/disable servers.
|
||||
- #2520684 by drunken monkey: Fixed "bundles" setting on indexes with "Index
|
||||
immediately".
|
||||
- #2489882 by dww: Fixed Views taxonomy filter with "is (not) empty" operator
|
||||
- #2447213 by drunken monkey: Fixed issues with stale field settings for MLT
|
||||
contextual filter.
|
||||
|
||||
Search API 1.15 (2015-06-03):
|
||||
-----------------------------
|
||||
- #2190627 by m1n0, drunken monkey: Fixed fatal errors for views of disabled
|
||||
indexes.
|
||||
- #2448849 by cgoffin: Added "year range" option for date filters.
|
||||
- #2414425 by Darren Oh, drunken monkey: Fixed backend form validation when
|
||||
adding or editing a server.
|
||||
- #2450333 by drunken monkey: Added performance improvement when indexing
|
||||
entity references.
|
||||
- #2450227 by StryKaizer, drunken monkey: Fixed OR facets on taxonomy terms.
|
||||
- #1184610 by drunken monkey: Added option to limit indexes to specific entity
|
||||
bundles.
|
||||
- #1396222 by drunken monkey: Added a "First letter" aggregation type to the
|
||||
"Aggregated fields" data alteration.
|
||||
- #2412895 by drunken monkey: Fixed entity load for Views entity access check.
|
||||
- #2414367 by Darren Oh, drunken monkey: Fixed detection of missing fields in
|
||||
Views.
|
||||
- #2387161 by drunken monkey: Added a hook for altering search results.
|
||||
|
||||
Search API 1.14 (2014-12-26):
|
||||
-----------------------------
|
||||
- #2382385 by illusionuk, drunken monkey: Fixed error handling when using
|
||||
invalid fulltext or sort field in Views.
|
||||
@@ -25,7 +62,7 @@ Search API 1.14 (12/26/2014):
|
||||
- #2278737 by drunken monkey: Fixed use of multiple Views fulltext search
|
||||
filters.
|
||||
|
||||
Search API 1.13 (07/23/2014):
|
||||
Search API 1.13 (2014-07-23):
|
||||
-----------------------------
|
||||
- #2281535 by areynolds, nicola85: Adapted to latest changes in Views cache
|
||||
plugins.
|
||||
@@ -38,7 +75,7 @@ Search API 1.13 (07/23/2014):
|
||||
- #2216345 by bacardi55, fabianderijk, drunken monkey: Fixed array to string
|
||||
conversion in Highlighting processor.
|
||||
|
||||
Search API 1.12 (05/23/2014):
|
||||
Search API 1.12 (2014-05-23):
|
||||
-----------------------------
|
||||
- #2265349 by drunken monkey: Marked _search_api_settings_equals() as
|
||||
deprecated.
|
||||
@@ -65,7 +102,7 @@ Search API 1.12 (05/23/2014):
|
||||
- #2150779 by hefox: Fixed "Overridden" detection for index features.
|
||||
- #1227702 by drunken monkey: Improved error handling.
|
||||
|
||||
Search API 1.11 (12/25/2013):
|
||||
Search API 1.11 (2013-12-25):
|
||||
-----------------------------
|
||||
- #1879196 by drunken monkey: Fixed invalid old indexes causing errors.
|
||||
- #2155127 by drunken monkey: Clarified the scope of the "Node access" and
|
||||
@@ -81,7 +118,7 @@ Search API 1.11 (12/25/2013):
|
||||
- #2146435 by timkang: Fixed Views paging with custom pager add-ons.
|
||||
- #2150347 by drunken monkey: Added access callbacks for indexes and servers.
|
||||
|
||||
Search API 1.10 (12/09/2013):
|
||||
Search API 1.10 (2013-12-09):
|
||||
-----------------------------
|
||||
- #2130819 by drunken monkey, Bojhan: Added UI improvements for the "View" tabs.
|
||||
- #2152327 by sirtet, miro_dietiker: Fixed typo in help text for drush sapi-c.
|
||||
@@ -122,7 +159,7 @@ Search API 1.10 (12/09/2013):
|
||||
- #2100191 by drunken monkey, Bojhan: Added an admin description to the Search
|
||||
API landing page.
|
||||
|
||||
Search API 1.9 (10/23/2013):
|
||||
Search API 1.9 (2013-10-23):
|
||||
----------------------------
|
||||
- #2113277 by moonray, drunken monkey: Fixed date facet count for active item.
|
||||
- #2086783 by drunken monkey: Removed Views field handlers for "virtual" fields.
|
||||
@@ -151,7 +188,7 @@ Search API 1.9 (10/23/2013):
|
||||
- #2075839 by leeomara, drunken monkey: Added descriptions to field lists for
|
||||
'Aggregated Fields'.
|
||||
|
||||
Search API 1.8 (09/01/2013):
|
||||
Search API 1.8 (2013-09-01):
|
||||
----------------------------
|
||||
- #1414048 by drunken monkey: Fixed exception in views.inc removes all Search
|
||||
API tables.
|
||||
@@ -185,7 +222,7 @@ Search API 1.8 (09/01/2013):
|
||||
- #2040111 by arpieb: Fixed Views URL argument handler to allow multiple values.
|
||||
- #1064520 by drunken monkey: Added a processor for highlighting.
|
||||
|
||||
Search API 1.7 (07/01/2013):
|
||||
Search API 1.7 (2013-07-01):
|
||||
----------------------------
|
||||
- #1612708 by drunken monkey: Fixed Views caching with facet blocks.
|
||||
- #2024189 by drunken monkey: Improved serialization of the query class.
|
||||
@@ -212,7 +249,7 @@ Search API 1.7 (07/01/2013):
|
||||
- #1285794 by drunken monkey: Fixed "All" option in Views' exposed "Items per
|
||||
page" setting.
|
||||
|
||||
Search API 1.6 (05/29/2013):
|
||||
Search API 1.6 (2013-05-29):
|
||||
----------------------------
|
||||
- #1649976 by Berdir, ilari.stenroth, drunken monkey: Fixed memory error during
|
||||
crons run for large indexes.
|
||||
@@ -230,7 +267,7 @@ Search API 1.6 (05/29/2013):
|
||||
- #1760706 by jgraham, das-peter, drunken monkey: Added a flexible way for
|
||||
determining whether an index contains entities.
|
||||
|
||||
Search API 1.5 (05/04/2013):
|
||||
Search API 1.5 (2013-05-04):
|
||||
----------------------------
|
||||
- #1169254 by cslavoie, drunken monkey, DYdave: Added transliteration processor.
|
||||
- #1959088 by drunken monkey: Fixed titles for contextual filters.
|
||||
@@ -247,7 +284,7 @@ Search API 1.5 (05/04/2013):
|
||||
in the Hierarchy data alteration.
|
||||
- #1702604 by JvE, slucero: Added option for maximum date facet depth.
|
||||
|
||||
Search API 1.4 (01/09/2013):
|
||||
Search API 1.4 (2013-01-09):
|
||||
----------------------------
|
||||
- #1827272 by drunken monkey: Fixed regression introduced by #1777710.
|
||||
- #1807622 by drunken monkey: Fixed definition of the default node index.
|
||||
@@ -257,7 +294,7 @@ Search API 1.4 (01/09/2013):
|
||||
filters.
|
||||
- #1823916 by aschiwi: Fixed batch_sise typos.
|
||||
|
||||
Search API 1.3 (10/10/2012):
|
||||
Search API 1.3 (2012-10-10):
|
||||
----------------------------
|
||||
- Patch by mr.baileys: Fixed "enable" function doesn't use security tokens.
|
||||
- #1318904 by becw, das-peter, orakili, drunken monkey: Added improved handling
|
||||
@@ -273,7 +310,7 @@ Search API 1.3 (10/10/2012):
|
||||
- #1414138 by drunken monkey: Fixed internal static index property cache.
|
||||
- #1253320 by drunken monkey, fago: Fixed improper error handling.
|
||||
|
||||
Search API 1.2 (07/07/2012):
|
||||
Search API 1.2 (2012-07-07):
|
||||
----------------------------
|
||||
- #1368548 by das-peter: Do not index views results by entity id.
|
||||
- #1422750 by drunken monkey, sepgil: Fixed illegal modification of entity
|
||||
@@ -286,7 +323,7 @@ Search API 1.2 (07/07/2012):
|
||||
changed.
|
||||
- #1528436 by jsacksick, drunken monkey: Fixed handling of exportable entities.
|
||||
|
||||
Search API 1.1 (05/23/2012):
|
||||
Search API 1.1 (2012-05-23):
|
||||
----------------------------
|
||||
- Fixed escaping of error messages.
|
||||
- #1330506 by drunken monkey: Removed the old Facets module.
|
||||
@@ -297,7 +334,7 @@ Search API 1.1 (05/23/2012):
|
||||
$service->configurationFormValidate() for empty forms.
|
||||
- #1400882 by mh86: Fixed "Index hierarchy" for "All parents".
|
||||
|
||||
Search API 1.0 (12/15/2011):
|
||||
Search API 1.0 (2011-12-15):
|
||||
----------------------------
|
||||
- #1350322 by drunken monkey: Fixed regressions introduced with cron queue
|
||||
indexing.
|
||||
@@ -315,7 +352,7 @@ Search API 1.0 (12/15/2011):
|
||||
dependency plugin.
|
||||
- #1337292 by drunken monkey: Fixed facet dependency system.
|
||||
|
||||
Search API 1.0, RC 1 (11/10/2011):
|
||||
Search API 1.0, RC 1 (2011-11-10):
|
||||
----------------------------------
|
||||
API changes:
|
||||
- #1260834 by drunken monkey: Added a way to define custom data types.
|
||||
@@ -386,7 +423,7 @@ Others:
|
||||
- #1161532 by drunken monkey: Fixed discerning between delete and revert in
|
||||
hook_*_delete().
|
||||
|
||||
Search API 1.0, Beta 10 (06/20/2011):
|
||||
Search API 1.0, Beta 10 (2011-06-20):
|
||||
-------------------------------------
|
||||
API changes:
|
||||
- #1068342 by drunken monkey: Added a 'fields to run on' option for processors.
|
||||
@@ -400,7 +437,7 @@ Others:
|
||||
- #1133864 by agentrickard, awolfey, greg.1.anderson, drunken monkey: Added
|
||||
Drush integration.
|
||||
|
||||
Search API 1.0, Beta 9 (06/06/2011):
|
||||
Search API 1.0, Beta 9 (2011-06-06):
|
||||
------------------------------------
|
||||
API changes:
|
||||
- #1089758 by becw, drunken monkey: Updated Views field handlers to utilize new
|
||||
@@ -442,7 +479,7 @@ Others:
|
||||
- #1120850 by drunken monkey, fangel: Fixed type of related entities in nested
|
||||
lists.
|
||||
|
||||
Search API 1.0, Beta 8 (04/02/2011):
|
||||
Search API 1.0, Beta 8 (2011-04-02):
|
||||
------------------------------------
|
||||
API changes:
|
||||
- #1012878 by drunken monkey: Added a way to index an entity directly.
|
||||
@@ -461,12 +498,12 @@ Others:
|
||||
search_api_facets_by_block_status().
|
||||
- #1081666 by danielnolde: Fixed PHP notices when property labels are missing.
|
||||
|
||||
Search API 1.0, Beta 7 (03/08/2011):
|
||||
Search API 1.0, Beta 7 (2011-03-08):
|
||||
------------------------------------
|
||||
- #1083828 by drunken monkey: Added documentation on indexing custom data.
|
||||
- #1081244 by drunken monkey: Fixed debug line still contained in DB backend.
|
||||
|
||||
Search API 1.0, Beta 6 (03/04/2011):
|
||||
Search API 1.0, Beta 6 (2011-03-04):
|
||||
------------------------------------
|
||||
API changes:
|
||||
- #1075810 by drunken monkey: Added API function for marking entities as dirty.
|
||||
@@ -509,7 +546,7 @@ Others:
|
||||
- #1024514: Error when preprocessing muli-valued fulltext fields.
|
||||
- #1020372: CSS classes for facets.
|
||||
|
||||
Search API 1.0, Beta 5 (01/05/2011):
|
||||
Search API 1.0, Beta 5 (2011-01-05):
|
||||
------------------------------------
|
||||
API changes:
|
||||
- #917998: Enhance data alterations by making them objects.
|
||||
@@ -531,7 +568,7 @@ Others:
|
||||
- #985324: Add "Current search" block.
|
||||
- #984174: Bug in Index::prepareProcessors() when processors have not been set.
|
||||
|
||||
Search API 1.0, Beta 4 (11/29/2010):
|
||||
Search API 1.0, Beta 4 (2010-11-29):
|
||||
------------------------------------
|
||||
API changes:
|
||||
- #976876: Move Solr module into its own project.
|
||||
@@ -573,7 +610,7 @@ Others:
|
||||
- #938982: Not all SearchApiQuery options are passed.
|
||||
- #931066 by luke_b: HTTP timeout not set correctly.
|
||||
|
||||
Search API 1.0, Beta 3 (09/30/2010):
|
||||
Search API 1.0, Beta 3 (2010-09-30):
|
||||
------------------------------------
|
||||
- API mostly stable.
|
||||
- Five contrib modules exist:
|
||||
|
||||
+21
@@ -54,6 +54,27 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy
|
||||
}
|
||||
elseif ($operator == FACETAPI_OPERATOR_OR) {
|
||||
$conjunction = 'OR';
|
||||
// When the operator is OR, remove parent terms from the active ones if
|
||||
// children are active. If we don't do this, sending a term and its
|
||||
// parent will produce the same results as just sending the parent.
|
||||
if ($settings['flatten'] == '0') {
|
||||
// Check the filters in reverse order, to avoid checking parents that
|
||||
// will afterwards be removed anyways.
|
||||
foreach (array_reverse(array_keys($active)) as $filter) {
|
||||
// Skip this filter if it was already removed, or if it is the
|
||||
// "missing value" filter ("!").
|
||||
if (!isset($active[$filter]) || !is_numeric($filter)) {
|
||||
continue;
|
||||
}
|
||||
$parents = taxonomy_get_parents_all($filter);
|
||||
// The return value of taxonomy_get_parents_all() includes the term
|
||||
// itself at index 0. Remove that to only get the term's ancestors.
|
||||
unset($parents[0]);
|
||||
foreach ($parents as $parent) {
|
||||
unset($active[$parent->tid]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$vars = array(
|
||||
|
||||
+3
-3
@@ -9,9 +9,9 @@ files[] = plugins/facetapi/adapter.inc
|
||||
files[] = plugins/facetapi/query_type_term.inc
|
||||
files[] = plugins/facetapi/query_type_date.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2014-12-26
|
||||
version = "7.x-1.14"
|
||||
; Information added by Drupal.org packaging script on 2015-08-30
|
||||
version = "7.x-1.16"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1419580682"
|
||||
datestamp = "1440962813"
|
||||
|
||||
|
||||
@@ -24,6 +24,22 @@ When these are present, the normal keywords should be ignored and the related
|
||||
items be returned as results instead. Sorting, filtering and range restriction
|
||||
should all work normally.
|
||||
|
||||
"Random sort" feature
|
||||
---------------------
|
||||
This module defines the "Random sort" feature (feature key:
|
||||
"search_api_random_sort") that allows to randomly sort the results returned by a
|
||||
search. With a server supporting this, you can use the "Global: Random" sort to
|
||||
sort the view's results randomly. Every time the query is run a different
|
||||
sorting will be provided.
|
||||
|
||||
For developers:
|
||||
A service class that wants to support this feature has to check for a
|
||||
"search_api_random" field in the search query's sorts and insert a random sort
|
||||
in that position. If the query is sorted in this way, then the
|
||||
"search_api_random_sort" query option can contain additional options for the
|
||||
random sort, as an associative array with any of the following keys:
|
||||
- seed: A numeric seed value to use for the random sort.
|
||||
|
||||
"Facets block" display
|
||||
----------------------
|
||||
Most features should be clear to users of Views. However, the module also
|
||||
|
||||
+6
-5
@@ -71,11 +71,12 @@ class SearchApiViewsHandlerArgumentMoreLikeThis extends SearchApiViewsHandlerArg
|
||||
$this->query->abort();
|
||||
return;
|
||||
}
|
||||
$fields = $this->options['fields'] ? $this->options['fields'] : array();
|
||||
if (empty($fields)) {
|
||||
foreach ($this->query->getIndex()->options['fields'] as $key => $field) {
|
||||
$fields[] = $key;
|
||||
}
|
||||
$index_fields = array_keys($this->query->getIndex()->options['fields']);
|
||||
if (empty($this->options['fields'])) {
|
||||
$fields = $index_fields;
|
||||
}
|
||||
else {
|
||||
$fields = array_intersect($this->options['fields'], $index_fields);
|
||||
}
|
||||
$mlt = array(
|
||||
'id' => $this->argument,
|
||||
|
||||
+20
@@ -16,6 +16,7 @@ class SearchApiViewsHandlerFilterDate extends SearchApiViewsHandlerFilter {
|
||||
public function option_definition() {
|
||||
return parent::option_definition() + array(
|
||||
'widget_type' => array('default' => 'default'),
|
||||
'year_range' => array('default' => '-3:+3'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,6 +44,24 @@ class SearchApiViewsHandlerFilterDate extends SearchApiViewsHandlerFilter {
|
||||
'#options' => $widget_options,
|
||||
);
|
||||
}
|
||||
|
||||
if (module_exists('date_api')) {
|
||||
$form['year_range'] = array(
|
||||
'#type' => 'date_year_range',
|
||||
'#default_value' => $this->options['year_range'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate extra options.
|
||||
*/
|
||||
public function extra_options_validate($form, &$form_state) {
|
||||
if (isset($form_state['values']['options']['year_range'])) {
|
||||
if (!preg_match('/^(?:\-[0-9]{1,4}|[0-9]{4}):(?:[\+|\-][0-9]{1,4}|[0-9]{4})$/', $form_state['values']['options']['year_range'])) {
|
||||
form_error($form['year_range'], t('Date year range must be in the format -9:+9, 2005:2010, -9:2010, or 2005:+9'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,6 +75,7 @@ class SearchApiViewsHandlerFilterDate extends SearchApiViewsHandlerFilter {
|
||||
if ($this->options['widget_type'] == 'date_popup' && module_exists('date_popup')) {
|
||||
$form['value']['#type'] = 'date_popup';
|
||||
$form['value']['#date_format'] = 'm/d/Y';
|
||||
$form['value']['#date_year_range'] = $this->options['year_range'];
|
||||
unset($form['value']['#description']);
|
||||
}
|
||||
elseif (empty($form_state['exposed'])) {
|
||||
|
||||
+13
@@ -178,12 +178,25 @@ class SearchApiViewsHandlerFilterTaxonomyTerm extends SearchApiViewsHandlerFilte
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// We need to know the operator, which is normally set in
|
||||
// views_handler_filter::accept_exposed_input(), before we actually call
|
||||
// the parent version of ourselves.
|
||||
if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator_id']) && isset($input[$this->options['expose']['operator_id']])) {
|
||||
$this->operator = $input[$this->options['expose']['operator_id']];
|
||||
}
|
||||
|
||||
// If view is an attachment and is inheriting exposed filters, then assume
|
||||
// exposed input has already been validated.
|
||||
if (!empty($this->view->is_attachment) && $this->view->display_handler->uses_exposed()) {
|
||||
$this->validated_exposed_input = (array) $this->view->exposed_raw_input[$this->options['expose']['identifier']];
|
||||
}
|
||||
|
||||
// If we're checking for EMPTY or NOT, we don't need any input, and we can
|
||||
// say that our input conditions are met by just having the right operator.
|
||||
if ($this->operator == 'empty' || $this->operator == 'not empty') {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// If it's non-required and there's no value don't bother filtering.
|
||||
if (!$this->options['expose']['required'] && empty($this->validated_exposed_input)) {
|
||||
return FALSE;
|
||||
|
||||
+75
-27
@@ -122,19 +122,63 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a sort to the query.
|
||||
* Adds a sort to the query.
|
||||
*
|
||||
* @param $selector
|
||||
* @param string $selector
|
||||
* The field to sort on. All indexed fields of the index are valid values.
|
||||
* In addition, the special fields 'search_api_relevance' (sort by
|
||||
* relevance) and 'search_api_id' (sort by item id) may be used.
|
||||
* @param $order
|
||||
* In addition, these special fields may be used:
|
||||
* - search_api_relevance: sort by relevance;
|
||||
* - search_api_id: sort by item id;
|
||||
* - search_api_random: random sort (available only if the server supports
|
||||
* the "search_api_random_sort" feature).
|
||||
* @param string $order
|
||||
* The order to sort items in - either 'ASC' or 'DESC'. Defaults to 'ASC'.
|
||||
*/
|
||||
public function add_selector_orderby($selector, $order = 'ASC') {
|
||||
$this->query->sort($selector, $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a sorting method as present in the Views default query plugin.
|
||||
*
|
||||
* This is provided so that the "Global: Random" sort included in Views will
|
||||
* work properly with Search API Views. Random sorting is only supported if
|
||||
* the active search server supports the "search_api_random_sort" feature,
|
||||
* though, otherwise the call will be ignored.
|
||||
*
|
||||
* This method can only be used to sort randomly, as would be done with the
|
||||
* default query plugin. All other calls are ignored.
|
||||
*
|
||||
* @param string|null $table
|
||||
* Only "rand" is recognized here, all other calls are ignored.
|
||||
* @param string|null $field
|
||||
* Is ignored and only present for compatibility reasons.
|
||||
* @param string $order
|
||||
* Either "ASC" or "DESC".
|
||||
* @param string|null $alias
|
||||
* Is ignored and only present for compatibility reasons.
|
||||
* @param array $params
|
||||
* The following optional parameters are recognized:
|
||||
* - seed: a predefined seed for the random generator.
|
||||
*
|
||||
* @see views_plugin_query_default::add_orderby()
|
||||
*/
|
||||
public function add_orderby($table, $field = NULL, $order = 'ASC', $alias = '', $params = array()) {
|
||||
$server = $this->getIndex()->server();
|
||||
if ($table == 'rand') {
|
||||
if ($server->supportsFeature('search_api_random_sort')) {
|
||||
$this->add_selector_orderby('search_api_random', $order);
|
||||
if ($params) {
|
||||
$this->setOption('search_api_random_sort', $params);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$variables['%server'] = $server->label();
|
||||
watchdog('search_api_views', 'Tried to sort results randomly on server %server which does not support random sorting.', $variables, WATCHDOG_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the options used by this query plugin.
|
||||
*
|
||||
@@ -203,6 +247,10 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
* Builds the necessary info to execute the query.
|
||||
*/
|
||||
public function build(&$view) {
|
||||
if (!empty($this->errors)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->view = $view;
|
||||
|
||||
// Setup the nested filter structure for this query.
|
||||
@@ -375,8 +423,8 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
// loading any items.
|
||||
foreach ($results as $id => $result) {
|
||||
if (!empty($this->options['entity_access']) && ($entity_type = $this->index->getEntityType())) {
|
||||
$entity = entity_load($entity_type, array($id));
|
||||
if (!entity_access('view', $entity_type, $entity[$id])) {
|
||||
$entity = $this->index->loadItems(array($id));
|
||||
if (!$entity || !entity_access('view', $entity_type, reset($entity))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -400,7 +448,7 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
}
|
||||
|
||||
// Check whether we need to extract any properties from the result item.
|
||||
$missing_fields = array_diff_key($this->fields, $row);
|
||||
$missing_fields = array_diff_key($this->fields, $row['_entity_properties']);
|
||||
if ($missing_fields) {
|
||||
$missing[$id] = $missing_fields;
|
||||
if (is_object($row['entity'])) {
|
||||
@@ -418,14 +466,14 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
// Load items of those rows which haven't got all field values, yet.
|
||||
if (!empty($ids)) {
|
||||
$items += $this->index->loadItems($ids);
|
||||
// $items now includes loaded items, and those already passed in the
|
||||
// search results.
|
||||
foreach ($items as $id => $item) {
|
||||
// Extract item properties.
|
||||
$wrapper = $this->index->entityWrapper($item, FALSE);
|
||||
$rows[$id]->_entity_properties += $this->extractFields($wrapper, $missing[$id]);
|
||||
$rows[$id]->entity = $item;
|
||||
}
|
||||
}
|
||||
// $items now includes all loaded items from which fields still need to be
|
||||
// extracted.
|
||||
foreach ($items as $id => $item) {
|
||||
// Extract item properties.
|
||||
$wrapper = $this->index->entityWrapper($item, FALSE);
|
||||
$rows[$id]->_entity_properties += $this->extractFields($wrapper, $missing[$id]);
|
||||
$rows[$id]->entity = $item;
|
||||
}
|
||||
|
||||
// Finally, add all rows to the Views result set.
|
||||
@@ -490,31 +538,31 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
* query backend.
|
||||
*/
|
||||
public function get_result_wrappers($results, $relationship = NULL, $field = NULL) {
|
||||
$entity_type = $this->index->getEntityType();
|
||||
$type = $this->index->item_type;
|
||||
$wrappers = array();
|
||||
$load_entities = array();
|
||||
$load_items = array();
|
||||
foreach ($results as $row_index => $row) {
|
||||
if ($entity_type && isset($row->entity)) {
|
||||
if (isset($row->entity)) {
|
||||
// If this entity isn't load, register it for pre-loading.
|
||||
if (!is_object($row->entity)) {
|
||||
$load_entities[$row->entity] = $row_index;
|
||||
$load_items[$row->entity] = $row_index;
|
||||
}
|
||||
else {
|
||||
$wrappers[$row_index] = $this->index->entityWrapper($row->entity);
|
||||
}
|
||||
|
||||
$wrappers[$row_index] = $this->index->entityWrapper($row->entity);
|
||||
}
|
||||
}
|
||||
|
||||
// If the results are entities, we pre-load them to make use of a multiple
|
||||
// load. (Otherwise, each result would be loaded individually.)
|
||||
if (!empty($load_entities)) {
|
||||
$entities = entity_load($entity_type, array_keys($load_entities));
|
||||
foreach ($entities as $entity_id => $entity) {
|
||||
$wrappers[$load_entities[$entity_id]] = $this->index->entityWrapper($entity);
|
||||
if (!empty($load_items)) {
|
||||
$items = $this->index->loadItems(array_keys($load_items));
|
||||
foreach ($items as $id => $item) {
|
||||
$wrappers[$load_items[$id]] = $this->index->entityWrapper($item);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the relationship, if necessary.
|
||||
$type = $entity_type ? $entity_type : $this->index->item_type;
|
||||
$selector_suffix = '';
|
||||
if ($field && ($pos = strrpos($field, ':'))) {
|
||||
$selector_suffix = substr($field, 0, $pos);
|
||||
|
||||
+3
-3
@@ -27,9 +27,9 @@ files[] = includes/handler_sort.inc
|
||||
files[] = includes/plugin_cache.inc
|
||||
files[] = includes/query.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2014-12-26
|
||||
version = "7.x-1.14"
|
||||
; Information added by Drupal.org packaging script on 2015-08-30
|
||||
version = "7.x-1.16"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1419580682"
|
||||
datestamp = "1440962813"
|
||||
|
||||
|
||||
@@ -10,6 +10,16 @@
|
||||
*/
|
||||
class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback {
|
||||
|
||||
/**
|
||||
* The type of aggregation currently performed.
|
||||
*
|
||||
* Used to temporarily store the current aggregation type for use of
|
||||
* SearchApiAlterAddAggregation::reduce() with array_reduce().
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reductionType;
|
||||
|
||||
public function configurationForm() {
|
||||
$form['#attached']['css'][] = drupal_get_path('module', 'search_api') . '/search_api.admin.css';
|
||||
|
||||
@@ -193,6 +203,12 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback {
|
||||
return isset($a) ? min($a, $b) : $b;
|
||||
case 'first':
|
||||
return isset($a) ? $a : $b;
|
||||
case 'first_char':
|
||||
$b = "$b";
|
||||
if (isset($a) || $b === '') {
|
||||
return $a;
|
||||
}
|
||||
return drupal_substr($b, 0, 1);
|
||||
case 'list':
|
||||
if (!isset($a)) {
|
||||
$a = array();
|
||||
@@ -200,6 +216,7 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback {
|
||||
$a[] = $b;
|
||||
return $a;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,6 +284,7 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback {
|
||||
'max' => t('Maximum'),
|
||||
'min' => t('Minimum'),
|
||||
'first' => t('First'),
|
||||
'first_char' => t('First letter'),
|
||||
'list' => t('List'),
|
||||
);
|
||||
case 'type':
|
||||
@@ -277,6 +295,7 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback {
|
||||
'max' => 'integer',
|
||||
'min' => 'integer',
|
||||
'first' => 'string',
|
||||
'first_char' => 'string',
|
||||
'list' => 'list<string>',
|
||||
);
|
||||
case 'description':
|
||||
@@ -287,6 +306,7 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback {
|
||||
'max' => t('The Maximum aggregation computes the numerically largest contained field value.'),
|
||||
'min' => t('The Minimum aggregation computes the numerically smallest contained field value.'),
|
||||
'first' => t('The First aggregation will simply keep the first encountered field value. This is helpful foremost when you know that a list field will only have a single value.'),
|
||||
'first_char' => t('The "First letter" aggregation uses just the first letter of the first encountered field value as the aggregated value. This can, for example, be used to build a Glossary view.'),
|
||||
'list' => t('The List aggregation collects all field values into a multi-valued field containing all values.'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,15 @@ class SearchApiAlterBundleFilter extends SearchApiAbstractAlterCallback {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsIndex(SearchApiIndex $index) {
|
||||
if ($this->isMultiEntityIndex($index)) {
|
||||
$info = entity_get_info();
|
||||
foreach ($index->options['datasource']['types'] as $type) {
|
||||
if (isset($info[$type]) && self::hasBundles($info[$type])) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
return $index->getEntityType() && ($info = entity_get_info($index->getEntityType())) && self::hasBundles($info);
|
||||
}
|
||||
|
||||
@@ -21,15 +30,24 @@ class SearchApiAlterBundleFilter extends SearchApiAbstractAlterCallback {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alterItems(array &$items) {
|
||||
$info = entity_get_info($this->index->getEntityType());
|
||||
if (self::hasBundles($info) && isset($this->options['bundles'])) {
|
||||
$bundles = array_flip($this->options['bundles']);
|
||||
$default = (bool) $this->options['default'];
|
||||
if (!$this->supportsIndex($this->index) || !isset($this->options['bundles'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isMultiEntityIndex()) {
|
||||
$bundle_prop = 'item_bundle';
|
||||
}
|
||||
else {
|
||||
$info = entity_get_info($this->index->getEntityType());
|
||||
$bundle_prop = $info['entity keys']['bundle'];
|
||||
foreach ($items as $id => $item) {
|
||||
if (isset($bundles[$item->$bundle_prop]) == $default) {
|
||||
unset($items[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
$bundles = array_flip($this->options['bundles']);
|
||||
$default = (bool) $this->options['default'];
|
||||
|
||||
foreach ($items as $id => $item) {
|
||||
if (isset($bundles[$item->$bundle_prop]) == $default) {
|
||||
unset($items[$id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,31 +56,53 @@ class SearchApiAlterBundleFilter extends SearchApiAbstractAlterCallback {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configurationForm() {
|
||||
$info = entity_get_info($this->index->getEntityType());
|
||||
if (self::hasBundles($info)) {
|
||||
if ($this->supportsIndex($this->index)) {
|
||||
$options = array();
|
||||
foreach ($info['bundles'] as $bundle => $bundle_info) {
|
||||
$options[$bundle] = isset($bundle_info['label']) ? $bundle_info['label'] : $bundle;
|
||||
if ($this->isMultiEntityIndex()) {
|
||||
$info = entity_get_info();
|
||||
$unsupported_types = array();
|
||||
foreach ($this->index->options['datasource']['types'] as $type) {
|
||||
if (isset($info[$type]) && self::hasBundles($info[$type])) {
|
||||
foreach ($info[$type]['bundles'] as $bundle => $bundle_info) {
|
||||
$options["$type:$bundle"] = $info[$type]['label'] . ' » ' . $bundle_info['label'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$unsupported_types[] = isset($info[$type]['label']) ? $info[$type]['label'] : $type;
|
||||
}
|
||||
}
|
||||
if ($unsupported_types) {
|
||||
$form['unsupported_types']['#markup'] = '<p>' . t('The following entity types do not contain any bundles: @types. All items of those types will therefore be included in the index.', array('@types' => implode(', ', $unsupported_types))) . '</p>';
|
||||
}
|
||||
}
|
||||
$form = array(
|
||||
'default' => array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Which items should be indexed?'),
|
||||
'#default_value' => isset($this->options['default']) ? $this->options['default'] : 1,
|
||||
'#options' => array(
|
||||
1 => t('All but those from one of the selected bundles'),
|
||||
0 => t('Only those from the selected bundles'),
|
||||
),
|
||||
),
|
||||
'bundles' => array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Bundles'),
|
||||
'#default_value' => isset($this->options['bundles']) ? $this->options['bundles'] : array(),
|
||||
'#options' => $options,
|
||||
'#size' => min(4, count($options)),
|
||||
'#multiple' => TRUE,
|
||||
else {
|
||||
$info = entity_get_info($this->index->getEntityType());
|
||||
foreach ($info['bundles'] as $bundle => $bundle_info) {
|
||||
$options[$bundle] = isset($bundle_info['label']) ? $bundle_info['label'] : $bundle;
|
||||
}
|
||||
}
|
||||
if (!empty($this->index->options['datasource']['bundles'])) {
|
||||
$form['message']['#markup'] = '<p>' . t("<strong>Note:</strong> This index is already restricted to certain bundles. If you use this data alteration, those will be reduced further. However, the index setting is better supported in the user interface and should therefore be prefered. For example, using this data alteration will not reduce the displayed total number of items to index (even though some of them will not be indexed). Consider creating a new index with appropriate bundle settings instead.") . '</p>';
|
||||
$included_bundles = array_flip($this->index->options['datasource']['bundles']);
|
||||
$options = array_intersect_key($options, $included_bundles);
|
||||
}
|
||||
$form['default'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Which items should be indexed?'),
|
||||
'#default_value' => isset($this->options['default']) ? $this->options['default'] : 1,
|
||||
'#options' => array(
|
||||
1 => t('All but those from one of the selected bundles'),
|
||||
0 => t('Only those from the selected bundles'),
|
||||
),
|
||||
);
|
||||
$form['bundles'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Bundles'),
|
||||
'#default_value' => isset($this->options['bundles']) ? $this->options['bundles'] : array(),
|
||||
'#options' => $options,
|
||||
'#size' => min(4, count($options)),
|
||||
'#multiple' => TRUE,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form = array(
|
||||
@@ -81,10 +121,25 @@ class SearchApiAlterBundleFilter extends SearchApiAbstractAlterCallback {
|
||||
* The entity type's entity_get_info() array.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the entity type has bundles, FASLE otherwise.
|
||||
* TRUE if the entity type has bundles, FALSE otherwise.
|
||||
*/
|
||||
protected static function hasBundles(array $entity_info) {
|
||||
return !empty($entity_info['entity keys']['bundle']) && !empty($entity_info['bundles']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given index contains multiple types of entities.
|
||||
*
|
||||
* @param SearchApiIndex|null $index
|
||||
* (optional) The index to examine. Defaults to the index set for this
|
||||
* plugin.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the index is a multi-entity index, FALSE otherwise.
|
||||
*/
|
||||
protected function isMultiEntityIndex(SearchApiIndex $index = NULL) {
|
||||
$index = $index ? $index : $this->index;
|
||||
return $index->datasource() instanceof SearchApiCombinedEntityDataSourceController;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the SearchApiAlterUserStatus class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Filters out blocked user accounts.
|
||||
*/
|
||||
class SearchApiAlterUserStatus extends SearchApiAbstractAlterCallback {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsIndex(SearchApiIndex $index) {
|
||||
return $index->getEntityType() == 'user';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alterItems(array &$items) {
|
||||
foreach ($items as $id => $account) {
|
||||
if (empty($account->status)) {
|
||||
unset($items[$id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -158,6 +158,10 @@ interface SearchApiDataSourceControllerInterface {
|
||||
* @param SearchApiIndex[] $indexes
|
||||
* The indexes for which items should be tracked.
|
||||
*
|
||||
* @return SearchApiIndex[]|null
|
||||
* All indexes for which any items were added; or NULL if items were added
|
||||
* for all of them.
|
||||
*
|
||||
* @throws SearchApiDataSourceException
|
||||
* If any error state was encountered.
|
||||
*/
|
||||
@@ -179,6 +183,10 @@ interface SearchApiDataSourceControllerInterface {
|
||||
* The concept of queued items will be removed in the Drupal 8 version of
|
||||
* this module.
|
||||
*
|
||||
* @return SearchApiIndex[]|null
|
||||
* All indexes for which any items were updated; or NULL if items were
|
||||
* updated for all of them.
|
||||
*
|
||||
* @throws SearchApiDataSourceException
|
||||
* If any error state was encountered.
|
||||
*/
|
||||
@@ -227,6 +235,10 @@ interface SearchApiDataSourceControllerInterface {
|
||||
* @param SearchApiIndex[] $indexes
|
||||
* The indexes for which the deletions should be tracked.
|
||||
*
|
||||
* @return SearchApiIndex[]|null
|
||||
* All indexes for which any items were deleted; or NULL if items were
|
||||
* deleted for all of them.
|
||||
*
|
||||
* @throws SearchApiDataSourceException
|
||||
* If any error state was encountered.
|
||||
*/
|
||||
@@ -281,6 +293,69 @@ interface SearchApiDataSourceControllerInterface {
|
||||
*/
|
||||
public function getEntityType();
|
||||
|
||||
/**
|
||||
* Form constructor for configuring the datasource for a given index.
|
||||
*
|
||||
* @param array $form
|
||||
* The form returned by configurationForm().
|
||||
* @param array $form_state
|
||||
* The form state. $form_state['index'] will contain the edited index. If
|
||||
* this key is empty, then a new index is being created. In case of an edit,
|
||||
* $form_state['index']->options['datasource'] contains the previous
|
||||
* settings for the datasource.
|
||||
*
|
||||
* @return array|false
|
||||
* A form array for configuring this callback, or FALSE if no configuration
|
||||
* is possible.
|
||||
*/
|
||||
public function configurationForm(array $form, array &$form_state);
|
||||
|
||||
/**
|
||||
* Validation callback for the form returned by configurationForm().
|
||||
*
|
||||
* This method will only be called if that form was non-empty.
|
||||
*
|
||||
* @param array $form
|
||||
* The form returned by configurationForm().
|
||||
* @param array $values
|
||||
* The part of the $form_state['values'] array corresponding to this form.
|
||||
* @param array $form_state
|
||||
* The complete form state.
|
||||
*/
|
||||
public function configurationFormValidate(array $form, array &$values, array &$form_state);
|
||||
|
||||
/**
|
||||
* Submit callback for the form returned by configurationForm().
|
||||
*
|
||||
* This method will only be called if that form was non-empty.
|
||||
*
|
||||
* Any necessary changes to the submitted values should be made, afterwards
|
||||
* they will automatically be stored as the index's "datasource" options. The
|
||||
* method can also be used by the datasource controller to react to the
|
||||
* possible change in its settings.
|
||||
*
|
||||
* @param array $form
|
||||
* The form returned by configurationForm().
|
||||
* @param array $values
|
||||
* The part of the $form_state['values'] array corresponding to this form.
|
||||
* @param array $form_state
|
||||
* The complete form state.
|
||||
*/
|
||||
public function configurationFormSubmit(array $form, array &$values, array &$form_state);
|
||||
|
||||
/**
|
||||
* Returns a summary of an index's current datasource configuration.
|
||||
*
|
||||
* @param SearchApiIndex $index
|
||||
* The index whose datasource configuration should be summarized.
|
||||
*
|
||||
* @return string|null
|
||||
* A translated string describing the index's current datasource
|
||||
* configuration. Or NULL, if there is no configuration (or no description
|
||||
* is available).
|
||||
*/
|
||||
public function getConfigurationSummary(SearchApiIndex $index);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -521,6 +596,10 @@ abstract class SearchApiAbstractDataSourceController implements SearchApiDataSou
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($indexes as $index) {
|
||||
$this->checkIndex($index);
|
||||
}
|
||||
|
||||
// Since large amounts of items can overstrain the database, only add items
|
||||
// in chunks.
|
||||
foreach (array_chunk($item_ids, 1000) as $chunk) {
|
||||
@@ -528,7 +607,6 @@ abstract class SearchApiAbstractDataSourceController implements SearchApiDataSou
|
||||
->fields(array($this->itemIdColumn, $this->indexIdColumn, $this->changedColumn));
|
||||
foreach ($chunk as $item_id) {
|
||||
foreach ($indexes as $index) {
|
||||
$this->checkIndex($index);
|
||||
$insert->values(array(
|
||||
$this->itemIdColumn => $item_id,
|
||||
$this->indexIdColumn => $index->id,
|
||||
@@ -545,23 +623,28 @@ abstract class SearchApiAbstractDataSourceController implements SearchApiDataSou
|
||||
*/
|
||||
public function trackItemChange($item_ids, array $indexes, $dequeue = FALSE) {
|
||||
if (!$this->table) {
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
$index_ids = array();
|
||||
|
||||
$ret = array();
|
||||
|
||||
foreach ($indexes as $index) {
|
||||
$this->checkIndex($index);
|
||||
$index_ids[] = $index->id;
|
||||
$update = db_update($this->table)
|
||||
->fields(array(
|
||||
$this->changedColumn => REQUEST_TIME,
|
||||
))
|
||||
->condition($this->indexIdColumn, $index->id)
|
||||
->condition($this->changedColumn, 0, $dequeue ? '<=' : '=');
|
||||
if ($item_ids !== FALSE) {
|
||||
$update->condition($this->itemIdColumn, $item_ids, 'IN');
|
||||
}
|
||||
if ($update->execute()) {
|
||||
$ret[] = $index;
|
||||
}
|
||||
}
|
||||
$update = db_update($this->table)
|
||||
->fields(array(
|
||||
$this->changedColumn => REQUEST_TIME,
|
||||
))
|
||||
->condition($this->indexIdColumn, $index_ids, 'IN')
|
||||
->condition($this->changedColumn, 0, $dequeue ? '<=' : '=');
|
||||
if ($item_ids !== FALSE) {
|
||||
$update->condition($this->itemIdColumn, $item_ids, 'IN');
|
||||
}
|
||||
$update->execute();
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -605,17 +688,22 @@ abstract class SearchApiAbstractDataSourceController implements SearchApiDataSou
|
||||
*/
|
||||
public function trackItemDelete(array $item_ids, array $indexes) {
|
||||
if (!$this->table) {
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
$index_ids = array();
|
||||
|
||||
$ret = array();
|
||||
|
||||
foreach ($indexes as $index) {
|
||||
$this->checkIndex($index);
|
||||
$index_ids[] = $index->id;
|
||||
$delete = db_delete($this->table)
|
||||
->condition($this->indexIdColumn, $index->id)
|
||||
->condition($this->itemIdColumn, $item_ids, 'IN');
|
||||
if ($delete->execute()) {
|
||||
$ret[] = $index;
|
||||
}
|
||||
}
|
||||
db_delete($this->table)
|
||||
->condition($this->itemIdColumn, $item_ids, 'IN')
|
||||
->condition($this->indexIdColumn, $index_ids, 'IN')
|
||||
->execute();
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -659,6 +747,32 @@ abstract class SearchApiAbstractDataSourceController implements SearchApiDataSou
|
||||
return array('indexed' => $indexed, 'total' => $total);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configurationForm(array $form, array &$form_state) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configurationFormValidate(array $form, array &$values, array &$form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configurationFormSubmit(array $form, array &$values, array &$form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfigurationSummary(SearchApiIndex $index) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given index is valid for this datasource controller.
|
||||
*
|
||||
|
||||
@@ -10,28 +10,69 @@
|
||||
*/
|
||||
class SearchApiEntityDataSourceController extends SearchApiAbstractDataSourceController {
|
||||
|
||||
/**
|
||||
* Entity type info for this type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $entityInfo;
|
||||
|
||||
/**
|
||||
* The ID key of this entity type, if any.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $idKey;
|
||||
|
||||
/**
|
||||
* The bundle key of this entity type, if any.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $bundleKey;
|
||||
|
||||
/**
|
||||
* Cached return values for getBundles(), keyed by index machine name.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $bundles = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($type) {
|
||||
parent::__construct($type);
|
||||
|
||||
$this->entityInfo = entity_get_info($this->entityType);
|
||||
if (!empty($this->entityInfo['entity keys']['id'])) {
|
||||
$this->idKey = $this->entityInfo['entity keys']['id'];
|
||||
}
|
||||
if (!empty($this->entityInfo['entity keys']['bundle'])) {
|
||||
$this->bundleKey = $this->entityInfo['entity keys']['bundle'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIdFieldInfo() {
|
||||
$info = entity_get_info($this->entityType);
|
||||
$properties = entity_get_property_info($this->entityType);
|
||||
if (empty($info['entity keys']['id'])) {
|
||||
throw new SearchApiDataSourceException(t("Entity type @type doesn't specify an ID key.", array('@type' => $info['label'])));
|
||||
if (!$this->idKey) {
|
||||
throw new SearchApiDataSourceException(t("Entity type @type doesn't specify an ID key.", array('@type' => $this->entityInfo['label'])));
|
||||
}
|
||||
$field = $info['entity keys']['id'];
|
||||
if (empty($properties['properties'][$field]['type'])) {
|
||||
throw new SearchApiDataSourceException(t("Entity type @type doesn't specify a type for the @prop property.", array('@type' => $info['label'], '@prop' => $field)));
|
||||
if (empty($properties['properties'][$this->idKey]['type'])) {
|
||||
throw new SearchApiDataSourceException(t("Entity type @type doesn't specify a type for the @prop property.", array('@type' => $this->entityInfo['label'], '@prop' => $this->idKey)));
|
||||
}
|
||||
$type = $properties['properties'][$field]['type'];
|
||||
$type = $properties['properties'][$this->idKey]['type'];
|
||||
if (search_api_is_list_type($type)) {
|
||||
throw new SearchApiDataSourceException(t("Entity type @type uses list field @prop as its ID.", array('@type' => $info['label'], '@prop' => $field)));
|
||||
throw new SearchApiDataSourceException(t("Entity type @type uses list field @prop as its ID.", array('@type' => $this->entityInfo['label'], '@prop' => $this->idKey)));
|
||||
}
|
||||
if ($type == 'token') {
|
||||
$type = 'string';
|
||||
}
|
||||
return array(
|
||||
'key' => $field,
|
||||
'key' => $this->idKey,
|
||||
'type' => $type,
|
||||
);
|
||||
}
|
||||
@@ -103,24 +144,24 @@ class SearchApiEntityDataSourceController extends SearchApiAbstractDataSourceCon
|
||||
// all items again without any key conflicts.
|
||||
$this->stopTracking($indexes);
|
||||
|
||||
$entity_info = entity_get_info($this->entityType);
|
||||
|
||||
if (!empty($entity_info['base table'])) {
|
||||
if (!empty($this->entityInfo['base table']) && $this->idKey) {
|
||||
// Use a subselect, which will probably be much faster than entity_load().
|
||||
|
||||
// Assumes that all entities use the "base table" property and the
|
||||
// "entity keys[id]" in the same way as the default controller.
|
||||
$id_field = $entity_info['entity keys']['id'];
|
||||
$table = $entity_info['base table'];
|
||||
$table = $this->entityInfo['base table'];
|
||||
|
||||
// We could also use a single insert (with a JOIN in the nested query),
|
||||
// We could also use a single insert (with a UNION in the nested query),
|
||||
// but this method will be mostly called with a single index, anyways.
|
||||
foreach ($indexes as $index) {
|
||||
// Select all entity ids.
|
||||
$query = db_select($table, 't');
|
||||
$query->addField('t', $id_field, 'item_id');
|
||||
$query->addField('t', $this->idKey, 'item_id');
|
||||
$query->addExpression(':index_id', 'index_id', array(':index_id' => $index->id));
|
||||
$query->addExpression('1', 'changed');
|
||||
if ($bundles = $this->getIndexBundles($index)) {
|
||||
$query->condition($this->bundleKey, $bundles);
|
||||
}
|
||||
|
||||
// INSERT ... SELECT ...
|
||||
db_insert($this->table)
|
||||
@@ -129,16 +170,147 @@ class SearchApiEntityDataSourceController extends SearchApiAbstractDataSourceCon
|
||||
}
|
||||
}
|
||||
else {
|
||||
// In the absence of a 'base table', use the slow entity_load().
|
||||
parent::startTracking($indexes);
|
||||
// In the absence of a 'base table', use the slower way of retrieving the
|
||||
// items and inserting them "manually". For each index we get the item IDs
|
||||
// (since selected bundles might differ) and insert all of them as new.
|
||||
foreach ($indexes as $index) {
|
||||
$query = new EntityFieldQuery();
|
||||
$query->entityCondition('entity_type', $this->entityType);
|
||||
if ($bundles = $this->getIndexBundles($index)) {
|
||||
$query->entityCondition('bundle', $bundles);
|
||||
}
|
||||
$result = $query->execute();
|
||||
$ids = !empty($result[$this->entityType]) ? array_keys($result[$this->entityType]) : array();
|
||||
if ($ids) {
|
||||
$this->trackItemInsert($ids, array($index));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAllItemIds() {
|
||||
return array_keys(entity_load($this->entityType));
|
||||
public function trackItemInsert(array $item_ids, array $indexes) {
|
||||
$ret = array();
|
||||
|
||||
foreach ($indexes as $index_id => $index) {
|
||||
$ids = $item_ids;
|
||||
if ($bundles = $this->getIndexBundles($index)) {
|
||||
$ids = drupal_map_assoc($ids);
|
||||
foreach (entity_load($this->entityType, $ids) as $id => $entity) {
|
||||
if (empty($bundles[$entity->{$this->bundleKey}])) {
|
||||
unset($ids[$id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($ids) {
|
||||
parent::trackItemInsert($ids, array($index));
|
||||
$ret[$index_id] = $index;
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configurationForm(array $form, array &$form_state) {
|
||||
$options = $this->getAvailableBundles();
|
||||
if (!$options) {
|
||||
return FALSE;
|
||||
}
|
||||
$form['bundles'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Bundles'),
|
||||
'#description' => t('Restrict the entity bundles that will be included in this index. Leave blank to include all bundles. This setting cannot be changed for existing indexes.'),
|
||||
'#options' => $options,
|
||||
'#attributes' => array('class' => array('search-api-checkboxes-list')),
|
||||
'#disabled' => !empty($form_state['index']),
|
||||
);
|
||||
if (!empty($form_state['index']->options['datasource'])) {
|
||||
$form['bundles']['#default_value'] = drupal_map_assoc($form_state['index']->options['datasource']['bundles']);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configurationFormSubmit(array $form, array &$values, array &$form_state) {
|
||||
if (!empty($values['bundles'])) {
|
||||
$values['bundles'] = array_keys(array_filter($values['bundles']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfigurationSummary(SearchApiIndex $index) {
|
||||
if ($bundles = $this->getIndexBundles($index)) {
|
||||
$args['!bundles'] = implode(', ', array_intersect_key($this->getAvailableBundles(), $bundles));
|
||||
return format_plural(count($bundles), 'Indexed bundle: !bundles.', 'Indexed bundles: !bundles.', $args);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the available bundles for this entity type.
|
||||
*
|
||||
* @return array
|
||||
* An array (which might be empty) mapping this entity type's bundle keys to
|
||||
* their labels.
|
||||
*/
|
||||
protected function getAvailableBundles() {
|
||||
if (!$this->bundleKey || empty($this->entityInfo['bundles'])) {
|
||||
return array();
|
||||
}
|
||||
$bundles = array();
|
||||
foreach ($this->entityInfo['bundles'] as $bundle => $bundle_info) {
|
||||
$bundles[$bundle] = isset($bundle_info['label']) ? $bundle_info['label'] : $bundle;
|
||||
}
|
||||
return $bundles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the bundles that should be indexed for an index.
|
||||
*
|
||||
* @param SearchApiIndex $index
|
||||
* The index for which to check.
|
||||
*
|
||||
* @return array
|
||||
* An array containing all bundles that should be included in this index, as
|
||||
* both the keys and values. An empty array means all current bundles should
|
||||
* be included.
|
||||
*
|
||||
* @throws SearchApiException
|
||||
* If the index doesn't belong to this datasource controller.
|
||||
*/
|
||||
protected function getIndexBundles(SearchApiIndex $index) {
|
||||
$this->checkIndex($index);
|
||||
|
||||
if (!isset($this->bundles[$index->machine_name])) {
|
||||
$this->bundles[$index->machine_name] = array();
|
||||
if (!empty($index->options['datasource']['bundles'])) {
|
||||
// We retrieve the available bundles here to check whether all of them
|
||||
// are included by the index's setting. In this case, we return an empty
|
||||
// array, too, to save on complexity.
|
||||
// On the other hand, we still want to return deleted bundles since we
|
||||
// do not want to suddenly include all bundles when all selected bundles
|
||||
// were deleted.
|
||||
$available = $this->getAvailableBundles();
|
||||
foreach ($index->options['datasource']['bundles'] as $bundle) {
|
||||
$this->bundles[$index->machine_name][$bundle] = $bundle;
|
||||
unset($available[$bundle]);
|
||||
}
|
||||
if (!$available) {
|
||||
$this->bundles[$index->machine_name] = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->bundles[$index->machine_name];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,357 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiCombinedEntityDataSourceController.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides a datasource for indexing multiple types of entities.
|
||||
*/
|
||||
class SearchApiCombinedEntityDataSourceController extends SearchApiAbstractDataSourceController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $table = 'search_api_item_string_id';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIdFieldInfo() {
|
||||
return array(
|
||||
'key' => 'item_id',
|
||||
'type' => 'string',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadItems(array $ids) {
|
||||
$ids_by_type = array();
|
||||
foreach ($ids as $id) {
|
||||
list($type, $entity_id) = explode('/', $id);
|
||||
$ids_by_type[$type][$entity_id] = $id;
|
||||
}
|
||||
|
||||
$items = array();
|
||||
foreach ($ids_by_type as $type => $type_ids) {
|
||||
foreach (entity_load($type, array_keys($type_ids)) as $entity_id => $entity) {
|
||||
$id = $type_ids[$entity_id];
|
||||
$item = (object) array($type => $entity);
|
||||
$item->item_id = $id;
|
||||
$item->item_type = $type;
|
||||
$item->item_entity_id = $entity_id;
|
||||
$item->item_bundle = NULL;
|
||||
try {
|
||||
list(, , $bundle) = entity_extract_ids($type, $entity);
|
||||
$item->item_bundle = $bundle ? "$type:$bundle" : NULL;
|
||||
}
|
||||
catch (EntityMalformedException $e) {
|
||||
// Will probably make problems at some other place, but for extracting
|
||||
// the bundle it is really not critical enough to fail on – just
|
||||
// ignore this exception.
|
||||
}
|
||||
$items[$id] = $item;
|
||||
unset($type_ids[$entity_id]);
|
||||
}
|
||||
if ($type_ids) {
|
||||
search_api_track_item_delete($type, array_keys($type_ids));
|
||||
}
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getPropertyInfo() {
|
||||
$info = array(
|
||||
'item_id' => array(
|
||||
'label' => t('ID'),
|
||||
'description' => t('The combined ID of the item, containing both entity type and entity ID.'),
|
||||
'type' => 'token',
|
||||
),
|
||||
'item_type' => array(
|
||||
'label' => t('Entity type'),
|
||||
'description' => t('The entity type of the item.'),
|
||||
'type' => 'token',
|
||||
'options list' => 'search_api_entity_type_options_list',
|
||||
),
|
||||
'item_entity_id' => array(
|
||||
'label' => t('Entity ID'),
|
||||
'description' => t('The entity ID of the item.'),
|
||||
'type' => 'token',
|
||||
),
|
||||
'item_bundle' => array(
|
||||
'label' => t('Bundle'),
|
||||
'description' => t('The bundle of the item, if applicable.'),
|
||||
'type' => 'token',
|
||||
'options list' => 'search_api_combined_bundle_options_list',
|
||||
),
|
||||
'item_label' => array(
|
||||
'label' => t('Label'),
|
||||
'description' => t('The label of the item.'),
|
||||
'type' => 'text',
|
||||
// Since this needs a bit more computation than the others, we don't
|
||||
// include it always when loading the item but use a getter callback.
|
||||
'getter callback' => 'search_api_get_multi_type_item_label',
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($this->getSelectedEntityTypeOptions() as $type => $label) {
|
||||
$info[$type] = array(
|
||||
'label' => $label,
|
||||
'description' => t('The indexed entity, if it is of type %type.', array('%type' => $label)),
|
||||
'type' => $type,
|
||||
);
|
||||
}
|
||||
|
||||
return array('property info' => $info);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItemId($item) {
|
||||
return isset($item->item_id) ? $item->item_id : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItemLabel($item) {
|
||||
return search_api_get_multi_type_item_label($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItemUrl($item) {
|
||||
if ($item->item_type == 'file') {
|
||||
return array(
|
||||
'path' => file_create_url($item->file->uri),
|
||||
'options' => array(
|
||||
'entity_type' => 'file',
|
||||
'entity' => $item,
|
||||
),
|
||||
);
|
||||
}
|
||||
$url = entity_uri($item->item_type, $item->{$item->item_type});
|
||||
return $url ? $url : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function startTracking(array $indexes) {
|
||||
if (!$this->table) {
|
||||
return;
|
||||
}
|
||||
// We first clear the tracking table for all indexes, so we can just insert
|
||||
// all items again without any key conflicts.
|
||||
$this->stopTracking($indexes);
|
||||
|
||||
foreach ($indexes as $index) {
|
||||
$types = $this->getEntityTypes($index);
|
||||
|
||||
// Wherever possible, use a sub-select instead of the much slower
|
||||
// entity_load().
|
||||
foreach ($types as $type) {
|
||||
$entity_info = entity_get_info($type);
|
||||
|
||||
if (!empty($entity_info['base table'])) {
|
||||
// Assumes that all entities use the "base table" property and the
|
||||
// "entity keys[id]" in the same way as the default controller.
|
||||
$id_field = $entity_info['entity keys']['id'];
|
||||
$table = $entity_info['base table'];
|
||||
|
||||
// Select all entity ids.
|
||||
$query = db_select($table, 't');
|
||||
$query->addExpression("CONCAT(:prefix, t.$id_field)", 'item_id', array(':prefix' => $type . '/'));
|
||||
$query->addExpression(':index_id', 'index_id', array(':index_id' => $index->id));
|
||||
$query->addExpression('1', 'changed');
|
||||
|
||||
// INSERT ... SELECT ...
|
||||
db_insert($this->table)
|
||||
->from($query)
|
||||
->execute();
|
||||
|
||||
unset($types[$type]);
|
||||
}
|
||||
}
|
||||
|
||||
// In the absence of a "base table", use the slow entity_load().
|
||||
if ($types) {
|
||||
foreach ($types as $type) {
|
||||
$query = new EntityFieldQuery();
|
||||
$query->entityCondition('entity_type', $type);
|
||||
$result = $query->execute();
|
||||
$ids = !empty($result[$type]) ? array_keys($result[$type]) : array();
|
||||
if ($ids) {
|
||||
foreach ($ids as $i => $id) {
|
||||
$ids[$i] = $type . '/' . $id;
|
||||
}
|
||||
$this->trackItemInsert($ids, array($index), TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts tracking the index status for the given items on the given indexes.
|
||||
*
|
||||
* @param array $item_ids
|
||||
* The IDs of new items to track.
|
||||
* @param SearchApiIndex[] $indexes
|
||||
* The indexes for which items should be tracked.
|
||||
* @param bool $skip_type_check
|
||||
* (optional) If TRUE, don't check whether the type matches the index's
|
||||
* datasource configuration. Internal use only.
|
||||
*
|
||||
* @return SearchApiIndex[]|null
|
||||
* All indexes for which any items were added; or NULL if items were added
|
||||
* for all of them.
|
||||
*
|
||||
* @throws SearchApiDataSourceException
|
||||
* If any error state was encountered.
|
||||
*/
|
||||
public function trackItemInsert(array $item_ids, array $indexes, $skip_type_check = FALSE) {
|
||||
$ret = array();
|
||||
|
||||
foreach ($indexes as $index_id => $index) {
|
||||
$ids = drupal_map_assoc($item_ids);
|
||||
|
||||
if (!$skip_type_check) {
|
||||
$types = $this->getEntityTypes($index);
|
||||
foreach ($ids as $id) {
|
||||
list($type) = explode('/', $id);
|
||||
if (!isset($types[$type])) {
|
||||
unset($ids[$id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($ids) {
|
||||
parent::trackItemInsert($ids, array($index));
|
||||
$ret[$index_id] = $index;
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configurationForm(array $form, array &$form_state) {
|
||||
$form['types'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Entity types'),
|
||||
'#description' => t('Select the entity types which should be included in this index.'),
|
||||
'#options' => search_api_entity_type_options_list(),
|
||||
'#attributes' => array('class' => array('search-api-checkboxes-list')),
|
||||
'#disabled' => !empty($form_state['index']),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
if (!empty($form_state['index']->options['datasource']['types'])) {
|
||||
$form['types']['#default_value'] = $this->getEntityTypes($form_state['index']);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configurationFormSubmit(array $form, array &$values, array &$form_state) {
|
||||
if (!empty($values['types'])) {
|
||||
$values['types'] = array_keys(array_filter($values['types']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfigurationSummary(SearchApiIndex $index) {
|
||||
if ($type_labels = $this->getSelectedEntityTypeOptions($index)) {
|
||||
$args['!types'] = implode(', ', $type_labels);
|
||||
return format_plural(count($type_labels), 'Indexed entity types: !types.', 'Indexed entity types: !types.', $args);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the index for which the current method was called.
|
||||
*
|
||||
* Very ugly method which uses the stack trace to find the right object.
|
||||
*
|
||||
* @return SearchApiIndex
|
||||
* The active index.
|
||||
*
|
||||
* @throws SearchApiException
|
||||
* Thrown if the active index could not be determined.
|
||||
*/
|
||||
protected function getCallingIndex() {
|
||||
foreach (debug_backtrace() as $trace) {
|
||||
if (isset($trace['object']) && $trace['object'] instanceof SearchApiIndex) {
|
||||
return $trace['object'];
|
||||
}
|
||||
}
|
||||
// If there's only a single index on the site, it's also easy.
|
||||
$indexes = search_api_index_load_multiple(FALSE);
|
||||
if (count($indexes) === 1) {
|
||||
return reset($indexes);
|
||||
}
|
||||
throw new SearchApiException('Could not determine the active index of the datasource.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity types for which this datasource is configured.
|
||||
*
|
||||
* Depends on the index from which this method is (indirectly) called.
|
||||
*
|
||||
* @param SearchApiIndex $index
|
||||
* (optional) The index for which to get the enabled entity types. If not
|
||||
* given, will be determined automatically.
|
||||
*
|
||||
* @return string[]
|
||||
* The machine names of the datasource's enabled entity types, as both keys
|
||||
* and values.
|
||||
*
|
||||
* @throws SearchApiException
|
||||
* Thrown if the active index could not be determined.
|
||||
*/
|
||||
protected function getEntityTypes(SearchApiIndex $index = NULL) {
|
||||
if (!$index) {
|
||||
$index = $this->getCallingIndex();
|
||||
}
|
||||
if (isset($index->options['datasource']['types'])) {
|
||||
return drupal_map_assoc($index->options['datasource']['types']);
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected entity type options for this datasource.
|
||||
*
|
||||
* Depends on the index from which this method is (indirectly) called.
|
||||
*
|
||||
* @param SearchApiIndex $index
|
||||
* (optional) The index for which to get the enabled entity types. If not
|
||||
* given, will be determined automatically.
|
||||
*
|
||||
* @return string[]
|
||||
* An associative array, mapping the machine names of the enabled entity
|
||||
* types to their labels.
|
||||
*
|
||||
* @throws SearchApiException
|
||||
* Thrown if the active index could not be determined.
|
||||
*/
|
||||
protected function getSelectedEntityTypeOptions(SearchApiIndex $index = NULL) {
|
||||
return array_intersect_key(search_api_entity_type_options_list(), $this->getEntityTypes($index));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -115,7 +115,8 @@ class SearchApiIndex extends Entity {
|
||||
public $item_type;
|
||||
|
||||
/**
|
||||
* An array of options for configuring this index. The layout is as follows:
|
||||
* An array of options for configuring this index. The layout is as follows
|
||||
* (with all keys being optional):
|
||||
* - cron_limit: The maximum number of items to be indexed per cron batch.
|
||||
* - index_directly: Boolean setting whether entities are indexed immediately
|
||||
* after they are created or updated.
|
||||
@@ -150,6 +151,8 @@ class SearchApiIndex extends Entity {
|
||||
* - weight: Used for sorting the processors.
|
||||
* - settings: Processor-specific settings, configured via the processor's
|
||||
* configuration form.
|
||||
* - datasource: Datasource-specific settings, configured via the datasource's
|
||||
* configuration form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
@@ -436,7 +439,6 @@ class SearchApiIndex extends Entity {
|
||||
return $this->server()->query($this, $options);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indexes items on this index.
|
||||
*
|
||||
|
||||
@@ -153,7 +153,9 @@ interface SearchApiQueryInterface {
|
||||
*
|
||||
* @param string $field
|
||||
* The field to sort by. The special fields 'search_api_relevance' (sort by
|
||||
* relevance) and 'search_api_id' (sort by item id) may be used.
|
||||
* relevance) and 'search_api_id' (sort by item id) may be used. Also, if
|
||||
* the search server supports the "search_api_random_sort" feature, the
|
||||
* "search_api_random_sort" special field can be used to sort randomly.
|
||||
* @param string $order
|
||||
* The order to sort items in - either 'ASC' or 'DESC'.
|
||||
*
|
||||
@@ -586,6 +588,10 @@ class SearchApiQuery implements SearchApiQueryInterface {
|
||||
'search_api_relevance' => array('type' => 'decimal'),
|
||||
'search_api_id' => array('type' => 'integer'),
|
||||
);
|
||||
if ($this->getIndex()->server()->supportsFeature('search_api_random_sort')) {
|
||||
$fields['search_api_random'] = array('type' => 'integer');
|
||||
}
|
||||
|
||||
if (empty($fields[$field])) {
|
||||
throw new SearchApiException(t('Trying to sort on unknown field @field.', array('@field' => $field)));
|
||||
}
|
||||
@@ -720,6 +726,9 @@ class SearchApiQuery implements SearchApiQueryInterface {
|
||||
public function postExecute(array &$results) {
|
||||
// Postprocess results.
|
||||
$this->index->postprocessSearchResults($results, $this);
|
||||
|
||||
// Let modules alter the results.
|
||||
drupal_alter('search_api_results', $results, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -420,7 +420,15 @@ abstract class SearchApiAbstractService implements SearchApiServiceInterface {
|
||||
public function preDelete() {
|
||||
$indexes = search_api_index_load_multiple(FALSE, array('server' => $this->server->machine_name));
|
||||
foreach ($indexes as $index) {
|
||||
$this->removeIndex($index);
|
||||
// removeIndex() might throw exceptions, but this method mustn't.
|
||||
try {
|
||||
$this->removeIndex($index);
|
||||
}
|
||||
catch (SearchApiException $e) {
|
||||
$variables['%index'] = $index->name;
|
||||
$variables['%server'] = $this->server->name;
|
||||
watchdog_exception('search_api', $e, '%type while trying to remove index %index from deleted server %server: !message in %function (line %line of %file).', $variables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -303,8 +303,9 @@ function search_api_admin_add_server_validate(array $form, array &$form_state) {
|
||||
return;
|
||||
}
|
||||
$form_state['values']['options']['service'] = $service;
|
||||
$values = isset($form_state['values']['options']['form']) ? $form_state['values']['options']['form'] : array();
|
||||
$service->configurationFormValidate($form['options']['form'], $values, $form_state);
|
||||
if (!empty($form_state['values']['options']['form'])) {
|
||||
$service->configurationFormValidate($form['options']['form'], $form_state['values']['options']['form'], $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -630,7 +631,9 @@ function search_api_admin_server_edit(array $form, array &$form_state, SearchApi
|
||||
* @see search_api_admin_server_edit_submit()
|
||||
*/
|
||||
function search_api_admin_server_edit_validate(array $form, array &$form_state) {
|
||||
$form_state['server']->configurationFormValidate($form['options']['form'], $form_state['values']['options']['form'], $form_state);
|
||||
if (!empty($form['options']['form']) && !empty($form_state['values']['options']['form'])) {
|
||||
$form_state['server']->configurationFormValidate($form['options']['form'], $form_state['values']['options']['form'], $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -682,83 +685,126 @@ function search_api_admin_form_delete_submit($form, &$form_state) {
|
||||
*
|
||||
* @ingroup forms
|
||||
*
|
||||
* @see search_api_admin_add_index_ajax_callback()
|
||||
* @see search_api_admin_add_index_validate()
|
||||
* @see search_api_admin_add_index_submit()
|
||||
*/
|
||||
function search_api_admin_add_index(array $form, array &$form_state) {
|
||||
drupal_set_title(t('Add index'));
|
||||
|
||||
$old_type = empty($form_state['values']['item_type']) ? '' : $form_state['values']['item_type'];
|
||||
|
||||
$form['#attached']['css'][] = drupal_get_path('module', 'search_api') . '/search_api.admin.css';
|
||||
$form['#tree'] = TRUE;
|
||||
$form['name'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Index name'),
|
||||
'#maxlength' => 50,
|
||||
'#required' => TRUE,
|
||||
);
|
||||
|
||||
$form['machine_name'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#maxlength' => 50,
|
||||
'#machine_name' => array(
|
||||
'exists' => 'search_api_index_load',
|
||||
),
|
||||
);
|
||||
if (empty($form_state['step_one'])) {
|
||||
$form['name'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Index name'),
|
||||
'#maxlength' => 50,
|
||||
'#required' => TRUE,
|
||||
);
|
||||
|
||||
$form['item_type'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Item type'),
|
||||
'#description' => t('Select the type of items that will be indexed in this index. ' .
|
||||
'This setting cannot be changed afterwards.'),
|
||||
'#options' => array(),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
foreach (search_api_get_item_type_info() as $type => $info) {
|
||||
$form['item_type']['#options'][$type] = $info['name'];
|
||||
$form['machine_name'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#maxlength' => 50,
|
||||
'#machine_name' => array(
|
||||
'exists' => 'search_api_index_load',
|
||||
),
|
||||
);
|
||||
|
||||
$form['item_type'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Item type'),
|
||||
'#description' => t('Select the type of items that will be indexed in this index. ' .
|
||||
'This setting cannot be changed afterwards.'),
|
||||
'#options' => array(),
|
||||
'#required' => TRUE,
|
||||
'#ajax' => array(
|
||||
'callback' => 'search_api_admin_add_index_ajax_callback',
|
||||
'wrapper' => 'search-api-datasource-options',
|
||||
),
|
||||
);
|
||||
$form['datasource'] = array();
|
||||
foreach (search_api_get_item_type_info() as $type => $info) {
|
||||
$form['item_type']['#options'][$type] = $info['name'];
|
||||
}
|
||||
$form['enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enabled'),
|
||||
'#description' => t('This will only take effect if you also select a server for the index.'),
|
||||
'#default_value' => TRUE,
|
||||
);
|
||||
$form['description'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Index description'),
|
||||
);
|
||||
$form['server'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Server'),
|
||||
'#description' => t('Select the server this index should reside on.'),
|
||||
'#default_value' => '',
|
||||
'#options' => array('' => t('< No server >'))
|
||||
);
|
||||
$servers = search_api_server_load_multiple(FALSE, array('enabled' => 1));
|
||||
// List enabled servers first.
|
||||
foreach ($servers as $server) {
|
||||
if ($server->enabled) {
|
||||
$form['server']['#options'][$server->machine_name] = $server->name;
|
||||
}
|
||||
}
|
||||
foreach ($servers as $server) {
|
||||
if (!$server->enabled) {
|
||||
$form['server']['#options'][$server->machine_name] = t('@server_name (disabled)', array('@server_name' => $server->name));
|
||||
}
|
||||
}
|
||||
$form['read_only'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Read only'),
|
||||
'#description' => t('Do not write to this index or track the status of items in this index.'),
|
||||
'#default_value' => FALSE,
|
||||
);
|
||||
$form['options']['index_directly'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Index items immediately'),
|
||||
'#description' => t('Immediately index new or updated items instead of waiting for the next cron run. ' .
|
||||
'This might have serious performance drawbacks and is generally not advised for larger sites.'),
|
||||
'#default_value' => FALSE,
|
||||
);
|
||||
$form['options']['cron_limit'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Cron batch size'),
|
||||
'#description' => t('Set how many items will be indexed at once when indexing items during a cron run. ' .
|
||||
'"0" means that no items will be indexed by cron for this index, "-1" means that cron should index all items at once.'),
|
||||
'#default_value' => SEARCH_API_DEFAULT_CRON_LIMIT,
|
||||
'#size' => 4,
|
||||
'#attributes' => array('class' => array('search-api-cron-limit')),
|
||||
'#element_validate' => array('element_validate_integer'),
|
||||
);
|
||||
}
|
||||
$form['enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enabled'),
|
||||
'#description' => t('This will only take effect if the selected server is also enabled.'),
|
||||
'#default_value' => TRUE,
|
||||
);
|
||||
$form['description'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Index description'),
|
||||
);
|
||||
$form['server'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Server'),
|
||||
'#description' => t('Select the server this index should reside on.'),
|
||||
'#default_value' => '',
|
||||
'#options' => array('' => t('< No server >'))
|
||||
);
|
||||
$servers = search_api_server_load_multiple(FALSE, array('enabled' => 1));
|
||||
// List enabled servers first.
|
||||
foreach ($servers as $server) {
|
||||
$form['server']['#options'][$server->machine_name] = $server->name;
|
||||
elseif (!$old_type) {
|
||||
$old_type = $form_state['step_one']['item_type'];
|
||||
}
|
||||
$form['read_only'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Read only'),
|
||||
'#description' => t('Do not write to this index or track the status of items in this index.'),
|
||||
'#default_value' => FALSE,
|
||||
|
||||
if ($old_type) {
|
||||
$datasource = search_api_get_datasource_controller($old_type);
|
||||
$datasource_form = array();
|
||||
$datasource_form = $datasource->configurationForm($datasource_form, $form_state);
|
||||
if ($datasource_form) {
|
||||
$form['datasource'] = $datasource_form;
|
||||
$form['datasource']['#parents'] = array('options', 'datasource');
|
||||
}
|
||||
}
|
||||
$form['datasource']['#prefix'] = '<div id="search-api-datasource-options">';
|
||||
$form['datasource']['#suffix'] = '</div>';
|
||||
|
||||
$form['old_type'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $old_type,
|
||||
);
|
||||
$form['options']['index_directly'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Index items immediately'),
|
||||
'#description' => t('Immediately index new or updated items instead of waiting for the next cron run. ' .
|
||||
'This might have serious performance drawbacks and is generally not advised for larger sites.'),
|
||||
'#default_value' => FALSE,
|
||||
);
|
||||
$form['options']['cron_limit'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Cron batch size'),
|
||||
'#description' => t('Set how many items will be indexed at once when indexing items during a cron run. ' .
|
||||
'"0" means that no items will be indexed by cron for this index, "-1" means that cron should index all items at once.'),
|
||||
'#default_value' => SEARCH_API_DEFAULT_CRON_LIMIT,
|
||||
'#size' => 4,
|
||||
'#attributes' => array('class' => array('search-api-cron-limit')),
|
||||
$form['datasource_config'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => !empty($datasource_form),
|
||||
);
|
||||
|
||||
$form['submit'] = array(
|
||||
@@ -769,22 +815,33 @@ function search_api_admin_add_index(array $form, array &$form_state) {
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX submit callback for search_api_admin_add_index().
|
||||
*
|
||||
* Used for displaying the matching datasource configuration form for the
|
||||
* selected item type.
|
||||
*/
|
||||
function search_api_admin_add_index_ajax_callback(array $form, array &$form_state) {
|
||||
return $form['datasource'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Form validation handler for search_api_admin_add_index().
|
||||
*
|
||||
* @see search_api_admin_add_index_submit()
|
||||
*/
|
||||
function search_api_admin_add_index_validate(array $form, array &$form_state) {
|
||||
$name = $form_state['values']['machine_name'];
|
||||
$values = $form_state['values'];
|
||||
$name = $values['machine_name'];
|
||||
if (is_numeric($name)) {
|
||||
form_set_error('machine_name', t('The machine name must not be a pure number.'));
|
||||
}
|
||||
|
||||
$cron_limit = $form_state['values']['options']['cron_limit'];
|
||||
if ($cron_limit != '' . ((int) $cron_limit)) {
|
||||
// We don't enforce stricter rules and treat all negative values as -1.
|
||||
form_set_error('options[cron_limit]', t('The cron batch size must be an integer.'));
|
||||
if (!$values['datasource_config'] || empty($values['item_type']) || $values['item_type'] != $values['old_type']) {
|
||||
return;
|
||||
}
|
||||
$datasource = search_api_get_datasource_controller($values['item_type']);
|
||||
$datasource->configurationFormValidate($form['datasource'], $form_state['values']['options']['datasource'], $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -794,10 +851,34 @@ function search_api_admin_add_index_validate(array $form, array &$form_state) {
|
||||
*/
|
||||
function search_api_admin_add_index_submit(array $form, array &$form_state) {
|
||||
form_state_values_clean($form_state);
|
||||
|
||||
$values = $form_state['values'];
|
||||
|
||||
// Validation of whether the server of an index is enabled is done in the
|
||||
if (!empty($form_state['step_one'])) {
|
||||
$values += $form_state['step_one'];
|
||||
unset($form_state['step_one']);
|
||||
}
|
||||
|
||||
// The type was changed (or the form submitted without JS for the first time).
|
||||
// If the new type has a configuration form, we have to display it now.
|
||||
$datasource = search_api_get_datasource_controller($values['item_type']);
|
||||
if ($values['item_type'] != $values['old_type']) {
|
||||
$datasource_form = array();
|
||||
if ($datasource->configurationForm($datasource_form, $form_state)) {
|
||||
unset($values['options']['datasource']);
|
||||
$form_state['step_one'] = $values;
|
||||
$form_state['rebuild'] = TRUE;
|
||||
drupal_set_message(t('Please specify further configuration options.'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If the current type has a configuration form, call the datasource
|
||||
// controller's config submit callback.
|
||||
if ($values['datasource_config']) {
|
||||
$datasource->configurationFormSubmit($form['datasource'], $values['options']['datasource'], $form_state);
|
||||
}
|
||||
|
||||
// Validation of whether a server is set for the index is done in the
|
||||
// SearchApiIndex::save() method.
|
||||
search_api_index_insert($values);
|
||||
|
||||
@@ -857,6 +938,7 @@ function search_api_admin_index_view(SearchApiIndex $index, $action = NULL) {
|
||||
'#machine_name' => $index->machine_name,
|
||||
'#description' => $index->description,
|
||||
'#item_type' => $index->item_type,
|
||||
'#datasource_config' => $index->datasource()->getConfigurationSummary($index),
|
||||
'#enabled' => $index->enabled,
|
||||
'#server' => $server,
|
||||
'#options' => $index->options,
|
||||
@@ -889,6 +971,7 @@ function search_api_admin_index_view(SearchApiIndex $index, $action = NULL) {
|
||||
* - machine_name: The index' machine name.
|
||||
* - description: The index' description.
|
||||
* - item_type: The type of items stored in this index.
|
||||
* - datasource_config: A summary of the datasource's configuration.
|
||||
* - enabled: Boolean indicating whether the index is enabled.
|
||||
* - server: The server this index currently rests on, if any.
|
||||
* - options: The index' options, like cron limit.
|
||||
@@ -912,6 +995,7 @@ function theme_search_api_index(array $variables) {
|
||||
$description = $variables['description'];
|
||||
$enabled = $variables['enabled'];
|
||||
$item_type = $variables['item_type'];
|
||||
$datasource_config = $variables['datasource_config'];
|
||||
$server = $variables['server'];
|
||||
$options = $variables['options'];
|
||||
$status = $variables['status'];
|
||||
@@ -962,6 +1046,12 @@ function theme_search_api_index(array $variables) {
|
||||
$info = check_plain($item_type);
|
||||
$rows[] = _search_api_deep_copy($row);
|
||||
|
||||
if ($datasource_config) {
|
||||
$label = t('Item type configuration');
|
||||
$info = check_plain($datasource_config);
|
||||
$rows[] = _search_api_deep_copy($row);
|
||||
}
|
||||
|
||||
if ($server) {
|
||||
$label = t('Server');
|
||||
$info = l($server->name, 'admin/config/search/search_api/server/' . $server->machine_name);
|
||||
@@ -1193,6 +1283,7 @@ function search_api_admin_index_status_form_submit(array $form, array &$form_sta
|
||||
*
|
||||
* @ingroup forms
|
||||
*
|
||||
* @see search_api_admin_index_edit_validate()
|
||||
* @see search_api_admin_index_edit_submit()
|
||||
*/
|
||||
function search_api_admin_index_edit(array $form, array &$form_state, SearchApiIndex $index) {
|
||||
@@ -1208,7 +1299,7 @@ function search_api_admin_index_edit(array $form, array &$form_state, SearchApiI
|
||||
'#required' => TRUE,
|
||||
);
|
||||
try {
|
||||
$enabled_fixed = !$index->enabled && !$index->server();
|
||||
$enabled_fixed = !$index->server();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
watchdog_exception('search_api', $e);
|
||||
@@ -1220,7 +1311,7 @@ function search_api_admin_index_edit(array $form, array &$form_state, SearchApiI
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enabled'),
|
||||
'#default_value' => $index->enabled,
|
||||
// Can't enable an index lying on a disabled server, or no server at all.
|
||||
// Can't enable an index that's not lying on any server.
|
||||
'#disabled' => $enabled_fixed,
|
||||
);
|
||||
$form['description'] = array(
|
||||
@@ -1240,6 +1331,15 @@ function search_api_admin_index_edit(array $form, array &$form_state, SearchApiI
|
||||
foreach ($servers as $server) {
|
||||
$form['server']['#options'][$server->machine_name] = $server->name;
|
||||
}
|
||||
|
||||
$datasource_form = !empty($form['options']['datasource']) ? $form['options']['datasource'] : array();
|
||||
$datasource_form = $index->datasource()->configurationForm($datasource_form, $form_state);
|
||||
if ($datasource_form) {
|
||||
$form['options']['datasource'] = $datasource_form;
|
||||
$form['options']['datasource']['#type'] = 'fieldset';
|
||||
$form['options']['datasource']['#title'] = t('Datasource options');
|
||||
}
|
||||
|
||||
$form['read_only'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Read only'),
|
||||
@@ -1285,14 +1385,33 @@ function search_api_admin_index_edit(array $form, array &$form_state, SearchApiI
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form validation handler for search_api_admin_index_edit().
|
||||
*
|
||||
* @see search_api_admin_index_edit_submit()
|
||||
*/
|
||||
function search_api_admin_index_edit_validate(array $form, array &$form_state) {
|
||||
if (!empty($form['options']['datasource'])) {
|
||||
$form_state['values']['options'] += array('datasource' => array());
|
||||
$form_state['index']->datasource()->configurationFormValidate($form['options']['datasource'], $form_state['values']['options']['datasource'], $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for search_api_admin_index_edit().
|
||||
*
|
||||
* @see search_api_admin_index_edit_validate()
|
||||
*/
|
||||
function search_api_admin_index_edit_submit(array $form, array &$form_state) {
|
||||
form_state_values_clean($form_state);
|
||||
|
||||
$values = $form_state['values'];
|
||||
/** @var SearchApiIndex $index */
|
||||
$index = $form_state['index'];
|
||||
|
||||
if (!empty($form['options']['datasource'])) {
|
||||
$index->datasource()->configurationFormSubmit($form['options']['datasource'], $values['options']['datasource'], $form_state);
|
||||
}
|
||||
|
||||
$values['options'] += $index->options;
|
||||
|
||||
$ret = $index->update($values);
|
||||
@@ -1687,10 +1806,7 @@ function search_api_admin_index_fields(array $form, array &$form_state, SearchAp
|
||||
// An array of option arrays for types, keyed by nesting level.
|
||||
$types = array(0 => search_api_field_types());
|
||||
$entity_types = entity_get_info();
|
||||
// $boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0'));
|
||||
$boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0', '100', '1000', '1010', '1020', '1030', '1040', '1050', '1060'));
|
||||
|
||||
|
||||
$boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0'));
|
||||
|
||||
$fulltext_types = array(0 => array('text'));
|
||||
// Add all custom data types with fallback "text" to fulltext types as well.
|
||||
|
||||
@@ -341,7 +341,27 @@ function hook_search_api_query_alter(SearchApiQueryInterface $query) {
|
||||
// Exclude entities with ID 0. (Assume the ID field is always indexed.)
|
||||
if ($query->getIndex()->getEntityType()) {
|
||||
$info = entity_get_info($query->getIndex()->getEntityType());
|
||||
$query->condition($info['entity keys']['id'], 0, '!=');
|
||||
$query->condition($info['entity keys']['id'], 0, '<>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the search results before they are returned.
|
||||
*
|
||||
* @param array $results
|
||||
* The results returned by the server, which may be altered. The data
|
||||
* structure is the same as returned by SearchApiQueryInterface::execute().
|
||||
* @param SearchApiQueryInterface $query
|
||||
* The search query that was executed.
|
||||
*/
|
||||
function hook_search_api_results_alter(array &$results, SearchApiQueryInterface $query) {
|
||||
if ($query->getOption('search id') == 'search_api_views:my_search_view:page') {
|
||||
// Log the number of results.
|
||||
$vars = array(
|
||||
'@keys' => $query->getOriginalKeys(),
|
||||
'@num' => $results['result count'],
|
||||
);
|
||||
watchdog('my_module', 'Search view with query "@keys" had @num results.', $vars, WATCHDOG_DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -121,6 +121,39 @@ function search_api_drush_command() {
|
||||
'aliases' => array('sapi-sis'),
|
||||
);
|
||||
|
||||
$items['search-api-server-list'] = array(
|
||||
'description' => 'List all search servers.',
|
||||
'examples' => array(
|
||||
'drush search-api-server-list' => dt('List all search servers.'),
|
||||
'drush sapi-sl' => dt('Alias to list all search servers.'),
|
||||
),
|
||||
'aliases' => array('sapi-sl'),
|
||||
);
|
||||
|
||||
$items['search-api-server-enable'] = array(
|
||||
'description' => 'Enable a search server.',
|
||||
'examples' => array(
|
||||
'drush search-api-server-e my_solr_server' => dt('Enable the !server search server.', array('!server' => 'my_solr_server')),
|
||||
'drush sapi-se my_solr_server' => dt('Alias to enable the !server search server.', array('!server' => 'my_solr_server')),
|
||||
),
|
||||
'arguments' => array(
|
||||
'server_id' => dt('The numeric ID or machine name of a search server to enable.'),
|
||||
),
|
||||
'aliases' => array('sapi-se'),
|
||||
);
|
||||
|
||||
$items['search-api-server-disable'] = array(
|
||||
'description' => 'Disable a search server.',
|
||||
'examples' => array(
|
||||
'drush search-api-server-disable' => dt('Disable the !server search server.', array('!server' => 'my_solr_server')),
|
||||
'drush sapi-sd' => dt('Alias to disable the !server search server.', array('!server' => 'my_solr_server')),
|
||||
),
|
||||
'arguments' => array(
|
||||
'server_id' => dt('The numeric ID or machine name of a search server to disable.'),
|
||||
),
|
||||
'aliases' => array('sapi-sd'),
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
@@ -508,7 +541,8 @@ function search_api_drush_get_server($server_id = NULL) {
|
||||
$servers = search_api_server_load_multiple($ids);
|
||||
if (empty($servers)) {
|
||||
drush_set_error(dt('Invalid server_id or no servers present.'));
|
||||
// @todo: Maybe add logic to print table of all servers.
|
||||
drush_print();
|
||||
drush_search_api_server_list();
|
||||
}
|
||||
return $servers;
|
||||
}
|
||||
@@ -533,3 +567,75 @@ function search_api_drush_static($function) {
|
||||
$index[$function] = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all search servers.
|
||||
*/
|
||||
function drush_search_api_server_list() {
|
||||
if (search_api_drush_static(__FUNCTION__)) {
|
||||
return;
|
||||
}
|
||||
$servers = search_api_server_load_multiple(FALSE);
|
||||
if (empty($servers)) {
|
||||
drush_print(dt('There are no servers present.'));
|
||||
return;
|
||||
}
|
||||
$rows[] = array(
|
||||
dt('Machine name'),
|
||||
dt('Name'),
|
||||
dt('Status'),
|
||||
);
|
||||
foreach ($servers as $server) {
|
||||
$row = array(
|
||||
$server->machine_name,
|
||||
$server->name,
|
||||
$server->enabled ? dt('enabled') : dt('disabled'),
|
||||
);
|
||||
$rows[] = $row;
|
||||
}
|
||||
drush_print_table($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables a search server.
|
||||
*
|
||||
* @param int|string $server_id
|
||||
* The numeric ID or machine name of the server to enable.
|
||||
*/
|
||||
function drush_search_api_server_enable($server_id = NULL) {
|
||||
if (!isset($server_id)) {
|
||||
drush_print(dt('Please provide a valid server id.'));
|
||||
return;
|
||||
}
|
||||
$server = search_api_server_load($server_id);
|
||||
if (empty($server)) {
|
||||
drush_print(dt('The server was not able to load.'));
|
||||
return;
|
||||
}
|
||||
else {
|
||||
$server->update(array('enabled' => 1));
|
||||
drush_print(dt('The server was enabled successfully.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables a search server.
|
||||
*
|
||||
* @param int|string $server_id
|
||||
* The numeric ID or machine name of the server to disable.
|
||||
*/
|
||||
function drush_search_api_server_disable($server_id = NULL) {
|
||||
if (!isset($server_id)) {
|
||||
drush_print(dt('Please provide a valid server id.'));
|
||||
return;
|
||||
}
|
||||
$server = search_api_server_load($server_id);
|
||||
if (empty($server)) {
|
||||
drush_print(dt('The server was not able to load.'));
|
||||
return;
|
||||
}
|
||||
else {
|
||||
$server->update(array('enabled' => 0));
|
||||
drush_print(dt('The server was disabled successfully.'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,11 @@ files[] = includes/callback_language_control.inc
|
||||
files[] = includes/callback_node_access.inc
|
||||
files[] = includes/callback_node_status.inc
|
||||
files[] = includes/callback_role_filter.inc
|
||||
files[] = includes/callback_user_status.inc
|
||||
files[] = includes/datasource.inc
|
||||
files[] = includes/datasource_entity.inc
|
||||
files[] = includes/datasource_external.inc
|
||||
files[] = includes/datasource_multiple.inc
|
||||
files[] = includes/exception.inc
|
||||
files[] = includes/index_entity.inc
|
||||
files[] = includes/processor.inc
|
||||
@@ -34,9 +36,9 @@ files[] = includes/service.inc
|
||||
|
||||
configure = admin/config/search/search_api
|
||||
|
||||
; Information added by Drupal.org packaging script on 2014-12-26
|
||||
version = "7.x-1.14"
|
||||
; Information added by Drupal.org packaging script on 2015-08-30
|
||||
version = "7.x-1.16"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1419580682"
|
||||
datestamp = "1440962813"
|
||||
|
||||
|
||||
@@ -191,6 +191,35 @@ function search_api_schema() {
|
||||
'primary key' => array('item_id', 'index_id'),
|
||||
);
|
||||
|
||||
$schema['search_api_item_string_id'] = array(
|
||||
'description' => 'Stores the items which should be indexed for each index, and their status. Used only for items with string IDs.',
|
||||
'fields' => array(
|
||||
'item_id' => array(
|
||||
'description' => "The item's ID.",
|
||||
'type' => 'varchar',
|
||||
'length' => 64,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'index_id' => array(
|
||||
'description' => 'The {search_api_index}.id this item belongs to.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'changed' => array(
|
||||
'description' => 'Either a flag or a timestamp to indicate if or when the item was changed since it was last indexed.',
|
||||
'type' => 'int',
|
||||
'size' => 'big',
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'indexing' => array('index_id', 'changed'),
|
||||
),
|
||||
'primary key' => array('item_id', 'index_id'),
|
||||
);
|
||||
|
||||
$schema['search_api_task'] = array(
|
||||
'description' => 'Stores pending tasks for servers.',
|
||||
'fields' => array(
|
||||
@@ -1001,3 +1030,38 @@ function search_api_update_7117() {
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {search_api_item_string_id} table for items with string IDs.
|
||||
*/
|
||||
function search_api_update_7118() {
|
||||
$table = array(
|
||||
'description' => 'Stores the items which should be indexed for each index, and their status. Used only for items with string IDs.',
|
||||
'fields' => array(
|
||||
'item_id' => array(
|
||||
'description' => "The item's ID.",
|
||||
'type' => 'varchar',
|
||||
'length' => 64,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'index_id' => array(
|
||||
'description' => 'The {search_api_index}.id this item belongs to.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'changed' => array(
|
||||
'description' => 'Either a flag or a timestamp to indicate if or when the item was changed since it was last indexed.',
|
||||
'type' => 'int',
|
||||
'size' => 'big',
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'indexing' => array('index_id', 'changed'),
|
||||
),
|
||||
'primary key' => array('item_id', 'index_id'),
|
||||
);
|
||||
db_create_table('search_api_item_string_id', $table);
|
||||
}
|
||||
|
||||
@@ -270,6 +270,7 @@ function search_api_theme() {
|
||||
'machine_name' => '',
|
||||
'description' => NULL,
|
||||
'item_type' => NULL,
|
||||
'datasource_config' => NULL,
|
||||
'enabled' => NULL,
|
||||
'server' => NULL,
|
||||
'options' => array(),
|
||||
@@ -646,8 +647,9 @@ function search_api_search_api_index_insert(SearchApiIndex $index) {
|
||||
* Implements hook_search_api_index_update().
|
||||
*/
|
||||
function search_api_search_api_index_update(SearchApiIndex $index) {
|
||||
// Call the datasource update function with the table this module provides.
|
||||
// Call the datasource update function with the tables this module provides.
|
||||
search_api_index_update_datasource($index, 'search_api_item');
|
||||
search_api_index_update_datasource($index, 'search_api_item_string_id');
|
||||
|
||||
// If the server was changed, we have to call the appropriate service class
|
||||
// hook methods.
|
||||
@@ -856,6 +858,8 @@ function search_api_entity_insert($entity, $type) {
|
||||
list($id) = entity_extract_ids($type, $entity);
|
||||
if (isset($id)) {
|
||||
search_api_track_item_insert($type, array($id));
|
||||
$combined_id = $type . '/' . $id;
|
||||
search_api_track_item_insert('multiple', array($combined_id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -866,6 +870,8 @@ function search_api_entity_insert($entity, $type) {
|
||||
* datasource controller and calls search_api_track_item_change() for the
|
||||
* updated items.
|
||||
*
|
||||
* It also checks whether the entity's bundle changed and acts accordingly.
|
||||
*
|
||||
* @see search_api_search_api_item_type_info()
|
||||
*/
|
||||
function search_api_entity_update($entity, $type) {
|
||||
@@ -874,9 +880,20 @@ function search_api_entity_update($entity, $type) {
|
||||
if (!entity_get_property_info($type)) {
|
||||
return;
|
||||
}
|
||||
list($id) = entity_extract_ids($type, $entity);
|
||||
list($id, , $new_bundle) = entity_extract_ids($type, $entity);
|
||||
|
||||
// Check if the entity's bundle changed.
|
||||
if (isset($entity->original)) {
|
||||
list(, , $old_bundle) = entity_extract_ids($type, $entity->original);
|
||||
if ($new_bundle != $old_bundle) {
|
||||
_search_api_entity_datasource_bundle_change($type, $id, $old_bundle, $new_bundle);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($id)) {
|
||||
search_api_track_item_change($type, array($id));
|
||||
$combined_id = $type . '/' . $id;
|
||||
search_api_track_item_change('multiple', array($combined_id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -898,6 +915,28 @@ function search_api_entity_delete($entity, $type) {
|
||||
list($id) = entity_extract_ids($type, $entity);
|
||||
if (isset($id)) {
|
||||
search_api_track_item_delete($type, array($id));
|
||||
$combined_id = $type . '/' . $id;
|
||||
search_api_track_item_delete('multiple', array($combined_id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_attach_rename_bundle().
|
||||
*
|
||||
* This is implemented on behalf of the SearchApiEntityDataSourceController
|
||||
* datasource controller, to update any bundle settings that contain the changed
|
||||
* bundle.
|
||||
*/
|
||||
function search_api_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) {
|
||||
foreach (search_api_index_load_multiple(FALSE, array('item_type' => $entity_type)) as $index) {
|
||||
$bundles = &$index->options['datasource']['bundles'];
|
||||
if (isset($bundles) && ($pos = array_search($bundle_old, $bundles)) !== FALSE) {
|
||||
$bundles[$pos] = $bundle_new;
|
||||
$index->save();
|
||||
// Clear all caches that could contain the bundle information.
|
||||
$index->resetCaches();
|
||||
drupal_static_reset('search_api_get_datasource_controller');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -935,16 +974,19 @@ function search_api_flush_caches() {
|
||||
function search_api_search_api_item_type_info() {
|
||||
$types = array();
|
||||
|
||||
foreach (entity_get_property_info() as $type => $property_info) {
|
||||
if ($info = entity_get_info($type)) {
|
||||
$types[$type] = array(
|
||||
'name' => $info['label'],
|
||||
'datasource controller' => 'SearchApiEntityDataSourceController',
|
||||
'entity_type' => $type,
|
||||
);
|
||||
}
|
||||
foreach (search_api_entity_type_options_list() as $type => $label) {
|
||||
$types[$type] = array(
|
||||
'name' => $label,
|
||||
'datasource controller' => 'SearchApiEntityDataSourceController',
|
||||
'entity_type' => $type,
|
||||
);
|
||||
}
|
||||
|
||||
$types['multiple'] = array(
|
||||
'name' => t('Multiple types'),
|
||||
'datasource controller' => 'SearchApiCombinedEntityDataSourceController',
|
||||
);
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
@@ -1026,6 +1068,11 @@ function search_api_search_api_alter_callback_info() {
|
||||
'description' => t('Exclude unpublished nodes from the index. <strong>Caution:</strong> This only affects the indexed nodes themselves. If an enabled node has references to disabled nodes, those will still be indexed (or displayed) normally.'),
|
||||
'class' => 'SearchApiAlterNodeStatus',
|
||||
);
|
||||
$callbacks['search_api_alter_user_status'] = array(
|
||||
'name' => t('Exclude blocked users'),
|
||||
'description' => t('Exclude blocked users from the index. <strong>Caution:</strong> This only affects the indexed users themselves. If an active user account includes a reference to a disabled user, that reference will still be indexed (or displayed) normally.'),
|
||||
'class' => 'SearchApiAlterUserStatus',
|
||||
);
|
||||
|
||||
return $callbacks;
|
||||
}
|
||||
@@ -1100,7 +1147,10 @@ function search_api_track_item_insert($type, array $item_ids) {
|
||||
}
|
||||
|
||||
try {
|
||||
search_api_get_datasource_controller($type)->trackItemInsert($item_ids, $indexes);
|
||||
$returned_indexes = search_api_get_datasource_controller($type)->trackItemInsert($item_ids, $indexes);
|
||||
if (isset($returned_indexes)) {
|
||||
$indexes = $returned_indexes;
|
||||
}
|
||||
}
|
||||
catch (SearchApiException $e) {
|
||||
$vars['%item_type'] = $type;
|
||||
@@ -1137,7 +1187,10 @@ function search_api_track_item_change($type, array $item_ids) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
search_api_get_datasource_controller($type)->trackItemChange($item_ids, $indexes);
|
||||
$returned_indexes = search_api_get_datasource_controller($type)->trackItemChange($item_ids, $indexes);
|
||||
if (isset($returned_indexes)) {
|
||||
$indexes = $returned_indexes;
|
||||
}
|
||||
foreach ($indexes as $index) {
|
||||
if (!empty($index->options['index_directly'])) {
|
||||
// For indexes with the index_directly option set, queue the items to be
|
||||
@@ -1212,7 +1265,10 @@ function search_api_track_item_delete($type, array $item_ids) {
|
||||
$indexes = search_api_index_load_multiple(FALSE, $conditions);
|
||||
if ($indexes) {
|
||||
try {
|
||||
search_api_get_datasource_controller($type)->trackItemDelete($item_ids, $indexes);
|
||||
$changed_indexes = search_api_get_datasource_controller($type)->trackItemDelete($item_ids, $indexes);
|
||||
if (isset($changed_indexes)) {
|
||||
$indexes = $changed_indexes;
|
||||
}
|
||||
}
|
||||
catch (SearchApiException $e) {
|
||||
$vars['%item_type'] = $type;
|
||||
@@ -1222,8 +1278,9 @@ function search_api_track_item_delete($type, array $item_ids) {
|
||||
|
||||
// Then, delete it from all servers. Servers of disabled indexes have to be
|
||||
// considered, too!
|
||||
unset($conditions['enabled']);
|
||||
foreach (search_api_index_load_multiple(FALSE, $conditions) as $index) {
|
||||
$conditions['enabled'] = 0;
|
||||
$indexes = array_merge($indexes, search_api_index_load_multiple(FALSE, $conditions));
|
||||
foreach ($indexes as $index) {
|
||||
try {
|
||||
if ($server = $index->server()) {
|
||||
$server->deleteItems($item_ids, $index);
|
||||
@@ -2177,6 +2234,9 @@ function search_api_index_update_datasource(SearchApiIndex $index, $table, $colu
|
||||
* The $fields array with additional "value" and "original_type" keys set.
|
||||
*/
|
||||
function search_api_extract_fields(EntityMetadataWrapper $wrapper, array $fields, array $value_options = array()) {
|
||||
$value_options += array(
|
||||
'identifier' => TRUE,
|
||||
);
|
||||
// If $wrapper is a list of entities, we have to aggregate their field values.
|
||||
$wrapper_info = $wrapper->info();
|
||||
if (search_api_is_list_type($wrapper_info['type'])) {
|
||||
@@ -2214,19 +2274,31 @@ function search_api_extract_fields(EntityMetadataWrapper $wrapper, array $fields
|
||||
$info['original_type'] = $info['type'];
|
||||
if (isset($wrapper->$field)) {
|
||||
try {
|
||||
$info['value'] = $wrapper->$field->value($value_options);
|
||||
// For fulltext fields with options, also include the option labels.
|
||||
if (search_api_is_text_type($info['type']) && $wrapper->$field->optionsList('view')) {
|
||||
_search_api_add_option_values($info['value'], $wrapper->$field->optionsList('view'));
|
||||
}
|
||||
// Set the original type according to the field wrapper's info.
|
||||
$property_info = $wrapper->$field->info();
|
||||
$info['original_type'] = $property_info['type'];
|
||||
// For entities, we extract the entity ID instead of the whole object.
|
||||
// @todo Use 'identifier' => TRUE instead of always loading the object.
|
||||
$t = search_api_extract_inner_type($property_info['type']);
|
||||
if (isset($entity_infos[$t])) {
|
||||
// If no object is set, set this field to NULL.
|
||||
$info['value'] = $info['value'] ? _search_api_extract_entity_value($wrapper->$field, search_api_is_text_type($info['type'])) : NULL;
|
||||
|
||||
// Extract the basic value from the field wrapper.
|
||||
$info['value'] = $wrapper->$field->value($value_options);
|
||||
|
||||
// For entities, we need to take care to differentiate between
|
||||
// entities with ID 0 and empty fields. In the latter case, the
|
||||
// wrapper's value() method returns, when called with "identifier =
|
||||
// TRUE", FALSE instead of the (more logical) NULL.
|
||||
$is_entity = isset($entity_infos[search_api_extract_inner_type($property_info['type'])]);
|
||||
if ($is_entity && $info['value'] === FALSE) {
|
||||
$info['value'] = NULL;
|
||||
}
|
||||
|
||||
// If we index the field as fulltext, we also include the entity label
|
||||
// or option list label, if applicable.
|
||||
if (search_api_is_text_type($info['type']) && isset($info['value'])) {
|
||||
if ($wrapper->$field->optionsList('view')) {
|
||||
_search_api_add_option_values($info['value'], $wrapper->$field->optionsList('view'));
|
||||
}
|
||||
elseif ($is_entity) {
|
||||
$info['value'] = _search_api_extract_entity_value($wrapper->$field, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (EntityMetadataWrapperException $e) {
|
||||
@@ -2245,10 +2317,6 @@ function search_api_extract_fields(EntityMetadataWrapper $wrapper, array $fields
|
||||
foreach ($nested as $prefix => $nested_fields) {
|
||||
if (isset($wrapper->$prefix)) {
|
||||
$nested_fields = search_api_extract_fields($wrapper->$prefix, $nested_fields, $value_options);
|
||||
# http://drupal.org/node/1873910#comment-6876200
|
||||
// $subwrapper = $wrapper->$prefix;
|
||||
// $subwrapper->language( $wrapper->language->value() );
|
||||
// $nested_fields = search_api_extract_fields($subwrapper, $nested_fields, $value_options);
|
||||
foreach ($nested_fields as $field => $info) {
|
||||
$fields["$prefix:$field"] = $info;
|
||||
}
|
||||
@@ -2778,6 +2846,66 @@ function search_api_index_options_list() {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options list callback for entity types.
|
||||
*
|
||||
* Will only include entity types which specify entity property information.
|
||||
*
|
||||
* @return string[]
|
||||
* An array of entity type machine names mapped to their human-readable
|
||||
* names.
|
||||
*/
|
||||
function search_api_entity_type_options_list() {
|
||||
$types = array();
|
||||
foreach (array_keys(entity_get_property_info()) as $type) {
|
||||
$info = entity_get_info($type);
|
||||
if ($info) {
|
||||
$types[$type] = $info['label'];
|
||||
}
|
||||
}
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options list callback for entity type bundles.
|
||||
*
|
||||
* Will include all bundles for all entity types which specify entity property
|
||||
* information, in a format combining both entity type and bundle.
|
||||
*
|
||||
* @return string[]
|
||||
* An array of bundle identifiers mapped to their human-readable names.
|
||||
*/
|
||||
function search_api_combined_bundle_options_list() {
|
||||
$types = array();
|
||||
foreach (array_keys(entity_get_property_info()) as $type) {
|
||||
$info = entity_get_info($type);
|
||||
if (!empty($info['bundles'])) {
|
||||
foreach ($info['bundles'] as $bundle => $bundle_info) {
|
||||
$types["$type:$bundle"] = $bundle_info['label'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a human-readable label for a multi-type index item.
|
||||
*
|
||||
* Provided as a non-object alternative to
|
||||
* SearchApiCombinedEntityDataSourceController::getItemLabel() so it can be used
|
||||
* as a getter callback.
|
||||
*
|
||||
* @param object $item
|
||||
* An item of the "multiple" item type.
|
||||
*
|
||||
* @return string|null
|
||||
* Either a human-readable label for the item, or NULL if none is available.
|
||||
*/
|
||||
function search_api_get_multi_type_item_label($item) {
|
||||
$label = entity_label($item->item_type, $item->{$item->item_type});
|
||||
return $label ? $label : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown function which indexes all queued items, if any.
|
||||
*/
|
||||
@@ -2894,6 +3022,42 @@ function _search_api_deep_copy(array $array) {
|
||||
return $copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to a change in the bundle of an entity.
|
||||
*
|
||||
* Used as a helper function in search_api_entity_update().
|
||||
*
|
||||
* @param $type
|
||||
* The entity's type.
|
||||
* @param $id
|
||||
* The entity's ID.
|
||||
* @param $old_bundle
|
||||
* The entity's previous bundle.
|
||||
* @param $new_bundle
|
||||
* The entity's new bundle.
|
||||
*/
|
||||
function _search_api_entity_datasource_bundle_change($type, $id, $old_bundle, $new_bundle) {
|
||||
$controller = search_api_get_datasource_controller($type);
|
||||
$conditions = array(
|
||||
'enabled' => 1,
|
||||
'item_type' => $type,
|
||||
'read_only' => 0,
|
||||
);
|
||||
foreach (search_api_index_load_multiple(FALSE, $conditions) as $index) {
|
||||
if (!empty($index->options['datasource']['bundles'])) {
|
||||
$bundles = drupal_map_assoc($index->options['datasource']['bundles']);
|
||||
if (empty($bundles[$new_bundle]) != empty($bundles[$old_bundle])) {
|
||||
if (empty($bundles[$new_bundle])) {
|
||||
$controller->trackItemDelete(array($id), array($index));
|
||||
}
|
||||
else {
|
||||
$controller->trackItemInsert(array($id), array($index));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and sets a batch for indexing items.
|
||||
*
|
||||
|
||||
@@ -830,6 +830,7 @@ class SearchApiUnitTest extends DrupalWebTestCase {
|
||||
$this->index = entity_create('search_api_index', array(
|
||||
'id' => 1,
|
||||
'name' => 'test',
|
||||
'machine_name' => 'test',
|
||||
'enabled' => 1,
|
||||
'item_type' => 'user',
|
||||
'options' => array(
|
||||
@@ -859,6 +860,7 @@ class SearchApiUnitTest extends DrupalWebTestCase {
|
||||
$this->checkIgnoreCaseProcessor();
|
||||
$this->checkTokenizer();
|
||||
$this->checkHtmlFilter();
|
||||
$this->checkEntityDatasource();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1122,4 +1124,75 @@ END;
|
||||
$this->assertEqual($tmp[1]['name']['value'], $processed1, 'Text was correctly processed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the entity datasource controller and its bundle setting.
|
||||
*/
|
||||
protected function checkEntityDatasource() {
|
||||
// First, create the necessary content types.
|
||||
$type = (object) array(
|
||||
'type' => 'article',
|
||||
'base' => 'article',
|
||||
);
|
||||
node_type_save($type);
|
||||
$type->type = $type->base = 'page';
|
||||
node_type_save($type);
|
||||
|
||||
// Now, create some nodes.
|
||||
$node = (object) array(
|
||||
'title' => 'Foo',
|
||||
'type' => 'article',
|
||||
);
|
||||
node_save($node);
|
||||
$nid1 = $node->nid;
|
||||
$node = (object) array(
|
||||
'title' => 'Bar',
|
||||
'type' => 'article',
|
||||
);
|
||||
node_save($node);
|
||||
$node = (object) array(
|
||||
'title' => 'Baz',
|
||||
'type' => 'page',
|
||||
);
|
||||
node_save($node);
|
||||
|
||||
// We can't use $this->index here, since users don't have bundles.
|
||||
$index = entity_create('search_api_index', array(
|
||||
'id' => 2,
|
||||
'name' => 'test2',
|
||||
'machine_name' => 'test2',
|
||||
'enabled' => 1,
|
||||
'item_type' => 'node',
|
||||
'options' => array(
|
||||
'fields' => array(
|
||||
'nid' => array(
|
||||
'type' => 'integer',
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Now start tracking and check whether the index status is correct.
|
||||
$datasource = search_api_get_datasource_controller('node');
|
||||
$datasource->startTracking(array($index));
|
||||
$status = $datasource->getIndexStatus($index);
|
||||
$this->assertEqual($status['total'], 3, 'Correct number of items marked for indexing on not bundle-specific index.');
|
||||
$datasource->stopTracking(array($index));
|
||||
|
||||
// Once again, but with only indexing articles.
|
||||
$index->options['datasource']['bundles'] = array('article');
|
||||
drupal_static_reset('search_api_get_datasource_controller');
|
||||
$datasource = search_api_get_datasource_controller('node');
|
||||
$datasource->startTracking(array($index));
|
||||
$status = $datasource->getIndexStatus($index);
|
||||
$this->assertEqual($status['total'], 2, 'Correct number of items marked for indexing on bundle-specific index.');
|
||||
$datasource->stopTracking(array($index));
|
||||
|
||||
// Now test that bundle renaming works.
|
||||
$index->save();
|
||||
field_attach_rename_bundle('node', 'article', 'foo');
|
||||
$index = search_api_index_load('test2', TRUE);
|
||||
$this->assertEqual($index->options['datasource']['bundles'], array('foo'), 'Bundle was correctly renamed in index settings.');
|
||||
$index->delete();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ files[] = search_api_test.module
|
||||
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2014-12-26
|
||||
version = "7.x-1.14"
|
||||
; Information added by Drupal.org packaging script on 2015-08-30
|
||||
version = "7.x-1.16"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1419580682"
|
||||
datestamp = "1440962813"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user