tmgmt_entity.module 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. <?php
  2. /**
  3. * @file
  4. * Source plugin for the Translation Management system that handles entities.
  5. */
  6. /**
  7. * Implements hook_tmgmt_source_plugin_info().
  8. */
  9. function tmgmt_entity_tmgmt_source_plugin_info() {
  10. $info['entity'] = array(
  11. 'label' => t('Entity'),
  12. 'description' => t('Source handler for entities.'),
  13. 'plugin controller class' => 'TMGMTEntitySourcePluginController',
  14. 'item types' => array(),
  15. );
  16. $entity_types = array_filter(variable_get('entity_translation_entity_types', array()));
  17. foreach ($entity_types as $entity_key) {
  18. $entity_info = entity_get_info($entity_key);
  19. $info['entity']['item types'][$entity_key] = $entity_info['label'];
  20. }
  21. return $info;
  22. }
  23. /**
  24. * Implements hook_form_ID_alter().
  25. *
  26. * Alters comment node type select box to filter out comment types that belongs
  27. * to non entity translatable node types.
  28. */
  29. function tmgmt_entity_form_tmgmt_ui_entity_source_comment_overview_form_alter(&$form, &$form_state) {
  30. if (!isset($form['search_wrapper']['search']['node_type'])) {
  31. return;
  32. }
  33. // Change the select name to "type" as in the query submitted value will be
  34. // passed into node.type condition.
  35. $form['search_wrapper']['search']['type'] = $form['search_wrapper']['search']['node_type'];
  36. unset($form['search_wrapper']['search']['node_type']);
  37. // Set new default value.
  38. $form['search_wrapper']['search']['type']['#default_value'] = isset($_GET['type']) ? $_GET['type'] : NULL;
  39. }
  40. /**
  41. * Helper function to get entity translatable bundles.
  42. *
  43. * Note that for comment entity type it will return the same as for node as
  44. * comment bundles have no use (i.e. in queries).
  45. *
  46. * @param string $entity_type
  47. * Drupal entity type.
  48. *
  49. * @return array
  50. * Array of key => values, where key is type and value its label.
  51. */
  52. function tmgmt_entity_get_translatable_bundles($entity_type) {
  53. // If given entity type does not have entity translations enabled, no reason
  54. // to continue.
  55. if (!in_array($entity_type, variable_get('entity_translation_entity_types', array()))) {
  56. return array();
  57. }
  58. $entity_info = entity_get_info($entity_type);
  59. $translatable_bundle_types = array();
  60. foreach ($entity_info['bundles'] as $bundle_type => $bundle_definition) {
  61. if ($entity_type == 'comment') {
  62. $bundle_type = str_replace('comment_node_', '', $bundle_type);
  63. if (variable_get('language_content_type_' . $bundle_type) == ENTITY_TRANSLATION_ENABLED) {
  64. $translatable_bundle_types[$bundle_type] = $bundle_definition['label'];
  65. }
  66. }
  67. elseif ($entity_type == 'node') {
  68. if (variable_get('language_content_type_' . $bundle_type) == ENTITY_TRANSLATION_ENABLED) {
  69. $translatable_bundle_types[$bundle_type] = $bundle_definition['label'];
  70. }
  71. }
  72. else {
  73. $translatable_bundle_types[$bundle_type] = $bundle_definition['label'];
  74. }
  75. }
  76. return $translatable_bundle_types;
  77. }
  78. /**
  79. * Gets translatable entities of a given type.
  80. *
  81. * Additionally you can specify entity property conditions, pager and limit.
  82. *
  83. * @param string $entity_type
  84. * Drupal entity type.
  85. * @param array $property_conditions
  86. * Entity properties. There is no value processing so caller must make sure
  87. * the provided entity property exists for given entity type and its value
  88. * is processed.
  89. * @param bool $pager
  90. * Flag to determine if pager will be used.
  91. *
  92. * @return array
  93. * Array of translatable entities.
  94. */
  95. function tmgmt_entity_get_translatable_entities($entity_type, $property_conditions = array(), $pager = FALSE) {
  96. if (!in_array($entity_type, variable_get('entity_translation_entity_types', array()))) {
  97. return array();
  98. }
  99. $languages = drupal_map_assoc(array_keys(language_list()));
  100. $entity_info = entity_get_info($entity_type);
  101. $label_key = isset($entity_info['entity keys']['label']) ? $entity_info['entity keys']['label'] : NULL;
  102. $id_key = $entity_info['entity keys']['id'];
  103. $query = db_select($entity_info['base table'], 'e');
  104. $query->addTag('tmgmt_entity_get_translatable_entities');
  105. $query->addField('e', $id_key);
  106. // Language neutral entities are not translatable. Filter them out. To do
  107. // that: join {entity_translation} table, but only records with source column
  108. // empty. The {entity_translation}.language will represent the original entity
  109. // language in that case.
  110. $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));
  111. $query->condition("$source_table_alias.language", LANGUAGE_NONE, '<>');
  112. // Searching for sources with missing translation.
  113. if (!empty($property_conditions['target_status']) && !empty($property_conditions['target_language']) && in_array($property_conditions['target_language'], $languages)) {
  114. $translation_table_alias = db_escape_field('et_' . $property_conditions['target_language']);
  115. $query->leftJoin('entity_translation', $translation_table_alias, "%alias.entity_type = :entity_type AND %alias.entity_id = e.$id_key AND %alias.language = :language",
  116. array(':entity_type' => $entity_type, ':language' => $property_conditions['target_language']));
  117. // Exclude entities with having source language same as the target language
  118. // we search for.
  119. $query->condition('e.language', $property_conditions['target_language'], '<>');
  120. if ($property_conditions['target_status'] == 'untranslated_or_outdated') {
  121. $or = db_or();
  122. $or->isNull("$translation_table_alias.language");
  123. $or->condition("$translation_table_alias.translate", 1);
  124. $query->condition($or);
  125. }
  126. elseif ($property_conditions['target_status'] == 'outdated') {
  127. $query->condition("$translation_table_alias.translate", 1);
  128. }
  129. elseif ($property_conditions['target_status'] == 'untranslated') {
  130. $query->isNull("$translation_table_alias.language");
  131. }
  132. }
  133. // Remove the condition so we do not try to add it again below.
  134. unset($property_conditions['target_language']);
  135. unset($property_conditions['target_status']);
  136. // Searching for the source label.
  137. if (!empty($label_key) && isset($property_conditions[$label_key])) {
  138. $search_tokens = explode(' ', $property_conditions[$label_key]);
  139. $or = db_or();
  140. foreach ($search_tokens as $search_token) {
  141. $search_token = trim($search_token);
  142. if (strlen($search_token) > 2) {
  143. $or->condition($label_key, "%$search_token%", 'LIKE');
  144. }
  145. }
  146. if ($or->count() > 0) {
  147. $query->condition($or);
  148. }
  149. unset($property_conditions[$label_key]);
  150. }
  151. // Searching by taxonomy bundles - we need to switch to vid as the bundle key.
  152. if ($entity_type == 'taxonomy_term' && !empty($property_conditions['vocabulary_machine_name'])) {
  153. $property_name = 'vid';
  154. $vocabulary = taxonomy_vocabulary_machine_name_load($property_conditions['vocabulary_machine_name']);
  155. $property_value = $vocabulary->vid;
  156. $query->condition('e.' . $property_name, $property_value);
  157. // Remove the condition so we do not try to add it again below.
  158. unset($property_conditions['vocabulary_machine_name']);
  159. }
  160. // Searching by the node bundles - that applies for node entities as well as
  161. // comment.
  162. elseif (in_array($entity_type, array('comment', 'node'))) {
  163. $node_table_alias = 'e';
  164. // For comments join node table so that we can filter based on type.
  165. if ($entity_type == 'comment') {
  166. $query->join('node', 'n', 'e.nid = n.nid');
  167. $node_table_alias = 'n';
  168. }
  169. // Get translatable node types and check if it is worth to continue.
  170. $translatable_node_types = array_keys(tmgmt_entity_get_translatable_bundles('node'));
  171. if (empty($translatable_node_types)) {
  172. return array();
  173. }
  174. // If we have type property add condition.
  175. if (isset($property_conditions['type'])) {
  176. $query->condition($node_table_alias . '.type', $property_conditions['type']);
  177. // Remove the condition so we do not try to add it again below.
  178. unset($property_conditions['type']);
  179. }
  180. // If not, query db only for translatable node types.
  181. else {
  182. $query->condition($node_table_alias . '.type', $translatable_node_types);
  183. }
  184. }
  185. // Add remaining query conditions which are expected to be handled in a
  186. // generic way.
  187. foreach ($property_conditions as $property_name => $property_value) {
  188. $query->condition('e.' . $property_name, $property_value);
  189. }
  190. if ($pager) {
  191. $query = $query->extend('PagerDefault')->limit(variable_get('tmgmt_source_list_limit', 20));
  192. }
  193. else {
  194. $query->range(0, variable_get('tmgmt_source_list_limit', 20));
  195. }
  196. $query->orderBy($entity_info['entity keys']['id'], 'DESC');
  197. $entity_ids = $query->execute()->fetchCol();
  198. $entities = array();
  199. if (!empty($entity_ids)) {
  200. $entities = entity_load($entity_type, $entity_ids);
  201. }
  202. return $entities;
  203. }
  204. /**
  205. * Implements hook_tmgmt_source_suggestions()
  206. */
  207. function tmgmt_entity_tmgmt_source_suggestions(array $items, TMGMTJob $job) {
  208. $suggestions = array();
  209. // Get all translatable entity types.
  210. $entity_types = array_filter(variable_get('entity_translation_entity_types', array()));
  211. foreach ($items as $item) {
  212. if (($item instanceof TMGMTJobItem) && ($item->plugin == 'entity') || ($item->plugin == 'node')) {
  213. // Load the entity and extract the bundle name to get all fields from the
  214. // current entity.
  215. $entity = entity_load_single($item->item_type, $item->item_id);
  216. list(, , $bundle) = entity_extract_ids($item->item_type, $entity);
  217. $field_instances = field_info_instances($item->item_type, $bundle);
  218. // Loop over all fields, check if they are NOT translatable. Only if a
  219. // field is not translatable we may suggest a referenced entity. If so,
  220. // check for a supported field type (image and file currently here).
  221. foreach ($field_instances as $instance) {
  222. $field = field_info_field($instance['field_name']);
  223. $field_type = $field['type'];
  224. $field_name = $field['field_name'];
  225. switch ($field_type) {
  226. case 'file':
  227. case 'image':
  228. // 'File' (and images) must be translatable entity types.
  229. // Other files we not suggest here. Get all field items from the
  230. // current field and suggest them as translatable.
  231. if (isset($entity_types['file']) && ($field_items = field_get_items($item->item_type, $entity, $field_name))) {
  232. // Add all files as a suggestion.
  233. foreach ($field_items as $field_item) {
  234. $file_entity = entity_load_single('file', $field_item['fid']);
  235. // Check if there is already a translation available for this
  236. // file. If so, just continue with the next file.
  237. $handler = entity_translation_get_handler('file', $file_entity);
  238. if ($handler instanceof EntityTranslationHandlerInterface) {
  239. $translations = $handler->getTranslations();
  240. if (isset($translations->data[$job->target_language])) {
  241. continue;
  242. }
  243. }
  244. // Add the translation as a suggestion.
  245. $suggestions[] = array(
  246. 'job_item' => tmgmt_job_item_create('entity', 'file', $file_entity->fid),
  247. 'reason' => t('Field @label', array('@label' => $instance['label'])),
  248. 'from_item' => $item->tjiid,
  249. );
  250. }
  251. }
  252. break;
  253. case 'entityreference':
  254. $target_type = $field['settings']['target_type'];
  255. // Make sure only tranlatable entity types are suggested.
  256. if (isset($entity_types[$target_type]) && ($field_items = field_get_items($item->item_type, $entity, $field_name))) {
  257. // Add all referenced entities as suggestion.
  258. foreach ($field_items as $field_item) {
  259. $ref_entity = entity_load_single($target_type, $field_item['target_id']);
  260. // Check if there is already a translation available for this
  261. // entity. If so, just continue with the next one.
  262. $handler = entity_translation_get_handler($target_type, $ref_entity);
  263. if ($handler instanceof EntityTranslationHandlerInterface) {
  264. $translations = $handler->getTranslations();
  265. if (isset($translations->data[$job->target_language])) {
  266. continue;
  267. }
  268. }
  269. // Add suggestion.
  270. $suggestions[] = array(
  271. 'job_item' => tmgmt_job_item_create('entity', $target_type, $field_item['target_id']),
  272. 'reason' => t('Field @label', array('@label' => $instance['label'])),
  273. 'from_item' => $item->tjiid,
  274. );
  275. }
  276. }
  277. break;
  278. }
  279. }
  280. }
  281. }
  282. return $suggestions;
  283. }