Index: l10n_community/ajax.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_community/Attic/ajax.inc,v
retrieving revision 1.1.2.18.2.5
diff -u -p -r1.1.2.18.2.5 ajax.inc
--- l10n_community/ajax.inc	18 Sep 2009 18:03:18 -0000	1.1.2.18.2.5
+++ l10n_community/ajax.inc	5 Dec 2009 13:25:01 -0000
@@ -56,6 +56,8 @@ function l10n_community_string_details($
 }
 
 function l10n_community_string_suggestions($langcode = NULL, $sid = 0) {
+  global $user;
+
   // Existing, "unresolved" suggestions.
   $suggestions = array();
   $result = db_query("SELECT t.tid, t.sid, t.translation, t.uid_entered, t.time_entered, u.name FROM {l10n_community_translation} t LEFT JOIN {users} u ON u.uid = t.uid_entered WHERE t.language = '%s' AND t.sid = %d AND t.is_active = 1 AND t.is_suggestion = 1 ORDER BY t.time_entered", $langcode, $sid);
@@ -64,8 +66,11 @@ function l10n_community_string_suggestio
   while ($suggestion = db_fetch_object($result)) {
     // This detail pane is only retrieved from JS, so we are free to output obtrusive JS here.
     $token = "'". drupal_get_token('l10n_server_'. $suggestion->tid .'_'. $suggestion->sid) ."'";
-    $approve_button = $perm == L10N_PERM_ALL ? theme('l10n_community_button', 'approve', 'l10n-approve', 'onclick="l10nCommunity.approveSuggestion('. $suggestion->tid .','. $suggestion->sid .', this, '. $token .');" title="'. t('Approve suggestion.') .'"') : '';
-    $decline_button = $perm == L10N_PERM_ALL ? theme('l10n_community_button', 'decline', 'l10n-decline', 'onclick="l10nCommunity.declineSuggestion('. $suggestion->tid .','. $suggestion->sid .', this, '. $token .');" title="'. t('Decline suggestion.') .'"') : '';
+    $approve_button = $decline_button = '';
+    if ($perm & ($suggestion->uid_entered == $user->uid ? L10N_PERM_MODERATE_OWN : L10N_PERM_MODERATE_OTHERS)) {
+      $approve_button = theme('l10n_community_button', 'approve', 'l10n-approve', 'onclick="l10nCommunity.approveSuggestion('. $suggestion->tid .','. $suggestion->sid .', this, '. $token .');" title="'. t('Approve suggestion.') .'"');
+      $decline_button = theme('l10n_community_button', 'decline', 'l10n-decline', 'onclick="l10nCommunity.declineSuggestion('. $suggestion->tid .','. $suggestion->sid .', this, '. $token .');" title="'. t('Decline suggestion.') .'"');
+    }
 
     // Plural versions are shown in a short form.
     // Since we no longer store an additional copy of strings in JS
@@ -95,8 +100,11 @@ function l10n_community_string_suggestio
  *   The suggestion object if validated $tid, user permissions and token.
  */
 function l10n_community_string_ajax_suggestion($tid = 0) {
+  global $user;
+  
   if (($suggestion = db_fetch_object(db_query("SELECT * FROM {l10n_community_translation} WHERE tid = %d AND is_suggestion = 1 AND is_active = 1", $tid))) &&
-      (l10n_community_get_permission($suggestion->language) == L10N_PERM_ALL) &&
+      ($perm = l10n_community_get_permission($suggestion->language)) &&
+      ($perm & ($suggestion->uid_entered == $user->uid ? L10N_PERM_MODERATE_OWN : L10N_PERM_MODERATE_OTHERS)) &&
       !empty($_GET['form_token']) &&
       drupal_valid_token($_GET['form_token'], 'l10n_server_'. $suggestion->tid .'_'. $suggestion->sid)) {
     return $suggestion;
Index: l10n_community/import.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_community/Attic/import.inc,v
retrieving revision 1.1.2.5.2.15
diff -u -p -r1.1.2.5.2.15 import.inc
--- l10n_community/import.inc	22 Oct 2009 15:13:44 -0000	1.1.2.5.2.15
+++ l10n_community/import.inc	5 Dec 2009 13:25:01 -0000
@@ -11,27 +11,22 @@
  */
 function l10n_community_import_page($langcode) {
   $languages = l10n_community_get_languages();
-  if (($perm = l10n_community_get_permission($langcode)) == L10N_PERM_ALL) {
-    drupal_set_title(t('Import translations to @language', array('@language' => $languages[$langcode]->name)));
-  }
-  else {
-    drupal_set_title(t('Import suggestions to @language', array('@language' => $languages[$langcode]->name)));
-  }
+  drupal_set_title(t('Import to @language', array('@language' => $languages[$langcode]->name)));
   // Add missing breadcrumb.
   drupal_set_breadcrumb(
     array(
       l(t('Home'), NULL),
       l(t('Translate projects'), 'translate'))
   );
-  return drupal_get_form('l10n_community_import_form', $langcode, $perm);
+  return drupal_get_form('l10n_community_import_form', $langcode);
 }
 
 /**
  * Translation import form.
  */
-function l10n_community_import_form($form_state, $langcode, $perm) {
+function l10n_community_import_form($form_state, $langcode) {
   global $user;
-  
+
   $form['#attributes']['enctype'] = 'multipart/form-data';
 
   $form['langcode'] = array(
@@ -46,27 +41,7 @@ function l10n_community_import_form($for
     '#description' => t('The maximum allowed size for uploads is %maxsize.', array('%maxsize' => format_size(file_upload_max_size()))),
   );
 
-  if ($perm == L10N_PERM_ALL) {
-    // If can submit translations, offer option to pick mode.
-    $form['is_suggestion'] = array(
-      '#title' => t('Status'),
-      '#type' => 'radios',
-      '#default_value' => 0,
-      '#options' => array(
-        0 => t('Store as approved translations'),
-        1 => t('Store as suggestions needing approval, possibly discussion'),
-      ),
-    );
-  }
-  else {
-    // Restrict to suggestions if no permission to submit translations directly.
-    $form['is_suggestion'] = array(
-      '#type' => 'value',
-      '#value' => 1,
-    );
-  }
-
-  if (($import_account = variable_get('l10n_community_import_user', '')) && ($account = user_load(array('name' => $import_account)))) {
+  if (($import_name = variable_get('l10n_community_import_user', '')) && ($import_account = user_load(array('name' => $import_name)))) {
     // If available, let the user import with attribution to a bulk import user.
     $form['import_uid'] = array(
       '#title' => t('Attribute import to'),
@@ -74,7 +49,7 @@ function l10n_community_import_form($for
       '#default_value' => $user->uid,
       '#options' => array(
         $user->uid => t('Yourself (%name)', array('%name' => $user->name)),
-        $account->uid => $import_account,
+        $import_account->uid => $import_name,
       ),
       '#description' => t('When importing the result of teamwork, If the imported translations were worked on by a team of people, it is common curtesy to not attribute them to your personal username to ensure you do not claim credit for work of the whole team.'),
     );
@@ -86,6 +61,28 @@ function l10n_community_import_form($for
     );
   }
 
+  if (!l10n_community_has_permission($langcode, L10N_PERM_MODERATE_OWN) && (!l10n_community_has_permission($langcode, L10N_PERM_MODERATE_OTHERS) || empty($import_account))) {
+    // Either has no permission to approve own or approve others, or we do not
+    // have a mass import account and no permission to approve own, so no way
+    // we can offer importing translations directly.
+    $form['is_suggestion'] = array(
+      '#type' => 'value',
+      '#value' => 1,
+    );
+  }
+  else {
+    // If can submit translations, offer option to pick mode.
+    $form['is_suggestion'] = array(
+      '#title' => t('Status'),
+      '#type' => 'radios',
+      '#default_value' => 0,
+      '#options' => array(
+        0 => t('Store as approved translations'),
+        1 => t('Store as suggestions needing approval, possibly discussion'),
+      ),
+    );
+  }
+
   $form['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Import')
@@ -94,6 +91,29 @@ function l10n_community_import_form($for
 }
 
 /**
+ * Validate the locale import form submission.
+ */
+function l10n_community_import_form_validate($form, &$form_state) {
+  global $user;
+
+  if (empty($form_state['values']['is_suggestion'])) {
+    if ($form_state['values']['import_uid'] == $user->uid) {
+      // Trying to submit translation in her own name.
+      if (!l10n_community_has_permission($form_state['values']['langcode'], L10N_PERM_MODERATE_OWN)) {
+        form_set_error('is_suggestion', t('You do not have sufficient permissions to submit translations directly in your name. Please submit as suggestions.'));
+      }
+    }
+    else {
+      // Trying to submit translation in the import_account's name.
+      if (!l10n_community_has_permission($form_state['values']['langcode'], L10N_PERM_MODERATE_OTHERS)) {
+        form_set_error('is_suggestion', t('You do not have sufficient permissions to submit translations directly in the name of other users. Please submit as suggestions.'));
+      }
+    }
+  }
+
+}
+
+/**
  * Process the locale import form submission.
  */
 function l10n_community_import_form_submit($form, &$form_state) {
@@ -325,12 +345,12 @@ function _l10n_community_import_one_stri
     // Result stats queried.
     return array($inserted, $updated, $unchanged, $suggested, $duplicates, $ignored);
   }
-  
+
   // Trim translation (we will apply source string based whitespace later).
   if (is_string($value['msgstr'])) {
     $value['msgstr'] = trim($value['msgstr']);
   }
-  
+
   if (!empty($value['msgid']) && !empty($value['msgstr'])) {
     // We only save non-empty translations/suggestions.
 
@@ -355,7 +375,7 @@ function _l10n_community_import_one_stri
 
       // Merge plural versions into one for saving values.
       $value['msgstr'] = is_array($value['msgstr']) ? join("\0", $value['msgstr']) : $value['msgstr'];
-      
+
       // Trim imported translation to have whitesapce from source string.
       $value['msgstr'] = l10n_community_trim($value['msgstr'], $value['msgid']);
 
Index: l10n_community/l10n_community.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_community/Attic/l10n_community.module,v
retrieving revision 1.1.2.23.2.60
diff -u -p -r1.1.2.23.2.60 l10n_community.module
--- l10n_community/l10n_community.module	11 Nov 2009 18:21:01 -0000	1.1.2.23.2.60
+++ l10n_community/l10n_community.module	5 Dec 2009 13:25:02 -0000
@@ -11,19 +11,49 @@
  */
 
 /**
- * No permission to suggest or translate, just view.
+ * Base permission; no access at all.
  */
 define('L10N_PERM_NONE', 0);
 
 /**
- * Permission to suggest.
+ * User may view the translations.
  */
-define('L10N_PERM_SUGGEST', 1);
+define('L10N_PERM_VIEW', 1);
 
 /**
- * Permission both to suggest and translate.
+ * User may contribute suggestions.
  */
-define('L10N_PERM_ALL', 2);
+define('L10N_PERM_SUGGEST', 2);
+
+/**
+ * User may approve or decline other user's suggestions.
+ */
+define('L10N_PERM_MODERATE_OTHERS', 4);
+
+/**
+ * User may approve her own suggestions.
+ */
+define('L10N_PERM_MODERATE_OWN', 8);
+
+/**
+ * User may import translations from a file.
+ */
+define('L10N_PERM_IMPORT', 16);
+
+/**
+ * User may export translation templates or translated .po files.
+ */
+define('L10N_PERM_EXPORT', 32);
+
+/**
+ * Accumulates all permissions a contributor may have.
+ */
+define('L10N_PERM_CONTRIBUTE', L10N_PERM_SUGGEST | L10N_PERM_IMPORT);
+
+/**
+ * Accumulates all permissions a reviewer may have.
+ */
+define('L10N_PERM_REVIEW', L10N_PERM_MODERATE_OTHERS | L10N_PERM_MODERATE_OWN);
 
 /**
  * Strings with any status.
@@ -226,7 +256,8 @@ function l10n_community_menu() {
     'page callback' => 'l10n_community_translate_page',
     'page arguments' => array(2),
     'file' => 'translate.inc',
-    'access arguments' => array('browse translations'),
+    'access callback' => 'l10n_community_has_permission',
+    'access arguments' => array(2, (string)L10N_PERM_VIEW),
     'type' => MENU_LOCAL_TASK,
     'weight' => -10,
   );
@@ -235,8 +266,8 @@ function l10n_community_menu() {
     'page callback' => 'l10n_community_translate_page',
     'page arguments' => array(2, 'edit'),
     'file' => 'translate.inc',
-    'access callback' => 'l10n_community_contribute_access',
-    'access arguments' => array(2),
+    'access callback' => 'l10n_community_has_permission',
+    'access arguments' => array(2, (string)L10N_PERM_SUGGEST),
     'type' => MENU_LOCAL_TASK,
     'weight' => -8,
   );
@@ -245,7 +276,7 @@ function l10n_community_menu() {
     'page callback' => 'l10n_community_moderate_page',
     'page arguments' => array(2),
     'file' => 'moderate.inc',
-    'access callback' => 'l10n_community_moderation_access',
+    'access callback' => 'l10n_community_has_review_permission',
     'access arguments' => array(2),
     'type' => MENU_LOCAL_TASK,
     'weight' => -6,
@@ -255,8 +286,8 @@ function l10n_community_menu() {
     'page callback' => 'l10n_community_import_page',
     'page arguments' => array(2),
     'file' => 'import.inc',
-    'access callback' => 'l10n_community_contribute_access',
-    'access arguments' => array(2, TRUE),
+    'access callback' => 'l10n_community_has_permission',
+    'access arguments' => array(2, (string)L10N_PERM_IMPORT),
     'type' => MENU_LOCAL_TASK,
     'weight' => -5,
   );
@@ -265,7 +296,8 @@ function l10n_community_menu() {
     'page callback' => 'l10n_community_export_page',
     'page arguments' => array(NULL, 2),
     'file' => 'export.inc',
-    'access callback' => 'l10n_community_export_access',
+    'access callback' => 'l10n_community_has_permission',
+    'access arguments' => array(2, (string)L10N_PERM_EXPORT),
     'type' => MENU_LOCAL_TASK,
     'weight' => 0,
   );
@@ -290,7 +322,8 @@ function l10n_community_menu() {
     'title' => 'Export template',
     'page callback' => 'l10n_community_export_page',
     'page arguments' => array(2),
-    'access callback' => 'l10n_community_export_access',
+    'access callback' => 'l10n_community_has_permission',
+    'access arguments' => array(2, (string)L10N_PERM_EXPORT),
     'file' => 'export.inc',
     'type' => MENU_LOCAL_TASK,
     'weight' => 0,
@@ -359,32 +392,6 @@ function l10n_community_project_admin_lo
 }
 
 /**
- * Access callback for editing and import paths. Requires edit privileges for given language.
- *
- * @param $langcode
- *  Language code.
- * @param $import
- *  Boolean. Whether this is a gettext import operation.
- */
-function l10n_community_contribute_access($langcode, $import = FALSE) {
-  return user_access('browse translations') && (l10n_community_get_permission($langcode) != L10N_PERM_NONE) && ($import ? user_access('import gettext files') : TRUE);
-}
-
-/**
- * Access callback for exporting paths. Requires access and export privileges.
- */
-function l10n_community_export_access() {
-  return user_access('browse translations') && user_access('export gettext templates and translations');
-}
-
-/**
- * Access to the moderation screen.
- */
-function l10n_community_moderation_access($langcode) {
-  return user_access('browse translations') && (l10n_community_get_permission($langcode) == L10N_PERM_ALL);
-}
-
-/**
  * Title callback for project pages.
  */
 function l10n_community_page_title_project($uri) {
@@ -413,7 +420,16 @@ function l10n_community_init() {
  * Implementation of hook_perm().
  */
 function l10n_community_perm() {
-  return array('access localization community', 'browse translations', 'submit suggestions', 'submit translations and approve suggestions', 'administer localization community', 'export gettext templates and translations', 'import gettext files');
+  return array(
+    'access localization community',
+    'browse translations',
+    'administer localization community',
+    'export gettext templates and translations',
+    'import gettext files',
+    'submit suggestions',
+    'moderate suggestions from others',
+    'moderate own suggestions',
+  );
 }
 
 /**
@@ -465,58 +481,54 @@ function l10n_community_block_help() {
     'content' => '',
   );
 
-  switch ($_GET['q']) {
-    case 'translate':
-      $customizable = db_result(db_query("SELECT custom FROM {blocks} WHERE theme = '%s' AND module = 'l10n_community' AND delta = 'help'", $theme_key));
-      $block['content'] = '<p>'. t('Welcome to the translation server. This block will show you tips and advice throughout the application.') . (($user->uid && $customizable) ? ' '. t('Once you are familiar with the system, feel free to disable help in <a href="@user-page">your user settings</a>.', array('@user-page' => url('user/'. $user->uid .'/edit'))) : '') .'</p>';
-      return $block;
-
-    // Help text for language overview page.
-    case 'translate/languages':
-      $block['content'] = '<p>'. t('Choose a language to dive in to suggest translations, approve suggestions, import and export.', array('@by-project-link' => url('translate/projects'))) .'</p>';
-      return $block;
-
-    // Help for project overview page.
-    case 'translate/projects':
-      $block['content'] = '<p>'. t('Choose a project to explore.', array('@by-language-link' => url('translate/languages'))) .'</p>';
-      return $block;
+  if ($_GET['q'] == 'translate') {
+    $customizable = db_result(db_query("SELECT custom FROM {blocks} WHERE theme = '%s' AND module = 'l10n_community' AND delta = 'help'", $theme_key));
+    $block['content'] = '<p>'. t('Welcome to the translation server. This block will show you tips and advice throughout the application.') . (($user->uid && $customizable) ? ' '. t('Once you are familiar with the system, feel free to disable help in <a href="@user-page">your user settings</a>.', array('@user-page' => url('user/'. $user->uid .'/edit'))) : '') .'</p>';
+    return $block;
   }
 
   // Match actual translation editing or review pages with the two different path models they could have.
-  if (preg_match('!translate/languages/(?P<langcode>[^/]+)(/(?P<action>view|edit|import|export))?$!', $_GET['q'], $args) ||
+  if (preg_match('!translate/languages/(?P<langcode>[^/]+)(/(?P<action>view|edit|import|export|moderate))?$!', $_GET['q'], $args) ||
       preg_match('!translate/projects/(?P<uri>[^/]+)(/(?P<action>|export))??$!', $_GET['q'], $args)) {
     $perm = isset($args['langcode']) ? l10n_community_get_permission($args['langcode']) : L10N_PERM_NONE;
-    $permission_help = '';
+    $permission_help = $permission_notes = array();
     if (module_exists('l10n_groups')) {
       // We are dealing with a groups based permission model.
-      $permission_help = l10n_groups_block_help($perm, isset($args['langcode']) ? $args['langcode'] : NULL);
+      $permission_help[] = l10n_groups_block_help($perm, isset($args['langcode']) ? $args['langcode'] : NULL);
     }
-    else {
-      // We are dealing with a simple permission model.
-      switch ($perm) {
-        case L10N_PERM_NONE:
-          // No user access to this page in this case.
-          break;
-        case L10N_PERM_SUGGEST:
-          $permission_help = t('You can suggest translations or import complete Gettext translation files to suggest multiple translations at once. People with permission to approve suggestions may either accept or decline your suggestions. To work offline, export a translation template, which contains the current state of the translation.');
-          break;
-        case L10N_PERM_ALL:
-          $permission_help = t('As a fully empowered user, you can suggest translations as well as approve translations suggested by others. Export/import of Gettext translation files is also possible.');
-          break;
-      }
-      $permission_help = !empty($permission_help) ? ('<p>'. $permission_help .'</p>') : '';
+    if ($perm & L10N_PERM_SUGGEST) {
+      $permission_notes[] = t('You can suggest translations to be reviewed by moderators of the team.');
+    }
+    if ($perm & L10N_PERM_MODERATE_OTHERS) {
+      $permission_notes[] = t('Moderation of suggestions submitted by others is possible.');
+    }
+    if ($perm & L10N_PERM_MODERATE_OWN) {
+      $permission_notes[] = t('You are empowered to directly submit translations.');
     }
+    if ($perm & L10N_PERM_IMPORT) {
+      $permission_notes[] = t('Import complete Gettext translation files to suggest multiple translations at once.');
+    }
+    if ($perm & L10N_PERM_EXPORT) {
+      $permission_notes[] = t('To work offline, export a translation template, which contains the current state of the translation.');
+    }
+    if (!empty($permission_notes)) {
+      $permission_help[] = '<p><ul><li>'. join('</li><li>', $permission_notes) .'</li></ul></p>';
+    }
+    $permission_help = join(' ', $permission_help);
 
     // Now construct the actual help text depending on whether we have project or language values from the address.
     if (isset($args['action'])) {
       // We have an import or export action.
       switch ($args['action']) {
+
         case 'import':
           $block['content'] = '<p>'. t('Because all project translations are shared, an imported file might provide translations for strings used in any project.') .'</p>'. $permission_help;
           return $block;
+
         case 'export':
-          $block['content'] = '<p>'. t('You have two different options to export. You can either export a template package for a project, or a package containing the existing translations for a given project release. Both types will use the GNU Gettext Portable Object (Template) format, which is understood by Drupal and desktop translation editing tools.') .'</p>'. $permission_help;
+          $block['content'] = '<p>'. t('The GNU Gettext Portable Object (Template) format is used for exports, which is understood by Drupal and desktop translation editing tools.') .'</p>'. $permission_help;
           return $block;
+
         case 'view':
         case 'edit':
           // Language code and project both present.
@@ -529,6 +541,10 @@ function l10n_community_block_help() {
 
           $block['content'] = $permission_help . theme('item_list', $items);
           return $block;
+
+        case 'moderate':
+          $block['content'] = $permission_help;
+          return $block;
       }
     }
     else {
@@ -935,13 +951,12 @@ function l10n_community_get_languages($k
 /**
  * Get translation permission level for a specific user.
  *
- *
  * @param $langcode
  *   Language code, for example 'hu', 'pt-br', 'de' or 'it'.
  * @param $account
- *   Optional user account.
+ *   (Optional) A user account object. If not passed, the current user is used.
  * @return
- *   L10N_PERM_NONE, L10N_PERM_SUGGEST or L10N_PERM_ALL
+ *   A bitmask of all permission flags the user has.
  */
 function l10n_community_get_permission($langcode, $account = NULL) {
   static $permissions = array();
@@ -954,28 +969,25 @@ function l10n_community_get_permission($
     $account = $user;
   }
 
-  if (isset($permissions[$account->uid][$langcode])) {
-    // Return cached value if available.
-    return $permissions[$account->uid][$langcode];
+  if (!isset($permissions[$account->uid])) {
+    $permissions[$account->uid] = array();
   }
+  if (!isset($permissions[$account->uid][$langcode])) {
+    // Minimal permission.
+    $global = L10N_PERM_NONE;
+    $global |= user_access('browse translations', $account) ? L10N_PERM_VIEW : 0;
+    $global |= user_access('submit suggestions', $account) ? L10N_PERM_SUGGEST : 0;
+    $global |= user_access('moderate suggestions from others', $account) ? L10N_PERM_MODERATE_OTHERS : 0;
+    $global |= user_access('moderate own suggestions', $account) ? L10N_PERM_MODERATE_OWN : 0;
+    $global |= user_access('import gettext files', $account) ? L10N_PERM_IMPORT : 0;
+    $global |= user_access('export gettext templates and translations', $account) ? L10N_PERM_EXPORT : 0;
 
-  // Initialize to lowest possible permission.
-  $permissions[$account->uid][$langcode] = L10N_PERM_NONE;
-
-  if ($account->uid == 1) {
-    // The administrator has all permissions in all languages.
-    return ($permissions[$account->uid][$langcode] = L10N_PERM_ALL);
-  }
-
-  $global_permission = user_access('submit translations and approve suggestions', $account) ? L10N_PERM_ALL : (user_access('submit suggestions', $account) ? L10N_PERM_SUGGEST : L10N_PERM_NONE);
-
-  if (($global_permission != L10N_PERM_NONE) && module_exists('l10n_groups')) {
-    // Fill up permission information for this account if it has any permission.
-    l10n_groups_get_permission($permissions, $global_permission, $langcode, $account);
-  }
-  else {
-    // Remember global permission if using l10n_community standalone.
-    $permissions[$account->uid][$langcode] = $global_permission;
+    if (!module_exists('l10n_groups')) {
+      $permissions[$account->uid][$langcode] = $global;
+    }
+    else {
+      l10n_groups_get_permission($permissions, $account, $global);
+    }
   }
 
   // Return from local cache.
@@ -983,6 +995,29 @@ function l10n_community_get_permission($
 }
 
 /**
+ * Checks whether the current user has a certain permission.
+ *
+ * @param $langcode
+ *   Language code, for example 'hu', 'pt-br', 'de' or 'it'.
+ * @param $permission
+ *   The permission to check for.
+ * @return
+ *   TRUE if the user has at least one of the specified permissions.
+ */
+function l10n_community_has_permission($langcode, $permission) {
+  return (bool)(l10n_community_get_permission($langcode, NULL) & $permission);
+}
+
+/**
+ * Check whether the user has either review permissions for $langcode.
+ *
+ * @see l10n_community_has_permission()
+ */
+function l10n_community_has_review_permission($langcode) {
+  return l10n_community_has_permission($langcode, L10N_PERM_MODERATE_OTHERS) || l10n_community_has_permission($langcode, L10N_PERM_MODERATE_OWN);
+}
+
+/**
  * Provides a list of projects from the database, ordered by uri.
  *
  * @param $options
@@ -1120,10 +1155,7 @@ function l10n_community_get_contexts() {
  * @param $suggested
  *   Counter to increment if a suggestion was saved.
  *
- * @todo
- *   Note that we are not tracking if the user entered something which
- *   was an existing active suggestion, in which case a new suggestion
- *   is added. This is an edge case, but possibly not desired.
+ * @see l10n_community_is_duplicate()
  */
 function l10n_community_target_save($sid, $translation, $langcode, $uid, $suggestion, &$inserted, &$updated, &$unchanged, &$suggested) {
 
Index: l10n_community/moderate.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_community/Attic/moderate.inc,v
retrieving revision 1.1.2.4
diff -u -p -r1.1.2.4 moderate.inc
--- l10n_community/moderate.inc	6 Nov 2009 16:00:57 -0000	1.1.2.4
+++ l10n_community/moderate.inc	5 Dec 2009 13:25:02 -0000
@@ -77,11 +77,12 @@ function l10n_community_moderation_form(
     '#type' => 'submit',
     '#value' => t('Decline all selected'),
   );
-  
+
   // All strings and suggestions
   $form['strings'] = array(
     '#tree' => TRUE,
   );
+
   foreach ($strings as $string) {
     $form['strings']['tid'][$string->tid] = array(
       '#type' => 'checkbox',
@@ -145,6 +146,8 @@ function l10n_community_moderation_form_
  *   An array of suggestion records from database.
  */
 function l10n_community_get_suggestions($langcode, $filters) {
+  global $user;
+
   $join = $join_args = $where = $where_args = array();
   $sql = $sql_count = '';
 
@@ -205,6 +208,19 @@ function l10n_community_get_suggestions(
     $join[] = "LEFT JOIN {l10n_community_translation} tt ON tt.sid = ts.sid AND tt.is_suggestion = 0 AND tt.is_active = 1 AND tt.language = '%s'";
     $join_args[] = $langcode;
   }
+  
+  // Only show strings matching the user's permissions. It should be already
+  // ensured that the user has either one of these permissions, when calling
+  // this function, or nothing will be returned.
+  $perm = l10n_community_get_permission($langcode);
+  if (!($perm & L10N_PERM_MODERATE_OWN)) {
+    $where[] = "(ts.uid_entered <> %d)";
+    $where_args[] = $user->uid;
+  }
+  if (!($perm & L10N_PERM_MODERATE_OTHERS)) {
+    $where[] = "(ts.uid_entered = %d)";
+    $where_args[] = $user->uid;
+  }
 
   // Build the queries
   $sql_args = array_merge($join_args, $where_args);
Index: l10n_community/translate.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_community/Attic/translate.inc,v
retrieving revision 1.1.2.7.2.25
diff -u -p -r1.1.2.7.2.25 translate.inc
--- l10n_community/translate.inc	6 Nov 2009 16:00:57 -0000	1.1.2.7.2.25
+++ l10n_community/translate.inc	5 Dec 2009 13:25:02 -0000
@@ -37,7 +37,7 @@ function l10n_community_translate_page($
   if (!count($strings)) {
     drupal_set_message(t('No strings found with this filter. Try adjusting the filter options.'));
   }
-  elseif ($perm == L10N_PERM_NONE || $mode == 'view') {
+  elseif (!($perm & L10N_PERM_SUGGEST) || $mode == 'view') {
     // For users without permission to translate or suggest, display the view.
     drupal_set_title(t('@language translations', array('@language' => $languages[$langcode]->name)));
     $output .= l10n_community_translate_view($strings, $languages[$langcode], $filters);
@@ -349,11 +349,11 @@ function l10n_community_translate_form(&
         $target = $string->sid .'-'. $i;
         if ($translated) {
           // Already translated so we ask for new translation or suggestion.
-          $description = ($perm == L10N_PERM_SUGGEST) ? t('New suggestion for variant #%d', array('%d' => $i)) : t('New translation for variant #%d', array('%d' => $i));
+          $description = !($perm & L10N_PERM_MODERATE_OWN) ? t('New suggestion for variant #%d', array('%d' => $i)) : t('New translation for variant #%d', array('%d' => $i));
         }
         else {
           // Not translated yet, so we ask for initial translation or suggestion.
-          $description = ($perm == L10N_PERM_SUGGEST) ? t('Suggestion for variant #%d', array('%d' => $i)) : t('Translation for variant #%d', array('%d' => $i));
+          $description = !($perm & L10N_PERM_MODERATE_OWN) ? t('Suggestion for variant #%d', array('%d' => $i)) : t('Translation for variant #%d', array('%d' => $i));
         }
 
         // Include editing area for each plural variant.
@@ -381,7 +381,7 @@ function l10n_community_translate_form(&
         // Use textarea for long and multiline strings.
         '#type' => ((strlen($string->value) > 45) || (count(explode("\n", $string->value)) > 1)) ? 'textarea' : 'textfield',
         // Provide accurate title based on previous data and permission.
-        '#description' => $translated ? (($perm == L10N_PERM_SUGGEST) ? t('Add a new suggestion') : t('Add a new translation')) : (($perm == L10N_PERM_SUGGEST) ? t('Suggestion') : ''),
+        '#description' => $translated ? (!($perm & L10N_PERM_MODERATE_OWN) ? t('Add a new suggestion') : t('Add a new translation')) : (!($perm & L10N_PERM_MODERATE_OWN) ? t('Suggestion') : ''),
         '#rows' => 4,
         '#resizable' => FALSE,
         '#cols' => NULL,
@@ -406,7 +406,7 @@ function l10n_community_translate_form(&
       '#type' => 'markup',
     );
 
-    if ($perm == L10N_PERM_SUGGEST) {
+    if (!($perm & L10N_PERM_MODERATE_OWN)) {
       // User with suggestion capability only, record this.
       $form[$string->sid]['translation']['is_suggestion'] = array(
         '#type' => 'value',
@@ -445,7 +445,7 @@ function l10n_community_translate_form(&
   // Let the user submit the form.
   $form['submit'] = array(
     '#type' => 'submit',
-    '#value' => ($perm == L10N_PERM_SUGGEST) ? t('Save suggestions') : t('Save translations')
+    '#value' => !($perm & L10N_PERM_MODERATE_OWN) ? t('Save suggestions') : t('Save translations')
   );
 
   $form['#theme'] = 'l10n_community_translate_form';
Index: l10n_community/welcome.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_community/Attic/welcome.inc,v
retrieving revision 1.1.2.7.2.13
diff -u -p -r1.1.2.7.2.13 welcome.inc
--- l10n_community/welcome.inc	2 Oct 2009 08:55:36 -0000	1.1.2.7.2.13
+++ l10n_community/welcome.inc	5 Dec 2009 13:25:02 -0000
@@ -170,7 +170,7 @@ function l10n_community_pick_go_submit($
     // Project surely selected, possibly with language too.
     if (!empty($form_state['values']['langcode'])) {
       if (user_access('browse translations')) {
-        $action = l10n_community_contribute_access($form_state['values']['langcode']) ? 'edit' : 'view';
+        $action = l10n_community_has_permission($form_state['values']['langcode'], L10N_PERM_SUGGEST) ? 'edit' : 'view';
         drupal_goto('translate/languages/'. $form_state['values']['langcode'] .'/'. $action, 'project='. $uri);
       }
       else {
Index: l10n_community/tests/l10n_community.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_community/tests/Attic/l10n_community.test,v
retrieving revision 1.1.2.13
diff -u -p -r1.1.2.13 l10n_community.test
--- l10n_community/tests/l10n_community.test	22 Oct 2009 14:34:23 -0000	1.1.2.13
+++ l10n_community/tests/l10n_community.test	5 Dec 2009 13:25:02 -0000
@@ -28,7 +28,7 @@ class L10nServerTestCase extends DrupalW
     $this->u_auth = $this->drupalCreateUser(array('access localization community', 'browse translations'));
     $this->u_member_1 = $this->drupalCreateUser(array('access localization community', 'browse translations', 'submit suggestions', 'import gettext files'));
     $this->u_member_2 = $this->drupalCreateUser(array('access localization community', 'browse translations', 'submit suggestions', 'import gettext files'));
-    $this->u_moderator = $this->drupalCreateUser(array('access localization community', 'browse translations', 'submit translations and approve suggestions', 'import gettext files'));
+    $this->u_moderator = $this->drupalCreateUser(array('access localization community', 'browse translations', 'moderate own suggestions', 'moderate suggestions from others', 'import gettext files', 'submit suggestions'));
     $this->u_admin = $this->drupalCreateUser(array('administer languages', 'administer localization community', 'administer localization community for local packages', 'import gettext files'));
     $this->u_team = $this->drupalCreateUser();
 
@@ -474,7 +474,7 @@ class L10nServerTestCase extends DrupalW
     $this->assertCount('<input type="checkbox"', 1);
     $this->assertRaw('This is a test string. suggestion-3');
   }
-  
+
   /**
    * Test importing functionality.
    */
@@ -484,9 +484,9 @@ class L10nServerTestCase extends DrupalW
     $this->addProject(6);
     $this->addProject(6, FALSE);
     $this->addProject(7);
-    
+
     // UNPRIVILEGED
-    
+
     // Verify that users without permission will not see import screen.
     $this->drupalLogin($this->u_auth);
     $this->drupalGet('translate/languages/hu/import');
@@ -501,14 +501,14 @@ class L10nServerTestCase extends DrupalW
     $this->assertRaw(t('Gettext .po file with translations'));
     $this->assertNoRaw(t('Store as approved translations'));
     $this->assertRaw(t('Attribute import to'));
-    
+
     // Import our test suggestions
     $filename = drupal_get_path('module', 'l10n_community') .'/tests/l10n_community_test.po';
     $this->importPO($filename, $this->u_member_1->uid);
     $this->assertRaw(t('@count new suggestions added', array('@count' => 3)));
     $this->assertRaw(t('1 duplicate translation not saved'));
     $this->assertRaw(t('@count source strings not found, their translations were ignored', array('@count' => 2)));
- 
+
     // Check whether it is there.
     $this->drupalGet('translate/languages/hu/view', array('query' => 'limit=30&status=8'));
     $this->assertMsgID('This is a test string.');
@@ -551,9 +551,9 @@ class L10nServerTestCase extends DrupalW
     $this->importPO($filename, $this->u_moderator->uid);
     $this->assertRaw(t('@count duplicate translations not saved', array('@count' => 4)));
     $this->assertRaw(t('@count source strings not found, their translations were ignored', array('@count' => 2)));
-    
+
     // IMPORT AS TEAM
-    
+
     // Modify existing suggestions, so we can test importing again.
     db_query("UPDATE {l10n_community_translation} SET translation = 'saved for testing' WHERE sid = %d AND is_suggestion = 1", $sid1);
     db_query("UPDATE {l10n_community_translation} SET translation = '1 saved for testing\0@count saved for testing' WHERE sid = %d AND is_suggestion = 1", $sid2);
@@ -565,7 +565,7 @@ class L10nServerTestCase extends DrupalW
     $this->assertRaw(t('@count new suggestions added', array('@count' => 3)));
     $this->assertRaw(t('1 duplicate translation not saved'));
     $this->assertRaw(t('@count source strings not found, their translations were ignored', array('@count' => 2)));
-    
+
     $this->drupalGet('translate/suggestions/hu/'. $sid1);
     $this->assertCount('<span class="original hidden">saved for testing</span>', 1);
     $this->assertCount('by <em>'. $this->u_member_1->name .'</em>', 1);
Index: l10n_groups/l10n_groups.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_groups/Attic/l10n_groups.module,v
retrieving revision 1.1.2.4.2.7
diff -u -p -r1.1.2.4.2.7 l10n_groups.module
--- l10n_groups/l10n_groups.module	18 Sep 2009 18:03:19 -0000	1.1.2.4.2.7
+++ l10n_groups/l10n_groups.module	5 Dec 2009 13:25:02 -0000
@@ -242,19 +242,15 @@ function l10n_groups_block_help($perm, $
 
   $groups = l10n_groups_get_groups();
   $permission_help = '';
-  switch ($perm) {
-    // Inform user about her permission level in this group.
-    case L10N_PERM_NONE:
-      $permission_help = t('You are not a member of this translation group, but you can still view the existing translations and export templates or translations for your own use.') .' '. ($user->uid ? t('<a href="@group">Subscribe to this group</a> if you would like to help out.', array('@group' => url('node/'. $groups[$langcode]->nid))) : t('<a href="@register">Create an account or log in</a> and <a href="@group">subscribe to this group</a> if you would like to help out.', array('@register' => url('user'), '@group' => url('node/'. $groups[$langcode]->nid))));
-      break;
-    case L10N_PERM_SUGGEST:
-      $permission_help = t('You are a member of this translation group, so you can suggest translations or import complete Gettext translation files to suggest multiple translations at once. Administrators of this group either accept or decline suggestions provided by members of the group. To work offline, export a translation template, which contains the current state of the translation.');
-      break;
-    case L10N_PERM_ALL:
-      $permission_help = t('As a member of this translation group, you can suggest translations as well as approve translations suggested by other members of the group. Export/import of Gettext translation files is also possible.');
-      break;
+
+  if (empty($user->og_groups) || empty($user->og_groups[$groups[$langcode]->nid])) {
+    $permission_help = t('You are not a member of this translation group.') .' '. ($user->uid ? t('<a href="@group">Subscribe to this group</a> if you would like to help out.', array('@group' => url('node/'. $groups[$langcode]->nid))) : t('<a href="@register">Create an account or log in</a> and <a href="@group">subscribe to this group</a> if you would like to help out.', array('@register' => url('user'), '@group' => url('node/'. $groups[$langcode]->nid))));
+  }
+  elseif (!empty($user->og_groups[$groups[$langcode]->nid]['is_admin']) || ($groups[$langcode]->model == L10N_MODEL_OPEN)) {
+    $permission_help = t('You are an administrator in this translation group.');
   }
-  return '<p>'. $permission_help .'</p>';
+
+  return !empty($permission_help) ? '<p>'. $permission_help .'</p>' : '';
 }
 
 /**
@@ -280,36 +276,34 @@ function l10n_groups_get_groups() {
 /**
  * Get permission level for a specific user based on group membership.
  *
- * The group permissio model is taken into account and membership of
+ * The group permission model is taken into account and membership of
  * the user in language groups is checked.
  *
  * @param $permissions
  *   Permission cache array to tunnel cache values to.
- * @param $global_permission
- *   Permission based on user access settings to consider as a base.
- * @param $langcode
- *   Language code, for example 'hu', 'pt-br', 'de' or 'it'.
  * @param $account
  *   Optional user account.
+ * @param $global
+ *   Permission based on user access settings to consider as a base.
  */
-function l10n_groups_get_permission(&$permissions, $global_permission, $langcode, $account) {
+function l10n_groups_get_permission(&$permissions, $account, $global) {
   if ($groups = l10n_groups_get_groups()) {
     // Fill up local cache with all langcode permissions of this account for
     // future reference (eg. a page showing links for all languages).
     foreach ($groups as $group) {
-      $permission = L10N_PERM_NONE;
-      if (!empty($account->og_groups) and !empty($account->og_groups[$group->nid])) {
-        if (!empty($account->og_groups[$group->nid]['is_admin'])) {
-          // Administrators of groups have all permissions in the group.
-          $permission = L10N_PERM_ALL;
-        }
-        elseif ($account->og_groups[$group->nid]['is_active']) {
-          // Members in the group have a permission depending on the model used.
-          $permission = ($group->model == L10N_MODEL_OPEN ? L10N_PERM_ALL : L10N_PERM_SUGGEST);
-        }
+      $perm = $global;
+
+      if (empty($account->og_groups) || empty($account->og_groups[$group->nid])) {
+        // The user is not in the group and may therefore only view.
+        $perm = $perm & ~(L10N_PERM_CONTRIBUTE | L10N_PERM_REVIEW);
       }
-      // Save the lowest common denominator of permissions given.
-      $permissions[$account->uid][$group->language] = min($permission, $global_permission);
+      elseif (empty($account->og_groups[$group->nid]['is_admin']) && $group->model != L10N_MODEL_OPEN) {
+        // Non-admins of group don't have review permissions but can contribute.
+        // Open groups consider all members as admins regarding reviews.
+        $perm = $perm & ~(L10N_PERM_REVIEW);
+      }
+
+      $permissions[$account->uid][$group->language] = $perm;
     }
   }
 }
Index: l10n_remote/l10n_remote.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_remote/Attic/l10n_remote.module,v
retrieving revision 1.1.2.3
diff -u -p -r1.1.2.3 l10n_remote.module
--- l10n_remote/l10n_remote.module	30 Sep 2009 12:17:09 -0000	1.1.2.3
+++ l10n_remote/l10n_remote.module	5 Dec 2009 13:25:02 -0000
@@ -142,8 +142,8 @@ function l10n_remote_xmlrpc_string_submi
     return array('status' => FALSE, 'reason' => 'Language not accepted.');
   }
   
-  // Check if the user has permission to submit strings in this language. 
-  if (l10n_community_get_permission($langcode, $account) == L10N_PERM_NONE) {
+  // Check if the user has permission to submit strings in this language.
+  if (!(l10n_community_get_permission($langcode, $account) & L10N_PERM_SUGGEST)) {
     //watchdog('l10n_community', 'Not allowed to submit translations in this language remotely.', NULL, WATCHDOG_WARNING);
     return array('status' => FALSE, 'reason' => 'Not allowed to submit translations in this language.');
   }
