i18n_translation.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. <?php
  2. /**
  3. * @file
  4. * Internationalization (i18n) module - Translation set
  5. */
  6. class i18n_translation_set {
  7. public $tsid = NULL;
  8. public $type;
  9. public $bundle = '';
  10. public $status = 0;
  11. public $master_id = 0;
  12. // It may optionally have a title
  13. public $title = '';
  14. // Translations indexed by language
  15. protected $translations = NULL;
  16. // Translation languages indexed by oid
  17. protected $object_languages = array();
  18. // Related translation sets indexed by tsid
  19. // Keep track of old translation sets objects belong to.
  20. protected $related_translations = array();
  21. /**
  22. * Constructor from object/array
  23. */
  24. public function __construct($translation = NULL) {
  25. if ($translation) {
  26. foreach ((array)$translation as $key => $value) {
  27. $this->$key = $value;
  28. }
  29. }
  30. }
  31. /**
  32. * Delete a translation set
  33. *
  34. * @param $delete_translations
  35. * Whether to unlink translations from the set. Detaults to TRUE.
  36. */
  37. public function delete($delete_translations = TRUE) {
  38. db_delete('i18n_translation_set')
  39. ->condition('tsid', $this->tsid)
  40. ->execute();
  41. if ($delete_translations) {
  42. $this->delete_translations();
  43. }
  44. $this->invoke_all('delete');
  45. $this->tsid = NULL;
  46. }
  47. /**
  48. * Invoke all modules
  49. */
  50. public function invoke_all($op) {
  51. module_invoke_all('i18n_translation_set_' . $op, $this);
  52. module_invoke_all('entity_' . $op, $this, 'i18n_translation');
  53. }
  54. /**
  55. * Create a new translation set
  56. *
  57. * @param $save_translations
  58. * Whether to update linked objects so they belong to this set.
  59. */
  60. public function insert($save_translations = TRUE) {
  61. $this->created = $this->changed = REQUEST_TIME;
  62. $status = drupal_write_record('i18n_translation_set', $this);
  63. if ($save_translations) {
  64. $this->save_translations();
  65. $this->update_related();
  66. }
  67. $this->invoke_all('insert');
  68. return $status;
  69. }
  70. /**
  71. * Save translation set
  72. *
  73. * @param $save_translations
  74. * Whether to update linked objects so they belong to this set.
  75. */
  76. public function save($save_translations = TRUE) {
  77. $this->invoke_all('presave');
  78. return empty($this->tsid) ? $this->insert($save_translations) : $this->update($save_translations);
  79. }
  80. /**
  81. * Update a translation set
  82. *
  83. * @param $update_translations
  84. * Whether to update objects linked to this set.
  85. */
  86. public function update($update_translations = TRUE) {
  87. $this->changed = REQUEST_TIME;
  88. $status = drupal_write_record('i18n_translation_set', $this, 'tsid');
  89. if ($update_translations) {
  90. $this->clean_translations();
  91. $this->save_translations();
  92. $this->update_related();
  93. }
  94. $this->invoke_all('update');
  95. return $status;
  96. }
  97. /**
  98. * Update a translation set or delete if empty.
  99. */
  100. public function update_delete() {
  101. if ($this->get_translations()) {
  102. $result = $this->save(TRUE);
  103. // Update related translation sets too.
  104. $this->update_related();
  105. return $result;
  106. }
  107. else {
  108. return $this->delete(TRUE);
  109. }
  110. }
  111. /**
  112. * Update related translation sets
  113. */
  114. protected function update_related($op = 'update_delete') {
  115. foreach ($this->related_translations as $translation_set) {
  116. $translation_set->$op();
  117. }
  118. }
  119. /**
  120. * Clean all items in this translation set
  121. *
  122. * Unlink other items (not current translations from this translation set)
  123. */
  124. public function clean_translations() {
  125. if (($table = $this->get_table()) && ($field = $this->get_field())) {
  126. $query = db_update($table)
  127. ->fields(array($field => 0))
  128. ->condition($field, $this->tsid);
  129. if ($translations = $this->get_translations()) {
  130. $query->condition('language', array_keys($translations), 'NOT IN');
  131. }
  132. return $query->execute();
  133. }
  134. }
  135. /**
  136. * Save translations in this translation set
  137. */
  138. public function save_translations() {
  139. if (($table = $this->get_table()) && ($field = $this->get_field())) {
  140. if ($keys = $this->get_translations_keys()) {
  141. return db_update($table)
  142. ->fields(array($field => $this->tsid))
  143. ->condition($this->key_field(), $keys)
  144. ->execute();
  145. }
  146. else {
  147. return $this->delete_translations();
  148. }
  149. }
  150. }
  151. /**
  152. * Delete translations in this translation set
  153. *
  154. * It won't delete objects, just unlink them from translation set
  155. */
  156. public function delete_translations() {
  157. if (($table = $this->get_table()) && ($field = $this->get_field())) {
  158. return db_update($table)
  159. ->fields(array($field => 0))
  160. ->condition($field, $this->tsid)
  161. ->execute();
  162. }
  163. }
  164. /**
  165. * Get translations, indexed by language
  166. */
  167. public function get_translations() {
  168. $translations = array();
  169. foreach ($this->get_objects() as $lang => $object) {
  170. $translations[$lang] = $object->get_object();
  171. }
  172. return $translations;
  173. }
  174. /**
  175. * Reset translations, set empty array or new array of translations.
  176. *
  177. * @param $translations array
  178. * Array of langcode => item
  179. */
  180. public function reset_translations($translations = array()) {
  181. $this->translations = array();
  182. $this->add_translations($translations);
  183. return $this;
  184. }
  185. /**
  186. * Get translations as i18n objects, indexed by language
  187. */
  188. public function get_objects() {
  189. if (!isset($this->translations)) {
  190. $this->translations = array();
  191. // Disable selection query altering, just in case
  192. $previous = i18n_select(FALSE);
  193. $this->add_translations($this->load_translations());
  194. i18n_select($previous);
  195. }
  196. return $this->translations;
  197. }
  198. /**
  199. * Get item for language
  200. */
  201. public function get_item($langcode) {
  202. if (($translations = $this->get_translations()) && isset($translations[$langcode])) {
  203. return $translations[$langcode];
  204. }
  205. else {
  206. return NULL;
  207. }
  208. }
  209. /**
  210. * Get translations keys, indexed by language
  211. */
  212. public function get_translations_keys() {
  213. $keys = array();
  214. foreach ($this->get_objects() as $lang => $object) {
  215. if ($id = $object->get_key()) {
  216. $keys[$lang] = $id;
  217. }
  218. }
  219. return array_filter($keys);
  220. }
  221. /**
  222. * Get edit path for this translation set
  223. */
  224. public function get_edit_path() {
  225. if ($path = $this->get_info('edit path')) {
  226. return strtr($path, $this->get_path_placeholders('delete'));
  227. }
  228. else {
  229. return '';
  230. }
  231. }
  232. /**
  233. * Get operations as renderable links
  234. */
  235. public function get_operations() {
  236. $destination = drupal_get_destination();
  237. $operations = array();
  238. if ($path = $this->get_edit_path()) {
  239. $operations['edit'] = array(
  240. 'title' => t('edit'),
  241. 'href' => $path,
  242. 'query' => $destination,
  243. );
  244. }
  245. if ($path = $this->get_delete_path()) {
  246. $operations['delete'] = array(
  247. 'title' => t('delete'),
  248. 'href' => $path,
  249. 'query' => $destination,
  250. );
  251. }
  252. return $operations;
  253. }
  254. /**
  255. * Get items as renderable links
  256. */
  257. public function get_links() {
  258. $language_list = language_list();
  259. $items = array();
  260. foreach ($this->get_objects() as $langcode => $object) {
  261. $title = $object->get_title();
  262. $path = $object->get_path();
  263. $language = isset($language_list[$langcode]) ? $language_list[$langcode] : NULL;
  264. $items[$langcode] = array(
  265. 'title' => $title,
  266. 'href' => $path ? $path : NULL,
  267. 'language' => $language,
  268. );
  269. if ($language && function_exists('languageicons_link_add')) {
  270. languageicons_link_add($items[$langcode]);
  271. }
  272. }
  273. return $items;
  274. }
  275. /**
  276. * Get overview (list) path for this translation set
  277. */
  278. public function get_list_path() {
  279. return $this->get_info('list path');
  280. }
  281. /**
  282. * Get delete path for this translation set
  283. */
  284. public function get_delete_path() {
  285. if ($path = $this->get_info('delete path')) {
  286. return strtr($path, $this->get_path_placeholders('delete'));
  287. }
  288. else {
  289. return '';
  290. }
  291. }
  292. /**
  293. * Get placeholder values for path replacement
  294. */
  295. function get_path_placeholders($op = 'list') {
  296. $values['%operation'] = $op;
  297. $values['%type'] = $this->type;
  298. $values['%i18n_translation_set'] = $this->tsid;
  299. if ($placeholder = $this->get_info('placeholder')) {
  300. $values[$placeholder] = $this->tsid;
  301. }
  302. return $values;
  303. }
  304. /**
  305. * Get field on translations table that stores the translation set id (tsid)
  306. */
  307. protected function get_field() {
  308. return $this->get_info('field');
  309. }
  310. /**
  311. * Get property from translation set info
  312. */
  313. public function get_info($property, $default = NULL) {
  314. $info = i18n_translation_set_info($this->type);
  315. return $info && isset($info[$property]) ? $info[$property] : $default;
  316. }
  317. /**
  318. * Get table name, where translation items are stored.
  319. */
  320. protected function get_table() {
  321. return $this->get_info('table');
  322. }
  323. /**
  324. * Get title for this set
  325. */
  326. public function get_title() {
  327. if (!empty($this->title)) {
  328. return $this->title;
  329. }
  330. elseif ($translations = $this->get_objects()) {
  331. foreach ($translations as $object) {
  332. $names[] = $object->get_title();
  333. }
  334. return implode(' / ', $names);
  335. }
  336. else {
  337. return t('Undefined');
  338. }
  339. }
  340. /**
  341. * Get item list
  342. */
  343. public function item_list() {
  344. $language_list = language_list();
  345. $items = array();
  346. foreach ($this->get_objects() as $langcode => $object) {
  347. $title = $object->get_title();
  348. $path = $object->get_path();
  349. if ($title && $path) {
  350. $options = isset($language_list[$langcode]) ? array('language' => $language_list[$langcode]) : array();
  351. $items[$langcode] = l($title, $path, $options);
  352. }
  353. elseif ($title) {
  354. $items[$langcode] = check_plain($title);
  355. }
  356. }
  357. return $items;
  358. }
  359. /**
  360. * Add array of translation items
  361. *
  362. * @param $translations array
  363. * Translation items indexed by language code
  364. */
  365. public function add_translations($translations) {
  366. foreach ($translations as $langcode => $item) {
  367. $this->add_item($item, $langcode);
  368. }
  369. return $this;
  370. }
  371. /**
  372. * Add translation item
  373. */
  374. public function add_item($item, $langcode = NULL) {
  375. $object = i18n_object($this->type, $item);
  376. $langcode = $langcode ? $langcode : $object->get_langcode();
  377. // Check whether this item belongs to another translation set
  378. $old_tsid = $object->get_tsid();
  379. if ($old_tsid && $old_tsid != $this->tsid) {
  380. $this->related_translations[$old_tsid] = i18n_translation_set_load($old_tsid);
  381. $this->related_translations[$old_tsid]->remove_object($object);
  382. }
  383. if ($langcode) {
  384. $this->get_translations();
  385. $object->set_tsid($this->tsid);
  386. $this->translations[$langcode] = $object;
  387. $this->object_languages[$object->get_index()] = $langcode;
  388. }
  389. return $this;
  390. }
  391. /**
  392. * Remove item / language from translation set
  393. *
  394. * @param $item
  395. * Item to remove from this translation set, it must have a language property.
  396. */
  397. public function remove_item($item) {
  398. $this->remove_object(i18n_object($this->type, $item));
  399. return $this;
  400. }
  401. /**
  402. * Remove i18n object from translation set.
  403. */
  404. public function remove_object($object) {
  405. // As object's language may have changed, we use our object_languages index better.
  406. $index = $object->get_index();
  407. $this->get_translations();
  408. if (isset($this->object_languages[$index])) {
  409. $langcode = $this->object_languages[$index];
  410. unset($this->translations[$langcode]);
  411. unset($this->object_languages[$index]);
  412. }
  413. return $this;
  414. }
  415. /**
  416. * Remove language from translation set.
  417. *
  418. * @param $langcode
  419. * Language to remove from this translation set.
  420. */
  421. public function remove_language($langcode) {
  422. $this->get_translations();
  423. if (isset($this->translations[$langcode])) {
  424. $this->remove_object($this->translations[$langcode]);
  425. }
  426. return $this;
  427. }
  428. /**
  429. * Load all translations as objects indexed by language
  430. */
  431. public function load_translations() {
  432. if (($table = $this->get_table()) && ($field = $this->get_field())) {
  433. return db_select($table, 't')
  434. ->fields('t')
  435. ->condition('t.' . $field, $this->tsid)
  436. ->execute()
  437. ->fetchAllAssoc('language');
  438. }
  439. else {
  440. return array();
  441. }
  442. }
  443. /**
  444. * Get key field for this translation items
  445. */
  446. protected function key_field() {
  447. $info = i18n_object_info($this->type);
  448. return $info['key'];
  449. }
  450. }