. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ include_once('mica_field_description.features.inc'); include_once(drupal_get_path('module', 'facetapi') . '/facetapi.block.inc'); include_once(drupal_get_path('module', 'mica_core') . '/mica_core.search.utils.inc'); function mica_field_description_configure_facet_blocks() { $config = array( 'field_bundle' => array( 'name' => t('Content type'), ), 'field_value_type' => array( 'name' => t('Value type'), ), 'field_repeatable' => array( 'name' => t('Repeatable'), ), ); mica_core_configure_facet_blocks('fields_description_index', 'field_description', $config, 'fields-description-search'); } /** * Implements hook_node_presave() */ function mica_field_description_node_presave($node) { if ($node->type === 'field_description') { $wrapper = entity_metadata_wrapper('node', $node); $bundle = $wrapper->field_bundle->value(); $indexes = mica_core_find_indexes_by_bundle(); if (isset($indexes[$bundle]) && !empty($indexes[$bundle])) { foreach ($indexes[$bundle] as $index_machine_name => $index_name) { _mica_field_description_add_field_index($index_machine_name, $node); } } else { $relation = mica_relation_find_relation_by_child($bundle); if (!empty($relation)) { $parent_bundle = $relation->parent_bundle; if (isset($indexes[$parent_bundle]) && !empty($indexes[$parent_bundle])) { foreach ($indexes[$parent_bundle] as $index_machine_name => $index_name) { _mica_field_description_add_field_index($index_machine_name, $node); } } } } } } /** * Implements hook_node_delete() */ function mica_field_description_node_delete($node) { if ($node->type === 'field_description') { $wrapper = entity_metadata_wrapper('node', $node); $bundle = $wrapper->field_bundle->value(); $original_field_name = $wrapper->field_original_field_name->value(); _drop_search_index_field($bundle, $original_field_name); } } /** * Implements hook_field_delete_instance() */ function mica_field_description_field_delete_instance($instance) { $field_description = _find_field_description($instance['bundle'], $instance['field_name']); if (!empty($field_description)) { node_delete($field_description->nid); } } /** * Enhance the field settings form with field_description extensions. * Implements of hook_form_FORM_ID_alter() */ function mica_field_description_form_field_ui_field_edit_form_alter(&$form, $form_state, $form_id) { // check supported entity type $unsupported_entity_types = array( 'field_collection_item', ); $entity_type = $form_state['build_info']['args'][0]['entity_type']; if (in_array($entity_type, $unsupported_entity_types)) { return; } // check supported field type if (isset($form_state['field'])) { $keys = array_keys($form_state['field']); $field_name = $keys[0]; $field_type = $form_state['field'][$field_name][LANGUAGE_NONE]['field']['type']; // auto-detect value type based on field value type $default_value_type = mica_field_description_get_field_value_type($field_type); // check for unsupported types if (empty($default_value_type)) { return; } $bundle = $form_state['build_info']['args'][0]['bundle']; if ($bundle === 'field_description') { return; // cannot create field_description form field_description content type field } $field_name = $form_state['build_info']['args'][0]['field_name']; $field = field_read_field($field_name); if (!$field || $field['locked']) { return; // field is locked, cannot edit it } $field_description = _find_field_description($bundle, $field_name); if (empty($field_description)) { $default_label = NULL; $default_body = NULL; } else { $wrapper = entity_metadata_wrapper('node', $field_description); $default_value_type = $wrapper->field_value_type->value(); $default_label = $wrapper->field_original_field_label->value(); $default_body = $wrapper->body->value(); if (is_array($default_body)) { $default_body = $default_body['value']; } } $form['field']['settings']['field_description_infos'] = array( '#type' => 'fieldset', '#title' => t('Field Description'), '#collapsible' => FALSE, '#collapsed' => FALSE, '#attached' => array( 'js' => array(drupal_get_path('module', 'mica_field_description') . '/mica_field_description-field-form.js'), ), ); $form['field']['settings']['field_description_infos']['field_description'] = array( '#type' => 'checkbox', '#title' => t('Use Field Descriptor for this field'), '#default_value' => !empty($field_description), ); // retrieve available select options from field_description field 'field_value_type' $field_value_type_options = field_info_field('field_value_type'); $form['field']['settings']['field_description_infos']['field_description_value_type'] = array( '#type' => 'select', // enables value type input only if we were not able to detect it // '#disabled' => !empty($default_value_type), '#title' => t('Field value type'), '#required' => FALSE, '#options' => $field_value_type_options['settings']['allowed_values'], '#default_value' => $default_value_type, // Enabled only when the 'field_description' checkbox is checked '#states' => array( 'enabled' => array( 'input[name="field[settings][field_description_infos][field_description]"]' => array('checked' => TRUE) ), ), ); $form['field']['settings']['field_description_infos']['field_description_label'] = array( '#type' => 'textfield', '#title' => t('Label'), '#default_value' => $default_label, '#required' => FALSE, // Enabled only when the 'field_description' checkbox is checked '#states' => array( 'enabled' => array( 'input[name="field[settings][field_description_infos][field_description]"]' => array('checked' => TRUE) ), ), ); $form['field']['settings']['field_description_infos']['field_description_body'] = array( '#type' => 'textarea', '#title' => t('Description'), '#default_value' => $default_body, '#required' => FALSE, // Enabled only when the 'field_description' checkbox is checked '#states' => array( 'enabled' => array( 'input[name="field[settings][field_description_infos][field_description]"]' => array('checked' => TRUE) ), ), ); $form['#submit'][] = '_edit_field_field_description'; } } /** * Add or update field in search index * Add facet for non Full Text field * * @param string $index_name * @param stdClass $field_description */ function _mica_field_description_add_field_index($index_name, stdClass $field_description) { $field_description_wrapper = entity_metadata_wrapper('node', $field_description); $bundle = $field_description_wrapper->field_bundle->value(); $field_name = $field_description_wrapper->field_original_field_name->value(); $type = $field_description_wrapper->field_value_type->value(); $repeatable = $field_description_wrapper->field_repeatable->value(); $field_categories = $field_description_wrapper->field_categories->value(); $discrete = count($field_categories) > 1; if ($type === 'text') { $field_index_type = $discrete ? 'string' : 'text'; } else { $mapping = _magma_search_index_type_mapping(); $field_index_type = $mapping[$type]; } if ($repeatable) { $field_index_type = 'list<' . $field_index_type . '>'; } $field_instance = field_info_instance('node', $field_name, $bundle); $field_label = $field_instance['label']; $index = search_api_index_load($index_name, TRUE); $options = $index->options; $fields = $options['fields']; // check if this bundle is a child of a relation $relation = mica_relation_find_relation_by_child($bundle); if (!empty($relation)) { $field_name = $relation->options['node_reference'] . ':' . $field_name; $bundle_info = node_type_load($bundle); $field_label = $bundle_info->name . ' » ' . $field_label; } // check if we need to reindex content if (!isset($fields[$field_name]) || empty($fields[$field_name])) { $reindex = TRUE; } else { $reindex = !isset($fields[$field_name]['name']) || !isset($fields[$field_name]['indexed']) || !isset($fields[$field_name]['type']) || $fields[$field_name]['name'] != $field_label || !$fields[$field_name]['indexed'] || $fields[$field_name]['type'] != $field_index_type; } if ($field_index_type === 'date' || $field_index_type === 'list') { $field_name .= ':value'; } $fields[$field_name] = array( 'type' => $field_index_type, ); debug($fields, '$fields'); search_api_index_edit_fields($index->id, $fields); if ($reindex) { $index->reindex(); } // Facets & ranges $facet_supported = $discrete && _is_type_supports_facet($field_index_type); $range_supported = !$facet_supported && _is_type_supports_ranges($field_index_type); cache_clear_all(); try { $searcher = 'search_api@' . $index->machine_name; $facet = facetapi_facet_load($field_name, $searcher); if ($facet) { $adapter = facetapi_adapter_load($searcher); $realms = facetapi_get_realm_info(); foreach ($realms as $realm_name => $realm) { $delta = facetapi_hash_delta(facetapi_build_delta($searcher, $realm_name, $facet['name'])); if ($facet_supported || $range_supported) { $settings = $adapter->getFacet($facet)->getSettings($realm); if ($facet_supported) { $settings->settings['widget'] = 'facetapi_checkbox_links'; } if ($range_supported) { $settings->settings['widget'] = 'search_api_ranges_ui_slider'; } ctools_export_crud_save('facetapi', $settings); if (facetapi_save_facet_enabled($adapter, $realm, $facet)) { // overwrite facet label to use field display label if defined $facet_block_title = $field_description_wrapper->field_original_field_label->value(); mica_core_enable_facet_block($delta, $facet_block_title, 'studies-search'); } } elseif (facetapi_save_facet_disabled($adapter, $realm, $facet)) { _disable_search_block($delta); } } } } catch (Exception $e) { watchdog_exception('mica', $e, 'Error while configuring facet %facet for %index', array('%facet' => $field_name, '%index' => $index->machine_name)); throw $e; } } /** * Returns the value type mapping between Magma and Search API with Magma values as keys */ function _magma_search_index_type_mapping() { return array( 'text' => 'text', 'integer' => 'integer', 'decimal' => 'decimal', 'date' => 'date', 'boolean' => 'boolean', ); } function _disable_search_block($facet_delta) { $theme_default = variable_get('theme_default', 'mica_samara'); $nb = db_update('block')->fields( array( 'status' => 0, 'region' => BLOCK_REGION_NONE, ) ) ->condition('module', 'facetapi') ->condition('delta', $facet_delta) ->condition('theme', $theme_default) ->execute(); watchdog('mica', 'Disabled facet block %facet: %status', array('%facet' => $facet_delta, '%status' => ($nb > 0 ? 'Done' : 'Error')), WATCHDOG_DEBUG); } /** * Remove index field if exists for this bundle and field name * @param string $bundle * @param string $field_name */ function _drop_search_index_field($bundle, $field_name) { $indexes = mica_core_find_indexes_by_bundle(); if (isset($indexes[$bundle])) { $bundles_indexes = $indexes[$bundle]; if (!empty($bundles_indexes)) { foreach ($bundles_indexes as $index_machine_name => $index_name) { _remove_field_index($index_machine_name, $field_name); } } } } /** * Disable field in search index */ function _remove_field_index($index_name, $field_name) { $index = search_api_index_load($index_name, TRUE); $options = $index->options; $fields = $options['fields']; if (!empty($fields[$field_name])) { $fields[$field_name]['indexed'] = FALSE; search_api_index_edit_fields($index->id, $fields); $index->reindex(); } $searcher = 'search_api@' . $index->machine_name; $facet = facetapi_facet_load($field_name, $searcher); if ($facet) { $adapter = facetapi_adapter_load($searcher); $realms = facetapi_get_realm_info(); foreach ($realms as $realm_name => $realm) { if (facetapi_save_facet_disabled($adapter, $realm, $facet)) { $delta = facetapi_hash_delta(facetapi_build_delta($searcher, $realm_name, $facet['name'])); _disable_search_block($delta); } } } } function _is_type_supports_facet($field_index_type) { return $field_index_type === 'string' || $field_index_type === 'list' || $field_index_type === 'boolean' || $field_index_type === 'list' || $field_index_type === 'integer' || $field_index_type === 'list' || $field_index_type === 'decimal' || $field_index_type === 'list' || $field_index_type === 'date' || $field_index_type === 'list'; } function _is_type_supports_ranges($field_index_type) { return $field_index_type === 'integer' || $field_index_type === 'decimal' || $field_index_type === 'date'; } /** * @param $field_value_type * @return null|string return NULL if unsupported field type */ function mica_field_description_get_field_value_type($field_value_type) { switch ($field_value_type) { case 'list_boolean': return 'boolean'; case 'date': case 'datestamp': case 'datetime': return 'date'; case 'number_float': case 'list_float': case 'number_decimal': return 'decimal'; case 'email': case 'link_field': case 'list_text': case 'text_long': case 'text_with_summary': case 'text': case 'text_default': case 'list_default': return 'text'; case 'number_integer': case 'list_integer': return 'integer'; // unsupported types case 'field_collection': case 'name': case 'link_field': case 'viewreference': case 'node_reference': return NULL; default: watchdog('mica', 'Unknown field type: %type', array('%type', $field_value_type), WATCHDOG_NOTICE); return 'text'; } } /** * Returns a unique field_description for a bundle and field name or NULL */ function _find_field_description($bundle, $field_name) { $query = new EntityFieldQuery; $result = $query->entityCondition('entity_type', 'node') ->entityCondition('bundle', 'field_description') ->fieldCondition('field_bundle', 'value', $bundle, '=') ->fieldCondition('field_original_field_name', 'value', $field_name, '=') ->execute(); if (!empty($result['node'])) { $entities = entity_load('node', array_keys($result['node'])); $entity_id = array_keys($entities); return $entities[$entity_id[0]]; } return array(); } /** * Additional submit handler for the 'Edit field instance' form. */ function _edit_field_field_description($form, &$form_state) { $instance = $form_state['values']['instance']; $form_field_description_infos = $form_state['values']['field']['settings']['field_description_infos']; $is_field_description = $form_field_description_infos['field_description']; $field_name = $instance['field_name']; $bundle = $instance['bundle']; $field_info = field_info_field($field_name); $node_type = node_type_load($bundle); $categories = array(); if (!empty($field_info['settings']) && !empty($field_info['settings']['allowed_values'])) { foreach ($field_info['settings']['allowed_values'] as $key => $value) { $categories[] = $key . '=' . $value; } } if ($is_field_description) { $label = $form_field_description_infos['field_description_label']; if (empty($label)) { $label = isset($instance['display_label']) && !empty($instance['display_label']) ? $instance['display_label'] : $instance['label']; } } else { $label = ''; } $desc = $is_field_description ? $form_field_description_infos['field_description_body'] : ''; $field_description_infos = array( 'title' => $node_type->name . ' » ' . $label, 'body' => array( 'value' => $desc, 'summary' => $desc, 'format' => 'plain_text', ), 'field_bundle' => $bundle, 'field_bundle_label' => $node_type->name, 'field_original_field_name' => $field_name, 'field_original_field_label' => $label, 'field_value_type' => $is_field_description ? $form_field_description_infos['field_description_value_type'] : NULL, 'field_repeatable' => $form_state['values']['field']['cardinality'] == 1 ? 0 : 1, 'field_categories' => $categories, ); $field_description = _find_field_description($bundle, $field_name); if (empty($field_description)) { if ($is_field_description) { _create_field_description($field_description_infos); } } else { if ($is_field_description) { _update_field_description($field_description, $field_description_infos); } else { node_delete($field_description->nid); } } } /** * Create Field Description and corresponding search index * * @param array $field_description_infos with 'title', 'body', 'field_bundle', 'field_bundle_label', 'field_original_field_name', * 'field_original_field_label', 'field_value_type', 'field_repeatable', 'field_categories' */ function _create_field_description(array $field_description_infos) { $field_description_infos['type'] = 'field_description'; $field_description_infos['language'] = LANGUAGE_NONE; entity_property_values_create_entity('node', $field_description_infos)->save(); watchdog('mica', 'Create Field Description for %field of %bundle', array( '%field' => $field_description_infos['field_original_field_label'], '%bundle' => $field_description_infos['field_bundle_label'], ), WATCHDOG_INFO); } /** * Update Field Description and corresponding search index * * @param stdClass $field_description * @param array $field_description_infos with 'title', 'body', 'field_bundle', 'field_bundle_label', 'field_original_field_name', * 'field_original_field_label', 'field_value_type', 'field_repeatable', 'field_categories' */ function _update_field_description(stdClass $field_description, array $field_description_infos) { $wrapper = entity_metadata_wrapper('node', $field_description); foreach ($field_description_infos as $key => $value) { $wrapper->$key = $value; } $wrapper->save(); }