Skip to content
Snippets Groups Projects
Commit 7912fb53 authored by MegaChriz's avatar MegaChriz
Browse files

Issue #2529538 by twistor, MegaChriz, stefan.r, drclaw, olofjohansson, Manish...

Issue #2529538 by twistor, MegaChriz, stefan.r, drclaw, olofjohansson, Manish Jain, et al: Added generic entity language support.
parent 453dddfa
No related branches found
No related tags found
No related merge requests found
......@@ -46,6 +46,9 @@ files[] = tests/feeds_mapper_config.test
files[] = tests/feeds_fetcher_file.test
files[] = tests/feeds_mapper_format_config.test
files[] = tests/feeds_fetcher_http.test
files[] = tests/feeds_i18n.test
files[] = tests/feeds_i18n_node.test
files[] = tests/feeds_i18n_taxonomy.test
files[] = tests/feeds_parser_sitemap.test
files[] = tests/feeds_parser_syndication.test
files[] = tests/feeds_processor_node.test
......
......@@ -36,11 +36,10 @@ class FeedsNodeProcessor extends FeedsProcessor {
* Creates a new node in memory and returns it.
*/
protected function newEntity(FeedsSource $source) {
$node = new stdClass();
$node = parent::newEntity($source);
$node->type = $this->bundle();
$node->changed = REQUEST_TIME;
$node->created = REQUEST_TIME;
$node->language = LANGUAGE_NONE;
$node->is_new = TRUE;
node_object_prepare($node);
// Populate properties that are set by node_object_prepare().
......@@ -127,6 +126,8 @@ class FeedsNodeProcessor extends FeedsProcessor {
* Validates a node.
*/
protected function entityValidate($entity) {
parent::entityValidate($entity);
if (!isset($entity->uid) || !is_numeric($entity->uid)) {
$entity->uid = $this->config['author'];
}
......
......@@ -86,13 +86,22 @@ abstract class FeedsProcessor extends FeedsPlugin {
/**
* Create a new entity.
*
* @param $source
* @param FeedsSource $source
* The feeds source that spawns this entity.
*
* @return
* @return object
* A new entity object.
*/
protected abstract function newEntity(FeedsSource $source);
protected function newEntity(FeedsSource $source) {
$entity = new stdClass();
$info = $this->entityInfo();
if (!empty($info['entity keys']['language'])) {
$entity->{$info['entity keys']['language']} = $this->entityLanguage();
}
return $entity;
}
/**
* Load an existing entity.
......@@ -109,28 +118,46 @@ abstract class FeedsProcessor extends FeedsPlugin {
* existing ids first.
*/
protected function entityLoad(FeedsSource $source, $entity_id) {
$info = $this->entityInfo();
if ($this->config['update_existing'] == FEEDS_UPDATE_EXISTING) {
$entities = entity_load($this->entityType(), array($entity_id));
return reset($entities);
$entity = reset($entities);
}
else {
$args = array(':entity_id' => $entity_id);
$table = db_escape_table($info['base table']);
$key = db_escape_field($info['entity keys']['id']);
$entity = db_query("SELECT * FROM {" . $table . "} WHERE $key = :entity_id", $args)->fetchObject();
}
$info = $this->entityInfo();
$args = array(':entity_id' => $entity_id);
$table = db_escape_table($info['base table']);
$key = db_escape_field($info['entity keys']['id']);
if ($entity && !empty($info['entity keys']['language'])) {
$entity->{$info['entity keys']['language']} = $this->entityLanguage();
}
return db_query("SELECT * FROM {" . $table . "} WHERE $key = :entity_id", $args)->fetchObject();
return $entity;
}
/**
* Validate an entity.
* Validates an entity.
*
* @throws FeedsValidationException $e
* If validation fails.
* Thrown if validation fails.
*/
protected function entityValidate($entity) {}
protected function entityValidate($entity) {
$info = $this->entityInfo();
if (empty($info['entity keys']['language'])) {
return;
}
// Ensure that a valid language is always set.
$key = $info['entity keys']['language'];
$languages = language_list('enabled');
if (empty($entity->$key) || !isset($languages[1][$entity->$key])) {
$entity->$key = $this->entityLanguage();
}
}
/**
* Access check for saving an enity.
......@@ -169,6 +196,28 @@ abstract class FeedsProcessor extends FeedsPlugin {
return entity_get_info($this->entityType());
}
/**
* Returns the current language for entities.
*
* This checks if the configuration value is valid.
*
* @return string
* The current language code.
*/
protected function entityLanguage() {
if (!module_exists('locale')) {
// language_list() may return languages even if the locale module is
// disabled. See https://www.drupal.org/node/173227 why.
// When the locale module is disabled, there are no selectable languages
// in the UI, so the content should be imported in LANGUAGE_NONE.
return LANGUAGE_NONE;
}
$languages = language_list('enabled');
return isset($languages[1][$this->config['language']]) ? $this->config['language'] : LANGUAGE_NONE;
}
/**
* @}
*/
......@@ -764,6 +813,7 @@ abstract class FeedsProcessor extends FeedsPlugin {
'input_format' => NULL,
'skip_hash_check' => FALSE,
'bundle' => $bundle,
'language' => LANGUAGE_NONE,
);
}
......@@ -790,6 +840,16 @@ abstract class FeedsProcessor extends FeedsPlugin {
);
}
if (module_exists('locale') && !empty($info['entity keys']['language'])) {
$form['language'] = array(
'#type' => 'select',
'#options' => array(LANGUAGE_NONE => t('Language neutral')) + locale_language_list('name'),
'#title' => t('Language'),
'#required' => TRUE,
'#default_value' => $this->config['language'],
);
}
$tokens = array('@entities' => strtolower($info['label plural']));
$form['insert_new'] = array(
......
......@@ -31,9 +31,10 @@ class FeedsTermProcessor extends FeedsProcessor {
*/
protected function newEntity(FeedsSource $source) {
$vocabulary = $this->vocabulary();
$term = new stdClass();
$term = parent::newEntity($source);
$term->vid = $vocabulary->vid;
$term->vocabulary_machine_name = $vocabulary->machine_name;
return $term;
}
......@@ -41,6 +42,8 @@ class FeedsTermProcessor extends FeedsProcessor {
* Validates a term.
*/
protected function entityValidate($term) {
parent::entityValidate($term);
if (drupal_strlen($term->name) == 0) {
throw new FeedsValidationException(t('Term name missing.'));
}
......
......@@ -37,10 +37,11 @@ class FeedsUserProcessor extends FeedsProcessor {
* Creates a new user account in memory and returns it.
*/
protected function newEntity(FeedsSource $source) {
$account = new stdClass();
$account = parent::newEntity($source);
$account->uid = 0;
$account->roles = array_filter($this->config['roles']);
$account->status = $this->config['status'];
return $account;
}
......@@ -59,6 +60,8 @@ class FeedsUserProcessor extends FeedsProcessor {
* Validates a user account.
*/
protected function entityValidate($account) {
parent::entityValidate($account);
if (empty($account->name) || empty($account->mail) || !valid_email_address($account->mail)) {
throw new FeedsValidationException(t('User name missing or email not valid.'));
}
......
"guid","title","created","alpha","beta","gamma","delta","body","language"
1,"Lorem ipsum",1251936720,"Lorem",42,"4.2",3.14159265,"Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.","nl"
2,"Ut wisi enim ad minim veniam",1251932360,"Ut wisi",32,"1.2",5.62951413,"Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat."
<?php
/**
* @file
* Contains Feedsi18nTestCase.
*/
/**
* Tests importing data in a language.
*/
class Feedsi18nTestCase extends FeedsMapperTestCase {
/**
* The entity type to be tested.
*
* @var string
*/
protected $entityType;
/**
* The processor being used.
*
* @var string
*/
protected $processorName;
public function setUp($modules = array(), $permissions = array()) {
$modules = array_merge($modules, array(
'locale',
));
$permissions = array_merge(array(
'administer languages',
));
parent::setUp($modules, $permissions);
// Setup other languages.
$edit = array(
'langcode' => 'nl',
);
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
$this->assertText(t('The language Dutch has been created and can now be used.'));
$edit = array(
'langcode' => 'de',
);
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
$this->assertText(t('The language German has been created and can now be used.'));
// Include FeedsProcessor.inc to make its constants available.
module_load_include('inc', 'feeds', 'plugins/FeedsProcessor');
// Create and configure importer.
$this->createImporterConfiguration('Multilingual term importer', 'i18n');
$this->setPlugin('i18n', 'FeedsFileFetcher');
$this->setPlugin('i18n', 'FeedsCSVParser');
$this->setPlugin('i18n', $this->processorName);
}
/**
* Tests if entities get the language assigned that is set in the processor.
*/
public function testImport() {
// Import content in German.
$this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
// Assert that the entity's language is in German.
$entities = entity_load($this->entityType, array(1, 2));
foreach ($entities as $entity) {
$this->assertEqual('de', entity_language($this->entityType, $entity));
}
}
/**
* Tests if entities get a different language assigned when the processor's language
* is changed.
*/
public function testChangedLanguageImport() {
// Import content in German.
$this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
// Change processor's language to Dutch.
$this->setSettings('i18n', $this->processorName, array('language' => 'nl'));
// Re-import content.
$this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
// Assert that the entity's language is now in Dutch.
$entities = entity_load($this->entityType, array(1, 2));
foreach ($entities as $entity) {
$this->assertEqual('nl', entity_language($this->entityType, $entity));
}
}
/**
* Tests if items are imported in LANGUAGE_NONE if the processor's language is disabled.
*/
public function testDisabledLanguage() {
// Disable the German language.
$path = 'admin/config/regional/language';
$edit = array(
'enabled[de]' => FALSE,
);
$this->drupalPost($path, $edit, t('Save configuration'));
// Import content.
$this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
// Assert that the entities have no language assigned.
$entities = entity_load($this->entityType, array(1, 2));
foreach ($entities as $entity) {
$language = entity_language($this->entityType, $entity);
$this->assertEqual(LANGUAGE_NONE, $language, format_string('The entity is language neutral (actual: !language).', array('!language' => $language)));
}
}
/**
* Tests if items are imported in LANGUAGE_NONE if the processor's language is removed.
*/
public function testRemovedLanguage() {
// Remove the German language.
$path = 'admin/config/regional/language/delete/de';
$this->drupalPost($path, array(), t('Delete'));
// Import content.
$this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
// Assert that the entities have no language assigned.
$entities = entity_load($this->entityType, array(1, 2));
foreach ($entities as $entity) {
$language = entity_language($this->entityType, $entity);
$this->assertEqual(LANGUAGE_NONE, $language, format_string('The entity is language neutral (actual: !language).', array('!language' => $language)));
}
}
}
<?php
/**
* @file
* Contains Feedsi18nNodeTestCase.
*/
/**
* Tests importing nodes in a language.
*/
class Feedsi18nNodeTestCase extends Feedsi18nTestCase {
/**
* Name of created content type.
*
* @var string
*/
private $contentType;
public static function getInfo() {
return array(
'name' => 'Multilingual content',
'description' => 'Tests Feeds multilingual support for nodes.',
'group' => 'Feeds',
'dependencies' => array('locale'),
);
}
public function setUp($modules = array(), $permissions = array()) {
$this->entityType = 'node';
$this->processorName = 'FeedsNodeProcessor';
parent::setUp($modules, $permissions);
// Create content type.
$this->contentType = $this->createContentType();
// Configure importer.
$this->setSettings('i18n', $this->processorName, array(
'bundle' => $this->contentType,
'language' => 'de',
'update_existing' => FEEDS_UPDATE_EXISTING,
'skip_hash_check' => TRUE,
));
$this->addMappings('i18n', array(
0 => array(
'source' => 'guid',
'target' => 'guid',
'unique' => '1',
),
1 => array(
'source' => 'title',
'target' => 'title',
),
));
}
/**
* Tests if the language setting is available on the processor.
*/
public function testAvailableProcessorLanguageSetting() {
// Check if the language setting is available when the locale module is enabled.
$this->drupalGet('admin/structure/feeds/i18n/settings/FeedsNodeProcessor');
$this->assertField('language', 'Language field is available on the node processor settings when the locale module is enabled.');
// Disable the locale module and check if the language setting is no longer available.
module_disable(array('locale'));
$this->drupalGet('admin/structure/feeds/i18n/settings/FeedsNodeProcessor');
$this->assertNoField('language', 'Language field is not available on the node processor settings when the locale module is disabled.');
}
/**
* Tests processor language setting in combination with language mapping target.
*/
public function testWithLanguageMappingTarget() {
$this->addMappings('i18n', array(
2 => array(
'source' => 'language',
'target' => 'language',
),
));
// Import csv file. The first item has a language specified (Dutch), the second
// one has no language specified and should be imported in the processor's language (German).
$this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content_i18n.csv');
// The first node should be Dutch.
$node = node_load(1);
$this->assertEqual('nl', entity_language('node', $node), 'Item 1 has the Dutch language assigned.');
// The second node should be German.
$node = node_load(2);
$this->assertEqual('de', entity_language('node', $node), 'Item 2 has the German language assigned.');
}
/**
* Tests if nodes get imported in LANGUAGE_NONE when the locale module gets disabled.
*/
public function testDisabledLocaleModule() {
module_disable(array('locale'));
// Make sure that entity info is reset.
drupal_flush_all_caches();
drupal_static_reset();
// Import content.
$this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
// Assert that the content has no language assigned.
for ($i = 1; $i <= 2; $i++) {
$node = node_load($i);
$language = entity_language('node', $node);
$this->assertEqual(LANGUAGE_NONE, $language, format_string('The node is language neutral (actual: !language).', array('!language' => $language)));
}
}
/**
* Tests if nodes get imported in LANGUAGE_NONE when the locale module gets uninstalled.
*/
public function testUninstalledLocaleModule() {
module_disable(array('locale'));
drupal_uninstall_modules(array('locale'));
// Make sure that entity info is reset.
drupal_flush_all_caches();
drupal_static_reset();
// Import content.
$this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
// Assert that the content has no language assigned.
for ($i = 1; $i <= 2; $i++) {
$node = node_load($i);
$language = entity_language('node', $node);
$this->assertEqual(LANGUAGE_NONE, $language, format_string('The node is language neutral (actual: !language).', array('!language' => $language)));
}
}
}
<?php
/**
* @file
* Contains Feedsi18nTaxonomyTestCase.
*/
/**
* Tests importing terms in a language.
*/
class Feedsi18nTaxonomyTestCase extends Feedsi18nTestCase {
/**
* Name of created vocabulary.
*
* @var string
*/
private $vocabulary;
public static function getInfo() {
return array(
'name' => 'Multilingual terms',
'description' => 'Tests Feeds multilingual support for taxonomy terms.',
'group' => 'Feeds',
'dependencies' => array('locale', 'i18n_taxonomy'),
);
}
public function setUp($modules = array(), $permissions = array()) {
$this->entityType = 'taxonomy_term';
$this->processorName = 'FeedsTermProcessor';
$modules = array_merge($modules, array(
'i18n_taxonomy',
));
parent::setUp($modules, $permissions);
// Create vocabulary.
$this->vocabulary = strtolower($this->randomName(8));
$edit = array(
'name' => $this->vocabulary,
'machine_name' => $this->vocabulary,
);
$this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save'));
// Configure importer.
$this->setSettings('i18n', $this->processorName, array(
'bundle' => $this->vocabulary,
'language' => 'de',
'update_existing' => FEEDS_UPDATE_EXISTING,
'skip_hash_check' => TRUE,
));
$this->addMappings('i18n', array(
0 => array(
'source' => 'guid',
'target' => 'guid',
'unique' => '1',
),
1 => array(
'source' => 'title',
'target' => 'name',
),
));
}
/**
* Tests if the language setting is available on the processor.
*/
public function testAvailableProcessorLanguageSetting() {
// Check if the language setting is available when the i18n_taxonomy module is enabled.
$this->drupalGet('admin/structure/feeds/i18n/settings/FeedsTermProcessor');
$this->assertField('language', 'Language field is available on the term processor settings when the i18n_taxonomy module is enabled.');
// Disable the i18n_taxonomy module and check if the language setting is no longer available.
module_disable(array('i18n_taxonomy'));
$this->drupalGet('admin/structure/feeds/i18n/settings/FeedsTermProcessor');
$this->assertNoField('language', 'Language field is not available on the term processor settings when the i18n_taxonomy module is disabled.');
}
/**
* Tests if terms get imported in LANGUAGE_NONE when the i18n_taxonomy module gets disabled.
*/
public function testDisabledi18nTaxonomyModule() {
module_disable(array('i18n_taxonomy'));
// Make sure that entity info is reset.
drupal_flush_all_caches();
drupal_static_reset();
// Import content.
$this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
// Assert that the terms have no language assigned.
$entities = entity_load($this->entityType, array(1, 2));
foreach ($entities as $entity) {
// Terms shouldn't have a language property.
$this->assertFalse(isset($entity->language), 'The term does not have a language.');
}
}
/**
* Tests if terms get imported in LANGUAGE_NONE when the i18n_taxonomy module gets uninstalled.
*/
public function testUninstalledi18nTaxonomyModule() {
module_disable(array('i18n_taxonomy'));
drupal_uninstall_modules(array('i18n_taxonomy'));
// Make sure that entity info is reset.
drupal_flush_all_caches();
drupal_static_reset();
// Import content.
$this->importFile('i18n', $this->absolutePath() . '/tests/feeds/content.csv');
// Assert that the terms have no language assigned.
$entities = entity_load($this->entityType, array(1, 2));
foreach ($entities as $entity) {
$this->assertFalse(isset($entity->language), 'The term does not have a language.');
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment