condition('uid', $account->uid) ->execute(); $edit['i18n_access'] = array_filter($edit['i18n_access']); if (count($edit['i18n_access'])) { db_insert('i18n_access') ->fields(array( 'uid' => $account->uid, 'perm' => implode(', ', array_keys($edit['i18n_access'])), ))->execute(); } unset($edit['i18n_access']); } } } /** * Implements hook_user_delete(). */ function i18n_access_user_delete($account) { db_delete('i18n_access') ->condition('uid', $account->uid) ->execute(); } /** * Load the language permissions for a given user */ function i18n_access_load_permissions($uid = NULL) { $perms = &drupal_static(__FUNCTION__); // use the global user id if none is passed if (!isset($uid)) { $uid = $GLOBALS['user']->uid; $account = NULL; } else { $account = user_load($uid); } if (!isset($perms[$uid])) { $perm_string = db_query('SELECT perm FROM {i18n_access} WHERE uid = :uid', array(':uid' => $uid))->fetchField(); if ($perm_string) { $perms[$uid] = drupal_map_assoc(explode(', ', $perm_string)); } else { $perms[$uid] = array(); } } // adding the default languages if permission has been granted if (user_access('access selected languages', $account)) { $perms[$uid] = array_merge($perms[$uid], drupal_map_assoc(variable_get('i18n_access_languages', array()))); } return $perms[$uid]; } /** * Implements hook_permission(). */ function i18n_access_permission() { return array( 'access selected languages' => array( 'title' => t('Access selected languages'), 'description' => t('This permission gives this role edit/delete access to all content which are in the selected language. View/create access needs a different access level.', array('!url' => url('admin/config/regional/language/access'))), 'restrict access' => TRUE, ), ); } /** * Implements hook_form_node_form_alter(). */ function i18n_access_form_node_form_alter(&$form) { $form['#after_build'][] = '_i18n_access_form_node_form_alter'; } /** * Unset's languages from language options if user does not have permission to * use. * * @param $form * @param $form_state * @return mixed */ function _i18n_access_form_node_form_alter($form, &$form_state) { if (isset($form['language']['#options']) && !user_access('bypass node access')) { $perms = i18n_access_load_permissions(); foreach ($form['language']['#options'] as $key => $value) { if (empty($perms[$key])) { unset($form['language']['#options'][$key]); } } } return $form; } /** * Implements hook_form_alter(). */ function i18n_access_form_alter(&$form, &$form_state, $form_id) { //Configuring translation edit form to limit it to allowed language if ($form_id == 'i18n_node_select_translation' && !user_access('bypass node access')) { $perms = i18n_access_load_permissions(); foreach ($form['translations']['nid'] as $language => $translation) { if (!isset($perms[$language]) && $language != '#tree') { unset($form['translations']['nid'][$language]); } } foreach ($form['translations']['language'] as $language => $translation) { if (!isset($perms[$language]) && $language != '#tree') { unset($form['translations']['language'][$language]); } } foreach ($form['translations']['node'] as $language => $translation) { if (!isset($perms[$language]) && $language != '#tree') { unset($form['translations']['node'][$language]); } } } // Add i18n_access things to user/edit /user/add if ($form_id == 'user_register_form' || $form_id == 'user_profile_form' ) { $form['i18n_access'] = array( '#type' => 'fieldset', '#title' => t('Translation access'), '#tree' => 0, '#access' => user_access('administer users'), ); $form['i18n_access']['i18n_access'] = array( '#type' => 'checkboxes', '#options' => array(LANGUAGE_NONE => t('Language neutral')) + locale_language_list('name'), '#default_value' => i18n_access_load_permissions($form['#user']->uid), '#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.'), ); } } /** * Implements hook_node_access(). */ function i18n_access_node_access($node, $op, $account = NULL, $langcode = NULL) { // big re-work here. discarded entire original function-- replaced with our own if (is_object($node)) { // make sure that site administrators always have access $permissions = i18n_access_load_permissions($user); if (user_access('site administrator', $account)) { return TRUE; } // 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 elseif ($langcode == NULL) { global $language; $langcode = $language->language; switch ($op) { case 'view': return NODE_ACCESS_ALLOW; break; case 'update': if (empty($permissions[$langcode])) { return NODE_ACCESS_DENY; } else { return NODE_ACCESS_ALLOW; } break; case 'create': if (empty($permissions[$langcode])) { return NODE_ACCESS_DENY; } else { return NODE_ACCESS_ALLOW; } break; } } //if they are accessing by translation overview, the language code gets passed by the translation overview, we send true or false here else { switch ($op) { case 'view': return TRUE; break; case 'update': if (empty($permissions[$langcode])) { return FALSE; } else { return TRUE; } break; case 'create': if (empty($permissions[$langcode])) { return FALSE; } else { return TRUE; } break; } } } } /** * Implements hook_menu_alter(). */ function i18n_access_node_menu_alter(&$items) { // 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? $items['node/%node/translate']['page callback'] = 'i18n_access_translation_node_overview'; } /** * Most logic comes from translation/i18n_node module. * * We removes here only the "add translation" links for languages which are not your selected language. * * @see translation_node_overview * @see i18n_node_translation_overview * * @param object $node * * @return array. */ function i18n_access_translation_node_overview($node) { include_once DRUPAL_ROOT . '/includes/language.inc'; // include functions from i18n_node.pages.inc include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'i18n_node') . '/i18n_node.pages.inc'; // this is the part where this thing sorts out how to build a list of existing translations for this node // 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] $available_translations = $node->translations->data; // 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 foreach ($available_translations as $key => $value) { $translations[$key] = $node; } $header = array(t('Language'), t('Title'), t('Status'), t('Operations')); $rows = array(); global $user; $perms = i18n_access_load_permissions($user->uid); //end // Modes have different allowed languages foreach (i18n_node_language_list($node) as $langcode => $language_name) { if ($langcode == LANGUAGE_NONE) { // Never show language neutral on the overview. continue; } $options = array(); if (isset($translations[$langcode])) { // Existing translation in the translation set: display status. // We load the full node to check whether the user can edit it. $translation_node = node_load($translations[$langcode]->nid); $path = 'node/' . $translation_node->nid; // Account for title field module: if (isset($translation_node->title_field) && isset($translation_node->title_field[$langcode])) { $title = i18n_node_translation_link($translation_node->title_field[$langcode][0]['value'], $path, $langcode); } else { $title = i18n_node_translation_link($translation_node->title, $path, $langcode); } if (i18n_access_node_access($translation_node, 'update', $user, $langcode)) { $text = t('edit'); $path = 'node/' . $translation_node->nid . '/edit'; $options[] = i18n_node_translation_link($text, $path, $langcode); } $status = $translation_node->status ? t('Published') : t('Not published'); $status .= $translation_node->translate ? ' - ' . t('outdated') . '' : ''; if ($translation_node->nid == $tnid) { $language_name = t('@language_name (source)', array('@language_name' => $language_name)); } } else { // No such translation in the set yet: help user to create it. $title = t('n/a'); if (node_access('create', $node->type) && (!empty($perms[$langcode]) || user_access('bypass node access'))) { $text = t('add translation'); $path = 'node/add/' . str_replace('_', '-', $node->type); $query = array('query' => array('translation' => $node->nid, 'target' => $langcode)); $options[] = i18n_node_translation_link($text, $path, $langcode, $query); } $status = t('Not translated'); } $rows[] = array($language_name, $title, $status, implode(" | ", $options)); } drupal_set_title(t('Translations of %title', array('%title' => $node->title)), PASS_THROUGH); $build['translation_node_overview'] = array( '#theme' => 'table', '#header' => $header, '#rows' => $rows, ); if (user_access('administer content translations')) { $build['translation_node_select'] = drupal_get_form('i18n_node_select_translation', $node, $translations); } return $build; } /** * Implements hook_menu(). */ function i18n_access_menu() { $items['admin/config/regional/language/access'] = array( 'title' => 'Access', 'page callback' => 'drupal_get_form', 'page arguments' => array('i18n_access_admin_settings'), 'access arguments' => array('administer site configuration'), 'type' => MENU_LOCAL_TASK, 'weight' => 10, ); return $items; } /** * Node-specific menu alterations. */ function i18n_access_menu_alter(&$items, $backup) { if (isset($backup['node'])) { $item = $backup['node']; // Preserve the menu router item defined by other modules. $callback['page callback'] = $item['page callback']; $callback['file'] = $item['file']; $callback['module'] = $item['module']; $access_arguments = array_merge(array(1, $item['access callback']), $item['access arguments']); } else { $access_arguments = array(1); } // Point the 'translate' tab to point to the i18n_access version of the translation overview page $items['node/%node/translate']['page callback'] = 'i18n_access_translation_node_overview'; // There are 3 page arguments for the entity translation overview, only one for i18n_access: $items['node/%node/translate']['page arguments'] = array(1); // Pass in the i18n_access permissions $items['node/%node/translate']['access arguments'] = $access_arguments; // Point to i18n_access's include for the callback $items['node/%node/translate']['file'] = 'i18n_access.module'; // Point to i18n_access module $items['node/%node/translate']['module'] = 'i18n_access'; } /** * Implements hook_module_implements_alter(). */ function i18n_access_module_implements_alter(&$implementations, $hook) { switch ($hook) { case 'menu_alter': // Move our hook_menu_alter implementation to the end of the list. $group = $implementations['i18n_access']; unset($implementations['i18n_access']); $implementations['i18n_access'] = $group; break; } } /** * Admin settings form. */ function i18n_access_admin_settings($form) { $form['i18n_access_languages'] = array( '#title' => t('Select the default access languages'), '#type' => 'select', '#multiple' => TRUE, '#options' => array(LANGUAGE_NONE => t('Language neutral')) + locale_language_list('name'), '#default_value' => variable_get('i18n_access_languages', array()), '#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.") ); return system_settings_form($form); }