From 6f438a92d0540fe990e0ea6e15652982329ff0ac Mon Sep 17 00:00:00 2001 From: Alex Barth <alex_b@53995.no-reply.drupal.org> Date: Sat, 5 Dec 2009 01:38:27 +0000 Subject: [PATCH] #623424 Kars-T, Eugen Mayer, alex_b: Mapper for Taxonomy. --- CHANGELOG.txt | 1 + mappers/taxonomy.inc | 105 +++++++++++++++++ tests/feeds_mapper_taxonomy.test | 186 +++++++++++++++++++++++++++++++ 3 files changed, 292 insertions(+) create mode 100644 mappers/taxonomy.inc create mode 100644 tests/feeds_mapper_taxonomy.test diff --git a/CHANGELOG.txt b/CHANGELOG.txt index fd7c307e..78fc0957 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -3,6 +3,7 @@ Feeds 6.x 1.0 xxxx, 200x-xx-xx ------------------------------ +- #623424 Kars-T, Eugen Mayer, alex_b: Mapper for Taxonomy. - #649552 rsoden: Provide variable for data table name. - #631962 velosol, alex_b: FeedsNodeProcessor: Update when changed. - #623452 mongolito404: Port basic test infrastructure for mappers, test for diff --git a/mappers/taxonomy.inc b/mappers/taxonomy.inc new file mode 100644 index 00000000..c9370a5a --- /dev/null +++ b/mappers/taxonomy.inc @@ -0,0 +1,105 @@ +<?php +// $Id$ + +/** + * @file + * Mapper that exposes a node's taxonomy vocabularies as mapping targets. + */ + +/** + * Implementation of hook_feeds_node_processor_targets_alter(). + * + * @see FeedsNodeProcessor::getMappingTargets(). + */ +function taxonomy_feeds_node_processor_targets_alter(&$targets, $content_type) { + foreach (taxonomy_get_vocabularies($content_type) as $vocab) { + $description = t('The !name vocabulary of the node. If this is a "Tags" vocabulary, any new terms will be created on import. Otherwise only existing terms will be used. If this is not a "Tags" vocabulary and not a "Multiple select" vocabulary, only the first available term will be created. See !settings.', array('!name' => $vocab->name, '!settings' => l(t('vocabulary settings'), 'admin/content/taxonomy/edit/vocabulary/'. $vocab->vid, array('query' => 'destination='. $_GET['q'])))); + + $targets['taxonomy:'. $vocab->vid] = array( + 'name' => "Taxonomy: ". $vocab->name, + 'callback' => 'taxonomy_feeds_set_target', + 'description' => $description, + ); + } +} + +/** + * Callback for mapping. Here is where the actual mapping happens. + * + * @param $node + * Reference to the node object we are working on. + * + * @param $key + * A key as added to the $targets array by + * taxonomy_feeds_node_processor_targets_alter(). + * + * @param $terms + * Given terms as array. + * + * Add the given terms to the node object so the taxonomy module can add them + * on node_save(). + */ +function taxonomy_feeds_set_target(&$node, $key, $terms) { + + // Return if there are no terms. + if (empty($terms)) { + return; + } + + // Load target vocabulary to check, if it has the "tags" flag. + $vocab_id = (int) str_replace('taxonomy:', '', $key); + $vocab = taxonomy_vocabulary_load($vocab_id); + + // Cast a given single string to an array so we can use it. + if (!is_array($terms)) { + $terms = array($terms); + } + + if ($vocab->tags) { + // Simply add a comma separated list to the node for a "tags" vocabulary. + $node->taxonomy['tags'][$vocab->vid] = implode(',', $terms); + } + else { + foreach ($terms as $term_name) { + // Check if a term already exists. + if ($terms_found = taxonomy_get_term_by_name_vid($term_name, $vocab->vid)) { + // If any terms are found add them to the node's taxonomy by found tid. + foreach ($terms_found AS $term_found) { + $node->taxonomy[$vocab->vid][$term_found->tid] = $term_found->tid; + if (!$vocab->multiple) { + break; + } + } + // If the vocab is not for multiple tags break after the first hit. + if (!$vocab->multiple) { + break; + } + + } + } + } +} + +/** + * Try to map a string to an existing term by name and vocabulary id. + * + * Provides a case-insensitive and trimmed mapping, to maximize the likelihood + * of a successful match limited by a vocabulary id. + * + * @param $name + * Name of the term to search for. + * + * @param $vid + * The vocabulary's ID. + * + * @return + * An array of matching term objects. + */ +function taxonomy_get_term_by_name_vid($name, $vid) { + $db_result = db_query(db_rewrite_sql("SELECT t.tid, t.name FROM {term_data} t WHERE LOWER(t.name) = LOWER('%s') AND vid=%d", 't', 'tid'), trim($name), $vid); + $result = array(); + while ($term = db_fetch_object($db_result)) { + $result[] = $term; + } + return $result; +} diff --git a/tests/feeds_mapper_taxonomy.test b/tests/feeds_mapper_taxonomy.test new file mode 100644 index 00000000..1a43038e --- /dev/null +++ b/tests/feeds_mapper_taxonomy.test @@ -0,0 +1,186 @@ +<?php +// $Id$ + +/** + * @file + * Test case for taxonomy field mapper mappers/taxonomy.inc. + */ + +require_once(drupal_get_path('module', 'feeds') . '/tests/feeds_mapper_test.inc'); + +/** + * Class for testing Feeds <em>content</em> mapper. + */ +class FeedsMapperTaxonomyTestCase extends FeedsMapperTestCase { + + public static function getInfo() { + return array( + 'name' => t('Mapper: Taxonomy'), + 'description' => t('Test Feeds Mapper support for Taxonomy.'), + 'group' => t('Feeds'), + ); + } + + /** + * Set up the test. + */ + function setUp() { + // Call parent setup with required modules. + parent::setUp('feeds', 'feeds_ui', 'ctools', 'taxonomy'); + + // Create user and login. + $this->drupalLogin($this->drupalCreateUser( + array( + 'administer content types', + 'administer feeds', + 'administer nodes', + 'administer site configuration', + 'administer taxonomy', + ) + )); + } + + /** + * Basic test loading an RSS feed. + */ + function test() { + + // Add a new taxonomy vocabulary, add to story content type. + $edit = array( + 'name' => 'Tags', + 'tags' => TRUE, + 'nodes[story]' => TRUE, + ); + $this->drupalPost('admin/content/taxonomy/add/vocabulary', $edit, 'Save'); + + // Create a feed, include mapping to taxonomy. + $this->createFeedConfiguration('Syndication', 'syndication'); + $this->addMappings('syndication', + array( + array( + 'source' => 'title', + 'target' => 'title', + 'unique' => FALSE, + ), + array( + 'source' => 'description', + 'target' => 'body', + 'unique' => FALSE, + ), + array( + 'source' => 'timestamp', + 'target' => 'created', + 'unique' => FALSE, + ), + array( + 'source' => 'url', + 'target' => 'url', + 'unique' => TRUE, + ), + array( + 'source' => 'guid', + 'target' => 'guid', + 'unique' => TRUE, + ), + array( + 'source' => 'tags', + 'target' => 'taxonomy:1', + ), + ) + ); + + // Aggregate feed node with "Tag" vocabulary. + $nid = $this->createFeedNode(); + // Assert 10 items aggregated after creation of the node. + $this->assertText('Created 10 Story nodes.'); + // There should be 30 terms and 44 term-node relations. + $this->assertEqual(30, db_result(db_query('SELECT count(*) FROM {term_data}')), 'Found correct number of terms.'); + $this->assertEqual(44, db_result(db_query('SELECT count(*) FROM {term_node}')), 'Found correct number of term-node relations.'); + + // Take a look at the actual terms on frontpage. + $this->drupalGet('node'); + $terms = array( + 'authentication', + 'custom mapping', + 'data visualization', + 'Drupal', + 'Drupal planet', + 'faceted search', + 'GeoDC', + 'graphs', + 'interface', + 'intranet', + 'localization', + 'localization client', + 'localization server', + 'map-basec browser', + 'mapbox', + 'microfinance', + 'MIX Market', + 'open atrium', + 'open data', + 'open source', + 'Peru', + 'salesforce', + 'siteminder', + 'siteminder module', + 'software freedom day', + 'translation', + 'translation server', + 'usability', + 'Washington DC', + 'World Bank', + ); + foreach ($terms as $term) { + $term = check_plain($term); + $this->assertPattern('/<a href="(.*?)\/taxonomy\/term\/([0-9]*?)"(.*)>'. $term .'<\/a>/', 'Found '. $term); + } + + // Delete all items, all associations are gone. + $this->drupalPost('node/'. $nid .'/delete-items', array(), 'Delete'); + $this->assertText('Deleted 10 nodes.'); + $this->assertEqual(30, db_result(db_query('SELECT count(*) FROM {term_data}')), 'Found correct number of terms.'); + $this->assertEqual(0, db_result(db_query('SELECT count(*) FROM {term_node}')), 'Found correct number of term-node relations.'); + + // Remove "Tag" setting, import again. + $edit = array( + 'tags' => FALSE, + ); + $this->drupalPost('admin/content/taxonomy/edit/vocabulary/1', $edit, 'Save'); + $this->drupalPost('node/'. $nid .'/import', array(), 'Import'); + $this->assertText('Created 10 Story nodes.'); + + // We should only get one term-node association per node. + $this->assertEqual(30, db_result(db_query('SELECT count(*) FROM {term_data}')), 'Found correct number of terms.'); + $this->assertEqual(10, db_result(db_query('SELECT count(*) FROM {term_node}')), 'Found correct number of term-node relations.'); + + // Delete all items. + $this->drupalPost('node/'. $nid .'/delete-items', array(), 'Delete'); + + // Set vocabulary to multiple terms, import again. + $edit = array( + 'multiple' => TRUE, + ); + $this->drupalPost('admin/content/taxonomy/edit/vocabulary/1', $edit, 'Save'); + $this->drupalPost('node/'. $nid .'/import', array(), 'Import'); + $this->assertText('Created 10 Story nodes.'); + + // We should get all term-node associations again. + $this->assertEqual(30, db_result(db_query('SELECT count(*) FROM {term_data}')), 'Found correct number of terms.'); + $this->assertEqual(44, db_result(db_query('SELECT count(*) FROM {term_node}')), 'Found correct number of term-node relations.'); + + // Delete all items. + $this->drupalPost('node/'. $nid .'/delete-items', array(), 'Delete'); + + // Remove a term, import again. + $this->drupalPost('admin/content/taxonomy/edit/term/1', array(), 'Delete'); + $this->drupalPost(NULL, array(), 'Delete'); + $this->assertText('Deleted term'); + $this->drupalPost('node/'. $nid .'/import', array(), 'Import'); + $this->assertText('Created 10 Story nodes.'); + + // This term should now be missing from term-node associations. + $this->assertEqual(29, db_result(db_query('SELECT count(*) FROM {term_data}')), 'Found correct number of terms.'); + $this->assertEqual(39, db_result(db_query('SELECT count(*) FROM {term_node}')), 'Found correct number of term-node relations.'. db_result(db_query('SELECT count(*) FROM {term_node}'))); + } +} -- GitLab