Drupal: modules update Search API, ctools
This commit is contained in:
parent
bac4358a13
commit
a8e2b9b4fd
@ -4,8 +4,8 @@ core = 7.x
|
||||
dependencies[] = ctools
|
||||
package = Chaos tool suite
|
||||
|
||||
; Information added by Drupal.org packaging script on 2020-10-23
|
||||
version = "7.x-1.17"
|
||||
; Information added by Drupal.org packaging script on 2021-01-30
|
||||
version = "7.x-1.19"
|
||||
core = "7.x"
|
||||
project = "ctools"
|
||||
datestamp = "1603490551"
|
||||
datestamp = "1611988843"
|
||||
|
@ -19,8 +19,8 @@ files[] = tests/object_cache.test
|
||||
files[] = tests/object_cache_unit.test
|
||||
files[] = tests/page_tokens.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2020-10-23
|
||||
version = "7.x-1.17"
|
||||
; Information added by Drupal.org packaging script on 2021-01-30
|
||||
version = "7.x-1.19"
|
||||
core = "7.x"
|
||||
project = "ctools"
|
||||
datestamp = "1603490551"
|
||||
datestamp = "1611988843"
|
||||
|
@ -4,8 +4,8 @@ core = 7.x
|
||||
package = Chaos tool suite
|
||||
dependencies[] = ctools
|
||||
|
||||
; Information added by Drupal.org packaging script on 2020-10-23
|
||||
version = "7.x-1.17"
|
||||
; Information added by Drupal.org packaging script on 2021-01-30
|
||||
version = "7.x-1.19"
|
||||
core = "7.x"
|
||||
project = "ctools"
|
||||
datestamp = "1603490551"
|
||||
datestamp = "1611988843"
|
||||
|
@ -4,8 +4,8 @@ package = Chaos tool suite
|
||||
dependencies[] = ctools
|
||||
core = 7.x
|
||||
|
||||
; Information added by Drupal.org packaging script on 2020-10-23
|
||||
version = "7.x-1.17"
|
||||
; Information added by Drupal.org packaging script on 2021-01-30
|
||||
version = "7.x-1.19"
|
||||
core = "7.x"
|
||||
project = "ctools"
|
||||
datestamp = "1603490551"
|
||||
datestamp = "1611988843"
|
||||
|
@ -4,8 +4,8 @@ core = 7.x
|
||||
package = Chaos tool suite
|
||||
dependencies[] = ctools
|
||||
|
||||
; Information added by Drupal.org packaging script on 2020-10-23
|
||||
version = "7.x-1.17"
|
||||
; Information added by Drupal.org packaging script on 2021-01-30
|
||||
version = "7.x-1.19"
|
||||
core = "7.x"
|
||||
project = "ctools"
|
||||
datestamp = "1603490551"
|
||||
datestamp = "1611988843"
|
||||
|
@ -7,8 +7,8 @@ dependencies[] = page_manager
|
||||
dependencies[] = advanced_help
|
||||
core = 7.x
|
||||
|
||||
; Information added by Drupal.org packaging script on 2020-10-23
|
||||
version = "7.x-1.17"
|
||||
; Information added by Drupal.org packaging script on 2021-01-30
|
||||
version = "7.x-1.19"
|
||||
core = "7.x"
|
||||
project = "ctools"
|
||||
datestamp = "1603490551"
|
||||
datestamp = "1611988843"
|
||||
|
@ -1416,16 +1416,12 @@ function ctools_context_get_context_from_relationships($relationships, &$context
|
||||
if (is_array($rdata['context'])) {
|
||||
$rcontexts = array();
|
||||
foreach ($rdata['context'] as $cid) {
|
||||
if (empty($contexts[$cid])) {
|
||||
continue 2;
|
||||
if (!empty($contexts[$cid])) {
|
||||
$rcontexts[] = $contexts[$cid];
|
||||
}
|
||||
$rcontexts[] = $contexts[$cid];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (empty($contexts[$rdata['context']])) {
|
||||
continue;
|
||||
}
|
||||
elseif (!empty($contexts[$rdata['context']])) {
|
||||
$rcontexts = $contexts[$rdata['context']];
|
||||
}
|
||||
|
||||
|
@ -456,18 +456,20 @@ function ctools_export_ui_switcher_page($plugin_name, $op) {
|
||||
|
||||
// Load the $plugin information.
|
||||
$plugin = ctools_get_export_ui($plugin_name);
|
||||
$handler = ctools_export_ui_get_handler($plugin);
|
||||
|
||||
if ($handler) {
|
||||
$method = $op . '_page';
|
||||
if (method_exists($handler, $method)) {
|
||||
// Replace the first two arguments:
|
||||
$args[0] = $js;
|
||||
$args[1] = $_POST;
|
||||
return call_user_func_array(array($handler, $method), $args);
|
||||
}
|
||||
if (!$plugin) {
|
||||
return t('Configuration error. No plugin found: %plugin_name.', array('%plugin_name' => $plugin_name));
|
||||
}
|
||||
else {
|
||||
|
||||
$handler = ctools_export_ui_get_handler($plugin);
|
||||
if (!$handler) {
|
||||
return t('Configuration error. No handler found.');
|
||||
}
|
||||
|
||||
$method = $op . '_page';
|
||||
if (method_exists($handler, $method)) {
|
||||
// Replace the first two arguments:
|
||||
$args[0] = $js;
|
||||
$args[1] = $_POST;
|
||||
return call_user_func_array(array($handler, $method), $args);
|
||||
}
|
||||
}
|
||||
|
@ -637,11 +637,19 @@ function ctools_get_default_object($table, $name) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get export object defaults.
|
||||
*
|
||||
* Call the hook to get all default objects of the given type from the
|
||||
* export. If configured properly, this could include loading up an API
|
||||
* to get default objects.
|
||||
*
|
||||
* @param string $table
|
||||
* The name of the table to be loaded. Data is expected to be in the
|
||||
* schema to make all this work.
|
||||
* @param array $export
|
||||
* The export definition from the table's hook_schema() definition.
|
||||
*/
|
||||
function _ctools_export_get_defaults($table, $export) {
|
||||
function _ctools_export_get_defaults($table, array $export) {
|
||||
$cache = &drupal_static(__FUNCTION__, array());
|
||||
|
||||
// If defaults may be cached, first see if we can load from cache.
|
||||
@ -684,7 +692,8 @@ function _ctools_export_get_defaults($table, $export) {
|
||||
$cache[$table][$name] = $object;
|
||||
}
|
||||
else {
|
||||
// If version checking is enabled, ensure that the object can be used.
|
||||
// If version checking is enabled, ensure that the object can be
|
||||
// used.
|
||||
if (isset($object->api_version) &&
|
||||
version_compare($object->api_version, $export['api']['minimum_version']) >= 0 &&
|
||||
version_compare($object->api_version, $export['api']['current_version']) <= 0) {
|
||||
@ -866,6 +875,7 @@ function ctools_var_export($var, $prefix = '') {
|
||||
}
|
||||
else {
|
||||
$output = "array(\n";
|
||||
ksort($var);
|
||||
foreach ($var as $key => $value) {
|
||||
$output .= $prefix . " " . ctools_var_export($key) . " => " . ctools_var_export($value, $prefix . ' ') . ",\n";
|
||||
}
|
||||
|
@ -238,8 +238,6 @@ function ctools_field_label($field_name) {
|
||||
function ctools_field_invoke_field($field_name, $op, $entity_type, $entity, &$a = NULL, &$b = NULL, $options = array()) {
|
||||
if (is_array($field_name)) {
|
||||
$instance = $field_name;
|
||||
$field = empty($field_name['field']) ? field_info_field($instance['field_name']) : $field_name['field'];
|
||||
$field_name = $instance['field_name'];
|
||||
}
|
||||
else {
|
||||
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
@ -250,6 +248,11 @@ function ctools_field_invoke_field($field_name, $op, $entity_type, $entity, &$a
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep the variables consistent regardless if we retrieve the field instance
|
||||
// ourself, or if one is provided to us via the $field_name variable.
|
||||
$field = field_info_field($instance['field_name']);
|
||||
$field_name = $instance['field_name'];
|
||||
|
||||
// Merge default options.
|
||||
$default_options = array(
|
||||
'default' => FALSE,
|
||||
|
@ -97,7 +97,7 @@ function page_manager_page_wizard($name, $step = NULL) {
|
||||
}
|
||||
|
||||
// Check for possibly more complex access callback on plugin.
|
||||
if ($function = ctools_plugin_get_function($plugin, 'access callback') && !$function($plugin)) {
|
||||
if (($function = ctools_plugin_get_function($plugin, 'access callback')) && !$function($plugin)) {
|
||||
return MENU_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
// Grab all the links that match this url and add the fetching class.
|
||||
// This allows the caching system to grab each url once and only once
|
||||
// instead of grabbing the url once per <a>.
|
||||
var $objects = $('a[href="' + old_url + '"]')
|
||||
var $objects = $('a[href="' + old_url + '"]');
|
||||
$objects.addClass('ctools-fetching');
|
||||
try {
|
||||
url = old_url.replace(/\/nojs(\/|$)/g, '/ajax$1');
|
||||
|
@ -98,5 +98,5 @@ Drupal.behaviors.CToolsAutoSubmit = {
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
||||
|
@ -194,7 +194,7 @@
|
||||
Drupal.CTools.CollapsibleCallbacksAfterToggle[i]($container, handle, content, toggle);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var clickMe = function () {
|
||||
if (Drupal.CTools.CollapsibleCallbacks) {
|
||||
@ -222,7 +222,7 @@
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Let both the toggle and the handle be clickable.
|
||||
toggle.click(clickMe);
|
||||
@ -237,5 +237,5 @@
|
||||
attach: function(context) {
|
||||
$('.ctools-collapsible-container', context).once('ctools-collapsible', Drupal.CTools.bindCollapsible);
|
||||
}
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
||||
|
@ -14,7 +14,7 @@
|
||||
* - Checkboxes don't have their own id, so you need to add one in a div
|
||||
* around the checkboxes via #prefix and #suffix. You actually need to add TWO
|
||||
* divs because it's the parent that gets hidden. Also be sure to retain the
|
||||
* 'expand_checkboxes' in the #process array, because the CTools process will
|
||||
* 'form_process_checkboxes' in the #process array, because the CTools process will
|
||||
* override it.
|
||||
*/
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Drupal.CTools.dependent.autoAttach = function() {
|
||||
@ -118,7 +118,7 @@
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
var setChangeTrigger = function(trigger_id, bind_id) {
|
||||
// Triggered when change() is clicked.
|
||||
@ -205,7 +205,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$(trigger_id).bind('change.ctools-dependent', function() {
|
||||
// Trigger the internal change function
|
||||
@ -214,11 +214,11 @@
|
||||
});
|
||||
// Trigger initial reaction
|
||||
changeTrigger(trigger_id, bind_id);
|
||||
}
|
||||
};
|
||||
setChangeTrigger(trigger_id, bind_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Drupal.behaviors.CToolsDependent = {
|
||||
attach: function (context) {
|
||||
@ -240,5 +240,5 @@
|
||||
})
|
||||
.trigger('change.ctools-dependent');
|
||||
}
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
||||
|
@ -69,7 +69,7 @@
|
||||
$secondaryActions.animate({height: "show", opacity: "show"}, 100);
|
||||
$dropbutton.addClass('open');
|
||||
}
|
||||
}
|
||||
};
|
||||
// Hide the secondary actions initially.
|
||||
$secondaryActions.hide();
|
||||
|
||||
@ -90,5 +90,5 @@
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
||||
|
@ -61,7 +61,7 @@
|
||||
$("div.ctools-dropdown-container", $dropdown)
|
||||
.animate({height: "show", opacity: "show"}, 100);
|
||||
}
|
||||
}
|
||||
};
|
||||
$("a.ctools-dropdown-link", $dropdown).click(function() {
|
||||
toggle();
|
||||
return false;
|
||||
@ -83,5 +83,5 @@
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
||||
|
@ -38,5 +38,5 @@
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
||||
|
@ -86,7 +86,7 @@
|
||||
'width': (width - Drupal.CTools.Modal.currentSettings.modalSize.contentRight) + 'px',
|
||||
'height': (height - Drupal.CTools.Modal.currentSettings.modalSize.contentBottom) + 'px'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (!Drupal.CTools.Modal.modal) {
|
||||
Drupal.CTools.Modal.modal = $(Drupal.theme(settings.modalTheme));
|
||||
@ -120,9 +120,9 @@
|
||||
* Provide the HTML to create the modal dialog.
|
||||
*/
|
||||
Drupal.theme.prototype.CToolsModalDialog = function () {
|
||||
var html = ''
|
||||
html += '<div id="ctools-modal">'
|
||||
html += ' <div class="ctools-modal-content">' // panels-modal-content
|
||||
var html = '';
|
||||
html += '<div id="ctools-modal">';
|
||||
html += ' <div class="ctools-modal-content">'; // panels-modal-content
|
||||
html += ' <div class="modal-header">';
|
||||
html += ' <a class="close" href="#">';
|
||||
html += Drupal.CTools.Modal.currentSettings.closeText + Drupal.CTools.Modal.currentSettings.closeImage;
|
||||
@ -135,7 +135,7 @@
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide the HTML to create the throbber.
|
||||
@ -159,7 +159,7 @@
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Click function for modals that can be cached.
|
||||
@ -186,7 +186,7 @@
|
||||
|
||||
setTimeout(function() { Drupal.CTools.AJAX.ajaxSubmit($form, url); }, 1);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Bind links that will open modals to the appropriate function.
|
||||
@ -250,7 +250,7 @@
|
||||
|
||||
element_settings.url = $this.attr('action');
|
||||
element_settings.event = 'submit';
|
||||
element_settings.progress = { 'type': 'throbber' }
|
||||
element_settings.progress = { 'type': 'throbber' };
|
||||
var base = $this.attr('id');
|
||||
|
||||
Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
|
||||
@ -291,9 +291,15 @@
|
||||
* AJAX responder command to place HTML within the modal.
|
||||
*/
|
||||
Drupal.CTools.Modal.modal_display = function(ajax, response, status) {
|
||||
var settings = response.settings || ajax.settings || Drupal.settings;
|
||||
// If the modal does not exist yet, create it.
|
||||
if ($('#modalContent').length == 0) {
|
||||
Drupal.CTools.Modal.show(Drupal.CTools.Modal.getSettings(ajax.element));
|
||||
}
|
||||
// If the modal exists run detachBehaviors before removing existing content.
|
||||
else {
|
||||
Drupal.detachBehaviors($('#modalContent'), settings, 'unload');
|
||||
}
|
||||
$('#modal-title').html(response.title);
|
||||
// Simulate an actual page load by scrolling to the top after adding the
|
||||
// content. This is helpful for allowing users to see error messages at the
|
||||
@ -302,7 +308,6 @@
|
||||
$(document).trigger('CToolsAttachBehaviors', $('#modalContent'));
|
||||
|
||||
// Attach behaviors within a modal dialog.
|
||||
var settings = response.settings || ajax.settings || Drupal.settings;
|
||||
Drupal.attachBehaviors($('#modalContent'), settings);
|
||||
|
||||
if ($('#modal-content').hasClass('ctools-modal-loading')) {
|
||||
@ -315,7 +320,7 @@
|
||||
// button by the show() function called above.)
|
||||
$('#modal-content :focusable:first').focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* AJAX responder command to dismiss the modal.
|
||||
@ -323,7 +328,7 @@
|
||||
Drupal.CTools.Modal.modal_dismiss = function(command) {
|
||||
Drupal.CTools.Modal.dismiss();
|
||||
$('link.ctools-temporary-css').remove();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Display loading
|
||||
@ -334,7 +339,7 @@
|
||||
output: Drupal.theme(Drupal.CTools.Modal.currentSettings.throbberTheme),
|
||||
title: Drupal.CTools.Modal.currentSettings.loadingText
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Find a URL for an AJAX button.
|
||||
@ -590,6 +595,7 @@
|
||||
$('body').unbind( 'keydown', modalTabTrapHandler );
|
||||
$('.close', $modalHeader).unbind('click', modalContentClose);
|
||||
$(document).unbind('keydown', modalEventEscapeCloseHandler);
|
||||
$(document).trigger('CToolsCloseModalBehaviors', $('#modalContent'));
|
||||
$(document).trigger('CToolsDetachBehaviors', $('#modalContent'));
|
||||
|
||||
// Closing animation.
|
||||
@ -675,7 +681,7 @@
|
||||
var $modalContent = $('#modalContent');
|
||||
var $modalHeader = $modalContent.find('.modal-header');
|
||||
$('.close', $modalHeader).unbind('click', modalContentClose);
|
||||
$('body').unbind('keypress', modalEventEscapeCloseHandler);
|
||||
$(document).unbind('keydown', modalEventEscapeCloseHandler);
|
||||
$(document).trigger('CToolsDetachBehaviors', $modalContent);
|
||||
|
||||
// jQuery magic loop through the instances and run the animations or removal.
|
||||
|
@ -180,7 +180,7 @@
|
||||
);
|
||||
$(this).after(lock);
|
||||
locks.push(lock);
|
||||
};
|
||||
}
|
||||
|
||||
// Add hook
|
||||
var $this = $(this);
|
||||
@ -216,5 +216,5 @@
|
||||
$widget.attr('checked', !$widget.attr('checked') || $widget.is('input[type=radio]'));
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
||||
|
@ -6,8 +6,8 @@ package = Chaos tool suite
|
||||
|
||||
files[] = tests/head_links.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2020-10-23
|
||||
version = "7.x-1.17"
|
||||
; Information added by Drupal.org packaging script on 2021-01-30
|
||||
version = "7.x-1.19"
|
||||
core = "7.x"
|
||||
project = "ctools"
|
||||
datestamp = "1603490551"
|
||||
datestamp = "1611988843"
|
||||
|
@ -21,7 +21,7 @@ $plugin = array(
|
||||
* Check for access.
|
||||
*/
|
||||
function ctools_node_status_ctools_access_check($conf, $context) {
|
||||
return (!empty($context->data) && $context->data->status);
|
||||
return (!empty($context->data->status) && $context->data->status);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,6 +152,12 @@ function ctools_entity_form_field_content_type_render($subtype, $conf, $panel_ar
|
||||
* Returns the administrative title for a type.
|
||||
*/
|
||||
function ctools_entity_form_field_content_type_admin_title($subtype, $conf, $context) {
|
||||
// Return early because we don't have context to build this field from.
|
||||
if (!$context || !isset($context->identifier)) {
|
||||
watchdog('ctools_entity_form_field_content_type_admin_title', 'Context is missing for field: @name', array('@name' => $subtype), WATCHDOG_NOTICE);
|
||||
return t('Deleted/missing field @name', array('@name' => $subtype));
|
||||
}
|
||||
|
||||
list($entity_type, $field_name) = explode(':', $subtype, 2);
|
||||
|
||||
if (!empty($context->restrictions)) {
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 460 B |
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Plugins are described by creating a $plugin array which will be used
|
||||
* by the system that includes this file.
|
||||
*/
|
||||
$plugin = array(
|
||||
'single' => TRUE,
|
||||
'icon' => 'icon_user_form.png',
|
||||
'title' => t('User form actions / buttons'),
|
||||
'description' => t('The user form actions / buttons.'),
|
||||
'required context' => new ctools_context_required(t('Form'), 'form'),
|
||||
'category' => t('Form'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Ctools plugin content type render for the picture form field.
|
||||
*/
|
||||
function ctools_user_form_actions_content_type_render($subtype, $conf, $panel_args, &$context) {
|
||||
$block = new stdClass();
|
||||
$block->module = t('user-form');
|
||||
|
||||
$block->delta = 'title-options';
|
||||
|
||||
if (isset($context->form)) {
|
||||
if (!empty($context->form['actions'])) {
|
||||
$block->content['actions'] = $context->form['actions'];
|
||||
unset($context->form['actions']);
|
||||
}
|
||||
// Because we are adding the submit buttons outside the General form
|
||||
// we can assume the necessary hidden components should be added as well.
|
||||
if (!empty($context->form['form_build_id'])) {
|
||||
$block->content['form_build_id'] = $context->form['form_build_id'];
|
||||
unset($context->form['form_build_id']);
|
||||
}
|
||||
if (!empty($context->form['form_token'])) {
|
||||
$block->content['form_token'] = $context->form['form_token'];
|
||||
unset($context->form['form_token']);
|
||||
}
|
||||
if (!empty($context->form['form_id'])) {
|
||||
$block->content['form_id'] = $context->form['form_id'];
|
||||
unset($context->form['form_id']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$block->content = t('User actions / buttons form components.');
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin admin title function for the actions form field.
|
||||
*/
|
||||
function ctools_user_form_actions_content_type_admin_title($subtype, $conf, $context) {
|
||||
return t('"@s" user form actions / buttons field', array('@s' => $context->identifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin configuration edit form for the actions form field.
|
||||
*
|
||||
* Provide a blank form so we have a place to have context setting.
|
||||
*/
|
||||
function ctools_user_form_actions_content_type_edit_form($form, &$form_state) {
|
||||
return $form;
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Plugins are described by creating a $plugin array which will be used
|
||||
* by the system that includes this file.
|
||||
*/
|
||||
$plugin = array(
|
||||
'single' => TRUE,
|
||||
'icon' => 'icon_user_form.png',
|
||||
'title' => t('User form: add a specific component'),
|
||||
'description' => t('The user form component by selection.'),
|
||||
'required context' => new ctools_context_required(t('Form'), 'form'),
|
||||
'category' => t('Form'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Ctools plugin content type render for the picture form field.
|
||||
*/
|
||||
function ctools_user_form_component_content_type_render($subtype, $conf, $panel_args, &$context) {
|
||||
$block = new stdClass();
|
||||
$block->module = t('user-form');
|
||||
|
||||
$block->delta = 'title-options';
|
||||
|
||||
if (isset($context->form)) {
|
||||
if (!empty($context->form[$conf['field']])) {
|
||||
$block->content[$conf['field']] = $context->form[$conf['field']];
|
||||
unset($context->form[$conf['field']]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$block->content = t('User form edit components.');
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin admin title function for the a selectable form field.
|
||||
*/
|
||||
function ctools_user_form_component_content_type_admin_title($subtype, $conf, $context) {
|
||||
return t('"@s" user form @field field', array('@s' => $context->identifier, '@field' => $conf['field']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin configuration edit form for the selectable form field.
|
||||
*
|
||||
* Provide the list of fields in the user profile edit form to select from the
|
||||
* plugin configuration.
|
||||
*/
|
||||
function ctools_user_form_component_content_type_edit_form($form, &$form_state) {
|
||||
$conf = $form_state['conf'];
|
||||
$user_form = drupal_get_form('user_profile_form');
|
||||
|
||||
$field_keys = element_children($user_form);
|
||||
$options = array_combine($field_keys, $field_keys);
|
||||
|
||||
$form['field'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('User form field'),
|
||||
'#options' => $options,
|
||||
'#description' => t('Select a form field from the current user form to display in this pane.'),
|
||||
'#default_value' => !empty($conf['field']) ? $conf['field'] : '',
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin configuration edit form submit handler.
|
||||
*/
|
||||
function ctools_user_form_component_content_type_edit_form_submit($form, &$form_state) {
|
||||
$form_state['conf']['field'] = $form_state['values']['field'];
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Plugins are described by creating a $plugin array which will be used
|
||||
* by the system that includes this file.
|
||||
*/
|
||||
$plugin = array(
|
||||
'single' => TRUE,
|
||||
'icon' => 'icon_user_form.png',
|
||||
'title' => t('User form email field'),
|
||||
'description' => t('The user email form.'),
|
||||
'required context' => new ctools_context_required(t('Form'), 'form'),
|
||||
'category' => t('Form'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Ctools plugin content type render for the email form field.
|
||||
*/
|
||||
function ctools_user_form_email_content_type_render($subtype, $conf, $panel_args, &$context) {
|
||||
$block = new stdClass();
|
||||
$block->module = t('user-form');
|
||||
|
||||
$block->delta = 'title-options';
|
||||
|
||||
if (isset($context->form)) {
|
||||
// The current password is required to change the email.
|
||||
if (!empty($context->form['account']['current_pass'])) {
|
||||
$block->content['current_pass'] = $context->form['account']['current_pass'];
|
||||
unset($context->form['account']['current_pass']);
|
||||
}
|
||||
if (!empty($context->form['account']['mail'])) {
|
||||
$block->content['mail'] = $context->form['account']['mail'];
|
||||
unset($context->form['account']['mail']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$block->content = t('User email form.');
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin admin title function for the email form field.
|
||||
*/
|
||||
function ctools_user_form_email_content_type_admin_title($subtype, $conf, $context) {
|
||||
return t('"@s" user form email field', array('@s' => $context->identifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin configuration edit form for the email form field.
|
||||
*
|
||||
* Provide a blank form so we have a place to have context setting.
|
||||
*/
|
||||
function ctools_user_form_email_content_type_edit_form($form, &$form_state) {
|
||||
return $form;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Plugins are described by creating a $plugin array which will be used
|
||||
* by the system that includes this file.
|
||||
*/
|
||||
$plugin = array(
|
||||
'single' => TRUE,
|
||||
'icon' => 'icon_user_form.png',
|
||||
'title' => t('User form notify field'),
|
||||
'description' => t('The user notify form.'),
|
||||
'required context' => new ctools_context_required(t('Form'), 'form'),
|
||||
'category' => t('Form'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Ctools plugin content type render for the notify form field.
|
||||
*/
|
||||
function ctools_user_form_notify_content_type_render($subtype, $conf, $panel_args, &$context) {
|
||||
$block = new stdClass();
|
||||
$block->module = t('user-form');
|
||||
|
||||
$block->delta = 'title-options';
|
||||
|
||||
if (isset($context->form)) {
|
||||
if (!empty($context->form['account']['notify'])) {
|
||||
$block->content['notify'] = $context->form['account']['notify'];
|
||||
unset($context->form['account']['notify']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$block->content = t('User notify form.');
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin admin title function for the notify form field.
|
||||
*/
|
||||
function ctools_user_form_notify_content_type_admin_title($subtype, $conf, $context) {
|
||||
return t('"@s" user form notify field', array('@s' => $context->identifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin configuration edit form for the notify form field.
|
||||
*
|
||||
* Provide a blank form so we have a place to have context setting.
|
||||
*/
|
||||
function ctools_user_form_notify_content_type_edit_form($form, &$form_state) {
|
||||
return $form;
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Plugins are described by creating a $plugin array which will be used
|
||||
* by the system that includes this file.
|
||||
*/
|
||||
$plugin = array(
|
||||
'single' => TRUE,
|
||||
'icon' => 'icon_user_form.png',
|
||||
'title' => t('User form password field'),
|
||||
'description' => t('The user password form.'),
|
||||
'required context' => new ctools_context_required(t('Form'), 'form'),
|
||||
'category' => t('Form'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Ctools plugin content type render for the password form field.
|
||||
*/
|
||||
function ctools_user_form_password_content_type_render($subtype, $conf, $panel_args, &$context) {
|
||||
$block = new stdClass();
|
||||
$block->module = t('user-form');
|
||||
|
||||
$block->delta = 'title-options';
|
||||
|
||||
if (isset($context->form)) {
|
||||
// The current password is required to change the password.
|
||||
if (!empty($context->form['account']['current_pass'])) {
|
||||
$block->content['current_pass'] = $context->form['account']['current_pass'];
|
||||
unset($context->form['account']['current_pass']);
|
||||
}
|
||||
if (!empty($context->form['account']['pass'])) {
|
||||
$block->content['pass'] = $context->form['account']['pass'];
|
||||
unset($context->form['account']['pass']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$block->content = t('User password form.');
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin admin title function for the password form field.
|
||||
*/
|
||||
function ctools_user_form_password_content_type_admin_title($subtype, $conf, $context) {
|
||||
return t('"@s" user form password field', array('@s' => $context->identifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin configuration edit form for the password form field.
|
||||
*
|
||||
* Provide a blank form so we have a place to have context setting.
|
||||
*/
|
||||
function ctools_user_form_password_content_type_edit_form($form, &$form_state) {
|
||||
return $form;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Plugins are described by creating a $plugin array which will be used
|
||||
* by the system that includes this file.
|
||||
*/
|
||||
$plugin = array(
|
||||
'single' => TRUE,
|
||||
'icon' => 'icon_user_form.png',
|
||||
'title' => t('User form picture field'),
|
||||
'description' => t('The user picture form.'),
|
||||
'required context' => new ctools_context_required(t('Form'), 'form'),
|
||||
'category' => t('Form'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Ctools plugin content type render for the picture form field.
|
||||
*/
|
||||
function ctools_user_form_picture_content_type_render($subtype, $conf, $panel_args, &$context) {
|
||||
$block = new stdClass();
|
||||
$block->module = t('user-form');
|
||||
|
||||
$block->delta = 'title-options';
|
||||
|
||||
if (isset($context->form)) {
|
||||
if (!empty($context->form['picture'])) {
|
||||
$block->content['picture'] = $context->form['picture'];
|
||||
unset($context->form['picture']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$block->content = t('User picture form.');
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin admin title function for the picture form field.
|
||||
*/
|
||||
function ctools_user_form_picture_content_type_admin_title($subtype, $conf, $context) {
|
||||
return t('"@s" user form picture field', array('@s' => $context->identifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin configuration edit form for the picture form field.
|
||||
*
|
||||
* Provide a blank form so we have a place to have context setting.
|
||||
*/
|
||||
function ctools_user_form_picture_content_type_edit_form($form, &$form_state) {
|
||||
return $form;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Plugins are described by creating a $plugin array which will be used
|
||||
* by the system that includes this file.
|
||||
*/
|
||||
$plugin = array(
|
||||
'single' => TRUE,
|
||||
'icon' => 'icon_user_form.png',
|
||||
'title' => t('User form roles field'),
|
||||
'description' => t('The user roles form.'),
|
||||
'required context' => new ctools_context_required(t('Form'), 'form'),
|
||||
'category' => t('Form'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Ctools plugin content type render for the roles form field.
|
||||
*/
|
||||
function ctools_user_form_roles_content_type_render($subtype, $conf, $panel_args, &$context) {
|
||||
$block = new stdClass();
|
||||
$block->module = t('user-form');
|
||||
|
||||
$block->delta = 'title-options';
|
||||
|
||||
if (isset($context->form)) {
|
||||
if (!empty($context->form['account']['roles'])) {
|
||||
$block->content['roles'] = $context->form['account']['roles'];
|
||||
unset($context->form['account']['roles']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$block->content = t('User roles form.');
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin admin title function for the roles form field.
|
||||
*/
|
||||
function ctools_user_form_roles_content_type_admin_title($subtype, $conf, $context) {
|
||||
return t('"@s" user form roles field', array('@s' => $context->identifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin configuration edit form for the roles form field.
|
||||
*
|
||||
* Provide a blank form so we have a place to have context setting.
|
||||
*/
|
||||
function ctools_user_form_roles_content_type_edit_form($form, &$form_state) {
|
||||
return $form;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Plugins are described by creating a $plugin array which will be used
|
||||
* by the system that includes this file.
|
||||
*/
|
||||
$plugin = array(
|
||||
'single' => TRUE,
|
||||
'icon' => 'icon_user_form.png',
|
||||
'title' => t('User form signature settings field'),
|
||||
'description' => t('The user signature settings form.'),
|
||||
'required context' => new ctools_context_required(t('Form'), 'form'),
|
||||
'category' => t('Form'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Ctools plugin content type render for the signature settings form field.
|
||||
*/
|
||||
function ctools_user_form_signature_settings_content_type_render($subtype, $conf, $panel_args, &$context) {
|
||||
$block = new stdClass();
|
||||
$block->module = t('user-form');
|
||||
|
||||
$block->delta = 'title-options';
|
||||
|
||||
if (isset($context->form)) {
|
||||
if (!empty($context->form['signature_settings'])) {
|
||||
$block->content['signature_settings'] = $context->form['signature_settings'];
|
||||
unset($context->form['signature_settings']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$block->content = t('User signature settings form.');
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin admin title function for the signature settings form field.
|
||||
*/
|
||||
function ctools_user_form_signature_settings_content_type_admin_title($subtype, $conf, $context) {
|
||||
return t('"@s" user form signature settings field', array('@s' => $context->identifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin configuration edit form for the signature settings form field.
|
||||
*
|
||||
* Provide a blank form so we have a place to have context setting.
|
||||
*/
|
||||
function ctools_user_form_signature_settings_content_type_edit_form($form, &$form_state) {
|
||||
return $form;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Plugins are described by creating a $plugin array which will be used
|
||||
* by the system that includes this file.
|
||||
*/
|
||||
$plugin = array(
|
||||
'single' => TRUE,
|
||||
'icon' => 'icon_user_form.png',
|
||||
'title' => t('User form status field'),
|
||||
'description' => t('The user status form.'),
|
||||
'required context' => new ctools_context_required(t('Form'), 'form'),
|
||||
'category' => t('Form'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Ctools plugin content type render for the status form field.
|
||||
*/
|
||||
function ctools_user_form_status_content_type_render($subtype, $conf, $panel_args, &$context) {
|
||||
$block = new stdClass();
|
||||
$block->module = t('user-form');
|
||||
|
||||
$block->delta = 'title-options';
|
||||
|
||||
if (isset($context->form)) {
|
||||
if (!empty($context->form['account']['status'])) {
|
||||
$block->content['status'] = $context->form['account']['status'];
|
||||
unset($context->form['account']['status']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$block->content = t('User status form.');
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin admin title function for the status form field.
|
||||
*/
|
||||
function ctools_user_form_status_content_type_admin_title($subtype, $conf, $context) {
|
||||
return t('"@s" user form status field', array('@s' => $context->identifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin configuration edit form for the status form field.
|
||||
*
|
||||
* Provide a blank form so we have a place to have context setting.
|
||||
*/
|
||||
function ctools_user_form_status_content_type_edit_form($form, &$form_state) {
|
||||
return $form;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Plugins are described by creating a $plugin array which will be used
|
||||
* by the system that includes this file.
|
||||
*/
|
||||
$plugin = array(
|
||||
'single' => TRUE,
|
||||
'icon' => 'icon_user_form.png',
|
||||
'title' => t('User form timezone field'),
|
||||
'description' => t('The user timezone form.'),
|
||||
'required context' => new ctools_context_required(t('Form'), 'form'),
|
||||
'category' => t('Form'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Ctools plugin content type render for the timezone form field.
|
||||
*/
|
||||
function ctools_user_form_timezone_content_type_render($subtype, $conf, $panel_args, &$context) {
|
||||
$block = new stdClass();
|
||||
$block->module = t('user-form');
|
||||
|
||||
$block->delta = 'title-options';
|
||||
|
||||
if (isset($context->form)) {
|
||||
if (!empty($context->form['timezone'])) {
|
||||
$block->content['timezone'] = $context->form['timezone'];
|
||||
unset($context->form['timezone']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$block->content = t('User timezone form.');
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin admin title function for the timezone form field.
|
||||
*/
|
||||
function ctools_user_form_timezone_content_type_admin_title($subtype, $conf, $context) {
|
||||
return t('"@s" user form timezone field', array('@s' => $context->identifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin configuration edit form for the timezone form field.
|
||||
*
|
||||
* Provide a blank form so we have a place to have context setting.
|
||||
*/
|
||||
function ctools_user_form_timezone_content_type_edit_form($form, &$form_state) {
|
||||
return $form;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Plugins are described by creating a $plugin array which will be used
|
||||
* by the system that includes this file.
|
||||
*/
|
||||
$plugin = array(
|
||||
'single' => TRUE,
|
||||
'icon' => 'icon_user_form.png',
|
||||
'title' => t('User form username field'),
|
||||
'description' => t('The user username form.'),
|
||||
'required context' => new ctools_context_required(t('Form'), 'form'),
|
||||
'category' => t('Form'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Ctools plugin content type render for the username form field.
|
||||
*/
|
||||
function ctools_user_form_username_content_type_render($subtype, $conf, $panel_args, &$context) {
|
||||
$block = new stdClass();
|
||||
$block->module = t('user-form');
|
||||
|
||||
$block->delta = 'title-options';
|
||||
|
||||
if (isset($context->form)) {
|
||||
if (!empty($context->form['account']['name'])) {
|
||||
$block->content['name'] = $context->form['account']['name'];
|
||||
unset($context->form['account']['name']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$block->content = t('User username form.');
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin admin title function for the username form field.
|
||||
*/
|
||||
function ctools_user_form_username_content_type_admin_title($subtype, $conf, $context) {
|
||||
return t('"@s" user form username field', array('@s' => $context->identifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ctools plugin configuration edit form for the username form field.
|
||||
*
|
||||
* Provide a blank form so we have a place to have context setting.
|
||||
*/
|
||||
function ctools_user_form_username_content_type_edit_form($form, &$form_state) {
|
||||
return $form;
|
||||
}
|
@ -41,10 +41,13 @@ function ctools_context_create_user($empty, $data = NULL, $conf = FALSE) {
|
||||
if ($conf) {
|
||||
if ($data['type'] == 'current') {
|
||||
global $user;
|
||||
$data = user_load($user->uid);
|
||||
if (user_is_logged_in()) {
|
||||
$data = user_load($user->uid);
|
||||
$data->logged_in_user = TRUE;
|
||||
}
|
||||
else {
|
||||
$data = drupal_anonymous_user();
|
||||
}
|
||||
}
|
||||
else {
|
||||
$data = user_load($data['uid']);
|
||||
|
@ -310,7 +310,7 @@ class ctools_export_ui {
|
||||
|
||||
$form['bottom row']['reset'] = array(
|
||||
'#type' => 'submit',
|
||||
'#id' => 'ctools-export-ui-list-items-apply',
|
||||
'#id' => 'ctools-export-ui-list-items-reset',
|
||||
'#value' => t('Reset'),
|
||||
'#attributes' => array('class' => array('use-ajax-submit')),
|
||||
);
|
||||
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file relationships/comment_parent.inc
|
||||
* Plugin to provide a relationship handler for comment parent.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Plugins are described by creating a $plugin array which will be used
|
||||
* by the system that includes this file.
|
||||
*/
|
||||
$plugin = array(
|
||||
'title' => t('Parent comment'),
|
||||
'keyword' => 'parent_comment',
|
||||
'description' => t('Adds a parent comment from a comment context.'),
|
||||
'required context' => new ctools_context_required(t('Comment'), 'entity:comment'),
|
||||
'context' => 'ctools_comment_parent_context',
|
||||
);
|
||||
|
||||
/**
|
||||
* Return a new context based on an existing context.
|
||||
*/
|
||||
function ctools_comment_parent_context($context, $conf) {
|
||||
if (empty($context->data)) {
|
||||
return ctools_context_create_empty('entity:comment');
|
||||
}
|
||||
|
||||
if (isset($context->data->pid) && ($context->data->pid !== 0)) {
|
||||
$parent_comment = comment_load($context->data->pid);
|
||||
return ctools_context_create('entity:comment', $parent_comment);
|
||||
}
|
||||
}
|
@ -5,8 +5,8 @@ package = Chaos tool suite
|
||||
dependencies[] = ctools
|
||||
dependencies[] = color
|
||||
|
||||
; Information added by Drupal.org packaging script on 2020-10-23
|
||||
version = "7.x-1.17"
|
||||
; Information added by Drupal.org packaging script on 2021-01-30
|
||||
version = "7.x-1.19"
|
||||
core = "7.x"
|
||||
project = "ctools"
|
||||
datestamp = "1603490551"
|
||||
datestamp = "1611988843"
|
||||
|
@ -4,8 +4,8 @@ core = 7.x
|
||||
dependencies[] = ctools
|
||||
package = Chaos tool suite
|
||||
|
||||
; Information added by Drupal.org packaging script on 2020-10-23
|
||||
version = "7.x-1.17"
|
||||
; Information added by Drupal.org packaging script on 2021-01-30
|
||||
version = "7.x-1.19"
|
||||
core = "7.x"
|
||||
project = "ctools"
|
||||
datestamp = "1603490551"
|
||||
datestamp = "1611988843"
|
||||
|
@ -7,8 +7,8 @@ hidden = TRUE
|
||||
|
||||
files[] = ctools_export.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2020-10-23
|
||||
version = "7.x-1.17"
|
||||
; Information added by Drupal.org packaging script on 2021-01-30
|
||||
version = "7.x-1.19"
|
||||
core = "7.x"
|
||||
project = "ctools"
|
||||
datestamp = "1603490551"
|
||||
datestamp = "1611988843"
|
||||
|
@ -5,8 +5,8 @@ core = 7.x
|
||||
dependencies[] = ctools
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2020-10-23
|
||||
version = "7.x-1.17"
|
||||
; Information added by Drupal.org packaging script on 2021-01-30
|
||||
version = "7.x-1.19"
|
||||
core = "7.x"
|
||||
project = "ctools"
|
||||
datestamp = "1603490551"
|
||||
datestamp = "1611988843"
|
||||
|
@ -385,13 +385,6 @@ class views_content_plugin_display_panel_pane extends views_plugin_display {
|
||||
return (bool) $conf['more_link'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has_path() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -0,0 +1,11 @@
|
||||
name = Views content panes Test
|
||||
description = Test module for Views content panes.
|
||||
package = Views
|
||||
core = 7.x
|
||||
dependencies[] = views_content
|
||||
hidden = TRUE
|
||||
; Information added by Drupal.org packaging script on 2021-01-30
|
||||
version = "7.x-1.19"
|
||||
core = "7.x"
|
||||
project = "ctools"
|
||||
datestamp = "1611988843"
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Helper module for Views content pane tests.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_api().
|
||||
*/
|
||||
function views_content_test_views_api() {
|
||||
return array(
|
||||
'api' => 3.0,
|
||||
);
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Tests views.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_default_views().
|
||||
*/
|
||||
function views_content_test_views_default_views() {
|
||||
$view = new view();
|
||||
$view->name = 'views_content_more_link';
|
||||
$view->description = '';
|
||||
$view->tag = 'default';
|
||||
$view->base_table = 'node';
|
||||
$view->human_name = 'views_content_more_link';
|
||||
$view->core = 7;
|
||||
$view->api_version = '3.0';
|
||||
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
|
||||
|
||||
/* Display: Master */
|
||||
$handler = $view->new_display('default', 'Master', 'default');
|
||||
$handler->display->display_options['title'] = 'views_content_more_link';
|
||||
$handler->display->display_options['use_more'] = TRUE;
|
||||
$handler->display->display_options['use_more_always'] = FALSE;
|
||||
$handler->display->display_options['access']['type'] = 'perm';
|
||||
$handler->display->display_options['cache']['type'] = 'none';
|
||||
$handler->display->display_options['query']['type'] = 'views_query';
|
||||
$handler->display->display_options['exposed_form']['type'] = 'basic';
|
||||
$handler->display->display_options['pager']['type'] = 'none';
|
||||
$handler->display->display_options['pager']['options']['offset'] = '0';
|
||||
$handler->display->display_options['style_plugin'] = 'table';
|
||||
$handler->display->display_options['style_options']['columns'] = array(
|
||||
'title' => 'title',
|
||||
);
|
||||
$handler->display->display_options['style_options']['class'] = '';
|
||||
$handler->display->display_options['style_options']['default'] = '-1';
|
||||
$handler->display->display_options['style_options']['info'] = array(
|
||||
'title' => array(
|
||||
'sortable' => 0,
|
||||
'default_sort_order' => 'asc',
|
||||
'align' => '',
|
||||
'separator' => '',
|
||||
'empty_column' => 0,
|
||||
),
|
||||
);
|
||||
/* Field: Content: Title */
|
||||
$handler->display->display_options['fields']['title']['id'] = 'title';
|
||||
$handler->display->display_options['fields']['title']['table'] = 'node';
|
||||
$handler->display->display_options['fields']['title']['field'] = 'title';
|
||||
$handler->display->display_options['fields']['title']['label'] = '';
|
||||
$handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
|
||||
$handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
|
||||
/* Sort criterion: Content: Post date */
|
||||
$handler->display->display_options['sorts']['created']['id'] = 'created';
|
||||
$handler->display->display_options['sorts']['created']['table'] = 'node';
|
||||
$handler->display->display_options['sorts']['created']['field'] = 'created';
|
||||
$handler->display->display_options['sorts']['created']['order'] = 'DESC';
|
||||
/* Filter criterion: Content: Published status */
|
||||
$handler->display->display_options['filters']['status']['id'] = 'status';
|
||||
$handler->display->display_options['filters']['status']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['status']['field'] = 'status';
|
||||
$handler->display->display_options['filters']['status']['value'] = 1;
|
||||
$handler->display->display_options['filters']['status']['group'] = 1;
|
||||
$handler->display->display_options['filters']['status']['expose']['operator'] = FALSE;
|
||||
|
||||
/* Display: Page */
|
||||
$handler = $view->new_display('page', 'Page', 'page');
|
||||
$handler->display->display_options['path'] = 'views-content-more-link';
|
||||
|
||||
/* Display: Content pane */
|
||||
$handler = $view->new_display('panel_pane', 'Content pane', 'panel_pane_1');
|
||||
$handler->display->display_options['defaults']['pager'] = FALSE;
|
||||
$handler->display->display_options['pager']['type'] = 'some';
|
||||
$handler->display->display_options['pager']['options']['items_per_page'] = '3';
|
||||
$handler->display->display_options['pager']['options']['offset'] = '0';
|
||||
|
||||
$views[$view->name] = $view;
|
||||
|
||||
return $views;
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains ViewsContentPanesTest.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests rendering views content pane displays.
|
||||
*/
|
||||
class ViewsContentPanesTest extends ViewsSqlTest {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Views content panes tests',
|
||||
'description' => 'Tests rendering views content pane displays.',
|
||||
'group' => 'ctools',
|
||||
'dependencies' => array('ctools', 'views'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp(array $modules = array()) {
|
||||
$modules[] = 'ctools';
|
||||
$modules[] = 'views_content';
|
||||
$modules[] = 'views_content_test';
|
||||
parent::setUp($modules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rendering a content pane with a more link.
|
||||
*/
|
||||
public function testViewMoreLink() {
|
||||
// The view "views_content_more_link" has two displays: a content pane
|
||||
// display and a page display. The content pane display displays 3 nodes,
|
||||
// the page display displays all items.
|
||||
// On the content pane display a "more" link is configured that should link
|
||||
// to the page.
|
||||
$view = views_get_view('views_content_more_link');
|
||||
|
||||
// Create four nodes that the view will display. The first three
|
||||
// will get displayed on the content pane display and the remaining
|
||||
// on the page display.
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
$this->drupalCreateNode();
|
||||
}
|
||||
|
||||
// Render the content pane display and assert that a more link is shown.
|
||||
$view->set_display('panel_pane_1');
|
||||
$rendered_output = $view->preview();
|
||||
$this->verbose($rendered_output);
|
||||
$this->storeViewPreview($rendered_output);
|
||||
|
||||
$this->assertLink('more');
|
||||
$this->assertLinkByHref('views-content-more-link');
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a view output in the elements.
|
||||
*
|
||||
* @param string $output
|
||||
* The Views HTML output.
|
||||
*/
|
||||
protected function storeViewPreview($output) {
|
||||
$html_dom = new DOMDocument();
|
||||
@$html_dom->loadHTML($output);
|
||||
if ($html_dom) {
|
||||
// It's much easier to work with simplexml than DOM, luckily enough
|
||||
// we can just simply import our DOM tree.
|
||||
$this->elements = simplexml_import_dom($html_dom);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -8,9 +8,10 @@ package = Chaos tool suite
|
||||
files[] = plugins/views/views_content_plugin_display_ctools_context.inc
|
||||
files[] = plugins/views/views_content_plugin_display_panel_pane.inc
|
||||
files[] = plugins/views/views_content_plugin_style_ctools_context.inc
|
||||
files[] = tests/src/views_content.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2020-10-23
|
||||
version = "7.x-1.17"
|
||||
; Information added by Drupal.org packaging script on 2021-01-30
|
||||
version = "7.x-1.19"
|
||||
core = "7.x"
|
||||
project = "ctools"
|
||||
datestamp = "1603490551"
|
||||
datestamp = "1611988843"
|
||||
|
@ -8,7 +8,7 @@
|
||||
"latedef": true,
|
||||
"noarg": true,
|
||||
"onevar": true,
|
||||
"quotmark": "double",
|
||||
"quotmark": "single",
|
||||
"trailing": true,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
|
@ -1,7 +1,40 @@
|
||||
Search API 1.27 (2021-02-01):
|
||||
-----------------------------
|
||||
- #3178035 by mvc, drunken monkey: Fixed OR facets for non-numeric hierarchical
|
||||
fields.
|
||||
- #3132404 by janadam, klausi, drunken monkey: Fixed support for latest Date
|
||||
module version.
|
||||
- #3123171 by drunken monkey: Fixed wrong Views filter handler used for URI
|
||||
fields.
|
||||
- #3109880 by drunken monkey: Fixed unnecessary processing of disabled indexes.
|
||||
- #3122167 by pandaski, drunken monkey: Provided a processor for excluding
|
||||
private file entities from being indexed.
|
||||
- #3119271 by mibfire, drunken monkey: Fixed incomplete whitespace detection in
|
||||
HTML filter.
|
||||
- #3052798 by drunken monkey, smhnaji: Fixed errors on duplicate tracking of
|
||||
new items.
|
||||
- #3096993 by KarlShea, drunken monkey: Fixed Views "between" filters with only
|
||||
maximum value.
|
||||
- #3081180 by mibfire, drunken monkey: Fixed HTML filter sometimes leaving whitespace.
|
||||
- #3070125 by chrisclark, drunken monkey: Fixed Views "Reset" button validating
|
||||
minimum keywords length.
|
||||
- #2378945 by DamienMcKenna, drunken monkey, capysara, JPHuxley, minorOffense:
|
||||
Added option to keep facets when submitting Views exposed form.
|
||||
- #3035977 by das-peter, drunken monkey: Fixed edge case problems with "Entity
|
||||
HTML output" on multilingual sites.
|
||||
- #3056882 by drunken monkey, WalkingDexter: Fixed highlighting in some edge
|
||||
cases.
|
||||
- #3041704 by drunken monkey: Added multi-type support for entity status filter
|
||||
processors.
|
||||
- #2290019 by n3or, azinck, drunken monkey: Added support for OR operator to
|
||||
date facets.
|
||||
- #3040980 by drunken monkey: Fixed undefined index notice in Highlighting
|
||||
processor.
|
||||
|
||||
Search API 1.26 (2019-03-11):
|
||||
-----------------------------
|
||||
- #2324023 by drumm, drunken monkey: Changed Views field definition for to
|
||||
float.
|
||||
- #2324023 by drumm, drunken monkey: Changed Views field definition for
|
||||
Relevance to float.
|
||||
- #3008849 by pamatt, drunken monkey: Fixed non-exposed numeric and date
|
||||
filters in Views.
|
||||
- #3009744 by evgeny.chernyavskiy, drunken monkey: Fixed wrong "continue" in
|
||||
|
@ -285,9 +285,6 @@ class SearchApiFacetapiAdapter extends FacetapiAdapter {
|
||||
'#default_value' => isset($options['date_granularity']) ? $options['date_granularity'] : FACETAPI_DATE_MINUTE,
|
||||
);
|
||||
|
||||
// Date facets don't support the "OR" operator (for now).
|
||||
$form['global']['operator']['#access'] = FALSE;
|
||||
|
||||
$default_value = FACETAPI_DATE_YEAR;
|
||||
if (isset($options['date_granularity_min'])) {
|
||||
$default_value = $options['date_granularity_min'];
|
||||
|
@ -54,13 +54,29 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
|
||||
$options['search_api_facets'][$this->facet['name']]['limit'] = -1;
|
||||
}
|
||||
|
||||
if ($active = $this->adapter->getActiveItems($this->facet)) {
|
||||
$item = end($active);
|
||||
if ($active_items = $this->adapter->getActiveItems($this->facet)) {
|
||||
$field = $this->facet['field'];
|
||||
$filter = $this->createRangeFilter($item['value']);
|
||||
if ($filter) {
|
||||
$this->addFacetFilter($query, $field, $filter);
|
||||
$operator = 'OR';
|
||||
if ($settings['operator'] !== FACETAPI_OPERATOR_OR) {
|
||||
$operator = 'AND';
|
||||
// If the operator is AND, we just need to apply the lowest-level
|
||||
// filter(s) to make this work correctly. For single-valued fields, this
|
||||
// will always just be the last value, so just use that to improve
|
||||
// performance for that case.
|
||||
$fields = $query->getIndex()->getFields();
|
||||
if (isset($fields[$field]['type'])
|
||||
&& !search_api_is_list_type($fields[$field]['type'])) {
|
||||
$active_items = array(end($active_items));
|
||||
}
|
||||
}
|
||||
$date_query = $query->createFilter($operator, array("facet:$field"));
|
||||
foreach($active_items as $active_item) {
|
||||
$filter = $this->createRangeFilter($active_item['value']);
|
||||
if ($filter) {
|
||||
$this->addFacetFilter($date_query, $field, $filter);
|
||||
}
|
||||
}
|
||||
$query->filter($date_query);
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,8 +240,9 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
|
||||
// Executes query, iterates over results.
|
||||
if (isset($results['search_api_facets']) && isset($results['search_api_facets'][$this->facet['name']])) {
|
||||
$values = $results['search_api_facets'][$this->facet['name']];
|
||||
$mincount = $facet->getSettings()->settings['facet_mincount'];
|
||||
foreach ($values as $value) {
|
||||
if ($value['count']) {
|
||||
if ($value['count'] >= $mincount) {
|
||||
$filter = $value['filter'];
|
||||
// We only process single values further. The "missing" filter and
|
||||
// range filters will be passed on unchanged.
|
||||
@ -286,9 +303,6 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
|
||||
$build[$parent]['#item_children'][$value] = &$build[$value];
|
||||
$build[$value]['#item_parents'][$parent] = $parent;
|
||||
}
|
||||
|
||||
// Stores the last value iterated over.
|
||||
$parent = $value;
|
||||
}
|
||||
|
||||
if (empty($raw_values)) {
|
||||
|
@ -65,7 +65,7 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy
|
||||
foreach (array_reverse($values) 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)) {
|
||||
if (!isset($active[$filter]) || $filter == '!') {
|
||||
continue;
|
||||
}
|
||||
// Go through the entire hierarchy of the value and remove all its
|
||||
|
@ -9,8 +9,8 @@ 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 2019-03-11
|
||||
version = "7.x-1.26"
|
||||
; Information added by Drupal.org packaging script on 2021-02-01
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1552334832"
|
||||
datestamp = "1612192165"
|
||||
|
@ -507,6 +507,62 @@ function search_api_facetapi_search_api_admin_index_fields_submit($form, &$form_
|
||||
cache_clear_all($cid, 'cache', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for views_exposed_form().
|
||||
*
|
||||
* Custom integration for facets. When a Views exposed filter is modified on a
|
||||
* search results page, any facets which have been already selected will be
|
||||
* removed. This (optionally) adds hidden fields for each facet so their values
|
||||
* are retained.
|
||||
*/
|
||||
function search_api_facetapi_form_views_exposed_form_alter(array &$form, array &$form_state) {
|
||||
if (empty($form_state['view'])) {
|
||||
return;
|
||||
}
|
||||
$view = $form_state['view'];
|
||||
|
||||
// Check if this is a Search API-based view and if the "Preserve facets"
|
||||
// option is enabled. ("search_api_multi" would be the exact base table name,
|
||||
// not just a prefix, but since it's just 16 characters long, we can still use
|
||||
// this check to make the condition less complex.)
|
||||
$base_table_prefix = substr($view->base_table, 0, 17);
|
||||
if (in_array($base_table_prefix, array('search_api_index_', 'search_api_multi'))
|
||||
&& _search_api_preserve_views_facets($view)) {
|
||||
// Get query parameters.
|
||||
$query_parameters = drupal_get_query_parameters();
|
||||
|
||||
// Check if any facet query parameters are provided.
|
||||
if (!empty($query_parameters['f'])) {
|
||||
// Iterate through facet query parameters.
|
||||
foreach ($query_parameters['f'] as $key => $value) {
|
||||
// Add hidden form field for facet parameter.
|
||||
$form['f[' . $key . ']'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#value' => $value,
|
||||
'#weight' => -1,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether "Preserve facets" option is enabled on the given view.
|
||||
*
|
||||
* If the view display is overridden, use its configuration. Otherwise, use the
|
||||
* default configuration.
|
||||
*
|
||||
* @param view $view
|
||||
* The search view.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if "Preserve facets" is enabled, FALSE otherwise.
|
||||
*/
|
||||
function _search_api_preserve_views_facets(view $view) {
|
||||
$query_options = $view->display_handler->get_option('query');
|
||||
return !empty($query_options['options']['preserve_facet_query_args']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the granularity of a date facet filter.
|
||||
*
|
||||
|
@ -137,6 +137,12 @@ Fulltext search" filter or contextual filter – using a normal filter on a
|
||||
fulltext field won't parse the search keys, which means multiple words will only
|
||||
be found when they appear as that exact phrase.
|
||||
|
||||
- Preserve facets while using filters
|
||||
This is another option under "Advanced" > "Query settings", only available when
|
||||
the Search Facets module is installed. When enabled, facet filters are persisted
|
||||
when submitting an exposed filters form. When disabled (the default), exposed
|
||||
filters will override and reset the selected facet filters.
|
||||
|
||||
FAQ: Why „*Indexed* Node“?
|
||||
--------------------------
|
||||
The group name used for the search result itself (in fields, filters, etc.) is
|
||||
|
@ -104,6 +104,10 @@ class SearchApiViewsHandlerFilterDate extends SearchApiViewsHandlerFilterNumeric
|
||||
// If we are using the date popup widget, overwrite the settings of the form
|
||||
// according to what date_popup expects.
|
||||
elseif ($is_date_popup) {
|
||||
// Add an "id" for the "value" field, since it is expected in
|
||||
// date_views_form_views_exposed_form_alter().
|
||||
// @see date_views_filter_handler_simple::value_form()
|
||||
$form['value']['#id'] = 'date_views_exposed_filter-' . bin2hex(drupal_random_bytes(16));
|
||||
$form['value']['#type'] = 'date_popup';
|
||||
$form['value']['#date_format'] = $this->options['date_popup_format'];
|
||||
$form['value']['#date_year_range'] = $this->options['year_range'];
|
||||
|
@ -100,6 +100,13 @@ class SearchApiViewsHandlerFilterFulltext extends SearchApiViewsHandlerFilterTex
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't validate on form reset.
|
||||
if (!empty($form_state['triggering_element'])
|
||||
&& !empty($form['reset'])
|
||||
&& $form_state['triggering_element']['#value'] === $form['reset']['#value']) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We only need to validate if there is a minimum word length set.
|
||||
if ($this->options['min_length'] < 2) {
|
||||
return;
|
||||
|
@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiViewsHandlerFilterNumeric.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views filter handler class for handling numeric and "string" fields.
|
||||
*/
|
||||
class SearchApiViewsHandlerFilterNumeric extends SearchApiViewsHandlerFilter {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init(&$view, &$options) {
|
||||
parent::init($view, $options);
|
||||
|
||||
$this->normalizeValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function option_definition() {
|
||||
$options = parent::option_definition();
|
||||
$options['value'] = array(
|
||||
'contains' => array(
|
||||
'value' => array('default' => ''),
|
||||
'min' => array('default' => ''),
|
||||
'max' => array('default' => ''),
|
||||
),
|
||||
);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function operator_options() {
|
||||
$operators = parent::operator_options();
|
||||
|
||||
$index = search_api_index_load(substr($this->table, 17));
|
||||
$server = NULL;
|
||||
try {
|
||||
if ($index) {
|
||||
$server = $index->server();
|
||||
}
|
||||
}
|
||||
catch (SearchApiException $e) {
|
||||
// Ignore.
|
||||
}
|
||||
if ($server && $server->supportsFeature('search_api_between')) {
|
||||
$operators += array(
|
||||
'between' => t('Is between'),
|
||||
'not between' => t('Is not between'),
|
||||
);
|
||||
}
|
||||
|
||||
return $operators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a form for setting the filter value.
|
||||
*
|
||||
* Heavily borrowed from views_handler_filter_numeric.
|
||||
*
|
||||
* @see views_handler_filter_numeric::value_form()
|
||||
*/
|
||||
public function value_form(&$form, &$form_state) {
|
||||
$form['value']['#tree'] = TRUE;
|
||||
|
||||
$single_field_operators = $this->operator_options();
|
||||
unset(
|
||||
$single_field_operators['empty'],
|
||||
$single_field_operators['not empty'],
|
||||
$single_field_operators['between'],
|
||||
$single_field_operators['not between']
|
||||
);
|
||||
$between_operators = array('between', 'not between');
|
||||
|
||||
// We have to make some choices when creating this as an exposed
|
||||
// filter form. For example, if the operator is locked and thus
|
||||
// not rendered, we can't render dependencies; instead we only
|
||||
// render the form items we need.
|
||||
$which = 'all';
|
||||
$source = NULL;
|
||||
if (!empty($form['operator'])) {
|
||||
$source = ($form['operator']['#type'] == 'radios') ? 'radio:options[operator]' : 'edit-options-operator';
|
||||
}
|
||||
|
||||
$identifier = NULL;
|
||||
if (!empty($form_state['exposed'])) {
|
||||
$identifier = $this->options['expose']['identifier'];
|
||||
if (empty($this->options['expose']['use_operator']) || empty($this->options['expose']['operator_id'])) {
|
||||
// Exposed and locked.
|
||||
$which = in_array($this->operator, $between_operators) ? 'minmax' : 'value';
|
||||
}
|
||||
else {
|
||||
$source = 'edit-' . drupal_html_id($this->options['expose']['operator_id']);
|
||||
}
|
||||
}
|
||||
|
||||
// Hide the value box if the operator is 'empty' or 'not empty'.
|
||||
// Radios share the same selector so we have to add some dummy selector.
|
||||
if ($which == 'all') {
|
||||
$form['value']['value'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => empty($form_state['exposed']) ? t('Value') : '',
|
||||
'#size' => 30,
|
||||
'#default_value' => $this->value['value'],
|
||||
'#dependency' => array($source => array_keys($single_field_operators)),
|
||||
);
|
||||
if ($identifier && !isset($form_state['input'][$identifier]['value'])) {
|
||||
$form_state['input'][$identifier]['value'] = $this->value['value'];
|
||||
}
|
||||
}
|
||||
elseif ($which == 'value') {
|
||||
// When exposed we drop the value-value and just do value if
|
||||
// the operator is locked.
|
||||
$form['value'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => empty($form_state['exposed']) ? t('Value') : '',
|
||||
'#size' => 30,
|
||||
'#default_value' => isset($this->value['value']) ? $this->value['value'] : '',
|
||||
);
|
||||
if ($identifier && !isset($form_state['input'][$identifier])) {
|
||||
$form_state['input'][$identifier] = isset($this->value['value']) ? $this->value['value'] : '';
|
||||
}
|
||||
}
|
||||
|
||||
if ($which == 'all' || $which == 'minmax') {
|
||||
$form['value']['min'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => empty($form_state['exposed']) ? t('Min') : '',
|
||||
'#size' => 30,
|
||||
'#default_value' => $this->value['min'],
|
||||
);
|
||||
$form['value']['max'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => empty($form_state['exposed']) ? t('And max') : t('And'),
|
||||
'#size' => 30,
|
||||
'#default_value' => $this->value['max'],
|
||||
);
|
||||
|
||||
if ($which == 'all') {
|
||||
$form['value']['min']['#dependency'] = array($source => $between_operators);
|
||||
$form['value']['max']['#dependency'] = array($source => $between_operators);
|
||||
}
|
||||
|
||||
if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier]['min'])) {
|
||||
$form_state['input'][$identifier]['min'] = $this->value['min'];
|
||||
}
|
||||
if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier]['max'])) {
|
||||
$form_state['input'][$identifier]['max'] = $this->value['max'];
|
||||
}
|
||||
|
||||
if (!isset($form['value']['value'])) {
|
||||
// Ensure there is something in the 'value'.
|
||||
$form['value']['value'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => NULL,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function admin_summary() {
|
||||
if (!empty($this->options['exposed'])) {
|
||||
return t('exposed');
|
||||
}
|
||||
|
||||
if ($this->operator === 'empty') {
|
||||
return t('is empty');
|
||||
}
|
||||
if ($this->operator === 'not empty') {
|
||||
return t('is not empty');
|
||||
}
|
||||
|
||||
if (in_array($this->operator, array('between', 'not between'), TRUE)) {
|
||||
// This is of course wrong for translation purposes, but copied from
|
||||
// views_handler_filter_numeric::admin_summary() so probably still better
|
||||
// to re-use this than to do it correctly.
|
||||
$operator = $this->operator === 'between' ? t('between') : t('not between');
|
||||
$vars = array(
|
||||
'@min' => (string) $this->value['min'],
|
||||
'@max' => (string) $this->value['max'],
|
||||
);
|
||||
return $operator . ' ' . t('@min and @max', $vars);
|
||||
}
|
||||
|
||||
return check_plain((string) $this->operator) . ' ' . check_plain((string) $this->value['value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$this->normalizeValue();
|
||||
|
||||
if (in_array($this->operator, array('between', 'not between'), TRUE)) {
|
||||
$min = $this->value['min'];
|
||||
$max = $this->value['max'];
|
||||
if ($min !== '' && $max !== '') {
|
||||
$this->query->condition($this->real_field, array($min, $max), strtoupper($this->operator), $this->options['group']);
|
||||
}
|
||||
elseif ($min !== '') {
|
||||
$operator = $this->operator === 'between' ? '>=' : '<';
|
||||
$this->query->condition($this->real_field, $min, $operator, $this->options['group']);
|
||||
}
|
||||
elseif ($max !== '') {
|
||||
$operator = $this->operator === 'between' ? '<=' : '>';
|
||||
$this->query->condition($this->real_field, $max, $operator, $this->options['group']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The parent handler doesn't expect our value structure, just pass the
|
||||
// scalar value instead.
|
||||
$this->value = $this->value['value'];
|
||||
parent::query();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets $this->value to an array of options as defined by the filter.
|
||||
*
|
||||
* @see SearchApiViewsHandlerFilterNumeric::option_definition()
|
||||
*/
|
||||
protected function normalizeValue() {
|
||||
$value = $this->value;
|
||||
if (is_array($value) && isset($value[0])) {
|
||||
$value = $value[0];
|
||||
}
|
||||
if (!is_array($value)) {
|
||||
$value = array('value' => $value);
|
||||
}
|
||||
$this->value = array(
|
||||
'value' => isset($value['value']) ? $value['value'] : '',
|
||||
'min' => isset($value['min']) ? $value['min'] : '',
|
||||
'max' => isset($value['max']) ? $value['max'] : '',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the SearchApiViewsContentCache class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Plugin class for caching Search API views, with additional invalidation.
|
||||
*/
|
||||
class SearchApiViewsContentCache extends views_content_cache_plugin_cache {
|
||||
|
||||
/**
|
||||
* Static cache for get_results_key().
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_results_key = NULL;
|
||||
|
||||
/**
|
||||
* Static cache for getSearchApiQuery().
|
||||
*
|
||||
* @var SearchApiQueryInterface
|
||||
*/
|
||||
protected $search_api_query = NULL;
|
||||
|
||||
/**
|
||||
* Overrides views_plugin_cache::cache_set().
|
||||
*
|
||||
* Also stores Search API's internal search results.
|
||||
*/
|
||||
public function cache_set($type) {
|
||||
if ($type != 'results') {
|
||||
return parent::cache_set($type);
|
||||
}
|
||||
|
||||
$cid = $this->get_results_key();
|
||||
$results = NULL;
|
||||
$query_plugin = $this->view->query;
|
||||
if ($query_plugin instanceof SearchApiViewsQuery) {
|
||||
$results = $query_plugin->getSearchApiResults();
|
||||
}
|
||||
$data = array(
|
||||
'result' => $this->view->result,
|
||||
'total_rows' => isset($this->view->total_rows) ? $this->view->total_rows : 0,
|
||||
'current_page' => $this->view->get_current_page(),
|
||||
'search_api results' => $results,
|
||||
);
|
||||
cache_set($cid, $data, $this->table, $this->cache_set_expire($type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides views_plugin_cache::cache_get().
|
||||
*
|
||||
* Additionally stores successfully retrieved results with
|
||||
* search_api_current_search().
|
||||
*/
|
||||
public function cache_get($type) {
|
||||
if ($type != 'results') {
|
||||
return parent::cache_get($type);
|
||||
}
|
||||
|
||||
// Values to set: $view->result, $view->total_rows, $view->execute_time,
|
||||
// $view->current_page.
|
||||
if ($cache = cache_get($this->get_results_key(), $this->table)) {
|
||||
$cutoff = $this->cache_expire($type);
|
||||
if (!$cutoff || $cache->created > $cutoff) {
|
||||
$this->view->result = $cache->data['result'];
|
||||
$this->view->total_rows = $cache->data['total_rows'];
|
||||
$this->view->set_current_page($cache->data['current_page']);
|
||||
$this->view->execute_time = 0;
|
||||
|
||||
// Trick Search API into believing a search happened, to make facetting
|
||||
// et al. work.
|
||||
$query = $this->getSearchApiQuery();
|
||||
search_api_current_search($query->getOption('search id'), $query, $cache->data['search_api results']);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides views_plugin_cache::get_cache_key().
|
||||
*
|
||||
* Use the Search API query as the main source for the key. Note that in
|
||||
* Views < 3.8, this method does not exist.
|
||||
*/
|
||||
public function get_cache_key($key_data = array()) {
|
||||
global $user;
|
||||
|
||||
if (!isset($this->_results_key)) {
|
||||
$query = $this->getSearchApiQuery();
|
||||
$query->preExecute();
|
||||
$key_data += array(
|
||||
'query' => $query,
|
||||
'roles' => array_keys($user->roles),
|
||||
'super-user' => $user->uid == 1, // special caching for super user.
|
||||
'language' => $GLOBALS['language']->language,
|
||||
'base_url' => $GLOBALS['base_url'],
|
||||
'offset' => $this->view->get_current_page() . '*' . $this->view->get_items_per_page() . '+' . $this->view->get_offset(),
|
||||
);
|
||||
// Not sure what gets passed in exposed_info, so better include it. All
|
||||
// other parameters used in the parent method are already reflected in the
|
||||
// Search API query object we use.
|
||||
if (isset($_GET['exposed_info'])) {
|
||||
$key_data['exposed_info'] = $_GET['exposed_info'];
|
||||
}
|
||||
}
|
||||
$key = drupal_hash_base64(serialize($key_data));
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides views_plugin_cache::get_results_key().
|
||||
*
|
||||
* This is unnecessary for Views >= 3.8.
|
||||
*/
|
||||
public function get_results_key() {
|
||||
if (!isset($this->_results_key)) {
|
||||
$this->_results_key = $this->view->name . ':' . $this->display->id . ':results:' . $this->get_cache_key();
|
||||
}
|
||||
|
||||
return $this->_results_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Search API query object associated with the current view.
|
||||
*
|
||||
* @return SearchApiQueryInterface|null
|
||||
* The Search API query object associated with the current view; or NULL if
|
||||
* there is none.
|
||||
*/
|
||||
protected function getSearchApiQuery() {
|
||||
if (!isset($this->search_api_query)) {
|
||||
$this->search_api_query = FALSE;
|
||||
if (isset($this->view->query) && $this->view->query instanceof SearchApiViewsQuery) {
|
||||
$this->search_api_query = $this->view->query->getSearchApiQuery();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->search_api_query ? $this->search_api_query : NULL;
|
||||
}
|
||||
|
||||
}
|
@ -197,6 +197,10 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
'parse_mode' => array(
|
||||
'default' => 'terms',
|
||||
),
|
||||
'preserve_facet_query_args' => array(
|
||||
'default' => FALSE,
|
||||
'bool' => TRUE,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -243,6 +247,14 @@ class SearchApiViewsQuery extends views_plugin_query {
|
||||
);
|
||||
}
|
||||
}
|
||||
if (module_exists('facetapi')) {
|
||||
$form['preserve_facet_query_args'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Preserve facets while using filters'),
|
||||
'#description' => t('By default, changing an exposed filter would reset all selected facets. This option allows you to prevent this behavior.'),
|
||||
'#default_value' => $this->options['preserve_facet_query_args'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,8 +29,8 @@ files[] = includes/plugin_cache.inc
|
||||
files[] = includes/plugin_content_cache.inc
|
||||
files[] = includes/query.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2019-03-11
|
||||
version = "7.x-1.26"
|
||||
; Information added by Drupal.org packaging script on 2021-02-01
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1552334832"
|
||||
datestamp = "1612192165"
|
||||
|
@ -220,7 +220,7 @@ function _search_api_views_add_handlers($id, array $field, EntityMetadataWrapper
|
||||
$table[$id]['filter']['vocabulary'] = $vocabulary;
|
||||
}
|
||||
}
|
||||
elseif (in_array($inner_type, array('integer', 'decimal', 'duration', 'string'))) {
|
||||
elseif (in_array($inner_type, array('integer', 'decimal', 'duration', 'string', 'uri'))) {
|
||||
$table[$id]['filter']['handler'] = 'SearchApiViewsHandlerFilterNumeric';
|
||||
}
|
||||
else {
|
||||
|
@ -27,7 +27,12 @@ class SearchApiAlterAddViewedEntity extends SearchApiAbstractAlterCallback {
|
||||
$view_modes[$key] = $mode['label'];
|
||||
}
|
||||
}
|
||||
$this->options += array('mode' => reset($view_modes));
|
||||
$this->options += array(
|
||||
'mode' => reset($view_modes),
|
||||
// Backward compatible definition - if this is an existing config the
|
||||
// language processing is disabled by default.
|
||||
'global_language_switch' => !isset($this->options['mode']),
|
||||
);
|
||||
if (count($view_modes) > 1) {
|
||||
$form['mode'] = array(
|
||||
'#type' => 'select',
|
||||
@ -55,6 +60,12 @@ class SearchApiAlterAddViewedEntity extends SearchApiAbstractAlterCallback {
|
||||
);
|
||||
}
|
||||
}
|
||||
$form['global_language_switch'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Adjust environment language when indexing'),
|
||||
'#description' => t('If enabled, the indexing process will not just set the language for the entity view but also the global environment. This can prevent wrong translations leaking into the indexed data on multi-lingual sites, but causes problems in rare cases. Unless you notice any problems in connection with this, the recommended setting is enabled.'),
|
||||
'#default_value' => !empty($this->options['global_language_switch']),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
@ -62,6 +73,14 @@ class SearchApiAlterAddViewedEntity extends SearchApiAbstractAlterCallback {
|
||||
// Prevent session information from being saved while indexing.
|
||||
drupal_save_session(FALSE);
|
||||
|
||||
// Language handling.
|
||||
$languages = language_list();
|
||||
$global_language = array(
|
||||
'language' => $GLOBALS['language'],
|
||||
'language_url' => $GLOBALS['language_url'],
|
||||
'language_content' => $GLOBALS['language_content'],
|
||||
);
|
||||
|
||||
// Force the current user to anonymous to prevent access bypass in search
|
||||
// indexes.
|
||||
$original_user = $GLOBALS['user'];
|
||||
@ -74,6 +93,23 @@ class SearchApiAlterAddViewedEntity extends SearchApiAbstractAlterCallback {
|
||||
// we use try/catch. This will at least prevent some errors, even though
|
||||
// it's no protection against fatal errors and the like.
|
||||
try {
|
||||
// Check if the global language switch is enabled.
|
||||
if (!empty($this->options['global_language_switch'])) {
|
||||
// Language handling. We need to overwrite the global language
|
||||
// configuration because parts of entity rendering won't rely on the
|
||||
// passed in language (for instance, URL aliases).
|
||||
if (isset($languages[$item->search_api_language])) {
|
||||
$GLOBALS['language'] = $languages[$item->search_api_language];
|
||||
$GLOBALS['language_url'] = $languages[$item->search_api_language];
|
||||
$GLOBALS['language_content'] = $languages[$item->search_api_language];
|
||||
}
|
||||
else {
|
||||
$GLOBALS['language'] = $global_language['language'];
|
||||
$GLOBALS['language_url'] = $global_language['language_url'];
|
||||
$GLOBALS['language_content'] = $global_language['language_content'];
|
||||
}
|
||||
}
|
||||
|
||||
$render = entity_view($type, array(entity_id($type, $item) => $item), $mode, $item->search_api_language);
|
||||
$text = render($render);
|
||||
if (!$text) {
|
||||
@ -87,6 +123,13 @@ class SearchApiAlterAddViewedEntity extends SearchApiAbstractAlterCallback {
|
||||
}
|
||||
}
|
||||
|
||||
// Restore global language settings.
|
||||
if (!empty($this->options['global_language_switch'])) {
|
||||
$GLOBALS['language'] = $global_language['language'];
|
||||
$GLOBALS['language_url'] = $global_language['language_url'];
|
||||
$GLOBALS['language_content'] = $global_language['language_content'];
|
||||
}
|
||||
|
||||
// Restore the user.
|
||||
$GLOBALS['user'] = $original_user;
|
||||
drupal_save_session(TRUE);
|
||||
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains SearchApiAlterFileEntityPublic.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Excludes file entities in the private folder from being indexed.
|
||||
*/
|
||||
class SearchApiAlterFileEntityPublic extends SearchApiAbstractAlterCallback {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsIndex(SearchApiIndex $index) {
|
||||
if ($this->isMultiEntityIndex($index)) {
|
||||
return in_array('file', $index->options['datasource']['types']);
|
||||
}
|
||||
return $index->getEntityType() === 'file';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alterItems(array &$items) {
|
||||
$multi_types = $this->isMultiEntityIndex($this->index);
|
||||
foreach ($items as $id => $item) {
|
||||
$file = $item;
|
||||
if ($multi_types) {
|
||||
if ($item->item_type !== 'file') {
|
||||
continue;
|
||||
}
|
||||
$file = $item->file;
|
||||
}
|
||||
if (empty($file->uri) || substr($file->uri, 0, 10) === 'private://') {
|
||||
unset($items[$id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -22,6 +22,9 @@ class SearchApiAlterNodeStatus extends SearchApiAbstractAlterCallback {
|
||||
* TRUE if the callback can run on the given index; FALSE otherwise.
|
||||
*/
|
||||
public function supportsIndex(SearchApiIndex $index) {
|
||||
if ($this->isMultiEntityIndex($index)) {
|
||||
return in_array('node', $index->options['datasource']['types']);
|
||||
}
|
||||
return $index->getEntityType() === 'node';
|
||||
}
|
||||
|
||||
@ -35,9 +38,17 @@ class SearchApiAlterNodeStatus extends SearchApiAbstractAlterCallback {
|
||||
* An array of items to be altered, keyed by item IDs.
|
||||
*/
|
||||
public function alterItems(array &$items) {
|
||||
foreach ($items as $nid => &$item) {
|
||||
if (empty($item->status)) {
|
||||
unset($items[$nid]);
|
||||
$multi_types = $this->isMultiEntityIndex($this->index);
|
||||
foreach ($items as $id => $item) {
|
||||
$node = $item;
|
||||
if ($multi_types) {
|
||||
if ($item->item_type !== 'node') {
|
||||
continue;
|
||||
}
|
||||
$node = $item->node;
|
||||
}
|
||||
if (empty($node->status)) {
|
||||
unset($items[$id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,9 @@ class SearchApiAlterUserStatus extends SearchApiAbstractAlterCallback {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsIndex(SearchApiIndex $index) {
|
||||
if ($this->isMultiEntityIndex($index)) {
|
||||
return in_array('user', $index->options['datasource']['types']);
|
||||
}
|
||||
return $index->getEntityType() == 'user';
|
||||
}
|
||||
|
||||
@ -21,7 +24,15 @@ class SearchApiAlterUserStatus extends SearchApiAbstractAlterCallback {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alterItems(array &$items) {
|
||||
foreach ($items as $id => $account) {
|
||||
$multi_types = $this->isMultiEntityIndex($this->index);
|
||||
foreach ($items as $id => $item) {
|
||||
$account = $item;
|
||||
if ($multi_types) {
|
||||
if ($item->item_type !== 'user') {
|
||||
continue;
|
||||
}
|
||||
$account = $item->user;
|
||||
}
|
||||
if (empty($account->status)) {
|
||||
unset($items[$id]);
|
||||
}
|
||||
|
@ -605,8 +605,22 @@ abstract class SearchApiAbstractDataSourceController implements SearchApiDataSou
|
||||
foreach (array_chunk($item_ids, 1000) as $chunk) {
|
||||
$insert = db_insert($this->table)
|
||||
->fields(array($this->itemIdColumn, $this->indexIdColumn, $this->changedColumn));
|
||||
foreach ($chunk as $item_id) {
|
||||
foreach ($indexes as $index) {
|
||||
|
||||
foreach ($indexes as $index) {
|
||||
// We have to make sure we don't try to insert duplicate items.
|
||||
$select = db_select($this->table, 't');
|
||||
$select->addField('t', $this->itemIdColumn);
|
||||
$select->condition($this->indexIdColumn, $index->id);
|
||||
$select->condition($this->itemIdColumn, $chunk, 'IN');
|
||||
$existing = $select
|
||||
->execute()
|
||||
->fetchCol();
|
||||
$existing = array_flip($existing);
|
||||
|
||||
foreach ($chunk as $item_id) {
|
||||
if (isset($existing[$item_id])) {
|
||||
continue;
|
||||
}
|
||||
$insert->values(array(
|
||||
$this->itemIdColumn => $item_id,
|
||||
$this->indexIdColumn => $index->id,
|
||||
|
@ -270,7 +270,7 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
|
||||
$keywords = drupal_map_assoc(array_filter($keywords));
|
||||
// Remove quotes from keywords.
|
||||
foreach ($keywords as $key) {
|
||||
$keywords[$key] = trim($key, "'\"");
|
||||
$keywords[$key] = trim($key, "'\" ");
|
||||
}
|
||||
return drupal_map_assoc(array_filter($keywords));
|
||||
}
|
||||
@ -298,7 +298,7 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
|
||||
$keywords += $this->flattenKeysArray($key);
|
||||
}
|
||||
else {
|
||||
$keywords[$key] = $key;
|
||||
$keywords[$key] = trim($key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,7 +360,7 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
|
||||
// and ends with a space.
|
||||
$matches = array();
|
||||
|
||||
if (!$this->options['highlight_partial']) {
|
||||
if (empty($this->options['highlight_partial'])) {
|
||||
$found_position = FALSE;
|
||||
$regex = '/' . static::$boundary . preg_quote($key, '/') . static::$boundary . '/iu';
|
||||
if (preg_match($regex, ' ' . $text . ' ', $matches, PREG_OFFSET_CAPTURE, $look_start[$key])) {
|
||||
|
@ -101,9 +101,7 @@ class SearchApiHtmlFilter extends SearchApiAbstractProcessor {
|
||||
$value = $this->parseText($text);
|
||||
}
|
||||
else {
|
||||
$value = html_entity_decode(strip_tags($text));
|
||||
// Remove any multiple or leading/trailing spaces we might have introduced.
|
||||
$value = preg_replace('/\s\s+/', ' ', trim($value));
|
||||
$value = $this->decodeHtml(strip_tags($text));
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,11 +109,9 @@ class SearchApiHtmlFilter extends SearchApiAbstractProcessor {
|
||||
$ret = array();
|
||||
while (($pos = strpos($text, '<')) !== FALSE) {
|
||||
if ($boost && $pos > 0) {
|
||||
$token = html_entity_decode(substr($text, 0, $pos), ENT_QUOTES, 'UTF-8');
|
||||
// Remove any multiple or leading/trailing spaces we might have introduced.
|
||||
$token = preg_replace('/\s\s+/', ' ', trim($token));
|
||||
$token = substr($text, 0, $pos);
|
||||
$ret[] = array(
|
||||
'value' => $token,
|
||||
'value' => $this->decodeHtml($token),
|
||||
'score' => $boost,
|
||||
);
|
||||
}
|
||||
@ -137,11 +133,8 @@ class SearchApiHtmlFilter extends SearchApiAbstractProcessor {
|
||||
}
|
||||
}
|
||||
if ($text) {
|
||||
$token = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
|
||||
// Remove any multiple or leading/trailing spaces we might have introduced.
|
||||
$token = preg_replace('/\s\s+/', ' ', trim($token));
|
||||
$ret[] = array(
|
||||
'value' => $token,
|
||||
'value' => $this->decodeHtml($text),
|
||||
'score' => $boost,
|
||||
);
|
||||
$text = '';
|
||||
@ -149,4 +142,23 @@ class SearchApiHtmlFilter extends SearchApiAbstractProcessor {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes HTML entities in a token and normalizes whitespace.
|
||||
*
|
||||
* All whitespace in the token will be converted to single spaces, with no
|
||||
* leading or trailing whitespace.
|
||||
*
|
||||
* @param string $token
|
||||
* The token to process.
|
||||
*
|
||||
* @return string
|
||||
* The processed token.
|
||||
*/
|
||||
protected function decodeHtml($token) {
|
||||
$token = html_entity_decode($token, ENT_QUOTES, 'UTF-8');
|
||||
// Remove any multiple/leading/trailing spaces we might have introduced.
|
||||
$token = trim(preg_replace('/[\pZ\pC]+/u', ' ', $token));
|
||||
return $token;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,17 +13,8 @@ class SearchApiIgnoreCase extends SearchApiAbstractProcessor {
|
||||
protected function process(&$value) {
|
||||
// We don't touch integers, NULL values or the like.
|
||||
if (is_string($value)) {
|
||||
|
||||
// 1ka substring
|
||||
if (strlen ($value) > 5){
|
||||
$value = mb_substr($value, 0, -2, 'UTF-8');
|
||||
}elseif (strlen ($value) > 2) {
|
||||
$value = mb_substr($value, 0, -1, 'UTF-8');
|
||||
}
|
||||
|
||||
$value = drupal_strtolower($value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ files[] = includes/callback_add_url.inc
|
||||
files[] = includes/callback_add_viewed_entity.inc
|
||||
files[] = includes/callback_bundle_filter.inc
|
||||
files[] = includes/callback_comment_access.inc
|
||||
files[] = includes/callback_file_entity_public.inc
|
||||
files[] = includes/callback_language_control.inc
|
||||
files[] = includes/callback_node_access.inc
|
||||
files[] = includes/callback_node_status.inc
|
||||
@ -38,8 +39,8 @@ files[] = includes/service.inc
|
||||
|
||||
configure = admin/config/search/search_api
|
||||
|
||||
; Information added by Drupal.org packaging script on 2019-03-11
|
||||
version = "7.x-1.26"
|
||||
; Information added by Drupal.org packaging script on 2021-02-01
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1552334832"
|
||||
datestamp = "1612192165"
|
||||
|
@ -947,7 +947,12 @@ function search_api_entity_delete($entity, $type) {
|
||||
* "Comment access" data alteration.
|
||||
*/
|
||||
function search_api_node_access_records_alter(&$grants, $node) {
|
||||
foreach (search_api_index_load_multiple(FALSE) as $index) {
|
||||
$conditions = array(
|
||||
'enabled' => 1,
|
||||
'read_only' => 0,
|
||||
);
|
||||
$indexes = search_api_index_load_multiple(FALSE, $conditions);
|
||||
foreach ($indexes as $index) {
|
||||
$item_ids = array();
|
||||
if (!empty($index->options['data_alter_callbacks']['search_api_alter_node_access']['status'])) {
|
||||
$item_id = $index->datasource()->getItemId($node);
|
||||
@ -965,8 +970,11 @@ function search_api_node_access_records_alter(&$grants, $node) {
|
||||
}
|
||||
|
||||
if ($item_ids) {
|
||||
$indexes = array($index->machine_name => $index);
|
||||
search_api_track_item_change_for_indexes($index->item_type, $item_ids, $indexes);
|
||||
search_api_track_item_change_for_indexes(
|
||||
$index->item_type,
|
||||
$item_ids,
|
||||
array($index->machine_name => $index)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1121,6 +1129,11 @@ function search_api_search_api_alter_callback_info() {
|
||||
'description' => t('Allows to index hierarchical fields along with all their ancestors.'),
|
||||
'class' => 'SearchApiAlterAddHierarchy',
|
||||
);
|
||||
$callbacks['search_api_alter_file_entity_public'] = array(
|
||||
'name' => t('Exclude private files'),
|
||||
'description' => t('Exclude file entities in the private files folder from being indexed. <strong>Caution:</strong> This only affects the indexed file entities themselves. If an indexed entity has references to file entities in the private folder, those will still be indexed (or displayed) normally.'),
|
||||
'class' => 'SearchApiAlterFileEntityPublic',
|
||||
);
|
||||
$callbacks['search_api_alter_language_control'] = array(
|
||||
'name' => t('Language control'),
|
||||
'description' => t('Lets you determine the language of items in the index.'),
|
||||
@ -3188,7 +3201,9 @@ function _search_api_index_queued_items() {
|
||||
if ($queue) {
|
||||
$indexes = search_api_index_load_multiple(array_keys($queue));
|
||||
foreach ($indexes as $index_id => $index) {
|
||||
search_api_index_specific_items($index, $queue[$index_id]);
|
||||
if ($index->enabled && !$index->read_only) {
|
||||
search_api_index_specific_items($index, $queue[$index_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,8 @@ files[] = search_api_test.module
|
||||
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2019-03-11
|
||||
version = "7.x-1.26"
|
||||
; Information added by Drupal.org packaging script on 2021-02-01
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1552334832"
|
||||
datestamp = "1612192165"
|
||||
|
@ -9,8 +9,8 @@ files[] = search_api_test_service_2.module
|
||||
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2019-03-11
|
||||
version = "7.x-1.26"
|
||||
; Information added by Drupal.org packaging script on 2021-02-01
|
||||
version = "7.x-1.27"
|
||||
core = "7.x"
|
||||
project = "search_api"
|
||||
datestamp = "1552334832"
|
||||
datestamp = "1612192165"
|
||||
|
@ -1,3 +1,12 @@
|
||||
Search API Database Search 1.8 (2021-01-11):
|
||||
--------------------------------------------
|
||||
- #3191489 by drunken monkey, borisson_, Amir Simantov: Fixed searches with
|
||||
special characters in keywords.
|
||||
- #3142667 by drunken monkey, donquixote: Fixed reused variable name in
|
||||
SearchApiDbService::indexItem().
|
||||
- #3119370 by drunken monkey: Fixed errors during indexing cause by
|
||||
non-standard whitespace.
|
||||
|
||||
Search API Database Search 1.7 (2018-09-17):
|
||||
--------------------------------------------
|
||||
- #2982443 by KarlShea, drunken monkey: Added support for the "(not) between"
|
||||
|
@ -6,8 +6,8 @@ package = Search
|
||||
|
||||
files[] = search_api_db.test
|
||||
files[] = service.inc
|
||||
; Information added by Drupal.org packaging script on 2018-09-17
|
||||
version = "7.x-1.7"
|
||||
; Information added by Drupal.org packaging script on 2021-01-11
|
||||
version = "7.x-1.8"
|
||||
core = "7.x"
|
||||
project = "search_api_db"
|
||||
datestamp = "1537173484"
|
||||
datestamp = "1610364554"
|
||||
|
@ -891,21 +891,23 @@ class SearchApiDbService extends SearchApiAbstractService {
|
||||
// at 500 words and 0.3 at 1000 words.
|
||||
$focus = min(1, .01 + 3.5 / (2 + count($words) * .015));
|
||||
|
||||
$value = &$token['value'];
|
||||
if (is_numeric($value)) {
|
||||
$value = ltrim($value, '-0');
|
||||
$token_value = &$token['value'];
|
||||
$token_value = trim(preg_replace('/[\pZ\pC]+/u', ' ', $token_value));
|
||||
if (is_numeric($token_value)) {
|
||||
$token_value = ltrim($token_value, '-0');
|
||||
}
|
||||
elseif (drupal_strlen($value) < $this->options['min_chars']) {
|
||||
elseif (drupal_strlen($token_value) < $this->options['min_chars']) {
|
||||
continue;
|
||||
}
|
||||
$value = drupal_strtolower($value);
|
||||
$token_value = drupal_strtolower($token_value);
|
||||
$token['score'] *= $focus;
|
||||
if (!isset($words[$value])) {
|
||||
$words[$value] = $token;
|
||||
if (!isset($words[$token_value])) {
|
||||
$words[$token_value] = $token;
|
||||
}
|
||||
else {
|
||||
$words[$value]['score'] += $token['score'];
|
||||
$words[$token_value]['score'] += $token['score'];
|
||||
}
|
||||
unset($token_value);
|
||||
}
|
||||
if ($words) {
|
||||
$field_name = self::getTextFieldName($name);
|
||||
@ -1263,7 +1265,8 @@ class SearchApiDbService extends SearchApiAbstractService {
|
||||
protected function createDbQuery(SearchApiQueryInterface $query, array $fields) {
|
||||
$keys = &$query->getKeys();
|
||||
$keys_set = (boolean) $keys;
|
||||
$keys = $this->prepareKeys($keys);
|
||||
$tokenizer_active = static::isTokenizerActive($query->getIndex());
|
||||
$keys = $this->prepareKeys($keys, $tokenizer_active);
|
||||
// Special case: if the outermost $keys array has "#negation" set, we can't
|
||||
// handle it like other negated subkeys. To avoid additional complexity
|
||||
// later, we just wrap $keys so it becomes a subkey.
|
||||
@ -1317,7 +1320,7 @@ class SearchApiDbService extends SearchApiAbstractService {
|
||||
|
||||
$filter = $query->getFilter();
|
||||
if ($filter->getFilters()) {
|
||||
$condition = $this->createFilterCondition($filter, $fields, $db_query);
|
||||
$condition = $this->createFilterCondition($filter, $fields, $db_query, $query->getIndex());
|
||||
if ($condition) {
|
||||
$db_query->condition($condition);
|
||||
}
|
||||
@ -1341,24 +1344,28 @@ class SearchApiDbService extends SearchApiAbstractService {
|
||||
*
|
||||
* @param array|string|null $keys
|
||||
* The keys which should be preprocessed.
|
||||
* @param bool $tokenizer_active
|
||||
* (optional) TRUE if we can rely on the "Tokenizer" processor already
|
||||
* having preprocessed the keywords.
|
||||
*
|
||||
* @return array|string|null
|
||||
* The preprocessed keys.
|
||||
*/
|
||||
protected function prepareKeys($keys) {
|
||||
protected function prepareKeys($keys, $tokenizer_active = FALSE) {
|
||||
if (is_scalar($keys)) {
|
||||
$keys = $this->splitKeys($keys);
|
||||
$keys = $this->splitKeys($keys, $tokenizer_active);
|
||||
return is_array($keys) ? $this->eliminateDuplicates($keys) : $keys;
|
||||
}
|
||||
elseif (!$keys) {
|
||||
return NULL;
|
||||
}
|
||||
$keys = $this->eliminateDuplicates($this->splitKeys($keys));
|
||||
$keys = $this->splitKeys($keys, $tokenizer_active);
|
||||
$keys = $this->eliminateDuplicates($keys);
|
||||
$conj = $keys['#conjunction'];
|
||||
$neg = !empty($keys['#negation']);
|
||||
foreach ($keys as $i => &$nested) {
|
||||
if (is_array($nested)) {
|
||||
$nested = $this->prepareKeys($nested);
|
||||
$nested = $this->prepareKeys($nested, $tokenizer_active);
|
||||
if (is_array($nested) && $neg == !empty($nested['#negation'])) {
|
||||
if ($nested['#conjunction'] == $conj) {
|
||||
unset($nested['#conjunction'], $nested['#negation']);
|
||||
@ -1390,11 +1397,14 @@ class SearchApiDbService extends SearchApiAbstractService {
|
||||
*
|
||||
* @param array|string|null $keys
|
||||
* The keys to split.
|
||||
* @param bool $tokenizer_active
|
||||
* (optional) TRUE if we can rely on the "Tokenizer" processor already
|
||||
* having preprocessed the keywords.
|
||||
*
|
||||
* @return array|string|null
|
||||
* The keys split into separate words.
|
||||
*/
|
||||
protected function splitKeys($keys) {
|
||||
protected function splitKeys($keys, $tokenizer_active = FALSE) {
|
||||
if (is_scalar($keys)) {
|
||||
$proc = drupal_strtolower(trim($keys));
|
||||
if (is_numeric($proc)) {
|
||||
@ -1404,9 +1414,16 @@ class SearchApiDbService extends SearchApiAbstractService {
|
||||
$this->ignored[$keys] = 1;
|
||||
return NULL;
|
||||
}
|
||||
$words = preg_split('/[^\p{L}\p{N}]+/u', $proc, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
if ($tokenizer_active) {
|
||||
$words = array_filter(explode(' ', $proc), 'strlen');
|
||||
}
|
||||
else {
|
||||
$words = preg_split('/[^\p{L}\p{N}]+/u', $proc, -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
|
||||
if (count($words) > 1) {
|
||||
$proc = $this->splitKeys($words);
|
||||
$proc = $this->splitKeys($words, $tokenizer_active);
|
||||
if ($proc) {
|
||||
$proc['#conjunction'] = 'AND';
|
||||
}
|
||||
@ -1418,7 +1435,7 @@ class SearchApiDbService extends SearchApiAbstractService {
|
||||
}
|
||||
foreach ($keys as $i => $key) {
|
||||
if (element_child($i)) {
|
||||
$keys[$i] = $this->splitKeys($key);
|
||||
$keys[$i] = $this->splitKeys($key, $tokenizer_active);
|
||||
}
|
||||
}
|
||||
return array_filter($keys);
|
||||
@ -1670,6 +1687,8 @@ class SearchApiDbService extends SearchApiAbstractService {
|
||||
* Internal information about the index's fields.
|
||||
* @param SelectQueryInterface $db_query
|
||||
* The database query to which the condition will be added.
|
||||
* @param SearchApiIndex $index
|
||||
* (optional) The search index whose settings should be used.
|
||||
*
|
||||
* @return DatabaseCondition|null
|
||||
* The condition to set on the query, or NULL if none is necessary.
|
||||
@ -1677,7 +1696,7 @@ class SearchApiDbService extends SearchApiAbstractService {
|
||||
* @throws SearchApiException
|
||||
* If an unknown field was used in the filter.
|
||||
*/
|
||||
protected function createFilterCondition(SearchApiQueryFilterInterface $filter, array $fields, SelectQueryInterface $db_query) {
|
||||
protected function createFilterCondition(SearchApiQueryFilterInterface $filter, array $fields, SelectQueryInterface $db_query, SearchApiIndex $index = NULL) {
|
||||
$cond = db_condition($filter->getConjunction());
|
||||
// Store whether a JOIN already occurred for a field, so we don't JOIN
|
||||
// repeatedly for OR filters.
|
||||
@ -1686,7 +1705,7 @@ class SearchApiDbService extends SearchApiAbstractService {
|
||||
$tables = array();
|
||||
foreach ($filter->getFilters() as $f) {
|
||||
if (is_object($f)) {
|
||||
$c = $this->createFilterCondition($f, $fields, $db_query);
|
||||
$c = $this->createFilterCondition($f, $fields, $db_query, $index);
|
||||
if ($c) {
|
||||
$cond->condition($c);
|
||||
}
|
||||
@ -1712,7 +1731,10 @@ class SearchApiDbService extends SearchApiAbstractService {
|
||||
continue;
|
||||
}
|
||||
if ($text_type) {
|
||||
$keys = $this->prepareKeys($value);
|
||||
if (!isset($tokenizer_active)) {
|
||||
$tokenizer_active = $index && static::isTokenizerActive($index);
|
||||
}
|
||||
$keys = $this->prepareKeys($value, $tokenizer_active);
|
||||
if (!isset($keys)) {
|
||||
continue;
|
||||
}
|
||||
@ -2104,7 +2126,8 @@ class SearchApiDbService extends SearchApiAbstractService {
|
||||
|
||||
// Decide which methods we want to use.
|
||||
if ($incomplete_key && $settings['suggest_suffix']) {
|
||||
$processed_key = $this->splitKeys($incomplete_key);
|
||||
$tokenizer_active = static::isTokenizerActive($index);
|
||||
$processed_key = $this->splitKeys($incomplete_key, $tokenizer_active);
|
||||
if ($processed_key) {
|
||||
// In case the $incomplete_key turned out to be more than one word, add
|
||||
// all but the last one to the user input.
|
||||
@ -2292,4 +2315,18 @@ class SearchApiDbService extends SearchApiAbstractService {
|
||||
return substr($str, $start, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the "Tokenizer" processor is enabled for an index.
|
||||
*
|
||||
* @param SearchApiIndex $index
|
||||
* The index to check.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the built-in "Tokenizer" processor is enabled on the given index,
|
||||
* FALSE otherwise.
|
||||
*/
|
||||
protected static function isTokenizerActive(SearchApiIndex $index) {
|
||||
return !empty($index->options['processors']['search_api_tokenizer']['status']);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user