updated webform localization and phone, uuid, term_merge, spambot, performance
This commit is contained in:
@@ -6,48 +6,64 @@ by:
|
||||
|
||||
Description
|
||||
-----------
|
||||
When using taxonomy for free tagging purposes, it's easy to end up with
|
||||
several terms having the same meaning. This may be due to spelling errors,
|
||||
or different users simply making up synonymous terms as they go.
|
||||
When using taxonomy for free tagging purposes, it's easy to end up with several
|
||||
terms having the same meaning. This may be due to spelling errors, or different
|
||||
users simply making up synonymous terms as they go.
|
||||
|
||||
You, as an administrator, may then want to correct such errors or unify
|
||||
synonymous terms, thereby pruning the taxonomy to a more manageable set.
|
||||
This module allows you to merge multiple terms into one, while updating
|
||||
all fields referring to those terms to refer to the replacement term instead.
|
||||
synonymous terms, thereby pruning the taxonomy to a more manageable set. This
|
||||
module allows you to merge multiple terms into one, while updating all fields
|
||||
referring to those terms to refer to the replacement term instead.
|
||||
|
||||
Currently, the module only acts on:
|
||||
* fields of 'taxonomy term reference' type
|
||||
* fields of the following types: taxonomy term reference, entity reference, and
|
||||
other fields that correctly define their foreign keys
|
||||
* Views Taxonomy Term filter handlers
|
||||
* Redirects
|
||||
|
||||
It would be desirable to update other possible
|
||||
places where deleted terms are used.
|
||||
The term merging may happen in 2 flavors. You can either manually indicate what
|
||||
terms should be merged or you can use duplicate suggestion tool for this
|
||||
purpose. This tool intends to scan your vocabulary and detect such terms that
|
||||
are likely to be duplicates. You will then only review the list of suggested
|
||||
duplicates and will schedule for merging only those that actually are
|
||||
duplicates. The heuristics through which duplicate tool determines potential
|
||||
synonymous terms are made to be extendible by other modules. Refer to Term Merge
|
||||
advanced help if you want to write a custom one, though the module itself ships
|
||||
with the following heuristics:
|
||||
* search by the same name
|
||||
* search by the same description
|
||||
* search by the same parent
|
||||
|
||||
You can indicate which specific heuristics should be used for searching
|
||||
duplicates within the UI of duplicate suggestion tool.
|
||||
|
||||
Integration
|
||||
-------------
|
||||
Currently module integrates with the following core and contributed modules:
|
||||
* Redirect module (http://drupal.org/project/redirect). During term merging
|
||||
you may set up SEO friendly redirects from the branch terms to point to the
|
||||
trunk term
|
||||
* Synonyms module (http://drupal.org/project/synonyms). During term merging
|
||||
you will be able to choose a trunk term's field into which all the branch terms
|
||||
will be added as synonyms (until cardinality limit for that field is reached).
|
||||
* Redirect module (http://drupal.org/project/redirect). During term merging you
|
||||
may set up SEO friendly redirects from the branch terms to point to the trunk
|
||||
term.
|
||||
* Synonyms module (http://drupal.org/project/synonyms). During term merging you
|
||||
will be able to choose a trunk term's field into which all the branch terms
|
||||
will be added as synonyms (until cardinality limit for that field is
|
||||
reached).
|
||||
* Hierarchical Select (http://drupal.org/project/hierarchical_select). If
|
||||
Hierarchical Select module is configured to be used for working with Taxonomy,
|
||||
its widget will be shown on the form, where you choose what terms to merge into
|
||||
what term.
|
||||
Hierarchical Select module is configured to be used for working with
|
||||
Taxonomy, its widget will be shown on the form, where you choose what terms
|
||||
to merge.
|
||||
* Views (http://drupal.org/project/views). If the branch terms are to be
|
||||
deleted after the merging process, you could end up having some Views filters
|
||||
to filter on no longer existing terms. Term Merge module, while merging terms,
|
||||
will update those filters to filter not on the branch term, but on the trunk
|
||||
term. This way you will not have senseless filters and will not have to update
|
||||
them manually.
|
||||
deleted after the merging process, you could end up having some Views filters
|
||||
to filter on no longer existing terms. Term Merge module, while merging
|
||||
terms, will update those filters to filter not on the branch term, but on the
|
||||
trunk term. This way you will not have senseless filters and will not have to
|
||||
update them manually.
|
||||
|
||||
Requirements
|
||||
-------------
|
||||
The modules requires enabled the following modules:
|
||||
* Taxonomy module (ships with Drupal core)
|
||||
* Entity API (http://drupal.org/project/entity)
|
||||
* Entity API (https://drupal.org/project/entity)
|
||||
* cTools (https://www.drupal.org/project/ctools)
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
@@ -0,0 +1,9 @@
|
||||
[advanced help settings]
|
||||
line break = TRUE
|
||||
|
||||
[term_merge]
|
||||
title = Term Merge
|
||||
|
||||
[term_merge_duplicate_suggestion]
|
||||
title = Implementing custom duplicate suggestion
|
||||
parent = term_merge
|
@@ -0,0 +1,44 @@
|
||||
Terme merge module, as its title well implies it, allows administrators to merge terms one into another.
|
||||
|
||||
When using taxonomy for free tagging purposes, it's easy to end up with several terms having the same meaning. This may be due to spelling errors, or different users simply making up synonymous terms as they go.
|
||||
|
||||
You, as an administrator, may then want to correct such errors or unify synonymous terms, thereby pruning the taxonomy to a more manageable set. This module allows you to merge multiple terms into one, while updating all fields referring to those terms to refer to the replacement term instead.
|
||||
|
||||
Currently the module features the following set of functionality and useful tools:
|
||||
<dt>Updating all references to branch terms and changing them to the trunk term.</dt>
|
||||
<dd>The core feature of the module is to safely update all existing content and to make it point from a branch term to the trunk one. Right now the following Field types are supported: <ul>
|
||||
<li>Taxonomy term reference</li>
|
||||
<li>Entity reference (when the underlying entity type is Taxonomy term)</li>
|
||||
<li>and any other field type that correctly defines its foreign keys in the database</li>
|
||||
</ul></dd>
|
||||
<dt>Merging field values from the branch term into the same fields of the trunk term.</dt>
|
||||
<dd>Optionally you may merge field values from each term branch into the term trunk. You choose what fields should get merged and after combining the values from both terms only unique values are kept in the trunk term. It is a useful option if you do not want to lose field values of your term branches.</dd>
|
||||
<dt>Redirect module integration</dt>
|
||||
<dd>Module integrates with the current release of Redirect module, allowing you to set up SEO friendly HTTP redirects from branch terms to the trunk term.</dd>
|
||||
<dt>Synonyms module integration</dt>
|
||||
<dd>Synonyms module is also integrated: before merging you may choose into which field of trunk term the branch terms will be added as synonyms - this should prevent from growing your vocabularies in future, since now your terms will have the terms they have merged as synonyms.</dd>
|
||||
<dt>Duplicate merge</dt>
|
||||
<dd>Sometimes even the task of identifying duplicates can become tedious, especially on the big vocabularies. So Term Merge tries to facilitate this part of the process too. There are certain heuristics implemented based on which the module tries to identify the terms that are likely to be duplicates of each other. Your task is then only to review the suggested duplicates and fire off merging of those that actually are duplicates. If your vocabulary is too big or if you only want it so, you can invoke the duplicate search tool only on a subset of the vocabulary terms, on the children of some term.</dd>
|
||||
|
||||
<h2>Duplicate merge</h2>
|
||||
|
||||
This is a somewhat versatile tool, so we decided to dedicate it a separate paragraph for we want to make sure users take the full advantage of its capabilities.
|
||||
|
||||
When you face the task of finding duplicate terms, you are likely to deal with two complications: <ul>
|
||||
<li>by what criteria should I judge if 2 terms are duplicates?</li>
|
||||
<li>how easy and how much of additional information can I retrieve about possible duplicate terms in order to be able to decide whether they are actual duplicates or it was just a false positive?</li>
|
||||
</ul>
|
||||
|
||||
<h3>Criterion of duplicate</h3>
|
||||
|
||||
Out of the box Term Merge gives you 3 heuristics for this purpose. The terms are considered duplicates if:<ul>
|
||||
<li>Their name is the same, disregarding the letter case</li>
|
||||
<li>Their description is the same, disregarding the letter case</li>
|
||||
<li>They have the same parent</li>
|
||||
</ul>
|
||||
|
||||
And then through the UI of the duplicate tool you can enable a combination of the available heuristics based on which terms will be marked as duplicates. Additionally, you can introduce your own heuristic if you have a custom notion of what terms are possible duplicates. So you can plug in your duplicate heuristic into the Term Merge and leverage its whole power only providing your custom duplicate detection. If you are interested in writing custom duplicate detection heuristic, cheer up! It is not that difficult and you can read on about it on <a href="&topic:term_merge/term_merge_duplicate_suggestion&">this page</a>.
|
||||
|
||||
<h3>How to judge if suggestion duplicates are actually duplicates?</h3>
|
||||
|
||||
One way or another, but you will have your duplicates laid out in front of you. You certainly can merge all of them without extra worrying about it. But chances are, at least some of the suggested terms may actually not be duplicates. So you might want to quickly scan through the available suggestions and review them for any obvious false positives. You can use term name, term ID, description, parents and fields of those terms to tell the heads and tails in it.
|
@@ -0,0 +1,16 @@
|
||||
If you want to define your own custom logic of what terms are to be considered possible duplicates and then to plug this custom logic into all Term Merge machinnery, then this page is for you.
|
||||
|
||||
Duplicate detection heuristics are implemented through cTools plugins. If you have not worked with cTools plugins before, we highly recommend you to read documentation about them, such as <a href="&topic:ctools/plugins-implementing&">this one</a>.
|
||||
|
||||
Term Merge module defines the cTools plugin type of <em>duplicate_suggestion</em>. This plugin type expects the following properties: <ul>
|
||||
<li><b>title</b>: (string) Human readable translated title of your duplicate suggestion logic. The title will be inserted into the UI of duplicate tool whereafter you can enable it for duplicate detection</li>
|
||||
<li><b>description</b>: (string) [optional] Human readable translated description of how your duplicate suggestion functions. Try including here description of the logic.</li>
|
||||
<li><b>hash callback</b>: (string) Name of a function that hashes terms. Terms with the same hash will be considered possible duplicates. The function will receive the following input arguments: <ol>
|
||||
<li>(object) Taxonomy term object whose hash is requested. The term will not include fields attached to it. They are excluded for performance and scaling considerations.</li>
|
||||
</ol>Your hash function is expected to return string: hash of the provided Taxonomy term.</li>
|
||||
<li><b>weight</b>: (int) [optional] Weight of your duplicate suggestion. On the duplicate tool UI the duplicate suggestions are displayed in the order according to their weights.</li>
|
||||
</ul>
|
||||
|
||||
Having that said, core of your duplicate suggestion plugin should be the hash function. You can look at the exaples of hash function inside of Term Merge module, just study the <em>term_merge/plugins/duplicate_suggestion/*.inc</em> files. In fact your hash function does not necessarily have to be complicated to yield reasonable duplicate suggestions.
|
||||
|
||||
Good luck coding! And if you feel like you have coded a duplicate suggestion that others might benefit from, please, be kind to open an issue against Term Merge module and share your duplicate suggestion there. I thank you beforehand on behalf of all the community!
|
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Plugin definition for duplicate suggestion based on description.
|
||||
*/
|
||||
|
||||
$plugin = array(
|
||||
'title' => t('Terms description are the same'),
|
||||
'description' => t('Mark terms as duplicates if they have the same description.'),
|
||||
'hash callback' => 'term_merge_duplicate_suggestion_description_hash',
|
||||
);
|
||||
|
||||
/**
|
||||
* Hash term based on its description.
|
||||
*/
|
||||
function term_merge_duplicate_suggestion_description_hash($term) {
|
||||
return drupal_strtoupper($term->description);
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Plugin definition for duplicate suggestion based on names.
|
||||
*/
|
||||
|
||||
$plugin = array(
|
||||
'title' => t('Terms names are the same'),
|
||||
'description' => t('Mark terms as duplicates if they have the same name.'),
|
||||
'hash callback' => 'term_merge_duplicate_suggestion_name_hash',
|
||||
'weight' => -10,
|
||||
);
|
||||
|
||||
/**
|
||||
* Hash term based on its name.
|
||||
*/
|
||||
function term_merge_duplicate_suggestion_name_hash($term) {
|
||||
// Making upper case.
|
||||
$name = drupal_strtoupper($term->name);
|
||||
// Trying transliteration, if available.
|
||||
if (module_exists('transliteration')) {
|
||||
$name = transliteration_get($name);
|
||||
// Keeping only ASCII chars.
|
||||
$name = preg_replace('#\W#', '', $name);
|
||||
}
|
||||
return $name;
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Plugin definition for duplicate suggestion based on parents.
|
||||
*/
|
||||
|
||||
$plugin = array(
|
||||
'title' => t('Terms parents are the same'),
|
||||
'description' => t('Mark terms as duplicates if they have the same parent.'),
|
||||
'hash callback' => 'term_merge_duplicate_suggestion_parent_hash',
|
||||
);
|
||||
|
||||
/**
|
||||
* Hash term based on its parent.
|
||||
*/
|
||||
function term_merge_duplicate_suggestion_parent_hash($term) {
|
||||
return $term->parents[0];
|
||||
}
|
@@ -7,10 +7,11 @@ files[] = term_merge.test
|
||||
|
||||
dependencies[] = taxonomy
|
||||
dependencies[] = entity
|
||||
dependencies[] = ctools
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-01-14
|
||||
version = "7.x-1.2"
|
||||
; Information added by Drupal.org packaging script on 2015-11-29
|
||||
version = "7.x-1.3"
|
||||
core = "7.x"
|
||||
project = "term_merge"
|
||||
datestamp = "1421194982"
|
||||
datestamp = "1448826550"
|
||||
|
||||
|
@@ -14,16 +14,6 @@
|
||||
*/
|
||||
define('TERM_MERGE_NO_REDIRECT', -1);
|
||||
|
||||
/**
|
||||
* Constant to use in term merge action.
|
||||
*
|
||||
* Constant denotes "Create a new term and use it as the term trunk" logic
|
||||
* for term merge action.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
define('TERM_MERGE_NEW_TERM_TRUNK', -1);
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
@@ -149,6 +139,38 @@ function term_merge_help($path, $arg) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ctools_plugin_type().
|
||||
*/
|
||||
function term_merge_ctools_plugin_type() {
|
||||
$plugins = array();
|
||||
|
||||
$plugins['duplicate_suggestion'] = array(
|
||||
'defaults' => array(
|
||||
'title' => NULL,
|
||||
'description' => NULL,
|
||||
'hash callback' => NULL,
|
||||
'weight' => 0,
|
||||
),
|
||||
);
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ctools_plugin_directory().
|
||||
*/
|
||||
function term_merge_ctools_plugin_directory($owner, $plugin_type) {
|
||||
switch ($owner) {
|
||||
case 'term_merge':
|
||||
switch ($plugin_type) {
|
||||
case 'duplicate_suggestion':
|
||||
return 'plugins/' . $plugin_type;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback for term merge action.
|
||||
*
|
||||
@@ -297,7 +319,14 @@ function term_merge_action($object, $context) {
|
||||
if (!isset($term_branch->{$field_name}[$language])) {
|
||||
$term_branch->{$field_name}[$language] = array();
|
||||
}
|
||||
$term_trunk->{$field_name}[$language] = array_merge($term_trunk->{$field_name}[$language], $term_branch->{$field_name}[$language]);
|
||||
$items = array_merge($term_trunk->{$field_name}[$language], $term_branch->{$field_name}[$language]);
|
||||
$unique_items = array();
|
||||
foreach ($items as $item) {
|
||||
$unique_items[serialize($item)] = $item;
|
||||
}
|
||||
$items = array_values($unique_items);
|
||||
|
||||
$term_trunk->{$field_name}[$language] = $items;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,36 +335,26 @@ function term_merge_action($object, $context) {
|
||||
taxonomy_term_save($term_trunk);
|
||||
}
|
||||
|
||||
// Updating all the links to $term_branch to point now to $term_trunk
|
||||
// firstly we go through the list of all fields searching for
|
||||
// taxonomy_term_reference field type because potentially some of these fields
|
||||
// values will have to be updated after merging terms.
|
||||
$fields = field_info_field_map();
|
||||
$result = array();
|
||||
foreach ($fields as $field_name => $v) {
|
||||
// Additionally we group by field_name to know what field has to be updated
|
||||
// in each found entity.
|
||||
// @todo: Here would be nice to throw in a hook, allowing other modules to
|
||||
// supply meta data about their field types if they also use taxonomy
|
||||
// references, defining it in their own field types.
|
||||
if ($v['type'] == 'taxonomy_term_reference') {
|
||||
$result[$field_name] = array();
|
||||
$query = new EntityFieldQuery();
|
||||
// Making sure we search in the entire scope of entities.
|
||||
$query->addMetaData('account', user_load(1));
|
||||
$query->fieldCondition($field_name, 'tid', $term_branch->tid);
|
||||
$_result = $query->execute();
|
||||
$result[$field_name] = array_merge_recursive($result[$field_name], $_result);
|
||||
}
|
||||
foreach (term_merge_fields_with_foreign_key('taxonomy_term_data', 'tid') as $field) {
|
||||
$result[$field['field_name']] = array();
|
||||
$query = new EntityFieldQuery();
|
||||
// Making sure we search in the entire scope of entities.
|
||||
$query->addMetaData('account', user_load(1));
|
||||
|
||||
$query->fieldCondition($field['field_name'], $field['term_merge_field_column'], $term_branch->tid);
|
||||
$_result = $query->execute();
|
||||
$result[$field['field_name']]['entities'] = $_result;
|
||||
$result[$field['field_name']]['column'] = $field['term_merge_field_column'];
|
||||
}
|
||||
// Now we load all entities that have taxonomy_term_reference pointing to
|
||||
// $term_branch.
|
||||
foreach ($result as $field_name => $entity_types) {
|
||||
foreach ($entity_types as $entity_type => $v) {
|
||||
|
||||
// Now we load all entities that have fields pointing to $term_branch.
|
||||
foreach ($result as $field_name => $field_data) {
|
||||
$column = $field_data['column'];
|
||||
foreach ($field_data['entities'] as $entity_type => $v) {
|
||||
$ids = array_keys($v);
|
||||
$entities = entity_load($entity_type, $ids);
|
||||
// After we have loaded it, we alter the taxonomy_term_reference
|
||||
// to point to $term_trunk.
|
||||
// After we have loaded it, we alter the field to point to $term_trunk.
|
||||
foreach ($entities as $entity) {
|
||||
// What is more, we have to do it for every available language.
|
||||
foreach ($entity->$field_name as $language => $items) {
|
||||
@@ -344,7 +363,7 @@ function term_merge_action($object, $context) {
|
||||
// 'keep_only_unique'.
|
||||
$is_trunk_added = FALSE;
|
||||
foreach ($entity->{$field_name}[$language] as $delta => $item) {
|
||||
if ($context['keep_only_unique'] && $is_trunk_added && in_array($item['tid'], array($term_trunk->tid, $term_branch->tid))) {
|
||||
if ($context['keep_only_unique'] && $is_trunk_added && in_array($item[$column], array($term_trunk->tid, $term_branch->tid))) {
|
||||
// We are instructed to keep only unique references and we already
|
||||
// have term trunk in this field, so we just unset value for this
|
||||
// delta.
|
||||
@@ -353,14 +372,14 @@ function term_merge_action($object, $context) {
|
||||
else {
|
||||
// Merging term references if necessary, and keep an eye on
|
||||
// whether we already have term trunk among this field values.
|
||||
switch ($item['tid']) {
|
||||
switch ($item[$column]) {
|
||||
case $term_trunk->tid:
|
||||
$is_trunk_added = TRUE;
|
||||
break;
|
||||
|
||||
case $term_branch->tid:
|
||||
$is_trunk_added = TRUE;
|
||||
$entity->{$field_name}[$language][$delta]['tid'] = $term_trunk->tid;
|
||||
$entity->{$field_name}[$language][$delta][$column] = $term_trunk->tid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -371,6 +390,16 @@ function term_merge_action($object, $context) {
|
||||
// numbers, because it is what it is supposed to be.
|
||||
$entity->{$field_name}[$language] = array_values($entity->{$field_name}[$language]);
|
||||
}
|
||||
|
||||
// Integration with workbench_moderation module. Without this code, if
|
||||
// we save the node for which workbench moderation is enabled, then
|
||||
// it will go from "published" state into "draft". Though in fact we do
|
||||
// not change anything in the node and therefore it should persist in
|
||||
// published state.
|
||||
if (module_exists('workbench_moderation') && $entity_type == 'node') {
|
||||
$entity->workbench_moderation['updating_live_revision'] = TRUE;
|
||||
}
|
||||
|
||||
// After updating all the references, save the entity.
|
||||
entity_save($entity_type, $entity);
|
||||
}
|
||||
@@ -570,6 +599,28 @@ function term_merge($term_branch, $term_trunk, $merge_settings = array()) {
|
||||
batch_set($batch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve information about ctools plugin of type 'duplicate suggestion'.
|
||||
*
|
||||
* @param string $id
|
||||
* Supply here ID of the cTool plugin information about which you want to
|
||||
* retrieve. You may omit this argument and then information on all duplicate
|
||||
* suggestion plugins will be returned
|
||||
*
|
||||
* @return array
|
||||
* Array of information on all available duplicate suggestion plugins or if
|
||||
* $id was provided, then information on that plugin
|
||||
*/
|
||||
function term_merge_duplicate_suggestion($id = NULL) {
|
||||
ctools_include('plugins');
|
||||
$plugins = ctools_get_plugins('term_merge', 'duplicate_suggestion', $id);
|
||||
if (!$id) {
|
||||
// Sort the list of plugins by their weight.
|
||||
uasort($plugins, 'drupal_sort_weight');
|
||||
}
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and return form elements that control behavior of merge action.
|
||||
*
|
||||
@@ -610,7 +661,7 @@ function term_merge_merge_options_elements($vocabulary) {
|
||||
$form['merge_fields'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Merge Term Fields'),
|
||||
'#description' => t('Check the fields whose values from branch terms you want to add to the values of corresponding fields of the trunk term. <b>Important note:</b> the values will be added until the cardinality limit for the selected fields is reached.'),
|
||||
'#description' => t('Check the fields whose values from branch terms you want to add to the values of corresponding fields of the trunk term. <b>Important note:</b> the values will be added until the cardinality limit for the selected fields is reached and only unique values for each field will be saved.'),
|
||||
'#options' => $options,
|
||||
);
|
||||
}
|
||||
@@ -619,6 +670,7 @@ function term_merge_merge_options_elements($vocabulary) {
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Keep only unique terms after merging'),
|
||||
'#description' => t('Sometimes after merging you may end up having a node (or any other entity) pointing twice to the same taxonomy term, tick this checkbox if want to keep only unique terms in other entities after merging.'),
|
||||
'#default_value' => TRUE,
|
||||
);
|
||||
|
||||
if (module_exists('redirect')) {
|
||||
@@ -709,3 +761,36 @@ function term_merge_merge_options_submit($merge_settings_element, &$form_state,
|
||||
);
|
||||
return $merge_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all fields that have a foreign key to provided column.
|
||||
*
|
||||
* @param string $foreign_table
|
||||
* Name of the table for which to look among foreign keys of all the fields
|
||||
* @param string $foreign_column
|
||||
* Name of the column for which to look among foreign keys of all the fields
|
||||
*
|
||||
* @return array
|
||||
* Array of all fields that have the specified table and column within their
|
||||
* foreign keys. Each of the fields array will be extended to include the
|
||||
* following additional keys:
|
||||
* - term_merge_field_column: (string) Name of the field column that holds
|
||||
* foreign key to the provided table and column
|
||||
*/
|
||||
function term_merge_fields_with_foreign_key($foreign_table, $foreign_column) {
|
||||
$fields = field_info_fields();
|
||||
$result = array();
|
||||
foreach ($fields as $field_name => $field_info) {
|
||||
foreach ($field_info['foreign keys'] as $foreign_key) {
|
||||
if ($foreign_key['table'] == $foreign_table) {
|
||||
$column = array_search($foreign_column, $foreign_key['columns']);
|
||||
if ($column) {
|
||||
$field_info['term_merge_field_column'] = $column;
|
||||
$result[] = $field_info;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
@@ -217,34 +217,16 @@ function term_merge_form_submit($form, &$form_state) {
|
||||
$form_state['storage']['confirm'] = 0;
|
||||
$form_state['rebuild'] = TRUE;
|
||||
|
||||
// Before storing the submitted values we slightly preprocess them to make
|
||||
// sure they correspond to what is expected by submit handler of taxonomy
|
||||
// creation form.
|
||||
if (isset($form_state['values']['relations'])) {
|
||||
$form_state['values'] += $form_state['values']['relations'];
|
||||
}
|
||||
$form_state['storage']['info'] = $form_state['values'];
|
||||
$form_state['storage']['merge_settings'] = term_merge_merge_options_submit($form, $form_state, $form);
|
||||
$form_state['storage']['old_form'] = $form;
|
||||
}
|
||||
else {
|
||||
// The user has confirmed merging. We pull up the submitted values.
|
||||
$form_state['values'] = $form_state['storage']['info'];
|
||||
|
||||
// If necessary, create the term trunk.
|
||||
if ($form_state['values']['term_trunk']['tid'] == TERM_MERGE_NEW_TERM_TRUNK) {
|
||||
// We try to mimic normal form submission for taxonomy module.
|
||||
module_load_include('inc', 'taxonomy', 'taxonomy.admin');
|
||||
taxonomy_form_term_submit($form_state['storage']['old_form']['term_trunk']['term_create'], $form_state);
|
||||
$term_trunk = $form_state['term'];
|
||||
}
|
||||
else {
|
||||
$term_trunk = taxonomy_term_load($form_state['values']['term_trunk']['tid']);
|
||||
}
|
||||
term_merge(array_values($form_state['values']['term_branch']), $form_state['values']['term_trunk']['tid'], $form_state['storage']['merge_settings']);
|
||||
|
||||
term_merge(array_values($form_state['values']['term_branch']), $term_trunk->tid, $form_state['storage']['merge_settings']);
|
||||
|
||||
$form_state['redirect'] = array('taxonomy/term/' . $term_trunk->tid);
|
||||
$form_state['redirect'] = array('taxonomy/term/' . $form_state['values']['term_trunk']['tid']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +265,6 @@ function term_merge_form_term_trunk_widget_select(&$form, &$form_state, $vocabul
|
||||
unset($options[$child->tid]);
|
||||
}
|
||||
}
|
||||
$options = array(TERM_MERGE_NEW_TERM_TRUNK => 'New Term') + $options;
|
||||
}
|
||||
else {
|
||||
// Term branch has not been selected yet.
|
||||
@@ -295,58 +276,7 @@ function term_merge_form_term_trunk_widget_select(&$form, &$form_state, $vocabul
|
||||
'#required' => TRUE,
|
||||
'#description' => t('Choose into what term you want to merge.'),
|
||||
'#options' => $options,
|
||||
'#ajax' => array(
|
||||
'callback' => 'term_merge_form_term_trunk_term_create',
|
||||
'wrapper' => 'term-merge-form-term-trunk-term-create',
|
||||
'method' => 'replace',
|
||||
'effect' => 'fade',
|
||||
),
|
||||
);
|
||||
|
||||
$form['term_trunk']['term_create'] = array(
|
||||
'#prefix' => '<div id="term-merge-form-term-trunk-term-create">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
// We throw in the Taxonomy native term create form only if the option for
|
||||
// creation of a new term was selected by user.
|
||||
if (isset($form_state['values']['term_trunk']['tid']) && $form_state['values']['term_trunk']['tid'] == TERM_MERGE_NEW_TERM_TRUNK) {
|
||||
module_load_include('inc', 'taxonomy', 'taxonomy.admin');
|
||||
|
||||
$form['term_trunk']['term_create'] += array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Create New Term'),
|
||||
);
|
||||
|
||||
$form['term_trunk']['term_create'] += taxonomy_form_term($form['term_trunk']['term_create'], $form_state, array(), $vocabulary);
|
||||
// We have our own submit button, so we unset the normal one from the term
|
||||
// create form.
|
||||
unset($form['term_trunk']['term_create']['actions']);
|
||||
// Additionally we have to filter out from "Parent Terms" select the already
|
||||
// selected branch terms and their children, because we can't merge into
|
||||
// the term itself or its children.
|
||||
// We do a trick here, since we know the 1st element is the <root> option
|
||||
// and all others are normal taxonomy terms, we keep the 1st element as it
|
||||
// is while all the other elements we substitute with our $options array
|
||||
// which is basically identical but already has been filtered out unwanted
|
||||
// terms. Plus we have to unset the 'New Term' option from $options.
|
||||
unset($options[TERM_MERGE_NEW_TERM_TRUNK]);
|
||||
if (is_array($form['term_trunk']['term_create']['relations']['parent']['#options'])) {
|
||||
$form['term_trunk']['term_create']['relations']['parent']['#options'] = array_slice($form['term_trunk']['term_create']['relations']['parent']['#options'], 0, 1, TRUE) + $options;
|
||||
}
|
||||
|
||||
// For each field attached to taxonomy term of this vocabulary that has
|
||||
// unlimited cardinality we have to extra process the results, otherwise
|
||||
// "Add another item" button doesn't work.
|
||||
$instances = field_info_instances($form['term_trunk']['term_create']['#entity_type'], $form['term_trunk']['term_create']['#bundle']);
|
||||
foreach ($instances as $instance) {
|
||||
$field = field_info_field($instance['field_name']);
|
||||
if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
|
||||
if (isset($form['term_trunk']['term_create'][$field['field_name']][LANGUAGE_NONE]['add_more']['#limit_validation_errors'])) {
|
||||
$form['term_trunk']['term_create'][$field['field_name']][LANGUAGE_NONE]['add_more']['#limit_validation_errors'] = array(array('term_branch'), array('term_trunk'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -504,16 +434,6 @@ function term_merge_form_term_trunk($form, $form_state) {
|
||||
return $form['term_trunk'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax callback function.
|
||||
*
|
||||
* Used in term_merge_term_merge_form() to replace the term create fieldset
|
||||
* depending on already selected term_branch values and the term_trunk value.
|
||||
*/
|
||||
function term_merge_form_term_trunk_term_create($form, $form_state) {
|
||||
return $form['term_trunk']['term_create'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate 'term_merge_duplicates_form'.
|
||||
*
|
||||
@@ -521,16 +441,15 @@ function term_merge_form_term_trunk_term_create($form, $form_state) {
|
||||
*
|
||||
* @param object $vocabulary
|
||||
* Fully loaded taxonomy vocabulary object inside of which term merging
|
||||
* occurs, if this argument is omitted, then $term is required and will be
|
||||
* used to obtain information about Taxonomy vocabulary
|
||||
* occurs, if this argument is omitted, then $parent_term is required and will
|
||||
* be used to obtain information about Taxonomy vocabulary
|
||||
* @param object $parent_term
|
||||
* Fully loaded taxonomy term object using which the function will pull up
|
||||
* the vocabulary inside of which term merging occurs. Duplicate terms will be
|
||||
* sought only among children of this term
|
||||
*/
|
||||
function term_merge_duplicates_form($form, &$form_state, $vocabulary = NULL, $parent_term = NULL) {
|
||||
// TODO: make this JavaScript #attached.
|
||||
drupal_add_js(drupal_get_path('module', 'term_merge') . '/js/duplicate.form.js');
|
||||
$form['#attached']['js'][drupal_get_path('module', 'term_merge') . '/js/duplicate.form.js'] = array();
|
||||
|
||||
// Checking if we were not given vocabulary object, we will use term object to
|
||||
// obtain the former.
|
||||
@@ -546,34 +465,61 @@ function term_merge_duplicates_form($form, &$form_state, $vocabulary = NULL, $pa
|
||||
'#markup' => '<p>' . t('Here you can merge terms with the same names. It is a useful tool against term-duplicates. If this tool is invoked on a term (not on the entire vocabulary), duplicate terms will be sought only among children of that term. The terms are grouped by names. Term into which the merging will occur is selected manually by user, however you must know that it is impossible to merge a parent term into any of its children.') . '</p>',
|
||||
);
|
||||
|
||||
$form['scaling'] = array(
|
||||
$form['settings'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Scaling for large vocabularies'),
|
||||
'#description' => t('Adjust these settings if your vocabulary is very large.'),
|
||||
'#title' => t('Advanced settings'),
|
||||
'#description' => t('Fine tune the duplicate search tool. You can adjust these settings if your vocabulary is very large. Also, you can control within these settings how the potential duplicates are presented below.'),
|
||||
'#tree' => TRUE,
|
||||
'#collapsible' => TRUE,
|
||||
);
|
||||
|
||||
$form['scaling']['help'] = array(
|
||||
$form['settings']['help'] = array(
|
||||
'#markup' => '<p>' . format_plural(count($tree), 'Vocabulary %vocabulary has only 1 term. It is very unlikely you will merge anything here.', 'Vocabulary %vocabulary has @count terms. If this tool works slow, you may instruct the duplicate finder tool to terminate its work after it has found a specific number of possible duplicates.', array(
|
||||
'%vocabulary' => $vocabulary->name,
|
||||
)) . '</p>',
|
||||
);
|
||||
|
||||
$form['scaling']['max_duplicates'] = array(
|
||||
$form['settings']['max_duplicates'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Show N duplicates'),
|
||||
'#description' => t('Input an integer here - this many duplicates will be show on the form. Once this amount of possible duplicates is found, the search process terminates.'),
|
||||
'#description' => t('Input an integer here - this many duplicates will be shown on the form. Once this amount of possible duplicates is found, the search process terminates.'),
|
||||
'#required' => TRUE,
|
||||
'#default_value' => isset($form_state['values']['scaling']['max_duplicates']) ? $form_state['values']['scaling']['max_duplicates'] : 300,
|
||||
'#default_value' => isset($form_state['values']['settings']['max_duplicates']) ? $form_state['values']['settings']['max_duplicates'] : 300,
|
||||
'#element_validate' => array('element_validate_integer_positive'),
|
||||
);
|
||||
|
||||
$form['scaling']['update'] = array(
|
||||
$options = array();
|
||||
foreach (term_merge_duplicate_suggestion() as $plugin) {
|
||||
$options[$plugin['name']] = $plugin['title'];
|
||||
}
|
||||
$form['settings']['duplicate_suggestion'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Mark terms as duplicate if all the checked conditions stand true'),
|
||||
'#options' => $options,
|
||||
'#default_value' => isset($form_state['values']['settings']['duplicate_suggestion']) ? $form_state['values']['settings']['duplicate_suggestion'] : array('name'),
|
||||
);
|
||||
|
||||
$options = array();
|
||||
$bundle = field_extract_bundle('taxonomy_term', $vocabulary);
|
||||
foreach (field_info_instances('taxonomy_term', $bundle) as $instance) {
|
||||
$options[$instance['field_name']] = $instance['label'];
|
||||
}
|
||||
|
||||
if (!empty($options)) {
|
||||
$form['settings']['fields'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Display fields'),
|
||||
'#description' => t('Check which fields you wish to display in the results below for each possible duplicate term.'),
|
||||
'#options' => $options,
|
||||
'#default_value' => isset($form_state['values']['settings']['fields']) ? array_values(array_filter($form_state['values']['settings']['fields'])) : array(),
|
||||
);
|
||||
}
|
||||
|
||||
$form['settings']['update'] = array(
|
||||
'#type' => 'button',
|
||||
'#value' => t('Re-run duplicate search'),
|
||||
'#ajax' => array(
|
||||
'callback' => 'term_merge_duplicates_form_scaling',
|
||||
'callback' => 'term_merge_duplicates_form_settings',
|
||||
'wrapper' => 'term-merge-duplicate-wrapper',
|
||||
'method' => 'replace',
|
||||
'effect' => 'fade',
|
||||
@@ -588,24 +534,31 @@ function term_merge_duplicates_form($form, &$form_state, $vocabulary = NULL, $pa
|
||||
$groups = array();
|
||||
|
||||
foreach ($tree as $term) {
|
||||
if ($count >= $form['scaling']['max_duplicates']['#default_value']) {
|
||||
if ($count >= $form['settings']['max_duplicates']['#default_value']) {
|
||||
// We have reached the limit of possible duplicates to be found.
|
||||
break;
|
||||
}
|
||||
$name = term_merge_duplicates_process_name($term->name);
|
||||
if (!isset($groups[$name])) {
|
||||
$groups[$name] = array();
|
||||
$hash = '';
|
||||
foreach ($form['settings']['duplicate_suggestion']['#default_value'] as $duplicate_suggestion) {
|
||||
$duplicate_suggestion = term_merge_duplicate_suggestion($duplicate_suggestion);
|
||||
$function = ctools_plugin_get_function($duplicate_suggestion, 'hash callback');
|
||||
if ($function) {
|
||||
$hash .= $function($term);
|
||||
}
|
||||
}
|
||||
if (!isset($groups[$hash])) {
|
||||
$groups[$hash] = array();
|
||||
}
|
||||
else {
|
||||
// We increment count by one for the just encountered duplicate. Plus, if
|
||||
// it is the second duplicate in this group, we also increment it by one
|
||||
// for the 1st duplicate in the group.
|
||||
$count++;
|
||||
if (count($groups[$name]) == 1) {
|
||||
if (count($groups[$hash]) == 1) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
$groups[$name][$term->tid] = $term;
|
||||
$groups[$hash][$term->tid] = $term;
|
||||
}
|
||||
|
||||
$form['wrapper'] = array(
|
||||
@@ -628,72 +581,156 @@ function term_merge_duplicates_form($form, &$form_state, $vocabulary = NULL, $pa
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
|
||||
$groups = array_filter($groups, 'term_merge_duplicates_form_filter');
|
||||
|
||||
$tids = array();
|
||||
foreach ($groups as $group) {
|
||||
$tids = array_merge($tids, array_keys($group));
|
||||
}
|
||||
|
||||
// This array will be keyed by term tid and values will be counts of how many
|
||||
// other entities reference to this term through values of fields attached to
|
||||
// them.
|
||||
$terms_count = array_fill_keys($tids, 0);
|
||||
if (!empty($tids)) {
|
||||
foreach (term_merge_fields_with_foreign_key('taxonomy_term_data', 'tid') as $referencing_field) {
|
||||
if ($referencing_field['storage']['type'] == 'field_sql_storage') {
|
||||
$table = array_keys($referencing_field['storage']['details']['sql'][FIELD_LOAD_CURRENT]);
|
||||
$table = reset($table);
|
||||
$column = $referencing_field['storage']['details']['sql'][FIELD_LOAD_CURRENT][$table][$referencing_field['term_merge_field_column']];
|
||||
$select = db_select($table, 'reference')
|
||||
->condition($column, $tids);
|
||||
$select->addField('reference', $column, 'tid');
|
||||
$select->addExpression('COUNT(1)', 'count');
|
||||
$select->groupBy($column);
|
||||
$select = $select->execute();
|
||||
foreach ($select as $row) {
|
||||
$terms_count[$row->tid] += $row->count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($form['settings']['fields']['#default_value'])) {
|
||||
// We need to load full term entities, because we are requested to show
|
||||
// fields.
|
||||
$terms = taxonomy_term_load_multiple($tids);
|
||||
foreach ($groups as $i => $group) {
|
||||
$groups[$i] = array_intersect_key($terms, $group);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($groups as $i => $group) {
|
||||
if (count($group) > 1) {
|
||||
// Sorting terms by tid for better usage experience.
|
||||
ksort($group);
|
||||
// Sorting terms by tid for better usage experience.
|
||||
ksort($group);
|
||||
|
||||
$first_term = reset($group);
|
||||
$first_term = reset($group);
|
||||
|
||||
$options = array();
|
||||
foreach ($group as $term) {
|
||||
$parents = array();
|
||||
// Adding Root to the hierarchy.
|
||||
$parents[] = t('Vocabulary Root');
|
||||
foreach (taxonomy_get_parents_all($term->tid) as $parent) {
|
||||
// We do not include the current term in the hierarchy.
|
||||
if ($parent->tid != $term->tid) {
|
||||
$parents[] = $parent->name;
|
||||
$options = array();
|
||||
foreach ($group as $term) {
|
||||
$parents = array();
|
||||
// Adding Root to the hierarchy.
|
||||
$parents[] = t('Vocabulary Root');
|
||||
foreach (taxonomy_get_parents_all($term->tid) as $parent) {
|
||||
// We do not include the current term in the hierarchy.
|
||||
if ($parent->tid != $term->tid) {
|
||||
$parents[] = $parent->name;
|
||||
}
|
||||
}
|
||||
$language = isset($term->language) ? $term->language : LANGUAGE_NONE;
|
||||
if ($language == LANGUAGE_NONE) {
|
||||
$language = t('Not Specified');
|
||||
}
|
||||
|
||||
$options[$term->tid] = array(
|
||||
'id' => $term->tid,
|
||||
'title' => l($term->name, 'taxonomy/term/' . $term->tid),
|
||||
'language' => $language,
|
||||
'description' => check_markup($term->description, $term->format),
|
||||
'parents' => implode(' » ', $parents),
|
||||
'count' => format_plural($terms_count[$term->tid], '@count time', '@count times'),
|
||||
);
|
||||
|
||||
if (isset($form['settings']['fields'])) {
|
||||
foreach ($form['settings']['fields']['#default_value'] as $instance) {
|
||||
$field = field_info_field($instance);
|
||||
$items = field_get_items('taxonomy_term', $term, $field['field_name']);
|
||||
$options[$term->tid][$field['field_name']] = '';
|
||||
if (is_array($items)) {
|
||||
$options[$term->tid][$field['field_name']] = array(
|
||||
'#theme' => 'item_list',
|
||||
'#items' => array(),
|
||||
);
|
||||
foreach ($items as $item) {
|
||||
switch ($field['type']) {
|
||||
case 'image':
|
||||
$display = array();
|
||||
$image_style = image_style_load('thumbnail');
|
||||
if ($image_style) {
|
||||
$cache = _field_info_field_cache();
|
||||
$display = $cache->prepareInstanceDisplay($display, $field['type']);
|
||||
$display['settings']['image_style'] = $image_style['name'];
|
||||
}
|
||||
$rendered_item = drupal_render(field_view_value('taxonomy_term', $term, $field['field_name'], $item, $display));
|
||||
break;
|
||||
|
||||
default:
|
||||
$rendered_item = drupal_render(field_view_value('taxonomy_term', $term, $field['field_name'], $item));
|
||||
break;
|
||||
}
|
||||
$options[$term->tid][$field['field_name']]['#items'][] = $rendered_item;
|
||||
}
|
||||
if (count($options[$term->tid][$field['field_name']]['#items']) > 1) {
|
||||
$options[$term->tid][$field['field_name']] = drupal_render($options[$term->tid][$field['field_name']]);
|
||||
}
|
||||
else {
|
||||
$options[$term->tid][$field['field_name']] = $options[$term->tid][$field['field_name']]['#items'][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
$language = isset($term->language) ? $term->language : LANGUAGE_NONE;
|
||||
if ($language == LANGUAGE_NONE) {
|
||||
$language = t('Not Specified');
|
||||
}
|
||||
|
||||
$options[$term->tid] = array(
|
||||
'id' => $term->tid,
|
||||
'title' => l($term->name, 'taxonomy/term/' . $term->tid),
|
||||
'language' => $language,
|
||||
'description' => check_markup($term->description, $term->format),
|
||||
'parents' => implode(' » ', $parents),
|
||||
);
|
||||
}
|
||||
|
||||
$form['wrapper']['group'][$i] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => check_plain($first_term->name),
|
||||
'#collapsible' => TRUE,
|
||||
'#pre_render' => array('term_merge_duplicates_fieldset_preprocess'),
|
||||
'#element_validate' => array('term_merge_duplicates_fieldset_validate'),
|
||||
);
|
||||
|
||||
$form['wrapper']['group'][$i]['duplicates'] = array(
|
||||
'#type' => 'tableselect',
|
||||
'#title' => 'Duplicates',
|
||||
'#header' => array(
|
||||
'id' => t('ID'),
|
||||
'title' => t('Title'),
|
||||
'description' => t('Description'),
|
||||
'language' => t('Language'),
|
||||
'parents' => t('Parents'),
|
||||
),
|
||||
'#options' => $options,
|
||||
);
|
||||
|
||||
$options = array();
|
||||
foreach ($group as $term) {
|
||||
$options[$term->tid] = $term->name;
|
||||
}
|
||||
$form['wrapper']['group'][$i]['trunk_tid'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Merge Into'),
|
||||
'#options' => $options,
|
||||
'#attributes' => array(
|
||||
'class' => array('term-merge-duplicate-trunk'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$form['wrapper']['group'][$i] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => check_plain($first_term->name),
|
||||
'#collapsible' => TRUE,
|
||||
'#pre_render' => array('term_merge_duplicates_fieldset_preprocess'),
|
||||
'#element_validate' => array('term_merge_duplicates_fieldset_validate'),
|
||||
);
|
||||
|
||||
$header = array(
|
||||
'id' => t('ID'),
|
||||
'title' => t('Title'),
|
||||
'description' => t('Description'),
|
||||
'language' => t('Language'),
|
||||
'parents' => t('Parents'),
|
||||
'count' => t('Referenced'),
|
||||
);
|
||||
|
||||
if (isset($form['settings']['fields'])) {
|
||||
$header += array_map('check_plain', array_intersect_key($form['settings']['fields']['#options'], drupal_map_assoc($form['settings']['fields']['#default_value'])));
|
||||
}
|
||||
|
||||
$form['wrapper']['group'][$i]['duplicates'] = array(
|
||||
'#type' => 'tableselect',
|
||||
'#title' => 'Duplicates',
|
||||
'#header' => $header,
|
||||
'#options' => $options,
|
||||
);
|
||||
|
||||
$options = array();
|
||||
foreach ($group as $term) {
|
||||
$options[$term->tid] = $term->name;
|
||||
}
|
||||
$form['wrapper']['group'][$i]['trunk_tid'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Merge Into'),
|
||||
'#options' => $options,
|
||||
'#attributes' => array(
|
||||
'class' => array('term-merge-duplicate-trunk'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if ($count > 0) {
|
||||
@@ -770,31 +807,6 @@ function term_merge_duplicates_form_submit($form, &$form_state) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* String process function.
|
||||
*
|
||||
* Manipulate supplied var $name and by the output of this function terms in a
|
||||
* vocabulary are grouped as duplicates.
|
||||
*
|
||||
* @param string $name
|
||||
* String that needs to be manipulated
|
||||
*
|
||||
* @return string
|
||||
* Processed string (normally it implies making it upper case, stripping down
|
||||
* any special chars, etc.)
|
||||
*/
|
||||
function term_merge_duplicates_process_name($name) {
|
||||
// Making upper case.
|
||||
$name = drupal_strtoupper($name);
|
||||
// Trying transliteration, if available.
|
||||
if (module_exists('transliteration')) {
|
||||
$name = transliteration_get($name);
|
||||
// Keeping only ASCII chars.
|
||||
$name = preg_replace('#\W#', '', $name);
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element preprocess function.
|
||||
*
|
||||
@@ -806,13 +818,13 @@ function term_merge_duplicates_fieldset_preprocess($element) {
|
||||
foreach ($options as $tid => $row) {
|
||||
$element['trunk_tid'][$tid]['#title_display'] = 'invisible';
|
||||
$options[$tid] = array(
|
||||
'trunk' => drupal_render($element['trunk_tid'][$tid]),
|
||||
) + $options[$tid];
|
||||
'trunk' => drupal_render($element['trunk_tid'][$tid]),
|
||||
) + $options[$tid];
|
||||
}
|
||||
$element['trunk_tid']['#title_display'] = 'invisible';
|
||||
$element['duplicates']['#header'] = array(
|
||||
'trunk' => $element['trunk_tid']['#title'],
|
||||
) + $element['duplicates']['#header'];
|
||||
'trunk' => $element['trunk_tid']['#title'],
|
||||
) + $element['duplicates']['#header'];
|
||||
|
||||
return $element;
|
||||
}
|
||||
@@ -837,8 +849,17 @@ function term_merge_duplicates_fieldset_validate($element, &$form_state, $form)
|
||||
* Ajax callback function.
|
||||
*
|
||||
* Used in term_merge_duplicates_form() to replace the duplicates tables with
|
||||
* new data per current scaling settings.
|
||||
* new data per current settings.
|
||||
*/
|
||||
function term_merge_duplicates_form_scaling($form, &$form_state) {
|
||||
function term_merge_duplicates_form_settings($form, &$form_state) {
|
||||
return $form['wrapper'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Supportive array_filter() callback function.
|
||||
*
|
||||
* Eliminate all array elements, whose dimension is less than 1.
|
||||
*/
|
||||
function term_merge_duplicates_form_filter($array_element) {
|
||||
return count($array_element) > 1;
|
||||
}
|
||||
|
@@ -89,7 +89,7 @@ class TermMergeTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
/**
|
||||
* GetInfo method.
|
||||
*/
|
||||
public function getInfo() {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Term Merge',
|
||||
'description' => 'Ensure that the module Term Merge works correctly.',
|
||||
@@ -176,6 +176,7 @@ class TermMergeTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
'term_merge_test_single' => 1,
|
||||
'term_merge_test_unlimited' => FIELD_CARDINALITY_UNLIMITED,
|
||||
'term_merge_do_not_merge' => 10,
|
||||
'term_merge_not_unique' => FIELD_CARDINALITY_UNLIMITED,
|
||||
);
|
||||
|
||||
foreach ($fields_map as $field_name => $cardinality) {
|
||||
@@ -191,8 +192,7 @@ class TermMergeTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'taxonomy_term',
|
||||
'bundle' => $bundle,
|
||||
'label' => $this->randomName(),
|
||||
'description' => $this->randomName(),
|
||||
'label' => $field_name,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -202,31 +202,32 @@ class TermMergeTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
);
|
||||
|
||||
foreach ($terms as $term_type => $tmp) {
|
||||
$url = 'admin/structure/taxonomy/vocabulary/add';
|
||||
$name = $this->randomName();
|
||||
$edit = array(
|
||||
'name' => $name,
|
||||
$term = (object) array(
|
||||
'vid' => $this->vocabulary->vid,
|
||||
'name' => $this->randomName(),
|
||||
);
|
||||
|
||||
foreach ($fields_map as $field_name => $cardinality) {
|
||||
switch ($field_name) {
|
||||
case 'term_merge_test_single':
|
||||
$edit[$field_name . '[' . LANGUAGE_NONE . '][0][value]'] = $this->randomName();
|
||||
$term->{$field_name}[LANGUAGE_NONE][0]['value'] = $this->randomName();
|
||||
break;
|
||||
|
||||
case 'term_merge_test_unlimited':
|
||||
case 'term_merge_do_not_merge':
|
||||
$count = rand(1, 3);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$edit[$field_name . '[' . LANGUAGE_NONE . '][' . $i . '][value]'] = $this->randomName();
|
||||
$this->drupalPost($url, $edit, 'Add another item');
|
||||
$url = NULL;
|
||||
$term->{$field_name}[LANGUAGE_NONE][$i]['value'] = $this->randomName();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'term_merge_not_unique':
|
||||
$term->{$field_name}[LANGUAGE_NONE][0]['value'] = 'term_merge_not_unique_value';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->drupalPost($url, $edit, 'Save');
|
||||
taxonomy_term_save($term);
|
||||
$terms[$term_type] = $this->getLastTerm($this->vocabulary);
|
||||
}
|
||||
|
||||
@@ -241,15 +242,23 @@ class TermMergeTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
$this->drupalGet('taxonomy/term/' . $terms['trunk']->tid);
|
||||
foreach ($fields_map as $field_name => $cardinality) {
|
||||
foreach (field_get_items('taxonomy_term', $terms['branch'], $field_name) as $item) {
|
||||
$this->assertNoText($item['value'], 'Values of field ' . $field_name . ' have not been added to the trunk term with disabled "merge_fields" option.');
|
||||
if ($field_name != 'term_merge_not_unique') {
|
||||
$this->assertNoText($item['value'], 'Values of field ' . $field_name . ' have not been added to the trunk term with disabled "merge_fields" option.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we try merging with merging 2 of 3 fields. The values of the branch
|
||||
// term should be added to the trunk term's values only in those 2 fields.
|
||||
// Now we try merging with merging fields. The values of the branch term
|
||||
// should be added to the trunk term's values only in where we asked them
|
||||
// to be added. Moreover, only unique values are to be kept in each of the
|
||||
// merged fields.
|
||||
actions_do('term_merge_action', $terms['branch'], array(
|
||||
'term_trunk' => $terms['trunk']->tid,
|
||||
'merge_fields' => array('term_merge_test_single', 'term_merge_test_unlimited'),
|
||||
'merge_fields' => array(
|
||||
'term_merge_test_single',
|
||||
'term_merge_test_unlimited',
|
||||
'term_merge_not_unique',
|
||||
),
|
||||
'term_branch_keep' => TRUE,
|
||||
));
|
||||
|
||||
@@ -267,6 +276,13 @@ class TermMergeTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'term_merge_not_unique':
|
||||
// Make sure only the unique values in merged field are kept.
|
||||
foreach (field_get_items('taxonomy_term', $terms['trunk'], $field_name) as $item) {
|
||||
$this->assertUniqueText($item['value'], 'Only unique field values are kept in the trunk term field after merging terms with enabled "merge_fields" option.');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'term_merge_test_unlimited':
|
||||
// Make sure values of fields that are instructed to be added to trunk
|
||||
// term's values are actually added.
|
||||
@@ -517,7 +533,6 @@ class TermMergeTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
'taxonomy_vocabulary_tab',
|
||||
'taxonomy_term_tab',
|
||||
'via_term_trunk_widget_select',
|
||||
'via_term_trunk_widget_select_creating_new_term_trunk',
|
||||
'via_term_trunk_widget_autocomplete',
|
||||
'merge_fields',
|
||||
'do_not_merge_fields',
|
||||
@@ -602,26 +617,6 @@ class TermMergeTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
$this->assertIdentical(FALSE, $str_pos, 'Child is not available as option for term trunk if its parent is chosen among term branches.');
|
||||
$str_pos = strpos($term_trunk_options, $terms['parent']->name);
|
||||
$this->assertIdentical(FALSE, $str_pos, 'Selected branch term is not available as an option for term trunk.');
|
||||
// At the same time asserting if we choose "New Term" option, among
|
||||
// the available parents for the new term there are no children of the
|
||||
// selected branch terms, nor the branch terms themselves.
|
||||
$this->drupalPostAJAX(NULL, array(
|
||||
'term_branch[]' => array($terms['parent']->tid),
|
||||
'term_trunk[widget]' => $term_trunk_widget,
|
||||
'term_trunk[tid]' => TERM_MERGE_NEW_TERM_TRUNK,
|
||||
), 'term_trunk[tid]');
|
||||
$matches = array();
|
||||
preg_match('#\<select[^>]+name="relations\[parent\]\[\]"[^>]*\>.+?\</select\>#si', $this->content, $matches);
|
||||
$new_term_parent_options = $matches[0];
|
||||
$str_pos = strpos($new_term_parent_options, $terms['child']->name);
|
||||
$this->assertIdentical(FALSE, $str_pos, 'Child is not available as option for parent term for a new term trunk if its parent is chosen among term branches.');
|
||||
$str_pos = strpos($new_term_parent_options, $terms['parent']->name);
|
||||
$this->assertIdentical(FALSE, $str_pos, 'Selected branch term is not available as an option for parent term for a new term trunk.');
|
||||
break;
|
||||
|
||||
case 'via_term_trunk_widget_select_creating_new_term_trunk':
|
||||
$init_url = 'taxonomy/term/' . $terms['parent']->tid . '/merge';
|
||||
$term_trunk_widget = 'select';
|
||||
break;
|
||||
|
||||
case 'via_term_trunk_widget_autocomplete':
|
||||
@@ -706,32 +701,6 @@ class TermMergeTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
switch ($term_trunk_widget) {
|
||||
case 'select':
|
||||
$term_trunk_edit += array('term_trunk[tid]' => $terms['term_trunk']->tid);
|
||||
if ($case == 'via_term_trunk_widget_select_creating_new_term_trunk') {
|
||||
// This is special case, we are gonna create a new term trunk using
|
||||
// taxonomy create term embedded form.
|
||||
$term_trunk_edit = array('term_trunk[tid]' => TERM_MERGE_NEW_TERM_TRUNK) + $term_trunk_edit;
|
||||
$this->drupalPostAJAX(NULL, array(
|
||||
'term_branch[]' => $term_branches_edit,
|
||||
'term_trunk[widget]' => $term_trunk_widget,
|
||||
) + $term_trunk_edit, 'term_trunk[tid]');
|
||||
|
||||
// Adding another delta for text field. This way we make sure Field
|
||||
// API gets embedded into our form without errors.
|
||||
$this->drupalPostAJAX(NULL, array(
|
||||
'term_branch[]' => $term_branches_edit,
|
||||
'term_trunk[widget]' => $term_trunk_widget,
|
||||
) + $term_trunk_edit, array('field_test_text_add_more' => 'Add another item'));
|
||||
// We store into $term_trunk_edit array the info about the just
|
||||
// created new trunk term for further assertions down below in the
|
||||
// code.
|
||||
$term_trunk_edit += array(
|
||||
'name' => $this->randomName(),
|
||||
'description[value]' => $this->randomName(),
|
||||
'field_test_text[' . LANGUAGE_NONE . '][0][value]' => $this->randomName(),
|
||||
'field_test_text[' . LANGUAGE_NONE . '][1][value]' => $this->randomName(),
|
||||
'relations[parent][]' => array($terms['term_trunk_parent']->tid),
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'autocomplete':
|
||||
@@ -761,23 +730,6 @@ class TermMergeTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
|
||||
// Adding any extra text assertions on per test-case basis.
|
||||
switch ($case) {
|
||||
case 'via_term_trunk_widget_select_creating_new_term_trunk':
|
||||
$term_trunk = $this->getLastTerm($this->vocabulary);
|
||||
|
||||
// Making sure the parent property of the just created trunk term is
|
||||
// correct.
|
||||
$parents = taxonomy_get_parents_all($term_trunk->tid);
|
||||
$this->assertTrue(count($parents) == 2 && $parents[0]->tid == $term_trunk->tid && $parents[1]->tid == $terms['term_trunk_parent']->tid, 'Parent property of the just created new term trunk is correct.');
|
||||
|
||||
// Adding the submitted field values for further assertions too. This
|
||||
// way we test whether the embedded form adds a new term along with
|
||||
// any fields we have submitted into it.
|
||||
$text_assertions['New Term name'] = $term_trunk_edit['name'];
|
||||
$text_assertions['New Term description'] = $term_trunk_edit['description[value]'];
|
||||
$text_assertions['New Term text field delta #0'] = $term_trunk_edit['field_test_text[' . LANGUAGE_NONE . '][0][value]'];
|
||||
$text_assertions['New Term text field delta #1'] = $term_trunk_edit['field_test_text[' . LANGUAGE_NONE . '][1][value]'];
|
||||
break;
|
||||
|
||||
case 'merge_fields':
|
||||
// Making sure the term trunk has been merged all the fields from term
|
||||
// branches into itself.
|
||||
@@ -841,7 +793,7 @@ class DuplicatesTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
/**
|
||||
* GetInfo method.
|
||||
*/
|
||||
public function getInfo() {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Duplicate terms merge',
|
||||
'description' => 'Ensure that the feature <i>merge duplicate terms</i> of module Term Merge works correctly.',
|
||||
@@ -901,6 +853,35 @@ class DuplicatesTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
$groups['triple_different_parent'][2]->parent = $groups['parent'][0]->tid;
|
||||
taxonomy_term_save($groups['triple_different_parent'][2]);
|
||||
|
||||
// Test duplicate suggestion plugin type. Make sure multiple duplicated
|
||||
// suggestions are properly handed and make sure each of the duplicate
|
||||
// suggestions does its function.
|
||||
$this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates');
|
||||
$this->assertSuggestedDuplicates(array_merge($groups['triple_different_parent'], $groups['random']), 'Filtering only by term names yields expected results.');
|
||||
|
||||
$this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates', array(
|
||||
'settings[duplicate_suggestion][name]' => FALSE,
|
||||
'settings[duplicate_suggestion][description]' => TRUE,
|
||||
), 'Re-run duplicate search');
|
||||
$this->assertSuggestedDuplicates(array_merge($groups['triple_different_parent'], $groups['random']), 'Filtering only by term description yields expected results.');
|
||||
|
||||
$this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates', array(
|
||||
'settings[duplicate_suggestion][name]' => FALSE,
|
||||
'settings[duplicate_suggestion][parent]' => TRUE,
|
||||
), 'Re-run duplicate search');
|
||||
$expected_terms = array();
|
||||
$expected_terms = array_merge($expected_terms, $groups['single'], $groups['random'], $groups['parent']);
|
||||
$expected_terms[] = $groups['triple_different_parent'][0];
|
||||
$this->assertSuggestedDuplicates($expected_terms, 'Filtering only by term parent yields expected results.');
|
||||
|
||||
$this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates', array(
|
||||
'settings[duplicate_suggestion][name]' => TRUE,
|
||||
'settings[duplicate_suggestion][parent]' => TRUE,
|
||||
), 'Re-run duplicate search');
|
||||
$expected_terms = $groups['triple_different_parent'];
|
||||
unset($expected_terms[0]);
|
||||
$this->assertSuggestedDuplicates($expected_terms, 'Filtering by term name and parent yields expected results, i.e. duplicate suggestions can be combined.');
|
||||
|
||||
// Assuring the single term is not listed as duplicate.
|
||||
$this->drupaLGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/merge/duplicates');
|
||||
$this->assertNoText($groups['single'][0]->name, 'Single term is not listed as a duplicate.');
|
||||
@@ -908,23 +889,23 @@ class DuplicatesTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
// Making sure the term in 'triple_different_parent' that does not have a
|
||||
// parent, is not listed when we invoke duplicate tool on a parent term.
|
||||
$this->drupalGet('taxonomy/term/' . $groups['parent'][0]->tid . '/merge/duplicates');
|
||||
$this->assertNoFieldByName('group[' . $this->duplicateProcessName('triple_different_parent') . '][duplicates][' . $groups['triple_different_parent'][0]->tid . ']', 'Duplicate term is not listed when it is not among children of a term, on which Term Merge module was invoked.');
|
||||
$this->assertNoFieldByName('group[' . $this->duplicateHashTerm($groups['triple_different_parent'][0]) . '][duplicates][' . $groups['triple_different_parent'][0]->tid . ']', 'Duplicate term is not listed when it is not among children of a term, on which Term Merge module was invoked.');
|
||||
|
||||
$edit = array();
|
||||
// Trying to merge a term into another, invoking Duplicate tool on a parent
|
||||
// term of both. Important note: we do not test merging options, because
|
||||
// supposedly those are tested in the main test of this module.
|
||||
$edit['group[' . $this->duplicateProcessName('triple_different_parent') . '][trunk_tid]'] = $groups['triple_different_parent'][1]->tid;
|
||||
$edit['group[' . $this->duplicateProcessName('triple_different_parent') . '][duplicates][' . $groups['triple_different_parent'][2]->tid . ']'] = TRUE;
|
||||
$edit['group[' . $this->duplicateHashTerm($groups['triple_different_parent'][1]) . '][trunk_tid]'] = $groups['triple_different_parent'][1]->tid;
|
||||
$edit['group[' . $this->duplicateHashTerm($groups['triple_different_parent'][2]) . '][duplicates][' . $groups['triple_different_parent'][2]->tid . ']'] = TRUE;
|
||||
$groups['triple_different_parent'][2]->merged = TRUE;
|
||||
$this->drupalPost('taxonomy/term/' . $groups['parent'][0]->tid . '/merge/duplicates', $edit, 'Submit');
|
||||
|
||||
// Trying to merge multiple terms. We merge all but the 1st term.
|
||||
$edit = array();
|
||||
$edit['group[' . $this->duplicateProcessName('random') . '][trunk_tid]'] = $groups['random'][0]->tid;
|
||||
$edit['group[' . $this->duplicateHashTerm($groups['random'][0]) . '][trunk_tid]'] = $groups['random'][0]->tid;
|
||||
foreach ($groups['random'] as $k => $term) {
|
||||
if ($k != 0) {
|
||||
$edit['group[' . $this->duplicateProcessName('random') . '][duplicates][' . $term->tid . ']'] = TRUE;
|
||||
$edit['group[' . $this->duplicateHashTerm($groups['random'][$k]) . '][duplicates][' . $term->tid . ']'] = TRUE;
|
||||
$groups['random'][$k]->merged = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -956,19 +937,29 @@ class DuplicatesTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
* Array of fully loaded taxonomy terms objects of the just created terms,
|
||||
* grouped by their group name
|
||||
*/
|
||||
private function createTerms($groups) {
|
||||
protected function createTerms($groups) {
|
||||
foreach ($groups as $name => $quantity) {
|
||||
$groups[$name] = array();
|
||||
$description = $this->randomName();
|
||||
for ($i = 0; $i < $quantity; $i++) {
|
||||
$term_name = '';
|
||||
$term_description = '';
|
||||
// Randomizing case of the group name.
|
||||
foreach (str_split($name) as $symbol) {
|
||||
$symbol = rand(0, 1) ? drupal_strtoupper($symbol) : drupal_strtolower($symbol);
|
||||
$term_name .= $symbol;
|
||||
}
|
||||
$this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add', array(
|
||||
// Getting description in different cases.
|
||||
foreach (str_split($description) as $symbol) {
|
||||
$symbol = rand(0, 1) ? drupal_strtoupper($symbol) : drupal_strtolower($symbol);
|
||||
$term_description .= $symbol;
|
||||
}
|
||||
$term = (object) array(
|
||||
'vid' => $this->vocabulary->vid,
|
||||
'name' => $term_name,
|
||||
), 'Save');
|
||||
'description' => $description,
|
||||
);
|
||||
taxonomy_term_save($term);
|
||||
$groups[$name][] = $this->getLastTerm($this->vocabulary);
|
||||
}
|
||||
}
|
||||
@@ -978,28 +969,61 @@ class DuplicatesTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
/**
|
||||
* Supportive method.
|
||||
*
|
||||
* Manipulate supplied var $name and by the output of this function terms in a
|
||||
* vocabulary are grouped as duplicates. This method should be identical to
|
||||
* the function term_merge_duplicates_process_name(), test case replies on the
|
||||
* fact that both manipulate the string in the identical way.
|
||||
* Calculate hash a term based on which it will be paired with other terms as
|
||||
* possible duplicates of each other.
|
||||
*
|
||||
* @param string $name
|
||||
* String that needs to be manipulated
|
||||
* @param object $term
|
||||
* Term whose duplicate suggestion hash is to be calculated
|
||||
* @param array $duplicate_suggestions
|
||||
* Array of duplicate suggestion names that to apply, when determining hash
|
||||
* of the provided term
|
||||
*
|
||||
* @return string
|
||||
* Processed string (normally it implies making it upper case, stripping
|
||||
* down any special chars, etc.)
|
||||
* Hash of the provided term according to enabled duplicate suggestions
|
||||
*/
|
||||
private function duplicateProcessName($name) {
|
||||
// Making upper case.
|
||||
$name = drupal_strtoupper($name);
|
||||
// Trying transliteration, if available.
|
||||
if (module_exists('transliteration')) {
|
||||
$name = transliteration_get($name);
|
||||
// Keeping only ASCII chars.
|
||||
$name = preg_replace('#\W#', '', $name);
|
||||
protected function duplicateHashTerm($term, $duplicate_suggestions = array('name')) {
|
||||
$hash = '';
|
||||
|
||||
foreach ($duplicate_suggestions as $duplicate_suggestion) {
|
||||
$hash_chunk = '';
|
||||
switch ($duplicate_suggestion) {
|
||||
case 'name':
|
||||
$hash_chunk = drupal_strtoupper($term->name);
|
||||
// Trying transliteration, if available.
|
||||
if (module_exists('transliteration')) {
|
||||
$hash_chunk = transliteration_get($hash_chunk);
|
||||
// Keeping only ASCII chars.
|
||||
$hash_chunk = preg_replace('#\W#', '', $hash_chunk);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'description':
|
||||
$hash_chunk = drupal_strtoupper($term->description);
|
||||
break;
|
||||
|
||||
case 'parent':
|
||||
$hash_chunk = $term->parents[0];
|
||||
break;
|
||||
}
|
||||
$hash .= $hash_chunk;
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert expected terms indeed are suggested as duplicates.
|
||||
*
|
||||
* @param array $expected_terms
|
||||
* Array of terms that are expected to be suggested as duplicates
|
||||
* @param string $message
|
||||
* Assertion message to display on the test results
|
||||
*/
|
||||
protected function assertSuggestedDuplicates($expected_terms, $message = '') {
|
||||
$i = 0;
|
||||
foreach ($expected_terms as $term) {
|
||||
$this->assertPattern('#\<input\s+[^>]*type="checkbox"\s+[^>]*name="[^"]+\[duplicates]\[' . $term->tid . '\]"#si', $message . ' (for term #' . $i . ')');
|
||||
$i++;
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1020,8 +1044,10 @@ class RedirectTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
/**
|
||||
* SetUp method.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp(array('redirect', 'path'));
|
||||
public function setUp(array $modules = array()) {
|
||||
$modules[] = 'redirect';
|
||||
$modules[] = 'path';
|
||||
parent::setUp($modules);
|
||||
|
||||
$this->superAdmin = $this->drupalCreateUser(array(
|
||||
'administer taxonomy',
|
||||
@@ -1036,7 +1062,7 @@ class RedirectTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
/**
|
||||
* GetInfo method.
|
||||
*/
|
||||
public function getInfo() {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Redirect module integration',
|
||||
'description' => 'Ensure that the module Term Merge integrates with ' . l('Redirect', 'http://drupal.org/project/redirect') . '/Path modules correctly.',
|
||||
@@ -1140,9 +1166,9 @@ class RedirectTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
'redirect' => TERM_MERGE_NO_REDIRECT,
|
||||
), 'Submit');
|
||||
$this->drupalPost(NULL, array(), 'Confirm');
|
||||
$this->assertRedirectIntegration($terms, 'No redirections made after running merge batch when not instructed to make redirections.');
|
||||
$this->assertRedirectIntegration($terms, 'No redirection made after running merge batch when not instructed to make redirection.');
|
||||
|
||||
// Trying to merge into an existing term with redirection.
|
||||
// Trying to merge into a term with redirection.
|
||||
$this->drupalPost('taxonomy/term/' . $terms['branch']->tid . '/merge', array(
|
||||
'term_branch[]' => array($terms['branch']->tid),
|
||||
'term_trunk[widget]' => 'select',
|
||||
@@ -1151,27 +1177,7 @@ class RedirectTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
), 'Submit');
|
||||
$terms['branch']->redirect = $terms['trunk'];
|
||||
$this->drupalPost(NULL, array(), 'Confirm');
|
||||
$this->assertRedirectIntegration($terms, 'Redirection is made after running merge batch merging into an existing term, when instructed to make redirections.');
|
||||
|
||||
// Trying to merge into a new term with redirection.
|
||||
$terms = $this->createTerms(array('branch'));
|
||||
$this->drupalPostAJAX('taxonomy/term/' . $terms['branch']->tid . '/merge', array(
|
||||
'term_branch[]' => array($terms['branch']->tid),
|
||||
'term_trunk[widget]' => 'select',
|
||||
'term_trunk[tid]' => TERM_MERGE_NEW_TERM_TRUNK,
|
||||
), 'term_trunk[tid]');
|
||||
|
||||
$this->drupalPost(NULL, array(
|
||||
'term_branch[]' => array($terms['branch']->tid),
|
||||
'term_trunk[widget]' => 'select',
|
||||
'term_trunk[tid]' => TERM_MERGE_NEW_TERM_TRUNK,
|
||||
'name' => $this->randomName(),
|
||||
'redirect' => 0,
|
||||
), 'Submit');
|
||||
$this->drupalPost(NULL, array(), 'Confirm');
|
||||
$terms['trunk'] = $this->getLastTerm($this->vocabulary);
|
||||
$terms['branch']->redirect = $terms['trunk'];
|
||||
$this->assertRedirectIntegration($terms, 'Redirection is made after running merge batch merging into a new term, when instructed to make redirections.');
|
||||
$this->assertRedirectIntegration($terms, 'Redirection is made after running merge batch merging into an existing term, when instructed to make redirection.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1245,21 +1251,44 @@ class RedirectTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
*/
|
||||
class SynonymsTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
|
||||
/**
|
||||
* Field definition array within which the testing will happen.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $field = array(
|
||||
'field_name' => 'term_merge_synonyms_test',
|
||||
'type' => 'text',
|
||||
);
|
||||
|
||||
/**
|
||||
* SetUp method.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp(array('synonyms'));
|
||||
public function setUp(array $modules = array()) {
|
||||
$modules[] = 'synonyms';
|
||||
parent::setUp($modules);
|
||||
// Additionally we enable default synonyms field in the vocabulary.
|
||||
$this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/edit', array(
|
||||
'synonyms[synonyms][' . SYNONYMS_DEFAULT_FIELD_NAME . ']' => TRUE,
|
||||
), 'Save');
|
||||
$this->field = field_create_field($this->field);
|
||||
$instance = array(
|
||||
'field_name' => $this->field['field_name'],
|
||||
'label' => 'Testing term merge synonyms integration',
|
||||
'entity_type' => 'taxonomy_term',
|
||||
'bundle' => $this->vocabulary->machine_name,
|
||||
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
|
||||
);
|
||||
$instance = field_create_instance($instance);
|
||||
$instance = field_info_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
|
||||
synonyms_behavior_settings_save(array(
|
||||
'instance_id' => $instance['id'],
|
||||
'behavior' => 'synonyms',
|
||||
'settings' => array(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* GetInfo method.
|
||||
*/
|
||||
public function getInfo() {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Synonyms module integration',
|
||||
'description' => 'Ensure that the module Term Merge integrates with ' . l('Synonyms', 'http://drupal.org/project/synonyms') . ' module correctly.',
|
||||
@@ -1303,7 +1332,7 @@ class SynonymsTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
// Testing adding as a synonym.
|
||||
actions_do('term_merge_action', $terms['branch'], array(
|
||||
'term_trunk' => $terms['trunk']->tid,
|
||||
'synonyms' => array(SYNONYMS_DEFAULT_FIELD_NAME),
|
||||
'synonyms' => array($this->field['field_name']),
|
||||
));
|
||||
|
||||
$terms['trunk']->synonyms = array($terms['branch']->name);
|
||||
@@ -1321,44 +1350,22 @@ class SynonymsTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
'term_trunk[widget]' => 'select',
|
||||
'term_trunk[tid]' => $terms['trunk']->tid,
|
||||
'term_branch_keep' => TRUE,
|
||||
'synonyms[' . SYNONYMS_DEFAULT_FIELD_NAME . ']' => FALSE,
|
||||
'synonyms[' . $this->field['field_name'] . ']' => FALSE,
|
||||
), 'Submit');
|
||||
$this->drupalPost(NULL, array(), 'Confirm');
|
||||
$this->assertSynonymsIntegration($terms, 'No synonyms are added after running merge batch when not instructed to add synonyms.');
|
||||
|
||||
// Trying to merge into an existing term with synonyms adding.
|
||||
// Trying to merge into a term with synonyms adding.
|
||||
$this->drupalPost('taxonomy/term/' . $terms['branch']->tid . '/merge', array(
|
||||
'term_branch[]' => array($terms['branch']->tid),
|
||||
'term_trunk[widget]' => 'select',
|
||||
'term_trunk[tid]' => $terms['trunk']->tid,
|
||||
'term_branch_keep' => TRUE,
|
||||
'synonyms[' . SYNONYMS_DEFAULT_FIELD_NAME . ']' => TRUE,
|
||||
'synonyms[' . $this->field['field_name'] . ']' => TRUE,
|
||||
), 'Submit');
|
||||
$terms['trunk']->synonyms = array($terms['branch']->name);
|
||||
$this->drupalPost(NULL, array(), 'Confirm');
|
||||
$this->assertSynonymsIntegration($terms, 'Synonyms are added after running merge batch merging into an existing term, when instructed to add synonyms.');
|
||||
|
||||
// Trying to merge into a new term with synonyms adding.
|
||||
$terms = $this->createTerms(array('branch'));
|
||||
$this->drupalPostAJAX('taxonomy/term/' . $terms['branch']->tid . '/merge', array(
|
||||
'term_branch[]' => array($terms['branch']->tid),
|
||||
'term_trunk[widget]' => 'select',
|
||||
'term_branch_keep' => TRUE,
|
||||
'term_trunk[tid]' => TERM_MERGE_NEW_TERM_TRUNK,
|
||||
), 'term_trunk[tid]');
|
||||
|
||||
$this->drupalPost(NULL, array(
|
||||
'term_branch[]' => array($terms['branch']->tid),
|
||||
'term_trunk[widget]' => 'select',
|
||||
'term_trunk[tid]' => TERM_MERGE_NEW_TERM_TRUNK,
|
||||
'term_branch_keep' => TRUE,
|
||||
'name' => $this->randomName(),
|
||||
'synonyms[' . SYNONYMS_DEFAULT_FIELD_NAME . ']' => TRUE,
|
||||
), 'Submit');
|
||||
$this->drupalPost(NULL, array(), 'Confirm');
|
||||
$terms['trunk'] = $this->getLastTerm($this->vocabulary);
|
||||
$terms['trunk']->synonyms = array($terms['branch']->name);
|
||||
$this->assertSynonymsIntegration($terms, 'Synonyms are added after running merge batch merging into a new term, when instructed to add synonyms.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1378,10 +1385,7 @@ class SynonymsTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
drupal_static_reset();
|
||||
foreach ($terms as $term) {
|
||||
// Getting an array of synonyms according to Synonyms module.
|
||||
$synonyms = array();
|
||||
foreach (synonyms_get_term_synonyms(taxonomy_term_load($term->tid)) as $tmp) {
|
||||
$synonyms[] = $tmp['value'];
|
||||
}
|
||||
$synonyms = synonyms_get_raw(taxonomy_term_load($term->tid));
|
||||
|
||||
$expected_synonyms = isset($term->synonyms) ? $term->synonyms : array();
|
||||
// Comparing $synonyms to $expected_synonyms.
|
||||
@@ -1435,8 +1439,9 @@ class ViewsTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
/**
|
||||
* SetUp method.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp(array('views'));
|
||||
public function setUp(array $modules = array()) {
|
||||
$modules[] = 'views';
|
||||
parent::setUp($modules);
|
||||
// Additionally we create a view.
|
||||
$view = views_new_view();
|
||||
$view->name = 'term_merge_view_test';
|
||||
@@ -1456,7 +1461,7 @@ class ViewsTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
/**
|
||||
* GetInfo method.
|
||||
*/
|
||||
public function getInfo() {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Views module integration',
|
||||
'description' => 'Ensure that the module Term Merge integrates with ' . l('Views', 'http://drupal.org/project/views') . ' module correctly.',
|
||||
@@ -1516,7 +1521,8 @@ class ViewsTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
$this->view->set_display('default');
|
||||
|
||||
// We use Field API info to look up necessary tables and columns.
|
||||
$table = reset(array_keys($field['storage']['details']['sql']['FIELD_LOAD_CURRENT']));
|
||||
$table = array_keys($field['storage']['details']['sql']['FIELD_LOAD_CURRENT']);
|
||||
$table = reset($table);
|
||||
$columns = $field['storage']['details']['sql']['FIELD_LOAD_CURRENT'][$table];
|
||||
|
||||
$this->view->display_handler->display->display_options['filters'][$columns['tid']]['id'] = $columns['tid'];
|
||||
@@ -1546,3 +1552,104 @@ class ViewsTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
$this->assertTrue(count($filter) == 1 && in_array($terms['trunk']->tid, array_keys($filter)), 'Views term reference filter gets updated to filter on trunk term instead of filtering on branch term if the branch term is instructed to be deleted during merging of terms.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test integration with Entity Reference module.
|
||||
*/
|
||||
class EntityReferenceTermMergeWebTestCase extends TermMergeWebTestCase {
|
||||
|
||||
/**
|
||||
* Content type used for testing the entity reference field integration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $content_type = 'term_merge_entity_reference';
|
||||
|
||||
/**
|
||||
* Field definition array used for entity reference integration testing.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $field = array(
|
||||
'type' => 'entityreference',
|
||||
'field_name' => 'term_merge_entity_reference',
|
||||
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
|
||||
'settings' => array(
|
||||
'target_type' => 'taxonomy_term',
|
||||
'handler' => 'base',
|
||||
'handler_settings' => array(),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Instance definition array used for entity reference integration testing.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $instance = array();
|
||||
|
||||
/**
|
||||
* GetInfo method.
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Term Merge Entity Reference',
|
||||
'description' => 'Ensure that the module Term Merge integrates with Entity Reference field type correctly.',
|
||||
'group' => 'Term Merge',
|
||||
);
|
||||
}
|
||||
|
||||
public function setUp(array $modules = array()) {
|
||||
$modules[] = 'entityreference';
|
||||
parent::setUp($modules);
|
||||
|
||||
$this->drupalPost('admin/structure/types/add', array(
|
||||
'name' => $this->randomName(),
|
||||
'type' => $this->content_type,
|
||||
), 'Save content type');
|
||||
|
||||
$this->field = field_create_field($this->field);
|
||||
$this->instance['field_name'] = $this->field['field_name'];
|
||||
$this->instance['entity_type'] = 'node';
|
||||
$this->instance['bundle'] = $this->content_type;
|
||||
$this->instance['label'] = $this->randomName();
|
||||
$this->instance = field_create_instance($this->instance);
|
||||
$this->instance = field_info_instance($this->instance['entity_type'], $this->instance['field_name'], $this->instance['bundle']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that entity reference field values get update upon term merging.
|
||||
*/
|
||||
public function testEntityReferenceField() {
|
||||
$terms = array(
|
||||
'trunk' => NULL,
|
||||
'branch' => NULL,
|
||||
);
|
||||
$nodes = array();
|
||||
foreach ($terms as $type => $v) {
|
||||
$terms[$type] = (object) array(
|
||||
'vid' => $this->vocabulary->vid,
|
||||
'name' => $this->randomName(),
|
||||
);
|
||||
taxonomy_term_save($terms[$type]);
|
||||
$nodes[$type] = (object) array(
|
||||
'type' => $this->content_type,
|
||||
'title' => $this->randomName(),
|
||||
$this->field['field_name'] => array(LANGUAGE_NONE => array(
|
||||
array('target_id' => $terms[$type]->tid),
|
||||
)),
|
||||
);
|
||||
node_save($nodes[$type]);
|
||||
}
|
||||
|
||||
actions_do('term_merge_action', $terms['branch'], array(
|
||||
'term_trunk' => $terms['trunk']->tid,
|
||||
'term_branch_keep' => FALSE,
|
||||
));
|
||||
|
||||
foreach ($nodes as $type => $node) {
|
||||
$node = entity_load_unchanged('node', $node->nid);
|
||||
$this->assertEqual($terms['trunk']->tid, $node->{$this->field['field_name']}[LANGUAGE_NONE][0]['target_id'], $type . ' node points to trunk term in the entity reference field after merging the terms.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user