tmgmt_ui.module 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196
  1. <?php
  2. /**
  3. * @file
  4. * Common Translation managment UI.
  5. */
  6. /**
  7. * Implements hook_entity_info().
  8. */
  9. function tmgmt_ui_entity_info() {
  10. $info['tmgmt_translator'] = array(
  11. 'admin ui' => array(
  12. 'controller class' => 'TMGMTTranslatorUIController',
  13. 'path' => 'admin/config/regional/tmgmt_translator',
  14. ),
  15. );
  16. $info['tmgmt_job'] = array(
  17. 'admin ui' => array(
  18. 'controller class' => 'TMGMTJobUIController',
  19. 'path' => 'admin/tmgmt/jobs',
  20. ),
  21. );
  22. $info['tmgmt_job_item'] = array(
  23. 'admin ui' => array(
  24. 'controller class' => 'TMGMTJobItemUIController',
  25. 'path' => 'admin/tmgmt/items',
  26. ),
  27. );
  28. foreach ($info as $key => $item) {
  29. // Entity API defaults to the path that implements the entity type, but
  30. // since this happens in the TMGMT core module, we need to manually
  31. // define the path here.
  32. $info[$key]['admin ui']['file path'] = drupal_get_path('module', 'tmgmt_ui');
  33. $info[$key]['admin ui']['file'] = 'includes/tmgmt_ui.pages.inc';
  34. }
  35. return $info;
  36. }
  37. /**
  38. * Implements hook_menu().
  39. */
  40. function tmgmt_ui_menu() {
  41. $items['admin/config/regional/tmgmt_settings'] = array(
  42. 'title' => 'Translation Management settings',
  43. 'page callback' => 'drupal_get_form',
  44. 'page arguments' => array('tmgmt_ui_settings_form'),
  45. 'access arguments' => array('administer tmgmt'),
  46. 'file' => 'includes/tmgmt_ui.pages.inc',
  47. 'weight' => 10,
  48. );
  49. $items['admin/tmgmt/cart'] = array(
  50. 'title' => 'Cart',
  51. 'title callback' => 'tmgmt_ui_cart_title',
  52. 'page callback' => 'drupal_get_form',
  53. 'page arguments' => array('tmgmt_ui_cart_content'),
  54. 'access arguments' => array('create translation jobs'),
  55. 'file' => 'includes/tmgmt_ui.pages.inc',
  56. 'type' => MENU_LOCAL_TASK,
  57. 'weight' => 20,
  58. );
  59. // Some source plugins might provide menu items.
  60. foreach (tmgmt_source_ui_controller() as $controller) {
  61. $items += $controller->hook_menu();
  62. }
  63. return $items;
  64. }
  65. /**
  66. * Cart page title callback.
  67. *
  68. * @return string
  69. */
  70. function tmgmt_ui_cart_title() {
  71. return t('Cart (@count)', array('@count' => tmgmt_ui_cart_get()->count()));
  72. }
  73. /**
  74. * Implements hook_module_implements_alter().
  75. */
  76. function tmgmt_ui_module_implements_alter(&$implementations, $hook) {
  77. if ($hook == 'menu_alter') {
  78. // Move tmgmt_ui_menu_alter() to the end to be able to see views-defined
  79. // source overviews.
  80. $group = $implementations['tmgmt_ui'];
  81. unset($implementations['tmgmt_ui']);
  82. $implementations['tmgmt_ui'] = $group;
  83. }
  84. }
  85. /**
  86. * Implements hook_menu_alter().
  87. */
  88. function tmgmt_ui_menu_alter(&$items) {
  89. $weight = NULL;
  90. $default_path = NULL;
  91. // Look for the sources overview local task with the lowest weight.
  92. foreach ($items as $path => $item) {
  93. $item += array('weight' => 0);
  94. if (strpos($path, 'admin/tmgmt/sources') !== FALSE) {
  95. if ($weight === NULL || $weight > $item['weight']) {
  96. $weight = $item['weight'];
  97. $default_path = $path;
  98. }
  99. }
  100. }
  101. // If we have found a default path, define a parent menu item based on it's
  102. // definitions and change the type to a default local task.
  103. if ($default_path) {
  104. $items['admin/tmgmt/sources'] = array(
  105. 'title' => 'Sources',
  106. 'description' => 'Source overview',
  107. 'type' => MENU_LOCAL_TASK,
  108. 'weight' => 10,
  109. ) + $items[$default_path];
  110. $items[$default_path]['type'] = MENU_DEFAULT_LOCAL_TASK;
  111. }
  112. }
  113. /**
  114. * Implements hook_theme().
  115. */
  116. function tmgmt_ui_theme() {
  117. return array(
  118. 'tmgmt_ui_translator_overview_item' => array(
  119. // We also have the entity_type here because we are still populating the
  120. // defaults via the entity api so we just need to add the description.
  121. 'variables' => array('label' => NULL, 'entity_type' => NULL, 'url' => FALSE, 'name' => FALSE, 'description' => FALSE),
  122. 'file' => 'includes/tmgmt_ui.theme.inc',
  123. ),
  124. 'tmgmt_ui_translator_overview_form' => array(
  125. 'render element' => 'form',
  126. 'file' => 'includes/tmgmt_ui.theme.inc',
  127. ),
  128. 'tmgmt_ui_translator_review_form' => array(
  129. 'render element' => 'element',
  130. 'file' => 'includes/tmgmt_ui.theme.inc',
  131. ),
  132. 'tmgmt_ui_translator_review_form_element' => array(
  133. 'render element' => 'element',
  134. 'file' => 'includes/tmgmt_ui.theme.inc',
  135. ),
  136. 'tmgmt_ui_translator_review_form_element_status' => array(
  137. 'render element' => 'status',
  138. 'file' => 'includes/tmgmt_ui.theme.inc',
  139. ),
  140. 'tmgmt_ui_translation_language_status_single' => array(
  141. 'file' => 'includes/tmgmt_ui.theme.inc',
  142. 'variables' => array('translation_status' => NULL, 'job_item' => NULL),
  143. ),
  144. );
  145. }
  146. /**
  147. * Implements hook_forms().
  148. */
  149. function tmgmt_ui_forms() {
  150. $forms = array();
  151. foreach (tmgmt_source_plugin_info() as $plugin => $info) {
  152. $forms['tmgmt_ui_' . $plugin . '_translation_review_form'] = array(
  153. 'callback' => 'tmgmt_ui_translation_review_form',
  154. 'wrapper_callback' => 'tmgmt_ui_translation_review_form_defaults',
  155. );
  156. }
  157. // Some source plugins might provide forms.
  158. foreach (tmgmt_source_ui_controller() as $controller) {
  159. $forms += $controller->hook_forms();
  160. }
  161. return $forms;
  162. }
  163. /**
  164. * Implements hook_system_info_alter().
  165. */
  166. function tmgmt_ui_system_info_alter(&$info, $file, $type) {
  167. if ($file->name == 'tmgmt') {
  168. $info['configure'] = 'admin/config/regional/tmgmt_settings';
  169. }
  170. }
  171. /**
  172. * Implements hook_views_api().
  173. */
  174. function tmgmt_ui_views_api() {
  175. return array('api' => 3.0);
  176. }
  177. /**
  178. * Implements hook_views_default_views().
  179. */
  180. function tmgmt_ui_views_default_views() {
  181. $views = _tmgmt_load_exports('tmgmt_ui', 'views', 'view.inc', 'view');
  182. // Some source controllers might provide custom views.
  183. foreach (tmgmt_source_ui_controller() as $controller) {
  184. $views += $controller->hook_views_default_views();
  185. }
  186. return $views;
  187. }
  188. /**
  189. * Embed a view but don't render it if it's empty.
  190. *
  191. * @param string $view
  192. * The machine-readable name of the view.
  193. * @param string $display_id
  194. * The display id for the view.
  195. * @param array $args
  196. * The arguments that should be passed to the view.
  197. *
  198. * @return
  199. * The rendered view or an empty string if the view doesn't exist if it was
  200. * empty.
  201. */
  202. function tmgmt_ui_embed_view($view, $display_id = NULL, array $args = array()) {
  203. $view = views_get_view($view);
  204. if (!empty($view)) {
  205. $view->init_display();
  206. $output = $view->preview($display_id, $args);
  207. if (!empty($view->result)) {
  208. return $output;
  209. }
  210. }
  211. return '';
  212. }
  213. /**
  214. * Form callback for the source overview form.
  215. */
  216. function tmgmt_ui_source_overview_form($form, &$form_state, $plugin, $item_type = NULL) {
  217. $controller = tmgmt_source_ui_controller($plugin);
  218. $form_state['plugin'] = $plugin;
  219. $form_state['item_type'] = $item_type;
  220. return $controller->overviewForm($form, $form_state, $item_type);
  221. }
  222. /**
  223. * Form callback for the source overview form.
  224. */
  225. function tmgmt_ui_source_overview_form_defaults($form, &$form_state, $plugin, $item_type = NULL) {
  226. $controller = tmgmt_source_plugin_controller($plugin);
  227. $info = tmgmt_source_plugin_info($plugin);
  228. // Set a generic title that includes the source plugin and item type label.
  229. drupal_set_title(t('@type overview (@plugin)', array('@type' => $controller->getItemTypeLabel($item_type), '@plugin' => $info['label'])), PASS_THROUGH);
  230. $form['actions'] = array(
  231. '#type' => 'fieldset',
  232. '#title' => t('Operations'),
  233. '#collapsible' => TRUE,
  234. '#collapsed' => FALSE,
  235. '#weight' => -10,
  236. '#attributes' => array('class' => array('tmgmt-source-operations-wrapper'))
  237. );
  238. tmgmt_ui_add_cart_form($form['actions'], $form_state, $plugin, $item_type);
  239. $form['actions']['submit'] = array(
  240. '#type' => 'submit',
  241. '#value' => t('Request translation'),
  242. );
  243. return $form;
  244. }
  245. /**
  246. * Validation callback for the source overview form.
  247. */
  248. function tmgmt_ui_source_overview_form_validate($form, &$form_state) {
  249. // Copy the form state so we are not removing important information from it
  250. // when sending it through form_state_values_clean().
  251. $cleaned = $form_state;
  252. form_state_values_clean($cleaned);
  253. if (empty($cleaned['values'])) {
  254. form_set_error('items', t("You didn't select any source objects"));
  255. }
  256. list($plugin, $item_type) = $form_state['build_info']['args'];
  257. // Execute the validation method on the source plugin controller.
  258. $controller = tmgmt_source_ui_controller($plugin);
  259. $controller->overviewFormValidate($form, $form_state, $item_type);
  260. }
  261. /**
  262. * Submit callback for the source overview form.
  263. */
  264. function tmgmt_ui_source_overview_form_submit($form, &$form_state) {
  265. list($plugin, $item_type) = $form_state['build_info']['args'];
  266. // Execute the submit method on the source plugin controller.
  267. $controller = tmgmt_source_ui_controller($plugin);
  268. $controller->overviewFormSubmit($form, $form_state, $item_type);
  269. }
  270. /**
  271. * Gets the tmgmt cart singleton object.
  272. *
  273. * @return TMGMTJobItemUICart
  274. * The cart object.
  275. */
  276. function tmgmt_ui_cart_get() {
  277. return TMGMTJobItemUICart::getInstance();
  278. }
  279. /**
  280. * @addtogroup tmgmt_ui_cart
  281. * @{
  282. */
  283. /**
  284. * Adds add to cart form elements.
  285. *
  286. * There are two use cases for this function:
  287. *
  288. * 1) Add the "Add to cart" submit action to the source overview form. In this
  289. * case the $item_id should not be provided and only the action button will be
  290. * displayed. The form is expected to submit job items ids as items[] which is
  291. * being validated via tmgmt_ui_source_add_to_cart_validate().
  292. *
  293. * 2) Add the "Add to cart" submit action to the translate tab. For this case
  294. * the $item_id is required and with the add to cart button also the cart
  295. * information is displayed. In this case there is no validation as the caller
  296. * needs to provide valid $item_id value.
  297. *
  298. * The "Add to cart" action submits the form by calling
  299. * tmgmt_ui_source_add_to_cart_submit() submit callback which processes either
  300. * one job item or multiple.
  301. *
  302. * @param array $form
  303. * Form to which to add the add to cart form elements.
  304. * @param array $form_state
  305. * The current form state object.
  306. * @param string $plugin
  307. * Current plugin name.
  308. * @param string $item_type
  309. * Type of the source item.
  310. * @param mixed $item_id
  311. * (Optional) It is required in case a single source is being added into the
  312. * cart.
  313. */
  314. function tmgmt_ui_add_cart_form(&$form, &$form_state, $plugin, $item_type, $item_id = NULL) {
  315. $form_state['tmgmt_cart'] = array(
  316. 'plugin' => $plugin,
  317. 'item_type' => $item_type,
  318. 'item_id' => $item_id,
  319. );
  320. $form['add_to_cart'] = array(
  321. '#type' => 'submit',
  322. '#value' => t('Add to cart'),
  323. '#submit' => array('tmgmt_ui_source_add_to_cart_submit'),
  324. '#attributes' => array('title' => t('Add marked items to the cart for later processing')),
  325. );
  326. if (empty($item_id)) {
  327. $form['add_to_cart']['#validate'] = array('tmgmt_ui_cart_source_overview_validate');
  328. }
  329. else {
  330. //
  331. $form['add_to_cart']['#limit_validation_errors'] = array();
  332. // Compose the cart info message for the translate tab.
  333. $count = tmgmt_ui_cart_get()->count();
  334. if (tmgmt_ui_cart_get()->isSourceItemAdded($plugin, $item_type, $item_id)) {
  335. $form['add_to_cart']['#disabled'] = TRUE;
  336. $message = format_plural($count, 'There is @count item in the <a href="@url">translation cart</a> including the current item.',
  337. 'There are @count items in the <a href="@url">translation cart</a> including the current item.', array('@url' => url('admin/tmgmt/cart')));
  338. }
  339. else {
  340. $message = format_plural($count, 'There is @count item in the <a href="@url">translation cart</a>.',
  341. 'There are @count items in the <a href="@url">translation cart</a>.', array('@url' => url('admin/tmgmt/cart')));
  342. }
  343. $form['add_to_cart']['#suffix'] = '<span class="tmgmt-ui-cart-status-message">' . $message . '</span>';
  344. }
  345. }
  346. /**
  347. * Submit handler to add items into the cart.
  348. *
  349. * Based on the submitted data it will create job items and add them into the
  350. * cart. Use it in combination with tmgmt_ui_add_cart_form() as that function
  351. * sets all the necessary values needed to crate a job an add it into the cart.
  352. *
  353. * @see tmgmt_ui_add_cart_form()
  354. */
  355. function tmgmt_ui_source_add_to_cart_submit($form, &$form_state) {
  356. $cart_info = $form_state['tmgmt_cart'];
  357. if (!empty($cart_info['plugin']) && !empty($cart_info['item_type']) && !empty($form_state['values']['items'])) {
  358. $source_items = array_filter($form_state['values']['items']);
  359. $item_type = $cart_info['item_type'];
  360. $plugin = $cart_info['plugin'];
  361. }
  362. elseif (!empty($cart_info['plugin']) && !empty($cart_info['item_type']) && !empty($cart_info['item_id'])) {
  363. $source_items = array($cart_info['item_id']);
  364. $item_type = $cart_info['item_type'];
  365. $plugin = $cart_info['plugin'];
  366. }
  367. else {
  368. drupal_set_message(t('Unable to add the content into the cart.'), 'error');
  369. return;
  370. }
  371. $i = 0;
  372. foreach ($source_items as $source_id) {
  373. if (tmgmt_ui_cart_get()->addJobItem($plugin, $item_type, $source_id)) {
  374. $i++;
  375. }
  376. }
  377. drupal_set_message(format_plural($i, '@count content source was added into the <a href="@url">cart</a>.',
  378. '@count content sources were added into the <a href="@url">cart</a>.', array('@url' => url('admin/tmgmt/cart'))));
  379. }
  380. /**
  381. * Cart form validation callback for the source overview.
  382. */
  383. function tmgmt_ui_cart_source_overview_validate($form, &$form_state) {
  384. $items = array_filter($form_state['values']['items']);
  385. if (empty($items)) {
  386. form_set_error('items', t('No job items were selected.'));
  387. }
  388. }
  389. /**
  390. * @} End of "addtogroup tmgmt_ui_cart".
  391. */
  392. /**
  393. * Attempts to check out a number of jobs. Performs a number of checks on each
  394. * job and also allows to alter the behavior through hooks.
  395. *
  396. * @param $jobs
  397. * The jobs to be checked out.
  398. *
  399. * @return
  400. * Array of redirect url's if there are any jobs that need manual checkout.
  401. *
  402. * @ingroup tmgmt_job
  403. *
  404. * @see tmgmt_ui_redirect_queue()
  405. * @see tmgmt_ui_job_checkout_and_redirect()
  406. */
  407. function tmgmt_ui_job_checkout_multiple(array $jobs) {
  408. $redirects = array();
  409. // Allow other modules to jump in and eg. auto-checkout with rules or use a
  410. // customized checkout form.
  411. drupal_alter('tmgmt_ui_job_checkout_before', $redirects, $jobs);
  412. $denied = 0;
  413. foreach ($jobs as $job) {
  414. if (!$job->isUnprocessed()) {
  415. // Job is already checked out, just ignore that one. This could happen
  416. // if jobs have already been submitted in the before hook.
  417. continue;
  418. }
  419. if (!variable_get('tmgmt_quick_checkout', TRUE) || tmgmt_ui_job_needs_checkout_form($job)) {
  420. if (!entity_access('submit', 'tmgmt_job', $job)) {
  421. // Ignore jobs if the user is not allowed to submit, ignore.
  422. $denied++;
  423. // Make sure that the job is saved.
  424. $job->save();
  425. continue;
  426. }
  427. $uri = $job->uri();
  428. $redirects[] = $uri['path'];
  429. }
  430. else {
  431. // @todo this is dangerous because we don't catch request fails at all.
  432. // Normally I would expect this to catch all failed requests and
  433. // afterwards send the user through a multistep form which contains the
  434. // failed elements.
  435. // No manual checkout required. Request translations now.
  436. tmgmt_ui_job_request_translation($job);
  437. }
  438. }
  439. // Allow other modules to jump in and eg. auto-checkout with rules or use a
  440. // customized checkout form.
  441. drupal_alter('tmgmt_ui_job_checkout_after', $redirects, $jobs);
  442. // Display message for created jobs that can not be checked out.
  443. if ($denied) {
  444. drupal_set_message(format_plural($denied, 'One job has been created.', '@count jobs have been created.'));
  445. }
  446. return $redirects;
  447. }
  448. /**
  449. * Check if a job needs a checkout form. The current checks include if there is
  450. * more than one translator available, if he has settings and if the job has a
  451. * fixed target language.
  452. *
  453. * @param TMGMTJob $job
  454. * The job item
  455. *
  456. * @return
  457. * TRUE if the job needs a checkout form.
  458. */
  459. function tmgmt_ui_job_needs_checkout_form(TMGMTJob $job) {
  460. // If the job has no target language (or source language, even though this
  461. // should never be the case in our use case), checkout is mandatory.
  462. if (empty($job->target_language) || empty($job->source_language)) {
  463. return TRUE;
  464. }
  465. // If no translator is pre-selected, try to pick one automatically.
  466. if (empty($job->translator)) {
  467. // If there is more than a single translator available or if there are no
  468. // translators available at all checkout is mandatory.
  469. $translators = tmgmt_translator_load_available($job);
  470. if (empty($translators) || count($translators) > 1) {
  471. return TRUE;
  472. }
  473. $translator = reset($translators);
  474. $job->translator = $translator->name;
  475. }
  476. // If that translator has settings, the checkout is mandatory.
  477. if ($job->getTranslator()->hasCheckoutSettings($job)) {
  478. return TRUE;
  479. }
  480. return FALSE;
  481. }
  482. /**
  483. * Requests translations for a job and prints messages which have happened since
  484. * then.
  485. *
  486. * @param TMGMTJob $job
  487. * The job object for which translations should be requested.
  488. *
  489. * @return
  490. * TRUE if it worked, FALSE if there were any errors of the type error which
  491. * means that something did go wrong.
  492. */
  493. function tmgmt_ui_job_request_translation(TMGMTJob $job) {
  494. // Process the translation request.
  495. $job->requestTranslation();
  496. return tmgmt_ui_write_request_messages($job);
  497. }
  498. /**
  499. * Print all messages that occurred since our request to the screen.
  500. *
  501. * @param $job
  502. * The translation job for which the message should be written.
  503. *
  504. * @return
  505. * FALSE if there are message with severity error, TRUE otherwise.
  506. */
  507. function tmgmt_ui_write_request_messages(TMGMTJob $job) {
  508. $errors = FALSE;
  509. foreach ($job->getMessagesSince() as $message) {
  510. // Ignore debug messages.
  511. if ($message->type == 'debug') {
  512. continue;
  513. }
  514. if ($message->type == 'error') {
  515. $errors = TRUE;
  516. }
  517. if ($text = $message->getMessage()) {
  518. drupal_set_message(filter_xss($text), $message->type);
  519. }
  520. }
  521. return !$errors;
  522. }
  523. /**
  524. * Form wrapper callback for the job item review form.
  525. *
  526. * @see tmgmt_ui_forms()
  527. */
  528. function tmgmt_ui_translation_review_form_defaults($form, &$form_state, TMGMTJobItem $item) {
  529. // We store the item in the root of the form state so we can easily access it
  530. // in all the form functions.
  531. $form_state['item'] = $item;
  532. $wrapper = entity_metadata_wrapper('tmgmt_job_item', $item);
  533. $form['info'] = array(
  534. '#type' => 'container',
  535. '#attributes' => array('class' => array('tmgmt-ui-job-info', 'clearfix')),
  536. '#weight' => 0,
  537. );
  538. $uri = $item->getSourceUri();
  539. $form['info']['source'] = array(
  540. '#type' => 'item',
  541. '#title' => t('Source'),
  542. '#markup' => !empty($uri) ? l($item->getSourceLabel(), $uri['path'], $uri['options']) : $item->getSourceLabel(),
  543. '#prefix' => '<div class="tmgmt-ui-source tmgmt-ui-info-item">',
  544. '#suffix' => '</div>',
  545. );
  546. $form['info']['sourcetype'] = array(
  547. '#type' => 'item',
  548. '#title' => t('Source type'),
  549. '#markup' => $item->getSourceType(),
  550. '#prefix' => '<div class="tmgmt-ui-source-type tmgmt-ui-info-item">',
  551. '#suffix' => '</div>',
  552. );
  553. $form['info']['changed'] = array(
  554. '#type' => 'item',
  555. '#title' => t('Last change'),
  556. '#markup' => format_date($wrapper->changed->value()),
  557. '#prefix' => '<div class="tmgmt-ui-changed tmgmt-ui-info-item">',
  558. '#suffix' => '</div>',
  559. );
  560. $form['info']['state'] = array(
  561. '#type' => 'item',
  562. '#title' => t('State'),
  563. '#markup' => $wrapper->state->label(),
  564. '#prefix' => '<div class="tmgmt-ui-item-state tmgmt-ui-info-item">',
  565. '#suffix' => '</div>',
  566. );
  567. $job = $item->getJob();
  568. $uri = $job->uri();
  569. $form['info']['job'] = array(
  570. '#type' => 'item',
  571. '#title' => t('Job'),
  572. '#markup' => l($job->label(), $uri['path']),
  573. '#prefix' => '<div class="tmgmt-ui-job tmgmt-ui-info-item">',
  574. '#suffix' => '</div>',
  575. );
  576. // Display selected translator for already submitted jobs.
  577. if (!$item->getJob()->isSubmittable()) {
  578. $translators = tmgmt_translator_labels();
  579. $form['info']['translator'] = array(
  580. '#type' => 'item',
  581. '#title' => t('Translator'),
  582. '#markup' => isset($translators[$item->getJob()->translator]) ? check_plain($translators[$item->getJob()->translator]) : t('Missing translator'),
  583. '#prefix' => '<div class="tmgmt-ui-translator tmgmt-ui-info-item">',
  584. '#suffix' => '</div>',
  585. );
  586. }
  587. // Actually build the review form elements...
  588. $form['review'] = array(
  589. '#type' => 'container',
  590. );
  591. // Build the review form.
  592. $data = $item->getData();
  593. // Need to keep the first hierarchy. So flatten must take place inside
  594. // of the foreach loop.
  595. $zebra = 'even';
  596. foreach (element_children($data) as $key) {
  597. $form['review'][$key] = _tmgmt_ui_review_form_element($form_state, tmgmt_flatten_data($data[$key], $key), $item, $zebra, $key);
  598. }
  599. if ($output = tmgmt_ui_embed_view('tmgmt_ui_job_item_messages', 'block', array($item->tjiid))) {
  600. $form['messages'] = array(
  601. '#type' => 'fieldset',
  602. '#title' => t('Messages'),
  603. '#collapsible' => TRUE,
  604. '#collapsed' => TRUE,
  605. '#weight' => 50,
  606. );
  607. $form['messages']['view'] = array(
  608. '#type' => 'item',
  609. '#markup' => $output,
  610. );
  611. }
  612. // Add the form actions as well.
  613. $form['actions']['#type'] = 'actions';
  614. $form['actions']['accept'] = array(
  615. '#type' => 'submit',
  616. '#value' => t('Save as completed'),
  617. '#access' => $item->isNeedsReview(),
  618. );
  619. $form['actions']['save'] = array(
  620. '#type' => 'submit',
  621. '#value' => t('Save'),
  622. '#access' => !$item->isAccepted() && !$item->isAborted(),
  623. );
  624. $uri = $item->getJob()->uri();
  625. $url = isset($_GET['destination']) ? $_GET['destination'] : $uri['path'];
  626. $form['actions']['cancel'] = array(
  627. '#type' => 'link',
  628. '#title' => t('Cancel'),
  629. '#href' => $url,
  630. );
  631. $form['#attached']['css'][] = drupal_get_path('module', 'tmgmt_ui') . '/css/tmgmt_ui.admin.css';
  632. // The reject functionality has to be implement by the translator plugin as
  633. // that process is completely unique and custom for each translation service.
  634. return $form;
  635. }
  636. /**
  637. * Helper function to output ajaxid.
  638. *
  639. * @param string $parent_key
  640. * Parent element key.
  641. *
  642. * @return string
  643. * The ajax id.
  644. */
  645. function tmgmt_ui_review_form_element_ajaxid($parent_key) {
  646. return 'tmgmt-ui-element-' . drupal_clean_css_identifier($parent_key) . '-wrapper';
  647. }
  648. /**
  649. * Build form elements for the review form using flatened data items.
  650. *
  651. * @todo Mention in the api documentation that the char '|' is not allowed in
  652. * field names.
  653. *
  654. * @param array $form_state
  655. * Drupal form form_state object.
  656. * @param $data
  657. * Flatened array of translation data items.
  658. * @param $job_item
  659. * The job item related to this data.
  660. * @param $zebra
  661. * String containing either odd or even. This is used to style the each
  662. * translation item with alternating colors.
  663. * @param $parent_key
  664. * The key for $data.
  665. */
  666. function _tmgmt_ui_review_form_element(&$form_state, $data, TMGMTJobItem $job_item, &$zebra, $parent_key) {
  667. $flip = array(
  668. 'even' => 'odd',
  669. 'odd' => 'even',
  670. );
  671. $form = array(
  672. '#theme' => 'tmgmt_ui_translator_review_form',
  673. '#ajaxid' => tmgmt_ui_review_form_element_ajaxid($parent_key),
  674. );
  675. foreach (element_children($data) as $key) {
  676. // The char sequence '][' confuses the form API so we need to replace it.
  677. $target_key = str_replace('][', '|', $key);
  678. if (isset($data[$key]['#text']) && _tmgmt_filter_data($data[$key])) {
  679. $zebra = $flip[$zebra];
  680. $form[$target_key] = array(
  681. '#tree' => TRUE,
  682. '#theme' => 'tmgmt_ui_translator_review_form_element',
  683. '#parent_label' => $data[$key]['#parent_label'],
  684. '#zebra' => $zebra,
  685. );
  686. $form[$target_key]['status'] = array(
  687. '#theme' => 'tmgmt_ui_translator_review_form_element_status',
  688. '#value' => $job_item->isAccepted() ? TMGMT_DATA_ITEM_STATE_ACCEPTED : $data[$key]['#status'],
  689. );
  690. $form[$target_key]['actions'] = array(
  691. '#type' => 'container',
  692. );
  693. // Display data item actions unless the job item is accepted or aborted.
  694. if (!$job_item->isAccepted() && !$job_item->isAborted()) {
  695. if ($data[$key]['#status'] != TMGMT_DATA_ITEM_STATE_REVIEWED) {
  696. $form[$target_key]['actions']['reviewed'] = array(
  697. '#type' => 'submit',
  698. // Unicode character &#x2713 CHECK MARK
  699. '#value' => '✓',
  700. '#attributes' => array('title' => t('Reviewed')),
  701. '#name' => 'reviewed-' . $target_key,
  702. '#submit' => array('tmgmt_ui_translation_review_form_update_state', 'tmgmt_ui_translation_review_form_submit'),
  703. '#ajax' => array(
  704. 'callback' => 'tmgmt_ui_translation_review_form_ajax',
  705. 'wrapper' => $form['#ajaxid'],
  706. ),
  707. );
  708. }
  709. else {
  710. $form[$target_key]['actions']['unreviewed'] = array(
  711. '#type' => 'submit',
  712. // Unicode character &#x2713 CHECK MARK
  713. '#value' => '✓',
  714. '#attributes' => array('title' => t('Not reviewed'), 'class' => array('unreviewed')),
  715. '#name' => 'unreviewed-' . $target_key,
  716. '#submit' => array('tmgmt_ui_translation_review_form_update_state', 'tmgmt_ui_translation_review_form_submit'),
  717. '#ajax' => array(
  718. 'callback' => 'tmgmt_ui_translation_review_form_ajax',
  719. 'wrapper' => $form['#ajaxid'],
  720. ),
  721. );
  722. }
  723. if ($job_item->getTranslatorController() instanceof TMGMTTranslatorRejectDataItem && $data[$key]['#status'] != TMGMT_DATA_ITEM_STATE_PENDING) {
  724. $form[$target_key]['actions']['reject'] = array(
  725. '#type' => 'submit',
  726. // Unicode character &#x2717 BALLOT X
  727. '#value' => '✗',
  728. '#attributes' => array('title' => t('Reject')),
  729. '#name' => 'reject-' . $target_key,
  730. '#submit' => array('tmgmt_ui_translation_review_form_update_state'),
  731. );
  732. }
  733. if (!empty($data[$key]['#translation']['#text_revisions'])) {
  734. $form[$target_key]['actions']['revert'] = array(
  735. '#type' => 'submit',
  736. // Unicode character U+21B6 ANTICLOCKWISE TOP SEMICIRCLE ARROW
  737. '#value' => '↶',
  738. '#attributes' => array('title' => t('Revert to previous revision')),
  739. '#name' => 'revert-' . $target_key,
  740. '#data_item_key' => $key,
  741. '#submit' => array('tmgmt_ui_translation_review_form_revert'),
  742. '#ajax' => array(
  743. 'callback' => 'tmgmt_ui_translation_review_form_ajax',
  744. 'wrapper' => $form['#ajaxid'],
  745. ),
  746. );
  747. }
  748. }
  749. if (!empty($data[$key]['#translation']['#text_revisions'])) {
  750. $revisions = array();
  751. foreach ($data[$key]['#translation']['#text_revisions'] as $revision) {
  752. $revisions[] = t('Origin: %origin, Created: %created<br />%text', array(
  753. '%origin' => $revision['#origin'],
  754. '%created' => format_date($revision['#timestamp']),
  755. '%text' => filter_xss($revision['#text']),
  756. ));
  757. }
  758. $form[$target_key]['below']['revisions_wrapper'] = array(
  759. '#type' => 'fieldset',
  760. '#title' => t('Translation revisions'),
  761. '#collapsed' => TRUE,
  762. '#collapsible' => TRUE,
  763. );
  764. $form[$target_key]['below']['revisions_wrapper']['revisions'] = array(
  765. '#theme' => 'item_list',
  766. '#items' => $revisions,
  767. );
  768. }
  769. $form[$target_key]['translation'] = array(
  770. '#type' => 'textarea',
  771. '#default_value' => isset($data[$key]['#translation']['#text']) ? $data[$key]['#translation']['#text'] : NULL,
  772. '#title' => t('Translation'),
  773. '#disabled' => $job_item->isAccepted(),
  774. );
  775. $form[$target_key]['source'] = array(
  776. '#type' => 'textarea',
  777. '#default_value' => $data[$key]['#text'],
  778. '#title' => t('Source'),
  779. '#disabled' => TRUE,
  780. '#allow_focus' => TRUE,
  781. );
  782. // Give the translator ui controller a chance to affect the data item element.
  783. if ($translator = $job_item->getTranslator()) {
  784. $form[$target_key] = tmgmt_translator_ui_controller($translator->plugin)
  785. ->reviewDataItemElement($form[$target_key], $form_state, $key, $parent_key, $data[$key], $job_item);
  786. }
  787. // Give the source ui controller a chance to affect the data item element.
  788. $form[$target_key] = tmgmt_source_ui_controller($job_item->plugin)
  789. ->reviewDataItemElement($form[$target_key], $form_state, $key, $parent_key, $data[$key], $job_item);
  790. }
  791. }
  792. return $form;
  793. }
  794. /**
  795. * Review form revert action callback.
  796. */
  797. function tmgmt_ui_translation_review_form_revert($form, &$form_state) {
  798. /** @var TMGMTJobItem $item */
  799. $item = $form_state['item'];
  800. $key = tmgmt_ensure_keys_array($form_state['triggering_element']['#data_item_key']);
  801. if ($item->dataItemRevert($key)) {
  802. // Update the form_state input values so that the new default vale will be
  803. // shown.
  804. $form_key = str_replace('][', '|', $form_state['triggering_element']['#data_item_key']);
  805. unset($form_state['input'][$form_key]['translation']);
  806. $item->save();
  807. }
  808. else {
  809. drupal_set_message(t('No past revision found, translation was not reverted.'), 'warning');
  810. }
  811. $form_state['rebuild'] = TRUE;
  812. }
  813. /**
  814. * Form callback for the job item review form.
  815. *
  816. * @see tmgmt_ui_forms()
  817. */
  818. function tmgmt_ui_translation_review_form($form, &$form_state, TMGMTJobItem $item) {
  819. // Give the source ui controller a chance to affect the review form.
  820. $source = tmgmt_source_ui_controller($item->plugin);
  821. $form = $source->reviewForm($form, $form_state, $item);
  822. // Give the translator ui controller a chance to affect the review form.
  823. if ($item->getTranslator()) {
  824. $translator = tmgmt_translator_ui_controller($item->getTranslator()->plugin);
  825. $form = $translator->reviewForm($form, $form_state, $item);
  826. }
  827. return $form;
  828. }
  829. /**
  830. * Validation callback for the job item review form.
  831. */
  832. function tmgmt_ui_translation_review_form_validate($form, &$form_state) {
  833. $item = $form_state['item'];
  834. // First invoke the validation method on the source controller.
  835. $source = tmgmt_source_ui_controller($item->plugin);
  836. $source->reviewFormValidate($form, $form_state, $item);
  837. // Invoke the validation method on the translator controller (if available).
  838. if($item->getTranslator()){
  839. $translator = tmgmt_translator_ui_controller($item->getTranslator()->plugin);
  840. $translator->reviewFormValidate($form, $form_state, $item);
  841. }
  842. }
  843. /**
  844. * Submit callback for the job item review form.
  845. */
  846. function tmgmt_ui_translation_review_form_submit($form, &$form_state) {
  847. /** @var TMGMTJobItem $item */
  848. $item = $form_state['item'];
  849. // First invoke the submit method on the source controller.
  850. $source = tmgmt_source_ui_controller($item->plugin);
  851. $source->reviewFormSubmit($form, $form_state, $item);
  852. // Invoke the submit method on the translator controller (if available).
  853. if($item->getTranslator()){
  854. $translator = tmgmt_translator_ui_controller($item->getTranslator()->plugin);
  855. $translator->reviewFormSubmit($form, $form_state, $item);
  856. }
  857. // Write changes back to item.
  858. foreach ($form_state['values'] as $key => $value) {
  859. if (is_array($value) && isset($value['translation'])) {
  860. // Update the translation, this will only update the translation in case
  861. // it has changed.
  862. $data = array(
  863. '#text' => $value['translation'],
  864. '#origin' => 'local',
  865. );
  866. $item->addTranslatedData($data, $key);
  867. }
  868. }
  869. // Check if the user clicked on 'Accept', 'Submit' or 'Reject'.
  870. if (!empty($form['actions']['accept']) && $form_state['triggering_element']['#value'] == $form['actions']['accept']['#value']) {
  871. $item->acceptTranslation();
  872. // Check if the item could be saved and accepted successfully and redirect
  873. // to the job item view if that is the case.
  874. if ($item->isAccepted()) {
  875. $uri = $item->uri();
  876. $form_state['redirect'] = $uri['path'];
  877. }
  878. // Print all messages that have been saved while accepting the reviewed
  879. // translation.
  880. foreach ($item->getMessagesSince() as $message) {
  881. // Ignore debug messages.
  882. if ($message->type == 'debug') {
  883. continue;
  884. }
  885. if ($text = $message->getMessage()) {
  886. drupal_set_message(filter_xss($text), $message->type);
  887. }
  888. }
  889. }
  890. $item->save();
  891. }
  892. /**
  893. * Callback for the action at the job item review form.
  894. */
  895. function tmgmt_ui_translation_review_form_update_state($form, &$form_state) {
  896. $matches = array();
  897. // We should have an #name element
  898. // and the name should beginn with approve-
  899. // and the $matches should now kontain an element with with name key.
  900. preg_match("/^(?P<action>[^-]+)-(?P<key>.+)/i", $form_state['triggering_element']['#name'], $matches);
  901. $values = $form_state['values'];
  902. $data = array();
  903. $job_item = $form_state['item'];
  904. $controller = $job_item->getTranslatorController();
  905. $success = TRUE;
  906. switch ($matches['action']) {
  907. case 'reviewed':
  908. $form_state['rebuild'] = TRUE;
  909. $data['#status'] = TMGMT_DATA_ITEM_STATE_REVIEWED;
  910. break;
  911. case 'unreviewed':
  912. $form_state['rebuild'] = TRUE;
  913. $data['#status'] = TMGMT_DATA_ITEM_STATE_TRANSLATED;
  914. break;
  915. case 'reject':
  916. if (empty($values['confirm'])) {
  917. if (isset($_GET['destination'])) {
  918. $destination = $_GET['destination'];
  919. unset($_GET['destination']);
  920. }
  921. else {
  922. $destination = '';
  923. }
  924. tmgmt_ui_redirect_queue_set(array(current_path()), $destination);
  925. $form_state['redirect'] = current_path() . '/reject/' . $matches['key'];
  926. $success = FALSE;
  927. }
  928. else {
  929. $form_state['redirect'] = array(tmgmt_ui_redirect_queue_dequeue(), array( 'query' => array('destination' => tmgmt_ui_redirect_queue_destination())));
  930. if ($controller instanceof TMGMTTranslatorRejectDataItem) {
  931. $success = $job_item->getTranslatorController()->rejectDataItem($job_item, tmgmt_ensure_keys_array($matches['key']), $values);
  932. }
  933. }
  934. default:
  935. $data['#status'] = TMGMT_DATA_ITEM_STATE_PENDING;
  936. break;
  937. }
  938. if ($success) {
  939. $job_item->updateData($matches['key'], $data);
  940. // If a data item has been rejected and the job is in needs review state,
  941. // set back to active.
  942. if ($matches['action'] == 'reject' && $job_item->isNeedsReview()) {
  943. $job_item->active(FALSE);
  944. }
  945. }
  946. tmgmt_ui_write_request_messages($job_item->getJob());
  947. }
  948. /**
  949. * Ajax callback for the job item review form.
  950. */
  951. function tmgmt_ui_translation_review_form_ajax($form, &$form_state) {
  952. $key = array_slice($form_state['triggering_element']['#array_parents'], 0, 2);
  953. $render_data = drupal_array_get_nested_value($form, $key);
  954. tmgmt_ui_write_request_messages($form_state['item']->getJob());
  955. return drupal_render($render_data);
  956. }
  957. /**
  958. * Form callback for the reject confirm form.
  959. */
  960. function tmgmt_ui_translation_review_form_reject_confirm($form, &$form_state, TMGMTJobItem $job_item, $key) {
  961. // Path of job item review form.
  962. $path = explode('/', current_path());
  963. $path = implode('/', array_slice($path, 0, count($path) - 2));
  964. $args = array(
  965. '@data_item' => $job_item->getData(tmgmt_ensure_keys_array($key), '#label'),
  966. '@job_item' => $job_item->label(),
  967. );
  968. $form = confirm_form( $form, t('Confirm rejection of @data_item in @job_item', $args), $path, '');
  969. $form_state['item'] = $job_item;
  970. $form['key'] = array('#type' => 'value', '#value' => $key);
  971. $form['actions']['submit']['#name'] = 'reject-' . $key;
  972. $form['actions']['submit']['#submit'] = array('tmgmt_ui_translation_review_form_update_state', 'tmgmt_ui_translation_review_form_submit');
  973. $form = $job_item->getTranslatorController()->rejectForm($form, $form_state);
  974. return $form;
  975. }
  976. /**
  977. * @addtogroup tmgmt_ui_redirect_queue
  978. * @{
  979. */
  980. /**
  981. * Set a redirect queue that can then be worked through.
  982. *
  983. * @param $redirects
  984. * An array of redirect url's to be processed. For example checkout pages as
  985. * returned by tmgmt_ui_job_checkout_multiple().
  986. * @param $destination
  987. * A final destination to go to after the queue has been processed.
  988. */
  989. function tmgmt_ui_redirect_queue_set(array $redirects, $destination = NULL) {
  990. $_SESSION['tmgmt_ui']['redirect_queue'] = $redirects;
  991. $_SESSION['tmgmt_ui']['destination'] = $destination;
  992. }
  993. /**
  994. * Returns the redirect queue destination.
  995. *
  996. * This is the final destination after all queue items have been processed.
  997. *
  998. * @param $destination
  999. * The default destination that should be returned if none exists.
  1000. *
  1001. * @return
  1002. * The stored destination if defined, otherwise the passed in default
  1003. * destination.
  1004. */
  1005. function tmgmt_ui_redirect_queue_destination($destination = NULL) {
  1006. if (!empty($_SESSION['tmgmt_ui']['destination'])) {
  1007. $destination = $_SESSION['tmgmt_ui']['destination'];
  1008. unset($_SESSION['tmgmt_ui']['destination']);
  1009. return $destination;
  1010. }
  1011. return $destination;
  1012. }
  1013. /**
  1014. * Returns the amount of entries in the redirect queue.
  1015. *
  1016. * @return
  1017. * The amount of entries in the redirect queue.
  1018. */
  1019. function tmgmt_ui_redirect_queue_count() {
  1020. if (!empty($_SESSION['tmgmt_ui']['redirect_queue'])) {
  1021. return count($_SESSION['tmgmt_ui']['redirect_queue']);
  1022. }
  1023. return 0;
  1024. }
  1025. /**
  1026. * Dequeues one redirect in the queue and returns that.
  1027. *
  1028. * @return
  1029. * A redirect URL or NULL if the queue is empty.
  1030. */
  1031. function tmgmt_ui_redirect_queue_dequeue() {
  1032. if (!empty($_SESSION['tmgmt_ui']['redirect_queue'])) {
  1033. return array_shift($_SESSION['tmgmt_ui']['redirect_queue']);
  1034. }
  1035. }
  1036. /**
  1037. * @} End of "addtogroup tmgmt_ui_redirect_queue".
  1038. */
  1039. /**
  1040. * Provides color legends for source statuses.
  1041. */
  1042. function tmgmt_ui_color_legend() {
  1043. global $theme;
  1044. drupal_add_css(drupal_get_path('module', 'tmgmt_ui') . '/css/tmgmt_ui.admin.css');
  1045. if ($theme == 'seven') {
  1046. drupal_add_css(drupal_get_path('module', 'tmgmt_ui') . '/css/tmgmt_ui.admin.seven.css');
  1047. }
  1048. $legends = array();
  1049. $legends[] = array('legend' => t('Source Language'), 'color' => 'tmgmt-ui-icon-white');
  1050. $legends[] = array('legend' => t('Not translated'), 'color' => 'tmgmt-ui-icon-grey');
  1051. $legends[] = array('legend' => t('In progress'), 'color' => 'tmgmt-ui-icon-blue');
  1052. $legends[] = array('legend' => t('Ready for review'), 'color' => 'tmgmt-ui-icon-yellow');
  1053. $legends[] = array('legend' => t('Translated'), 'color' => 'tmgmt-ui-icon-green');
  1054. $legends[] = array('legend' => t('Translation Outdated'), 'color' => 'tmgmt-ui-icon-orange');
  1055. $output = '<div class="tmgmt-color-legend clearfix">';
  1056. foreach ($legends as $legend) {
  1057. $output .= '<div class="tmgmt-one-legend">
  1058. <div class="tmgmt-legend-icon tmgmt-ui-icon tmgmt-ui-icon-10 ' .
  1059. $legend['color'] . '"><span></span></div>
  1060. <div class="tmgmt-legend-status">' . $legend['legend'] . '</div>
  1061. </div>';
  1062. }
  1063. $output .= '</div>';
  1064. return $output;
  1065. }
  1066. /**
  1067. * Attempts to checkout a number of jobs and prepare the necessary redirects.
  1068. *
  1069. * @param array $form_state
  1070. * Form state array, used to set the initial redirect.
  1071. * @param array $jobs
  1072. * Array of jobs to attempt checkout
  1073. *
  1074. * @ingroup tmgmt_job
  1075. *
  1076. * @see tmgmt_ui_job_checkout_multiple()
  1077. */
  1078. function tmgmt_ui_job_checkout_and_redirect(array &$form_state, array $jobs) {
  1079. $redirects = tmgmt_ui_job_checkout_multiple($jobs);
  1080. // If necessary, do a redirect.
  1081. if ($redirects) {
  1082. if (isset($_GET['destination'])) {
  1083. // Remove existing destination, as that will prevent us from being
  1084. // redirect to the job checkout page. Set the destination as the final
  1085. // redirect instead.
  1086. tmgmt_ui_redirect_queue_set($redirects, $_GET['destination']);
  1087. unset($_GET['destination']);
  1088. }
  1089. else {
  1090. tmgmt_ui_redirect_queue_set($redirects, current_path());
  1091. }
  1092. $form_state['redirect'] = tmgmt_ui_redirect_queue_dequeue();
  1093. // Count of the job messages is one less due to the final redirect.
  1094. drupal_set_message(format_plural(count($redirects), t('One job needs to be checked out.'), t('@count jobs need to be checked out.')));
  1095. }
  1096. }
  1097. /**
  1098. * Implements hook_help().
  1099. */
  1100. function tmgmt_ui_help($path, $arg) {
  1101. $output = '';
  1102. if (strpos($path, 'admin/tmgmt/sources') !== FALSE) {
  1103. $output = '<div class="source-help-wrapper">' . tmgmt_ui_color_legend() . '</div>';
  1104. }
  1105. if ($path == 'admin/tmgmt/cart') {
  1106. $output = '<p>' . t('The TMGMT cart is used to bundle text items from
  1107. different sources into one translation job. Use the "Add to cart" button to
  1108. add all selected items in any source list. <br/>From the cart page, you can
  1109. request a translation of all selected elements in the cart into any available
  1110. language. One translation job will be created for each language pair involved.')
  1111. . '</p>';
  1112. }
  1113. return $output;
  1114. }