i18n_access.module 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. <?php
  2. /**
  3. * @file
  4. * i18n_access.module
  5. */
  6. /**
  7. * Implements hook_user_insert().
  8. */
  9. function i18n_access_user_insert(&$edit, &$account, $category = NULL) {
  10. i18n_access_user_update($edit, $account, $category);
  11. }
  12. /**
  13. * Implements hook_user_update().
  14. */
  15. function i18n_access_user_update(&$edit, &$account, $category = NULL) {
  16. if ($category == 'account') {
  17. // see user_admin_perm_submit()
  18. if (isset($edit['i18n_access'])) {
  19. db_delete('i18n_access')
  20. ->condition('uid', $account->uid)
  21. ->execute();
  22. $edit['i18n_access'] = array_filter($edit['i18n_access']);
  23. if (count($edit['i18n_access'])) {
  24. db_insert('i18n_access')
  25. ->fields(array(
  26. 'uid' => $account->uid,
  27. 'perm' => implode(', ', array_keys($edit['i18n_access'])),
  28. ))->execute();
  29. }
  30. unset($edit['i18n_access']);
  31. }
  32. }
  33. }
  34. /**
  35. * Implements hook_user_delete().
  36. */
  37. function i18n_access_user_delete($account) {
  38. db_delete('i18n_access')
  39. ->condition('uid', $account->uid)
  40. ->execute();
  41. }
  42. /**
  43. * Load the language permissions for a given user
  44. */
  45. function i18n_access_load_permissions($uid = NULL) {
  46. $perms = &drupal_static(__FUNCTION__);
  47. // use the global user id if none is passed
  48. if (!isset($uid)) {
  49. $uid = $GLOBALS['user']->uid;
  50. $account = NULL;
  51. }
  52. else {
  53. $account = user_load($uid);
  54. }
  55. if (!isset($perms[$uid])) {
  56. $perm_string = db_query('SELECT perm FROM {i18n_access} WHERE uid = :uid', array(':uid' => $uid))->fetchField();
  57. if ($perm_string) {
  58. $perms[$uid] = drupal_map_assoc(explode(', ', $perm_string));
  59. }
  60. else {
  61. $perms[$uid] = array();
  62. }
  63. }
  64. // adding the default languages if permission has been granted
  65. if (user_access('access selected languages', $account)) {
  66. $perms[$uid] = array_merge($perms[$uid], drupal_map_assoc(variable_get('i18n_access_languages', array())));
  67. }
  68. return $perms[$uid];
  69. }
  70. /**
  71. * Implements hook_permission().
  72. */
  73. function i18n_access_permission() {
  74. return array(
  75. 'access selected languages' => array(
  76. 'title' => t('Access selected languages'),
  77. 'description' => t('This permission gives this role edit/delete access to all content which are in the <a href="!url" target="_blank">selected language</a>. View/create access needs a different access level.', array('!url' => url('admin/config/regional/language/access'))),
  78. 'restrict access' => TRUE,
  79. ),
  80. );
  81. }
  82. /**
  83. * Implements hook_form_node_form_alter().
  84. */
  85. function i18n_access_form_node_form_alter(&$form) {
  86. $form['#after_build'][] = '_i18n_access_form_node_form_alter';
  87. }
  88. /**
  89. * Unset's languages from language options if user does not have permission to
  90. * use.
  91. *
  92. * @param $form
  93. * @param $form_state
  94. * @return mixed
  95. */
  96. function _i18n_access_form_node_form_alter($form, &$form_state) {
  97. if (isset($form['language']['#options']) && !user_access('bypass node access')) {
  98. $perms = i18n_access_load_permissions();
  99. foreach ($form['language']['#options'] as $key => $value) {
  100. if (empty($perms[$key])) {
  101. unset($form['language']['#options'][$key]);
  102. }
  103. }
  104. }
  105. return $form;
  106. }
  107. /**
  108. * Implements hook_form_alter().
  109. */
  110. function i18n_access_form_alter(&$form, &$form_state, $form_id) {
  111. //Configuring translation edit form to limit it to allowed language
  112. if ($form_id == 'i18n_node_select_translation' && !user_access('bypass node access')) {
  113. $perms = i18n_access_load_permissions();
  114. foreach ($form['translations']['nid'] as $language => $translation) {
  115. if (!isset($perms[$language]) && $language != '#tree') {
  116. unset($form['translations']['nid'][$language]);
  117. }
  118. }
  119. foreach ($form['translations']['language'] as $language => $translation) {
  120. if (!isset($perms[$language]) && $language != '#tree') {
  121. unset($form['translations']['language'][$language]);
  122. }
  123. }
  124. foreach ($form['translations']['node'] as $language => $translation) {
  125. if (!isset($perms[$language]) && $language != '#tree') {
  126. unset($form['translations']['node'][$language]);
  127. }
  128. }
  129. }
  130. // Add i18n_access things to user/edit /user/add
  131. if ($form_id == 'user_register_form' || $form_id == 'user_profile_form' ) {
  132. $form['i18n_access'] = array(
  133. '#type' => 'fieldset',
  134. '#title' => t('Translation access'),
  135. '#tree' => 0,
  136. '#access' => user_access('administer users'),
  137. );
  138. $form['i18n_access']['i18n_access'] = array(
  139. '#type' => 'checkboxes',
  140. '#options' => array(LANGUAGE_NONE => t('Language neutral')) + locale_language_list('name'),
  141. '#default_value' => i18n_access_load_permissions($form['#user']->uid),
  142. '#description' => t('The user get edit, delete access to all content which are in this enabled languages. Create, view access needs a different access level.'),
  143. );
  144. }
  145. }
  146. /**
  147. * Implements hook_node_access().
  148. */
  149. function i18n_access_node_access($node, $op, $account = NULL, $langcode = NULL) {
  150. // big re-work here. discarded entire original function-- replaced with our own
  151. if (is_object($node)) {
  152. // make sure that site administrators always have access
  153. $permissions = i18n_access_load_permissions($user);
  154. if (user_access('site administrator', $account)) {
  155. return TRUE;
  156. }
  157. // if langcode is null it means the user is not accessing by translation overview, we throw access deny and allow to hard deny sneaky people and keep unpermitted tabs out of the menu system for the user
  158. elseif ($langcode == NULL) {
  159. global $language;
  160. $langcode = $language->language;
  161. switch ($op) {
  162. case 'view':
  163. return NODE_ACCESS_ALLOW;
  164. break;
  165. case 'update':
  166. if (empty($permissions[$langcode])) {
  167. return NODE_ACCESS_DENY;
  168. }
  169. else {
  170. return NODE_ACCESS_ALLOW;
  171. }
  172. break;
  173. case 'create':
  174. if (empty($permissions[$langcode])) {
  175. return NODE_ACCESS_DENY;
  176. }
  177. else {
  178. return NODE_ACCESS_ALLOW;
  179. }
  180. break;
  181. }
  182. }
  183. //if they are accessing by translation overview, the language code gets passed by the translation overview, we send true or false here
  184. else {
  185. switch ($op) {
  186. case 'view':
  187. return TRUE;
  188. break;
  189. case 'update':
  190. if (empty($permissions[$langcode])) {
  191. return FALSE;
  192. }
  193. else {
  194. return TRUE;
  195. }
  196. break;
  197. case 'create':
  198. if (empty($permissions[$langcode])) {
  199. return FALSE;
  200. }
  201. else {
  202. return TRUE;
  203. }
  204. break;
  205. }
  206. }
  207. }
  208. }
  209. /**
  210. * Implements hook_menu_alter().
  211. */
  212. function i18n_access_node_menu_alter(&$items) {
  213. // due to hook_module_implementation_alter calling entity translation last, we can't change the callback here, i've done it in entity_translation.node.inc - consider calling it here?
  214. $items['node/%node/translate']['page callback'] = 'i18n_access_translation_node_overview';
  215. }
  216. /**
  217. * Most logic comes from translation/i18n_node module.
  218. *
  219. * We removes here only the "add translation" links for languages which are not your selected language.
  220. *
  221. * @see translation_node_overview
  222. * @see i18n_node_translation_overview
  223. *
  224. * @param object $node
  225. *
  226. * @return array.
  227. */
  228. function i18n_access_translation_node_overview($node) {
  229. include_once DRUPAL_ROOT . '/includes/language.inc';
  230. // include functions from i18n_node.pages.inc
  231. include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'i18n_node') . '/i18n_node.pages.inc';
  232. // this is the part where this thing sorts out how to build a list of existing translations for this node
  233. // since we use entity translation, the tnid isn't what we're using to build the translation list. we're using node->translations->data[keys]
  234. $available_translations = $node->translations->data;
  235. // iterate over each available translation and add its key (which is the 2 letter language code) to the array we call $translations with the node object as the value
  236. foreach ($available_translations as $key => $value) {
  237. $translations[$key] = $node;
  238. }
  239. $header = array(t('Language'), t('Title'), t('Status'), t('Operations'));
  240. $rows = array();
  241. global $user;
  242. $perms = i18n_access_load_permissions($user->uid);
  243. //end
  244. // Modes have different allowed languages
  245. foreach (i18n_node_language_list($node) as $langcode => $language_name) {
  246. if ($langcode == LANGUAGE_NONE) {
  247. // Never show language neutral on the overview.
  248. continue;
  249. }
  250. $options = array();
  251. if (isset($translations[$langcode])) {
  252. // Existing translation in the translation set: display status.
  253. // We load the full node to check whether the user can edit it.
  254. $translation_node = node_load($translations[$langcode]->nid);
  255. $path = 'node/' . $translation_node->nid;
  256. // Account for title field module:
  257. if (isset($translation_node->title_field) && isset($translation_node->title_field[$langcode])) {
  258. $title = i18n_node_translation_link($translation_node->title_field[$langcode][0]['value'], $path, $langcode);
  259. }
  260. else {
  261. $title = i18n_node_translation_link($translation_node->title, $path, $langcode);
  262. }
  263. if (i18n_access_node_access($translation_node, 'update', $user, $langcode)) {
  264. $text = t('edit');
  265. $path = 'node/' . $translation_node->nid . '/edit';
  266. $options[] = i18n_node_translation_link($text, $path, $langcode);
  267. }
  268. $status = $translation_node->status ? t('Published') : t('Not published');
  269. $status .= $translation_node->translate ? ' - ' . t('outdated') . '' : '';
  270. if ($translation_node->nid == $tnid) {
  271. $language_name = t('@language_name (source)', array('@language_name' => $language_name));
  272. }
  273. }
  274. else {
  275. // No such translation in the set yet: help user to create it.
  276. $title = t('n/a');
  277. if (node_access('create', $node->type) && (!empty($perms[$langcode]) || user_access('bypass node access'))) {
  278. $text = t('add translation');
  279. $path = 'node/add/' . str_replace('_', '-', $node->type);
  280. $query = array('query' => array('translation' => $node->nid, 'target' => $langcode));
  281. $options[] = i18n_node_translation_link($text, $path, $langcode, $query);
  282. }
  283. $status = t('Not translated');
  284. }
  285. $rows[] = array($language_name, $title, $status, implode(" | ", $options));
  286. }
  287. drupal_set_title(t('Translations of %title', array('%title' => $node->title)), PASS_THROUGH);
  288. $build['translation_node_overview'] = array(
  289. '#theme' => 'table',
  290. '#header' => $header,
  291. '#rows' => $rows,
  292. );
  293. if (user_access('administer content translations')) {
  294. $build['translation_node_select'] = drupal_get_form('i18n_node_select_translation', $node, $translations);
  295. }
  296. return $build;
  297. }
  298. /**
  299. * Implements hook_menu().
  300. */
  301. function i18n_access_menu() {
  302. $items['admin/config/regional/language/access'] = array(
  303. 'title' => 'Access',
  304. 'page callback' => 'drupal_get_form',
  305. 'page arguments' => array('i18n_access_admin_settings'),
  306. 'access arguments' => array('administer site configuration'),
  307. 'type' => MENU_LOCAL_TASK,
  308. 'weight' => 10,
  309. );
  310. return $items;
  311. }
  312. /**
  313. * Node-specific menu alterations.
  314. */
  315. function i18n_access_menu_alter(&$items, $backup) {
  316. if (isset($backup['node'])) {
  317. $item = $backup['node'];
  318. // Preserve the menu router item defined by other modules.
  319. $callback['page callback'] = $item['page callback'];
  320. $callback['file'] = $item['file'];
  321. $callback['module'] = $item['module'];
  322. $access_arguments = array_merge(array(1, $item['access callback']), $item['access arguments']);
  323. }
  324. else {
  325. $access_arguments = array(1);
  326. }
  327. // Point the 'translate' tab to point to the i18n_access version of the translation overview page
  328. $items['node/%node/translate']['page callback'] = 'i18n_access_translation_node_overview';
  329. // There are 3 page arguments for the entity translation overview, only one for i18n_access:
  330. $items['node/%node/translate']['page arguments'] = array(1);
  331. // Pass in the i18n_access permissions
  332. $items['node/%node/translate']['access arguments'] = $access_arguments;
  333. // Point to i18n_access's include for the callback
  334. $items['node/%node/translate']['file'] = 'i18n_access.module';
  335. // Point to i18n_access module
  336. $items['node/%node/translate']['module'] = 'i18n_access';
  337. }
  338. /**
  339. * Implements hook_module_implements_alter().
  340. */
  341. function i18n_access_module_implements_alter(&$implementations, $hook) {
  342. switch ($hook) {
  343. case 'menu_alter':
  344. // Move our hook_menu_alter implementation to the end of the list.
  345. $group = $implementations['i18n_access'];
  346. unset($implementations['i18n_access']);
  347. $implementations['i18n_access'] = $group;
  348. break;
  349. }
  350. }
  351. /**
  352. * Admin settings form.
  353. */
  354. function i18n_access_admin_settings($form) {
  355. $form['i18n_access_languages'] = array(
  356. '#title' => t('Select the default access languages'),
  357. '#type' => 'select',
  358. '#multiple' => TRUE,
  359. '#options' => array(LANGUAGE_NONE => t('Language neutral')) + locale_language_list('name'),
  360. '#default_value' => variable_get('i18n_access_languages', array()),
  361. '#description' => t("This selection of languages will be connected with the 'access selected languages' permission which you can use to grant a role access to these languages at once.")
  362. );
  363. return system_settings_form($form);
  364. }