t('Entity'), 'description' => t('Source handler for entities.'), 'plugin controller class' => 'TMGMTEntitySourcePluginController', 'item types' => array(), ); $entity_types = array_filter(variable_get('entity_translation_entity_types', array())); foreach ($entity_types as $entity_key) { $entity_info = entity_get_info($entity_key); $info['entity']['item types'][$entity_key] = $entity_info['label']; } return $info; } /** * Implements hook_form_ID_alter(). * * Alters comment node type select box to filter out comment types that belongs * to non entity translatable node types. */ function tmgmt_entity_form_tmgmt_ui_entity_source_comment_overview_form_alter(&$form, &$form_state) { if (!isset($form['search_wrapper']['search']['node_type'])) { return; } // Change the select name to "type" as in the query submitted value will be // passed into node.type condition. $form['search_wrapper']['search']['type'] = $form['search_wrapper']['search']['node_type']; unset($form['search_wrapper']['search']['node_type']); // Set new default value. $form['search_wrapper']['search']['type']['#default_value'] = isset($_GET['type']) ? $_GET['type'] : NULL; } /** * Helper function to get entity translatable bundles. * * Note that for comment entity type it will return the same as for node as * comment bundles have no use (i.e. in queries). * * @param string $entity_type * Drupal entity type. * * @return array * Array of key => values, where key is type and value its label. */ function tmgmt_entity_get_translatable_bundles($entity_type) { // If given entity type does not have entity translations enabled, no reason // to continue. if (!in_array($entity_type, variable_get('entity_translation_entity_types', array()))) { return array(); } $entity_info = entity_get_info($entity_type); $translatable_bundle_types = array(); foreach ($entity_info['bundles'] as $bundle_type => $bundle_definition) { if ($entity_type == 'comment') { $bundle_type = str_replace('comment_node_', '', $bundle_type); if (variable_get('language_content_type_' . $bundle_type) == ENTITY_TRANSLATION_ENABLED) { $translatable_bundle_types[$bundle_type] = $bundle_definition['label']; } } elseif ($entity_type == 'node') { if (variable_get('language_content_type_' . $bundle_type) == ENTITY_TRANSLATION_ENABLED) { $translatable_bundle_types[$bundle_type] = $bundle_definition['label']; } } else { $translatable_bundle_types[$bundle_type] = $bundle_definition['label']; } } return $translatable_bundle_types; } /** * Gets translatable entities of a given type. * * Additionally you can specify entity property conditions, pager and limit. * * @param string $entity_type * Drupal entity type. * @param array $property_conditions * Entity properties. There is no value processing so caller must make sure * the provided entity property exists for given entity type and its value * is processed. * @param bool $pager * Flag to determine if pager will be used. * * @return array * Array of translatable entities. */ function tmgmt_entity_get_translatable_entities($entity_type, $property_conditions = array(), $pager = FALSE) { if (!in_array($entity_type, variable_get('entity_translation_entity_types', array()))) { return array(); } $languages = drupal_map_assoc(array_keys(language_list())); $entity_info = entity_get_info($entity_type); $label_key = isset($entity_info['entity keys']['label']) ? $entity_info['entity keys']['label'] : NULL; $id_key = $entity_info['entity keys']['id']; $query = db_select($entity_info['base table'], 'e'); $query->addTag('tmgmt_entity_get_translatable_entities'); $query->addField('e', $id_key); // Language neutral entities are not translatable. Filter them out. To do // that: join {entity_translation} table, but only records with source column // empty. The {entity_translation}.language will represent the original entity // language in that case. $source_table_alias = $query->leftJoin('entity_translation', NULL, "%alias.entity_type = :entity_type AND %alias.entity_id = e.$id_key AND %alias.source = ''", array(':entity_type' => $entity_type)); $query->condition("$source_table_alias.language", LANGUAGE_NONE, '<>'); // Searching for sources with missing translation. if (!empty($property_conditions['target_status']) && !empty($property_conditions['target_language']) && in_array($property_conditions['target_language'], $languages)) { $translation_table_alias = db_escape_field('et_' . $property_conditions['target_language']); $query->leftJoin('entity_translation', $translation_table_alias, "%alias.entity_type = :entity_type AND %alias.entity_id = e.$id_key AND %alias.language = :language", array(':entity_type' => $entity_type, ':language' => $property_conditions['target_language'])); // Exclude entities with having source language same as the target language // we search for. $query->condition('e.language', $property_conditions['target_language'], '<>'); if ($property_conditions['target_status'] == 'untranslated_or_outdated') { $or = db_or(); $or->isNull("$translation_table_alias.language"); $or->condition("$translation_table_alias.translate", 1); $query->condition($or); } elseif ($property_conditions['target_status'] == 'outdated') { $query->condition("$translation_table_alias.translate", 1); } elseif ($property_conditions['target_status'] == 'untranslated') { $query->isNull("$translation_table_alias.language"); } } // Remove the condition so we do not try to add it again below. unset($property_conditions['target_language']); unset($property_conditions['target_status']); // Searching for the source label. if (!empty($label_key) && isset($property_conditions[$label_key])) { $search_tokens = explode(' ', $property_conditions[$label_key]); $or = db_or(); foreach ($search_tokens as $search_token) { $search_token = trim($search_token); if (strlen($search_token) > 2) { $or->condition($label_key, "%$search_token%", 'LIKE'); } } if ($or->count() > 0) { $query->condition($or); } unset($property_conditions[$label_key]); } // Searching by taxonomy bundles - we need to switch to vid as the bundle key. if ($entity_type == 'taxonomy_term' && !empty($property_conditions['vocabulary_machine_name'])) { $property_name = 'vid'; $vocabulary = taxonomy_vocabulary_machine_name_load($property_conditions['vocabulary_machine_name']); $property_value = $vocabulary->vid; $query->condition('e.' . $property_name, $property_value); // Remove the condition so we do not try to add it again below. unset($property_conditions['vocabulary_machine_name']); } // Searching by the node bundles - that applies for node entities as well as // comment. elseif (in_array($entity_type, array('comment', 'node'))) { $node_table_alias = 'e'; // For comments join node table so that we can filter based on type. if ($entity_type == 'comment') { $query->join('node', 'n', 'e.nid = n.nid'); $node_table_alias = 'n'; } // Get translatable node types and check if it is worth to continue. $translatable_node_types = array_keys(tmgmt_entity_get_translatable_bundles('node')); if (empty($translatable_node_types)) { return array(); } // If we have type property add condition. if (isset($property_conditions['type'])) { $query->condition($node_table_alias . '.type', $property_conditions['type']); // Remove the condition so we do not try to add it again below. unset($property_conditions['type']); } // If not, query db only for translatable node types. else { $query->condition($node_table_alias . '.type', $translatable_node_types); } } // Add remaining query conditions which are expected to be handled in a // generic way. foreach ($property_conditions as $property_name => $property_value) { $query->condition('e.' . $property_name, $property_value); } if ($pager) { $query = $query->extend('PagerDefault')->limit(variable_get('tmgmt_source_list_limit', 20)); } else { $query->range(0, variable_get('tmgmt_source_list_limit', 20)); } $query->orderBy($entity_info['entity keys']['id'], 'DESC'); $entity_ids = $query->execute()->fetchCol(); $entities = array(); if (!empty($entity_ids)) { $entities = entity_load($entity_type, $entity_ids); } return $entities; } /** * Implements hook_tmgmt_source_suggestions() */ function tmgmt_entity_tmgmt_source_suggestions(array $items, TMGMTJob $job) { $suggestions = array(); // Get all translatable entity types. $entity_types = array_filter(variable_get('entity_translation_entity_types', array())); foreach ($items as $item) { if (($item instanceof TMGMTJobItem) && ($item->plugin == 'entity') || ($item->plugin == 'node')) { // Load the entity and extract the bundle name to get all fields from the // current entity. $entity = entity_load_single($item->item_type, $item->item_id); list(, , $bundle) = entity_extract_ids($item->item_type, $entity); $field_instances = field_info_instances($item->item_type, $bundle); // Loop over all fields, check if they are NOT translatable. Only if a // field is not translatable we may suggest a referenced entity. If so, // check for a supported field type (image and file currently here). foreach ($field_instances as $instance) { $field = field_info_field($instance['field_name']); $field_type = $field['type']; $field_name = $field['field_name']; switch ($field_type) { case 'file': case 'image': // 'File' (and images) must be translatable entity types. // Other files we not suggest here. Get all field items from the // current field and suggest them as translatable. if (isset($entity_types['file']) && ($field_items = field_get_items($item->item_type, $entity, $field_name))) { // Add all files as a suggestion. foreach ($field_items as $field_item) { $file_entity = entity_load_single('file', $field_item['fid']); // Check if there is already a translation available for this // file. If so, just continue with the next file. $handler = entity_translation_get_handler('file', $file_entity); if ($handler instanceof EntityTranslationHandlerInterface) { $translations = $handler->getTranslations(); if (isset($translations->data[$job->target_language])) { continue; } } // Add the translation as a suggestion. $suggestions[] = array( 'job_item' => tmgmt_job_item_create('entity', 'file', $file_entity->fid), 'reason' => t('Field @label', array('@label' => $instance['label'])), 'from_item' => $item->tjiid, ); } } break; case 'entityreference': $target_type = $field['settings']['target_type']; // Make sure only tranlatable entity types are suggested. if (isset($entity_types[$target_type]) && ($field_items = field_get_items($item->item_type, $entity, $field_name))) { // Add all referenced entities as suggestion. foreach ($field_items as $field_item) { $ref_entity = entity_load_single($target_type, $field_item['target_id']); // Check if there is already a translation available for this // entity. If so, just continue with the next one. $handler = entity_translation_get_handler($target_type, $ref_entity); if ($handler instanceof EntityTranslationHandlerInterface) { $translations = $handler->getTranslations(); if (isset($translations->data[$job->target_language])) { continue; } } // Add suggestion. $suggestions[] = array( 'job_item' => tmgmt_job_item_create('entity', $target_type, $field_item['target_id']), 'reason' => t('Field @label', array('@label' => $instance['label'])), 'from_item' => $item->tjiid, ); } } break; } } } } return $suggestions; }