Index: l10n_community/export.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_community/Attic/export.inc,v
retrieving revision 1.1.2.15.2.24.2.2
diff -u -p -r1.1.2.15.2.24.2.2 export.inc
--- l10n_community/export.inc	15 Feb 2010 09:40:01 -0000	1.1.2.15.2.24.2.2
+++ l10n_community/export.inc	17 Feb 2010 16:04:24 -0000
@@ -417,7 +417,6 @@ function l10n_community_export($uri, $re
     // Join differently based on compact method, so we can skip strings without
     // translation for compact method export.
     $translation_join = ($compact) ? 'INNER JOIN' : 'LEFT JOIN';
-    $translation_filter = ($compact) ? "AND t.translation != ''" : '';
     // Installer strings are POTX_STRING_INSTALLER or POTX_STRING_BOTH.
     $type_limit = ($installer ? 'AND type IN (0, 1) ' : '');
     // We only export active translations, not suggestions.
Index: l10n_community/extractor.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_community/Attic/extractor.inc,v
retrieving revision 1.1.2.13.2.9
diff -u -p -r1.1.2.13.2.9 extractor.inc
--- l10n_community/extractor.inc	12 Jan 2010 08:43:26 -0000	1.1.2.13.2.9
+++ l10n_community/extractor.inc	17 Feb 2010 16:04:24 -0000
@@ -208,6 +208,11 @@ function l10n_community_save_string($val
       // String does not exist.
       db_query("INSERT INTO {l10n_community_string} (value, context, hashkey) VALUES ('%s', '%s', MD5('%s'))", $value, $context, $value . $context);
       $sid = db_result(db_query("SELECT sid FROM {l10n_community_string} WHERE hashkey = MD5('%s')", $value . $context));
+      // Initialize status flags for this string.
+      $languages = l10n_community_get_languages();
+      foreach ($languages as $language) {
+        db_query("INSERT INTO {l10n_community_status_flag} (sid, language) VALUES (%d, '%s')", $sid, $language->language);
+      }
     }
     if (!db_result(db_query("SELECT fid FROM {l10n_community_line} WHERE fid = %d AND sid = %d AND lineno = %d AND type = %d", $fid, $sid, $line, $string_type))) {
       // Location does not exist with this string.
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.18.2.1
diff -u -p -r1.1.2.5.2.18.2.1 import.inc
--- l10n_community/import.inc	26 Jan 2010 13:58:22 -0000	1.1.2.5.2.18.2.1
+++ l10n_community/import.inc	17 Feb 2010 16:04:24 -0000
@@ -353,38 +353,26 @@ function _l10n_community_import_one_stri
     // If context was not set, set to empty.
     $value['msgctxt'] = !empty($value['msgctxt']) ? $value['msgctxt'] : '';
 
-    // We use BINARY matching here to avoid case insensitively matching
-    // strings like 'operations' and 'Operations'.
     if ($sid = db_result(db_query("SELECT sid FROM {l10n_community_string} WHERE hashkey = MD5('%s')", $value['msgid'] . $value['msgctxt']))) {
-      // We have this source string (otherwise we don't save anything).
-      $translation = db_fetch_object(db_query("SELECT translation FROM {l10n_community_translation} WHERE sid = %d AND language = '%s' AND is_suggestion = 0 AND is_active = 1", $sid, $langcode));
-
       // 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']);
-
-      if ($translation && (empty($translation->translation) || ($translation->translation != $value['msgstr']))) {
-        // If we have an empty translation placeholder (ie. no translation yet,
-        // but unresolved suggestions are there), or if we have some translation
-        // already, which is different from what we save now, redirect the data
-        // to be a suggestion instead. This allows conflict issues to be resolved.
-        $is_suggestion = TRUE;
-      }
-
-      if ($is_suggestion || !$translation) {
-        if (!l10n_community_is_duplicate($value['msgstr'], $sid, $langcode)) {
-          l10n_community_target_save($sid, $value['msgstr'], $langcode, $uid, $is_suggestion);
-        }
-        else {
-          l10n_community_counter(L10N_COUNT_DUPLICATE);
+      // Add this as a suggestion first.
+      $tid = l10n_community_add_suggestion($sid, $value['msgstr'], $langcode, $uid);
+      
+      if ($tid) {
+        if ($is_suggestion) {
+          l10n_community_counter(L10N_COUNT_SUGGESTED);
+        } else {
+          l10n_community_approve_string($langcode, $sid, $tid);
+          l10n_community_counter(L10N_COUNT_ADDED);
         }
       }
-      else {
-        // We certainly did not update this one.
-        l10n_community_counter(L10N_COUNT_UNCHANGED);
+      elseif ($tid === FALSE) {
+        l10n_community_counter(L10N_COUNT_DUPLICATE);
       }
+      
+      
     }
     else {
       // Source string not found, string ignored.
Index: l10n_community/l10n_community.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_community/Attic/l10n_community.admin.inc,v
retrieving revision 1.1.2.12
diff -u -p -r1.1.2.12 l10n_community.admin.inc
--- l10n_community/l10n_community.admin.inc	22 Oct 2009 14:34:22 -0000	1.1.2.12
+++ l10n_community/l10n_community.admin.inc	17 Feb 2010 16:04:24 -0000
@@ -342,18 +342,15 @@ function l10n_community_admin_projects_c
 function l10n_community_delete_project($pid, $skip_strings = FALSE) {
 
   // Drop errors stored for releases of this project.
-  db_query(
-    'DELETE e FROM {l10n_community_error} e
-     LEFT JOIN {l10n_community_release} r ON e.rid = r.rid
-     WHERE r.pid = %d',
-    $pid
-  );
+  db_query('DELETE FROM {l10n_community_error} WHERE rid IN (SELECT rid FROM {l10n_community_release} WHERE pid = %d)', $pid);
 
-  db_query('DELETE l FROM {l10n_community_line} l WHERE l.pid = %d', $pid);
-  db_query('DELETE f FROM {l10n_community_file} f WHERE f.pid = %d', $pid);
-  db_query('DELETE r FROM {l10n_community_release} r WHERE r.pid = %d', $pid);
-  db_query('DELETE p FROM {l10n_community_project} p WHERE p.pid = %d', $pid);
+  // Almost all other related data has the pid associated directly.
+  db_query('DELETE FROM {l10n_community_line} WHERE pid = %d', $pid);
+  db_query('DELETE FROM {l10n_community_file} WHERE pid = %d', $pid);
+  db_query('DELETE FROM {l10n_community_release} WHERE pid = %d', $pid);
+  db_query('DELETE FROM {l10n_community_project} WHERE pid = %d', $pid);
 
+  // Finally, we might need to drop the possibly left orphan strings.
   if (!$skip_strings) {
     l10n_community_delete_orphans();
   }
@@ -363,12 +360,10 @@ function l10n_community_delete_project($
  * Delete all orphan source strings and their associated translations.
  */
 function l10n_community_delete_orphans() {
-  // Drop all stale source strings and translations.
-  db_query(
-    'DELETE s, t FROM {l10n_community_string} s
-     LEFT JOIN {l10n_community_translation} t ON t.sid = s.sid
-     WHERE s.sid NOT IN (SELECT sid FROM {l10n_community_line})'
-  );
+  // Drop all stale source strings, translations and status flags.
+  db_query('DELETE FROM {l10n_community_string} WHERE sid NOT IN (SELECT sid FROM {l10n_community_line})');
+  db_query('DELETE FROM {l10n_community_translation} WHERE sid NOT IN (SELECT sid FROM {l10n_community_line})');
+  db_query('DELETE FROM {l10n_community_status_flag} WHERE sid NOT IN (SELECT sid FROM {l10n_community_line})');
 }
 
 /**
Index: l10n_community/l10n_community.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_community/Attic/l10n_community.install,v
retrieving revision 1.1.2.11.2.15
diff -u -p -r1.1.2.11.2.15 l10n_community.install
--- l10n_community/l10n_community.install	12 Jan 2010 08:43:26 -0000	1.1.2.11.2.15
+++ l10n_community/l10n_community.install	17 Feb 2010 16:04:24 -0000
@@ -332,13 +332,6 @@ function l10n_community_schema() {
         'default' => 0,
         'disp-width' => '11'
       ),
-      'has_suggestion' => array(
-        'description' => 'Cached flag of whether there is at least one other row in the table where is_suggestion = 1, is_active = 1 and sid and language is the same as this one. Only applicable to rows where is_suggestion = 0.',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-        'disp-width' => '11'
-      ),
       'is_suggestion' => array(
         'description' => 'Flag of whether this is a suggestion (1) or not (0). If 0, *_approved fields should also be 0.',
         'type' => 'int',
@@ -366,6 +359,42 @@ function l10n_community_schema() {
     ),
   );
 
+  $schema['l10n_community_status_flag'] = array(
+    'fields' => array(
+      'sid' => array(
+        'description' => 'Reference to the {l10n_community_string}.sid which is being translated.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'disp-width' => '11'
+      ),
+      'language' => array(
+        'description' => 'Reference to the {languages}.language to which the string is being translated.',
+        'type' => 'varchar',
+        'length' => '12',
+        'not null' => TRUE
+      ),
+      'has_suggestion' => array(
+        'description' => 'Cached status flag of whether there is at least one row in {l10n_community_translation} where is_suggestion = 1, is_active = 1 and sid and language is the same as this one.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'disp-width' => '11'
+      ),
+      'has_translation' => array(
+        'description' => 'Cached status flag of whether there is at least one row in {l10n_community_translation} where is_suggestion = 0, is_active = 1, translation is not empty and sid and language is the same as this one.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'disp-width' => '11'
+      ),
+    ),
+    'primary key' => array('sid', 'language'),
+    'indexes' => array(
+      'sid_language_has_suggestion' => array('sid', 'language', 'has_suggestion'),
+      'sid_language_has_translation' => array('sid', 'language', 'has_translation'),
+    ),
+  );
+
   return $schema;
 }
 
@@ -637,3 +666,60 @@ function l10n_community_update_6010() {
 
   return $ret;
 }
+
+/**
+ * Separate status flags from translations, so we can do more versatile searches quickly.
+ */
+function l10n_community_update_6011() {
+  $ret = array();
+
+  db_create_table($ret, 'l10n_community_status_flag', array(
+    'fields' => array(
+      'sid' => array(
+        'description' => 'Reference to the {l10n_community_string}.sid which is being translated.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'disp-width' => '11'
+      ),
+      'language' => array(
+        'description' => 'Reference to the {languages}.language to which the string is being translated.',
+        'type' => 'varchar',
+        'length' => '12',
+        'not null' => TRUE
+      ),
+      'has_suggestion' => array(
+        'description' => 'Cached status flag of whether there is at least one row in {l10n_community_translation} where is_suggestion = 1, is_active = 1 and sid and language is the same as this one.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'disp-width' => '11'
+      ),
+      'has_translation' => array(
+        'description' => 'Cached status flag of whether there is at least one row in {l10n_community_translation} where is_suggestion = 0, is_active = 1, translation is not empty and sid and language is the same as this one.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'disp-width' => '11'
+      ),
+    ),
+    'primary key' => array('sid', 'language'),
+    'indexes' => array(
+      'sid_language_has_suggestion' => array('sid', 'language', 'has_suggestion'),
+      'sid_language_has_translation' => array('sid', 'language', 'has_translation'),
+    ),
+  ));
+  
+  // Initialize status flags with values based on the current data.
+  $languages = l10n_community_get_languages();
+  foreach ($languages as $language) {
+    $ret[] = update_sql("INSERT INTO {l10n_community_status_flag} (sid, language) SELECT sid, '". db_escape_string($language->language) ."' FROM {l10n_community_string}");
+    $ret[] = update_sql("UPDATE {l10n_community_status_flag} SET has_suggestion = 1 WHERE sid IN (SELECT DISTINCT sid FROM {l10n_community_translation} WHERE is_suggestion = 1 AND is_active = 1 AND language = '". db_escape_string($language->language) ."') AND language = '". db_escape_string($language->language) ."'");
+    $ret[] = update_sql("UPDATE {l10n_community_status_flag} SET has_translation = 1 WHERE sid IN (SELECT DISTINCT sid FROM {l10n_community_translation} WHERE is_suggestion = 0 AND is_active = 1 AND translation != '' AND language = '". db_escape_string($language->language) ."') AND language = '". db_escape_string($language->language) ."'");
+  }
+  // Drop the has_suggestion field from the translation table.
+  db_drop_field($ret, 'l10n_community_translation', 'has_suggestion');
+  // Drop all placeholder records, which were only used to hold the has_suggestion flag.
+  $ret[] = update_sql("DELETE FROM {l10n_community_translation} WHERE is_active = 1 AND is_suggestion = 0 AND translation = ''");
+
+  return $ret;
+}
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.66.2.6
diff -u -p -r1.1.2.23.2.66.2.6 l10n_community.module
--- l10n_community/l10n_community.module	16 Feb 2010 16:22:34 -0000	1.1.2.23.2.66.2.6
+++ l10n_community/l10n_community.module	17 Feb 2010 16:04:24 -0000
@@ -5,7 +5,7 @@
  * @file
  *   A community web interface for Drupal project translation.
  *
- *   Builds on a connector (eg. l10n_drupalorg) and optionally l10n_groups
+ *   Builds on a connector (eg. l10n_localpacks) and optionaly l10n_groups
  *   to provide a convinient web interface for translators to collaborate
  *   on Drupal project translations.
  */
@@ -36,6 +36,16 @@ define('L10N_STATUS_NO_SUGGESTION', 4);
 define('L10N_STATUS_HAS_SUGGESTION', 8);
 
 /**
+ * Is a translation.
+ */
+define('L10N_STATUS_IS_TRANSLATION', 16);
+
+/**
+ * Is a suggestion.
+ */
+define('L10N_STATUS_IS_SUGGESTION', 32);
+
+/**
  * Used to mark counting duplicates.
  */
 define('L10N_COUNT_DUPLICATE', 'duplicate');
@@ -248,15 +258,6 @@ function l10n_community_menu() {
     'type' => MENU_LOCAL_TASK,
     'weight' => -10,
   );
-  $items['translate/languages/%l10n_community_language/moderate'] = array(
-    'title' => 'Moderate',
-    'page callback' => 'l10n_community_moderate_page',
-    'page arguments' => array(2),
-    'file' => 'moderate.inc',
-    'access callback' => 'l10n_community_review_access',
-    'type' => MENU_LOCAL_TASK,
-    'weight' => -6,
-  );
   $items['translate/languages/%l10n_community_language/import'] = array(
     'title' => 'Import',
     'page callback' => 'l10n_community_import_page',
@@ -464,7 +465,7 @@ function l10n_community_block_help() {
   }
 
   // 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|moderate))?$!', $_GET['q'], $args) ||
+  if (preg_match('!translate/languages/(?P<langcode>[^/]+)(/(?P<action>translate|import|export))?$!', $_GET['q'], $args) ||
       preg_match('!translate/projects/(?P<uri>[^/]+)(/(?P<action>|export))??$!', $_GET['q'], $args)) {
     $permission_help = $permission_notes = array();
     if (module_exists('l10n_groups')) {
@@ -504,11 +505,10 @@ function l10n_community_block_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':
+        case 'translate':
           // Language code and project both present.
           $items = array();
-          $items[] = t('!newline_image represents a line break. Remember to include a line break in the same position in the translation. Beginning and ending line breaks are saved properly, even if you forget to include them.', array('!newline_image' => ' <img src="'. base_path() . drupal_get_path('module', 'l10n_community') .'/images/newline.png" alt="'. t('Newline marker') .'" /> '));
+          $items[] = t('Remember to include a line break in the same positions in the translation where they appear in the source string. Beginning and ending line breaks are saved properly, even if you forget to include them.');
           $items[] = t('Variables are designated with !, @ and % (like %example, !example or @example), and should be kept in the translated text as-is.');
           $languages = l10n_community_get_languages();
           $formula = join(' ', preg_split('!(&&|\\|\\||%|<=|>=|==|\\!=|\\?|:)!', $languages[$args['langcode']]->formula, -1, PREG_SPLIT_DELIM_CAPTURE));
@@ -516,10 +516,6 @@ function l10n_community_block_help() {
 
           $block['content'] = $permission_help . theme('item_list', $items);
           return $block;
-
-        case 'moderate':
-          $block['content'] = $permission_help;
-          return $block;
       }
     }
     else {
@@ -1060,66 +1056,6 @@ function l10n_community_get_contexts() {
 }
 
 /**
- * Save a translated string into database.
- *
- * @param $sid
- *   Source string identifier.
- * @param $translation
- *   The translation string.
- * @param $langcode
- *   Language code, for example: 'hu', 'pt-br', 'de', 'it' and so on.
- * @param $uid
- *   User ID.
- * @param $suggestion
- *   TRUE if $translation is a suggestion, FALSE otherwise.
- *
- * @see l10n_community_is_duplicate()
- */
-function l10n_community_target_save($sid, $translation, $langcode, $uid, $suggestion) {
-
-  // Look for an existing active translation, if any.
-  $existing_string = db_fetch_object(db_query("SELECT sid, tid, translation FROM {l10n_community_translation} WHERE sid = %d AND language = '%s' AND is_suggestion = 0 AND is_active = 1", $sid, $langcode));
-
-  if (!empty($existing_string->sid)) {
-    // We have an active translation.
-    if ($existing_string->translation != $translation) {
-      // And what we should save now is different.
-      if ($suggestion) {
-        // Saving a suggestion, so set flag on translation.
-        db_query("UPDATE {l10n_community_translation} SET has_suggestion = 1 WHERE tid = %d", $existing_string->tid);
-        l10n_community_counter(L10N_COUNT_SUGGESTED);
-      }
-      else {
-        // Saving a different translation -> deactivate previous translations and suggestions.
-        db_query("UPDATE {l10n_community_translation} SET is_active = 0 WHERE sid = %d AND language = '%s';", $sid, $langcode);
-        l10n_community_counter(L10N_COUNT_UPDATED);
-      }
-      db_query("INSERT INTO {l10n_community_translation} (sid, translation, language, uid_entered, time_entered, uid_approved, time_approved, is_suggestion, is_active) VALUES (%d, '%s', '%s', %d, %d, %d, %d, %d, 1)", $sid, $translation, $langcode, $uid, time(), ($suggestion ? 0 : $uid), ($suggestion ? 0 : time()), $suggestion);
-    }
-    else {
-      // Same string as existing translation.
-      l10n_community_counter(L10N_COUNT_UNCHANGED);
-    }
-  }
-
-  else {
-    // No active translation exists.
-    if ($suggestion) {
-      // No translation yet -> INSERT empty placeholder so we can track
-      // suggestions. We track and exclude these by translation = '' later.
-      db_query("INSERT INTO {l10n_community_translation} (sid, translation, language, uid_entered, time_entered, has_suggestion, is_active) VALUES (%d, '', '%s', 0, %d, 1, 1)", $sid, $langcode, time());
-      db_query("INSERT INTO {l10n_community_translation} (sid, language, translation, uid_entered, time_entered, is_suggestion, is_active) VALUES (%d, '%s', '%s', %d, %d, 1, 1)", $sid, $langcode, $translation, $uid, time());
-      l10n_community_counter(L10N_COUNT_SUGGESTED);
-    }
-    else {
-      // No active translation yet -> INSERT.
-      db_query("INSERT INTO {l10n_community_translation} (sid, translation, language, uid_entered, uid_approved, time_entered, time_approved, is_active) VALUES (%d, '%s', '%s', %d, %d, %d, %d, 1)", $sid, $translation, $langcode, $uid, $uid, time(), time());
-      l10n_community_counter(L10N_COUNT_ADDED);
-    }
-  }
-}
-
-/**
  * Make spacing and newlines the same in translation as in the source.
  *
  * @param $translation
@@ -1176,7 +1112,7 @@ function l10n_community_update_message()
     $messages[] = format_plural($counters[L10N_COUNT_DECLINED], '1 translation declined', '@count translations declined');
   }
   if (!empty($counters[L10N_COUNT_SUGGESTION_DECLINED])) {
-    $messages[] = format_plural($counters[L10N_COUNT_SUGGESTION_DECLINED], '1 suggestion declined', '@count suggestion declined');
+    $messages[] = format_plural($counters[L10N_COUNT_SUGGESTION_DECLINED], '1 suggestion declined', '@count suggestions declined');
   }
   if (!empty($counters[L10N_COUNT_APPROVED])) {
     $messages[] = format_plural($counters[L10N_COUNT_APPROVED], '1 translation approved', '@count translations approved');
@@ -1263,15 +1199,6 @@ function l10n_community_project_uri_by_t
   return db_result(db_query("SELECT uri FROM {l10n_community_project} WHERE title = '%s'", $title));
 }
 
-/**
- * Check whether $suggestion is duplicate for $sid in $langcode.
- */
-function l10n_community_is_duplicate($suggestion, $sid, $langcode) {
-  // Use BINARY matching to avoid marking case-corrections as duplicate.
-  // Matches everything active, regardless of being translations or suggestions.
-  return (bool) db_result(db_query("SELECT s.sid FROM {l10n_community_string} s LEFT JOIN {l10n_community_translation} t ON s.sid = t.sid WHERE t.translation = BINARY '%s' AND t.is_active = 1 AND t.language = '%s' AND s.sid = %d", $suggestion, $langcode, $sid));
-}
-
 // = Theme functions ===========================================================
 
 /**
@@ -1321,81 +1248,10 @@ function l10n_community_theme($existing,
     'l10n_community_admin_releases_form' => array(
       'arguments' => array('form' => NULL),
     ),
-    // moderate.inc
-    'l10n_community_moderation_form' => array(
-      'arguments' => array('form' => NULL),
-    ),
   );
 }
 
 /**
- * Format string for display. Takes plurals into account.
- */
-function l10n_community_format_string($value, $rich_markup = TRUE) {
-  if (strpos($value, "\0") !== FALSE) {
-    $items = explode(chr(0), $value);
-    foreach ($items as &$item) {
-      $item = l10n_community_format_text($item, NULL, NULL, $rich_markup);
-    }
-    return theme('item_list', $items);
-  }
-  else {
-    return l10n_community_format_text($value, NULL, NULL, $rich_markup);
-  }
-}
-
-/**
- * Format translatable strings with custom icons.
- *
- * We emphasize some parts of strings, so those are easy to recognize.
- * Newlines and replacement strings are made more visible.
- *
- * @param $string
- *   Source string to translate.
- * @param $sid
- *   Source string ID.
- * @param $delta
- *   Sequence ID of plural version if $string is a plural variant.
- * @param $rich_markup
- *   Whether to output rich markup (used for the translaton UI).
- */
-function l10n_community_format_text($string, $sid = NULL, $delta = NULL, $rich_markup = TRUE) {
-  static $path = NULL, $title = NULL;
-
-  if (!isset($path)) {
-    $path = base_path() . drupal_get_path('module', 'l10n_community');
-    $title = t('line break');
-  }
-
-  // Replace all newline chars in the string with an indicator image.
-  $formatted = str_replace(
-    array("\n", "\\\\n"),
-    '<img src="'. $path .'/images/newline.png" alt="'. $title .'" title="'. $title .'" /><br />',
-    check_plain($string)
-  );
-  // Make all %, ! and @ marked pladeholders emphasized.
-  $formatted = preg_replace(
-    '~((%|!|@)[0-9a-zA-Z_-]+)~',
-    '<em class="l10n-community-marker">\\1</em>',
-    $formatted
-  );
-
-  if ($rich_markup) {
-    $class = '';
-    if (isset($sid) && isset($delta)) {
-      $class = ' class="string-'. $sid .'-'. $delta .'"';
-    }
-    elseif ($sid) {
-      $class = ' class="string-'. $sid .'"';
-    }
-    return '<div'. $class .'><span class="string">'. $formatted .'</span><span class="original hidden">'. check_plain($string) .'</span></div>';
-  }
-  else {
-    return '<span class="string">'. $formatted .'</span>';
-  }
-}
-
-/**
  * Compute language community stats.
  *
  * @param $langcode
@@ -1410,9 +1266,9 @@ function l10n_community_get_stats($langc
     else {
       $stats = array();
       $stats['strings'] = db_result(db_query('SELECT COUNT(*) FROM {l10n_community_string}'));
-      $stats['translations'] = db_result(db_query("SELECT COUNT(*) FROM {l10n_community_translation} WHERE is_suggestion = 0 AND is_active = 1 AND language = '%s' AND translation != ''", $langcode));
+      $stats['translations'] = db_result(db_query("SELECT COUNT(*) FROM {l10n_community_status_flag} WHERE has_translation = 1 AND language = '%s'", $langcode));
       $stats['suggestions'] = db_result(db_query("SELECT COUNT(*) FROM {l10n_community_translation} WHERE is_suggestion = 1 AND is_active = 1 AND language = '%s'", $langcode));
-      $stats['users'] = db_result(db_query("SELECT COUNT(DISTINCT uid_entered) FROM {l10n_community_translation} WHERE is_suggestion = 0 AND is_active = 1 AND language = '%s' AND translation != ''", $langcode));
+      $stats['users'] = db_result(db_query("SELECT COUNT(DISTINCT uid_entered) FROM {l10n_community_translation} WHERE is_suggestion = 0 AND is_active = 1 AND language = '%s'", $langcode));
 
       // Cache results for next time. Not setting a timestamp as cache validity
       // time, we would like to retain control of recalculating these values.
@@ -1427,13 +1283,13 @@ function l10n_community_get_stats($langc
     }
     else {
       $stats = array();
-      $stats['users'] = (int) db_result(db_query("SELECT COUNT(DISTINCT uid_entered) FROM {l10n_community_translation} WHERE translation != ''"));
+      $stats['users'] = (int) db_result(db_query("SELECT COUNT(DISTINCT uid_entered) FROM {l10n_community_translation}"));
       $stats['projects'] = db_result(db_query('SELECT COUNT(*) FROM {l10n_community_project} WHERE status = 1'));
       $stats['releases_parsed'] = db_result(db_query('SELECT COUNT(*) FROM {l10n_community_release} WHERE last_parsed != 0'));
       $stats['releases_queue'] = db_result(db_query('SELECT COUNT(*) FROM {l10n_community_release} r INNER JOIN {l10n_community_project} p ON r.pid = p.pid WHERE p.status = 1 AND r.last_parsed = 0'));
       $stats['files'] = db_result(db_query('SELECT COUNT(*) FROM {l10n_community_file}'));
       $stats['strings'] = db_result(db_query('SELECT COUNT(*) FROM {l10n_community_string}'));
-      $stats['translations'] = db_result(db_query("SELECT COUNT(*) FROM {l10n_community_translation} WHERE is_suggestion = 0 AND is_active = 1 AND translation != ''"));
+      $stats['translations'] = db_result(db_query("SELECT COUNT(*) FROM {l10n_community_status_flag} WHERE has_translation = 1"));
       $stats['suggestions'] = db_result(db_query('SELECT COUNT(*) FROM {l10n_community_translation} WHERE is_suggestion = 1 AND is_active = 1'));
 
       if (module_exists('l10n_groups')) {
@@ -1502,10 +1358,8 @@ function l10n_community_get_highlighted_
  *
  * @param $params
  *   Associative array with unsanitized values.
- * @param $suggestions
- *   Whether we build the filters for the suggestions (TRUE) or not (FALSE).
  */
-function l10n_community_build_filter_values($params, $suggestions = FALSE) {
+function l10n_community_build_filter_values($params) {
   $project = $release = NULL;
 
   // Convert array representation of flags to one integer.
@@ -1575,7 +1429,6 @@ function l10n_community_add_url_modifier
   $filters = l10n_community_flat_filters($filters);
   $urls = array(
     'translate/languages/'. $langcode .'/translate',
-    'translate/languages/'. $langcode .'/moderate',
     'translate/languages/'. $langcode .'/export'
   );
   $replacements = array();
@@ -1630,3 +1483,106 @@ function l10n_community_ahah_releases() 
     )
   );
 }
+
+// = API functions to save / update translations ===============================
+
+/**
+ * Adds a suggestion to a language/string.
+ *
+ * @param $sid
+ *   The string ID for which a new translation should be added.
+ * @param $translation
+ *   String representing the new translation.
+ * @param $langcode
+ *   The language of the new translation.
+ * @param $uid
+ *  User ID to use to save the string.
+ */
+function l10n_community_add_suggestion($sid, $translation, $langcode, $uid) {
+
+  // Load source string and adjust translation whitespace based on source.
+  $source_string = db_result(db_query('SELECT value FROM {l10n_community_string} WHERE sid = %d', $sid));
+  $translation = l10n_community_trim($translation, $source_string);
+
+  // Don't store empty translations.
+  if ($translation === '') {
+    return NULL;
+  }
+
+  // Look for an existing active translation, if any.
+  // Use BINARY matching to avoid marking case-corrections as duplicate.
+  $existing = db_fetch_object(db_query("SELECT tid, is_active, is_suggestion FROM {l10n_community_translation} WHERE sid = %d AND language = '%s' AND translation = BINARY '%s'", $sid, $langcode, $translation));
+  if (!empty($existing)) {
+    if ($existing->is_active == 0) {
+      // If the existing item is not active, make it an active suggestion and clean up its possible previous approval information.
+      db_query("UPDATE {l10n_community_translation} SET is_suggestion = 1, is_active = 1, uid_approved = 0, time_approved = 0 WHERE tid = %d", $existing->tid);
+      $tid = $existing->tid;
+    }
+    else {
+      return FALSE;
+    }
+  }
+  else {
+    // Insert the new suggestion.
+    db_query("INSERT INTO {l10n_community_translation} (sid, translation, language, uid_entered, time_entered, is_suggestion, is_active) VALUES (%d, '%s', '%s', %d, %d, 1, 1)", $sid, $translation, $langcode, $uid, time());
+    $tid = db_last_insert_id('l10n_community_translation', 'tid');
+  }
+
+  // Mark the existing or mock translation as having suggestions.
+  l10n_community_update_string_status($langcode, $sid);
+
+  return $tid;
+}
+
+/**
+ * Marks a translation as declined.
+ *
+ * @param $langcode
+ *   The language of the declined translation.
+ * @param $sid
+ *   The string ID the translation belongs to.
+ * @param $tid
+ *   The translation ID of the translation.
+ */
+function l10n_community_decline_string($langcode, $sid, $tid) {
+  db_query("UPDATE {l10n_community_translation} SET is_active = 0 WHERE tid = %d", $tid);
+  l10n_community_update_string_status($langcode, $sid);
+}
+
+/**
+ * Updates the status flags for the given source string.
+ *
+ * @param $langcode
+ *   The language of the string.
+ * @param $sid
+ *   The string ID that should be updated.
+ */
+function l10n_community_update_string_status($langcode, $sid) {
+  // Let's see if we have any suggestions remaining in this language.
+  $has_suggestion = db_result(db_query("SELECT 1 FROM {l10n_community_translation} WHERE sid = %d AND is_suggestion = 1 AND is_active = 1 AND language = '%s'", $sid, $langcode));
+  $has_translation = db_result(db_query("SELECT 1 FROM {l10n_community_translation} WHERE sid = %d AND is_suggestion = 0 AND is_active = 1 AND language = '%s'", $sid, $langcode));
+
+  // Update the status according to the number of suggestions.
+  db_query("UPDATE {l10n_community_status_flag} SET has_suggestion = %d, has_translation = %d WHERE sid = %d AND language = '%s'", (int) $has_suggestion, (int) $has_translation, $sid, $langcode);
+}
+
+/**
+ * Mark a translation as approved.
+ *
+ * @param $langcode
+ *   The language of the approved translation.
+ * @param $sid
+ *   The string ID the translation belongs to.
+ * @param $tid
+ *   The translation ID of the translation.
+ */
+function l10n_community_approve_string($langcode, $sid, $tid) {
+  global $user;
+
+  // Make the existing approved string a suggestion (if applicable).
+  db_query("UPDATE {l10n_community_translation} SET is_suggestion = 1 WHERE sid = %d AND language = '%s' AND is_suggestion = 0 AND is_active = 1", $sid, $langcode);
+
+  // Mark this exact suggestion as active translation, and set approval time.
+  db_query("UPDATE {l10n_community_translation} SET time_approved = %d, uid_approved = %d, is_suggestion = 0, is_active = 1 WHERE tid = %d;", time(), $user->uid, $tid);
+  l10n_community_update_string_status($langcode, $sid);
+}
Index: l10n_community/moderate.inc
===================================================================
RCS file: l10n_community/moderate.inc
diff -N l10n_community/moderate.inc
--- l10n_community/moderate.inc	25 Jan 2010 20:22:56 -0000	1.1.2.8
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,325 +0,0 @@
-<?php
-// $Id: moderate.inc,v 1.1.2.8 2010/01/25 20:22:56 goba Exp $
-
-/**
- * @file
- *   Moderation functionality or localization community.
- */
-
-// = Admin screens =============================================================
-
-/**
- * Moderation page menu callback.
- *
- * Displays a string approval page useful for bulk string operations.
- * Permissions are handled by the menu system.
- *
- * @param $langcode
- *   Language code, for example 'hu', 'pt-br', 'de', 'it'.
- */
-function l10n_community_moderate_page($langcode) {
-  module_load_include('inc', 'l10n_community', 'translate');
-  $languages = l10n_community_get_languages();
-  $filters = l10n_community_build_filter_values($_GET, TRUE);
-
-  $output = drupal_get_form('l10n_community_filter_form', $filters, TRUE);
-
-  // Include filters just so if user wants to quickly go back to translate or
-  // browse forms, they can do that without loosing their filters.
-  l10n_community_add_url_modifiers($langcode, $filters);
-  drupal_add_js(drupal_get_path('module', 'l10n_community') .'/l10n_community.js');
-
-  $strings = l10n_community_get_suggestions($langcode, $filters);
-  if (!count($strings)) {
-    drupal_set_message(t('No strings found with this filter. Try adjusting the filter options.'));
-  }
-  else{
-    // For users with some permission, display the form.
-    drupal_set_title(t('Suggestions for @language', array('@language' => $languages[$langcode]->name)));
-    $output .= drupal_get_form('l10n_community_moderation_form', $strings, $languages[$langcode], $filters);
-  }
-  return $output;
-}
-
-// == Moderation form ==========================================================
-
-/**
- * Translation web interface.
- *
- * @param $strings
- *   Array of string objects to display.
- * @param $language
- *   Language object.
- * @param $filters
- *   Filters used to present this moderation view.
- */
-function l10n_community_moderation_form(&$form_state, $strings = array(), $language = NULL, $filters) {
-
-  $form['#redirect'] = array($_GET['q'], l10n_community_flat_filters($filters));
-
-  $form['pager'] = array(
-    '#value' => theme('pager', NULL, $filters['limit'], 0)
-  );
-
-  // Keep language code in form for further reference.
-  $form['langcode'] = array(
-    '#type' => 'value',
-    '#value' => $language->language
-  );
-
-  // Operations.
-  $form['options'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Update options'),
-    '#prefix' => '<div class="container-inline">',
-    '#suffix' => '</div>',
-  );
-  $form['options']['approve'] = array(
-    '#type' => 'submit',
-    '#value' => t('Approve all selected'),
-  );
-  $form['options']['decline'] = array(
-    '#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',
-    );
-    $form['strings']['sid'][$string->tid] = array(
-      '#type' => 'value', '#value' => $string->sid,
-    );
-    $form['strings']['source'][$string->tid] = array(
-      //'#type' => 'item',
-      '#value' => l10n_community_format_string($string->source, FALSE) .' '. theme('l10n_community_in_context', $string),
-    );
-    $form['strings']['translation'][$string->tid] = array(
-      //'#type' => 'item',
-      '#value' => l10n_community_format_string($string->translation, FALSE),
-    );
-    $form['strings']['suggestion'][$string->tid] = array(
-      //'#type' => 'item',
-      '#value' => l10n_community_format_string($string->suggestion, FALSE),
-    );
-  }
-  return $form;
-}
-
-/**
- * Moderation form submission callback.
- */
-function l10n_community_moderation_form_submit($form, &$form_state) {
-  // Filter out unchecked translations.
-  $translations = array_filter($form_state['values']['strings']['tid']);
-  if (!empty($translations)) {
-    // Use these tids to get the list of sids.
-    $strings = array_intersect_key($form_state['values']['strings']['sid'], $translations);
-
-    // The tids are the keys of the array.
-    $tids = array_keys($translations);
-    // The strings array has tid => sid.
-    $sids = array_unique($strings);
-    if ($form_state['values']['op'] == t('Approve all selected')) {
-      l10n_community_moderate_approve_selected($form_state['values']['langcode'], $tids, $sids);
-    }
-    elseif ($form_state['values']['op'] == t('Decline all selected')) {
-      l10n_community_moderate_decline_selected($form_state['values']['langcode'], $tids, $sids);
-    }
-  }
-  else {
-    drupal_set_message(t('No suggestions selected for operation. Please select one or more suggestions to run the operation on.'), 'error');
-  }
-}
-
-
-// == API functions ============================================================
-
-/**
- * Get suggestions based on some conditions.
- *
- * @param $langcode
- *   Language code, for example 'hu', 'pt-br', 'de', 'it'.
- * @param $filters
- *   See l10n_community_get_strings().
- * @return
- *   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 = '';
-
-  $select = "SELECT DISTINCT ts.tid, ts.sid, tt.translation, ts.translation AS suggestion, s.value AS source, s.context FROM {l10n_community_translation} ts";
-  $select_count = "SELECT COUNT(DISTINCT(ts.tid)) FROM {l10n_community_translation} ts";
-  $join[] = "INNER JOIN {l10n_community_string} s ON ts.sid = s.sid";
-  $where[] = "ts.is_suggestion = 1 AND ts.is_active = 1 AND ts.language = '%s'";
-  $where_args[] = $langcode;
-
-  // Add submitted by condition
-  if (!empty($filters['author'])) {
-    $where[] = "ts.uid_entered = %d";
-    $where_args[] = $filters['author']->uid;
-  }
-
-  // Restrict based on project or release.
-  $release = empty($filters['release']) || $filters['release'] === 'all' ? NULL : $filters['release'];
-  $project = $filters['project'];
-  if ($release || $project) {
-    $join[] = "INNER JOIN {l10n_community_line} l ON ts.sid = l.sid";
-    // If we have a release we ignore the project
-    if ($release) {
-      // Release restriction.
-      $where_args[] = $release;
-      $where[] = 'l.rid = %d';
-    }
-    elseif ($project) {
-      $where[] = "l.pid = %d";
-      $where_args[] = $project->pid;
-    }
-  }
-
-  // Context based filtering.
-  if (isset($filters['context']) && $filters['context'] != 'all') {
-    // We use 'none' for no context, so '' can be the defaut (for all contexts).
-    $where_args[] = $filters['context'] == 'none' ? '' : $filters['context'];
-    $where[] = "s.context = '%s'";
-  }
-
-  // Search in the source or target (suggestion) strings.
-  if (!empty($filters['search'])) {
-    $where_args[] = $filters['search'];
-    $where_args[] = $filters['search'];
-    $where[] = "(s.value LIKE '%%%s%%' OR ts.translation LIKE '%%%s%%')";
-  }
-
-  // Restriction based on string status.
-  if ($filters['status'] & L10N_STATUS_UNTRANSLATED) {
-    $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;
-    $where[] = "(tt.tid IS NULL OR tt.translation = '')";
-  }
-  elseif ($filters['status'] & L10N_STATUS_TRANSLATED) {
-    $join[] = "INNER 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;
-  }
-  else {
-    $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.
-  if (!user_access('moderate own suggestions')) {
-    $where[] = "(ts.uid_entered <> %d)";
-    $where_args[] = $user->uid;
-  }
-  if (!user_access('moderate suggestions from others')) {
-    $where[] = "(ts.uid_entered = %d)";
-    $where_args[] = $user->uid;
-  }
-
-  // Build the queries
-  $sql_args = array_merge($join_args, $where_args);
-  $sql_where = implode(' ', $join) .' WHERE '. implode(' AND ', $where);
-  $sql = $select .' '. $sql_where;
-  $sql_count = $select_count .' '. $sql_where;
-
-  $strings = pager_query($sql, $filters['limit'], 0, $sql_count, $sql_args);
-  $result = array();
-  while ($string = db_fetch_object($strings)) {
-    $result[] = $string;
-  }
-  return $result;
-}
-
-/**
- * Mass approve callback.
- */
-function l10n_community_moderate_approve_selected($langcode, $tids, $strings) {
-  global $user;
-
-  // In case we have more than one sugggestion for an sid, error.
-  $sids = array_unique($strings);
-  if (count($sids) < count($strings)) {
-    drupal_set_message(t('You cannot approve more than one suggestion per string.'), 'error');
-    return;
-  }
-  $tid_placeholders = db_placeholders($tids);
-  $sid_placeholders = db_placeholders($sids);
-
-  // Mark existing translations and suggestions as inactive in this language.
-  $args = array_merge($sids, array($langcode));
-  db_query("UPDATE {l10n_community_translation} SET is_active = 0 WHERE sid IN ($sid_placeholders) AND language = '%s'", $args);
-
-  // Remove placeholder translation record (which was there if
-  // first came suggestions, before an actual translation).
-  db_query("DELETE FROM {l10n_community_translation} WHERE sid IN ($sid_placeholders) AND translation = '' AND language = '%s'", $args);
-
-  // Mark this exact suggestions as active, and set approval time.
-  $args = array_merge(array(time(), $user->uid), $tids);
-  db_query("UPDATE {l10n_community_translation} SET time_approved = %d, uid_approved = %d, has_suggestion = 0, is_suggestion = 0, is_active = 1 WHERE tid IN ($tid_placeholders)", $args);
-
-  drupal_set_message(format_plural(count($tids), 'A suggestion has been approved.', '@count suggestions have been approved.'));
-}
-
-/**
- * Mass decline callback.
- */
-function l10n_community_moderate_decline_selected($langcode, $tids, $sids) {
-  // We are not interested in duplicated sids.
-  $sids = array_unique($sids);
-  $tid_placeholders = db_placeholders($tids);
-  $sid_placeholders = db_placeholders($sids);
-
-  // Deactive the selected suggestions.
-  db_query("UPDATE {l10n_community_translation} SET is_active = 0 WHERE tid IN ($tid_placeholders)", $tids);
-  drupal_set_message(format_plural(db_affected_rows(), 'A suggestion has been declined.', '@count suggestions have been declined.'));
-
-  // Update 'has suggestion' option for remaining string translations
-  $args = array_merge(array($langcode), $sids);
-  $result = db_query(
-    "SELECT tt.tid
-     FROM {l10n_community_translation} tt
-     LEFT JOIN {l10n_community_translation} ts ON tt.sid = ts.sid AND tt.language = ts.language AND ts.is_active = 1 AND ts.is_suggestion = 1
-     WHERE tt.is_active = 1 AND tt.is_suggestion = 0 AND tt.has_suggestion = 1 AND tt.language = '%s' AND tt.sid IN ($sid_placeholders)
-     GROUP BY tt.tid
-     HAVING COUNT(ts.tid) = 0",
-    $args
-  );
-  while ($string = db_fetch_object($result)) {
-    db_query("UPDATE {l10n_community_translation} SET has_suggestion = 0 WHERE tid = %d", $string->tid);
-  }
-}
-
-// == Theme functions ==========================================================
-
-/**
- * Theme the approval form
- */
-function theme_l10n_community_moderation_form($form) {
-  $output = '';
-  $pager = isset($form['pager']) ? drupal_render($form['pager']) : '';
-  $output .= $pager;
-  $output .= drupal_render($form['options']);
-  $header = array(theme('table_select_header_cell'), t('Suggestion'), t('Translation'), t('Source'));
-  foreach (element_children($form['strings']['tid']) as $tid) {
-    $rows[] = array(
-      drupal_render($form['strings']['tid'][$tid]),
-      drupal_render($form['strings']['suggestion'][$tid]),
-      drupal_render($form['strings']['translation'][$tid]),
-      array('class' => 'source', 'data' => drupal_render($form['strings']['source'][$tid])),
-    );
-  }
-  $output .= theme('table', $header, $rows);
-  $output .= $pager;
-  $output .= drupal_render($form);
-  return $output;
-}
Index: l10n_community/pages.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_community/Attic/pages.inc,v
retrieving revision 1.1.2.20.2.24
diff -u -p -r1.1.2.20.2.24 pages.inc
--- l10n_community/pages.inc	27 Dec 2009 08:08:51 -0000	1.1.2.20.2.24
+++ l10n_community/pages.inc	17 Feb 2010 16:04:24 -0000
@@ -459,14 +459,14 @@ function l10n_community_get_string_count
         $sums = array();
         if (!isset($id)) {
           // Simple count query if we are not filtering by project.
-          $count1_sql = "SELECT COUNT(sid) AS translation_count, language FROM {l10n_community_translation} WHERE is_active = 1 AND translation != '' AND is_suggestion = 0 GROUP BY language";
-          $count2_sql = "SELECT COUNT(sid) AS translation_count, language FROM {l10n_community_translation} WHERE has_suggestion = 1 AND is_active = 1 GROUP BY language";
+          $count1_sql = "SELECT COUNT(sid) AS translation_count, language FROM {l10n_community_status_flag} WHERE has_translation = 1 GROUP BY language";
+          $count2_sql = "SELECT COUNT(sid) AS translation_count, language FROM {l10n_community_status_flag} WHERE has_suggestion = 1 GROUP BY language";
           $count_args = array();
         }
         else {
           // More complex joins if we also need to factor the project in.
-          $count1_sql = "SELECT COUNT(DISTINCT t.sid) AS translation_count, t.language FROM {l10n_community_line} l LEFT JOIN {l10n_community_translation} t ON l.sid = t.sid WHERE l.pid = %d AND t.is_active = 1 AND t.translation != '' AND t.is_suggestion = 0 GROUP BY t.language";
-          $count2_sql = "SELECT COUNT(DISTINCT t.sid) AS translation_count, t.language FROM {l10n_community_line} l LEFT JOIN {l10n_community_translation} t ON l.sid = t.sid WHERE l.pid = %d AND t.has_suggestion = 1 AND is_active = 1 GROUP BY t.language";
+          $count1_sql = "SELECT COUNT(DISTINCT ts.sid) AS translation_count, ts.language FROM {l10n_community_line} l INNER JOIN {l10n_community_status_flag} ts ON l.sid = ts.sid WHERE l.pid = %d AND ts.has_translation = 1 GROUP BY ts.language";
+          $count1_sql = "SELECT COUNT(DISTINCT ts.sid) AS translation_count, ts.language FROM {l10n_community_line} l INNER JOIN {l10n_community_status_flag} ts ON l.sid = ts.sid WHERE l.pid = %d AND ts.has_suggestion = 1 GROUP BY ts.language";
           $count_args = array($id);
         }
         $result = db_query($count1_sql, $count_args);
@@ -497,10 +497,10 @@ function l10n_community_get_string_count
           $sums[$row->pid] = array('count' => $row->string_count);
         }
         // Get the count of distinct strings translated per project.
-        $count_sql = "SELECT COUNT(DISTINCT t.tid) AS translation_count, l.pid FROM {l10n_community_line} l LEFT JOIN {l10n_community_translation} t ON l.sid = t.sid WHERE t.is_active = 1 AND t.translation != '' AND t.is_suggestion = 0 ";
+        $count_sql = "SELECT COUNT(DISTINCT ts.sid) AS translation_count, l.pid FROM {l10n_community_line} l LEFT JOIN {l10n_community_status_flag} ts ON l.sid = ts.sid WHERE ts.has_translation = 1 ";
         if (isset($id)) {
           // Limit to language if desired.
-          $count_sql .= "AND t.language = '%s' ";
+          $count_sql .= "AND ts.language = '%s' ";
           $count_args[] = $id;
         }
         $count_sql .= 'GROUP BY l.pid';
@@ -509,7 +509,7 @@ function l10n_community_get_string_count
           $sums[$row->pid]['translations'] = $row->translation_count;
         }
         // Get the count of distinct strings having suggestions per project.
-        $count_sql = "SELECT COUNT(DISTINCT t.sid) AS translation_count, l.pid FROM {l10n_community_line} l LEFT JOIN {l10n_community_translation} t ON l.sid = t.sid WHERE t.has_suggestion = 1 AND is_active = 1 ";
+        $count_sql = "SELECT COUNT(DISTINCT t.sid) AS translation_count, l.pid FROM {l10n_community_line} l LEFT JOIN {l10n_community_status_flag} ts ON l.sid = ts.sid WHERE ts.has_suggestion = 1 ";
         if (isset($id)) {
           // Limit to language if desired.
           $count_sql .= "AND t.language = '%s' ";
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.31.2.10
diff -u -p -r1.1.2.7.2.31.2.10 translate.inc
--- l10n_community/translate.inc	16 Feb 2010 16:22:34 -0000	1.1.2.7.2.31.2.10
+++ l10n_community/translate.inc	17 Feb 2010 16:04:24 -0000
@@ -23,11 +23,13 @@ function l10n_community_filter_form(&$fo
     L10N_STATUS_ALL            => '<'. t('Any') .'>',
     L10N_STATUS_UNTRANSLATED   => t('Untranslated'),
     L10N_STATUS_TRANSLATED     => t('Translated'),
+    L10N_STATUS_IS_TRANSLATION => t('Is translation'),
   );
   $suggestion_options = array(
     L10N_STATUS_ALL            => '<'. t('Any') .'>',
     L10N_STATUS_NO_SUGGESTION  => t('Has no suggestion'),
     L10N_STATUS_HAS_SUGGESTION => t('Has suggestion'),
+    L10N_STATUS_IS_SUGGESTION  => t('Is suggestion'),
   );
 
   // This form can be submitted multiple times to support AHAH flows.
@@ -114,13 +116,13 @@ function l10n_community_filter_form(&$fo
   $form['status']['translation'] = array(
     '#type' => 'select',
     '#options' => $translation_options,
-    '#default_value' => $form_state['values']['status'] & (L10N_STATUS_TRANSLATED | L10N_STATUS_UNTRANSLATED),
+    '#default_value' => $form_state['values']['status'] & (L10N_STATUS_TRANSLATED | L10N_STATUS_UNTRANSLATED | L10N_STATUS_IS_TRANSLATION),
   );
   if (!$limited) {
     $form['status']['suggestion'] = array(
       '#type' => 'select',
       '#options' => $suggestion_options,
-      '#default_value' => $form_state['values']['status'] & (L10N_STATUS_HAS_SUGGESTION | L10N_STATUS_NO_SUGGESTION),
+      '#default_value' => $form_state['values']['status'] & (L10N_STATUS_HAS_SUGGESTION | L10N_STATUS_NO_SUGGESTION | L10N_STATUS_IS_SUGGESTION),
     );
   }
 
@@ -630,6 +632,8 @@ function theme_l10n_community_translate_
  * @see l10n_community_translate_form().
  */ 
 function l10n_community_translate_form_submit($form, &$form_state) {
+  global $user;
+
   $langcode = $form_state['values']['langcode'];
 
   // Iterate outer structure built in l10n_community_translate_form().
@@ -651,7 +655,7 @@ function l10n_community_translate_form_s
         }
         // If we had value in any of the textareas, add new suggestion.
         if ($tid === 'new' && count($options['value']) > $empty_values) {
-          $tid = l10n_community_add_suggestion($langcode, $sid, $options['value']);
+          $tid = l10n_community_add_suggestion($sid, l10n_community_pack_string($options['value']), $langcode, $user->uid);
           if ($tid) {
             if ($string['active'] === 'new') {
               // This new string was selected to be approved, so remember $tid
@@ -663,6 +667,10 @@ function l10n_community_translate_form_s
               l10n_community_counter(L10N_COUNT_SUGGESTED);
             }
           }
+          elseif ($tid === FALSE) {
+            // We found this as an active string already in the DB.
+            l10n_community_counter(L10N_COUNT_DUPLICATE);
+          }
         }
       }
 
@@ -719,10 +727,11 @@ function l10n_community_get_strings($lan
   $join = $join_args = $where = $where_args = array();
   $sql = $sql_count = '';
 
-  $select = "SELECT DISTINCT s.sid, s.value, s.context, t.tid, t.language, t.translation, t.uid_entered, t.uid_approved, t.time_entered, t.time_approved, t.has_suggestion, t.is_suggestion, t.is_active, u.name as username, u2.name as username_approved FROM {l10n_community_string} s";
+  $select = "SELECT DISTINCT s.sid, s.value, s.context, t.tid, t.language, t.translation, t.uid_entered, t.uid_approved, t.time_entered, t.time_approved, t.is_suggestion, t.is_active, u.name as username, u2.name as username_approved, ts.has_suggestion, ts.has_translation FROM {l10n_community_string} s";
   $select_count = "SELECT COUNT(DISTINCT(s.sid)) FROM {l10n_community_string} s";
-  $join[] = "LEFT JOIN {l10n_community_translation} t ON s.sid = t.sid AND t.language = '%s' AND t.is_active = 1 AND t.is_suggestion = 0";
+  $join[] = "INNER JOIN {l10n_community_status_flag} ts ON s.sid = ts.sid AND ts.language = '%s'";
   $join_args[] = $langcode;
+  $join[] = "LEFT JOIN {l10n_community_translation} t ON ts.sid = t.sid AND ts.language = t.language AND t.is_active = 1";
   $join[] = "LEFT JOIN {users} u ON u.uid = t.uid_entered";
   $join[] = "LEFT JOIN {users} u2 ON u2.uid = t.uid_approved";
 
@@ -766,25 +775,23 @@ function l10n_community_get_strings($lan
   // Restriction based on string status by translation / suggestions.
   if (isset($filters['status'])) {
     if ($filters['status'] & L10N_STATUS_UNTRANSLATED) {
-      // We are doing a LEFT JOIN especially to look into the case, when we have nothing
-      // to match in the translation table, but we still have the string. (We get our
-      // records in the result set in this case). The translation field is empty or
-      // NULL in this case, as we are not allowing NULL there and only saving an empty
-      // translation if there are suggestions but no translation yet.
-      $where[] = "(t.translation is NULL OR t.translation = '')";
+      $where[] = "ts.has_translation = 0";
     }
     elseif ($filters['status'] & L10N_STATUS_TRANSLATED) {
-      $where[] = "t.translation != ''";
+      $where[] = "ts.has_translation = 1";
+    }
+    elseif ($filters['status'] & L10N_STATUS_IS_TRANSLATION) {
+      $where[] = "t.is_suggestion = 0";
     }
+    
     if ($filters['status'] & L10N_STATUS_HAS_SUGGESTION) {
-      // Note that we are not searching in the suggestions themselfs, only
-      // the source and active translation values. The user interface underlines
-      // that we are  looking for strings which have suggestions, not the
-      // suggestions themselfs.
-      $where[] = "t.has_suggestion = 1";
+      $where[] = "ts.has_suggestion = 1";
     }
     elseif ($filters['status'] & L10N_STATUS_NO_SUGGESTION) {
-      $where[] = "((t.has_suggestion IS NULL) OR (t.has_suggestion = 0))";
+      $where[] = "ts.has_suggestion = 0";
+    }
+    elseif ($filters['status'] & L10N_STATUS_IS_SUGGESTION) {
+      $where[] = "t.is_suggestion = 1";
     }
   }
 
@@ -803,131 +810,26 @@ function l10n_community_get_strings($lan
   }
   $result = array();
   while ($string = db_fetch_object($strings)) {
+    if ($string->is_suggestion) {
+      // This string is not a translation, but we need that as a "parent" to display.
+      if ($string->has_translation && ($translation = db_fetch_array(db_query("SELECT t.tid, t.translation, t.uid_entered, t.uid_approved, t.time_entered, t.time_approved, t.is_suggestion, t.is_active, u.name as username, u2.name as username_approved FROM {l10n_community_translation} t LEFT JOIN {users} u ON u.uid = t.uid_entered LEFT JOIN {users} u2 ON u2.uid = t.uid_approved WHERE t.language = '%s' AND t.sid = %d AND t.is_active = 1 AND t.is_suggestion = 0", $string->language, $string->sid)))) {
+        // It does have a translation however, so let's load it, and override.
+        foreach ($translation as $key => $value) {
+          $string->$key = $value;
+        }
+      }
+      else {
+        // No parent translation. Pretend this does not exist. 
+        // The display code will call for the suggestions.
+        $string->translation = '';
+      }
+    }
     $result[] = $string;
   }
   return $result;
 }
 
 /**
- * Ensures that there is a mock translation for a given language/string.
- *
- * @param $langcode
- *   The language to be checked for a mock translation.
- * @param $sid
- *   The string ID that needs to have a mock translation.
- */
-function l10n_community_mock_translation($langcode, $sid) {
-  if (!db_result(db_query("SELECT COUNT(*) FROM {l10n_community_translation} WHERE sid = %d AND language = '%s' AND is_suggestion = 0 AND is_active = 1", $sid, $langcode))) {
-    // Insert mock tuple that acts as placeholder.
-    db_query("INSERT INTO {l10n_community_translation} (sid, translation, language, uid_entered, time_entered, is_suggestion, is_active) VALUES (%d, '', '%s', 0, 0, 0, 1)", $sid, $langcode);
-  }
-}
-
-/**
- * Adds a suggestion to a language/string.
- *
- * @param $langcode
- *   The language of the new translation.
- * @param $sid
- *   The string ID for which a new translation should be added.
- * @param $translation
- *   An array of strings which constitute the new translation.
- */
-function l10n_community_add_suggestion($langcode, $sid, $translation) {
-  global $user;
-
-  // Load source string and adjust translation whitespace based on source.
-  $source_string = db_result(db_query('SELECT value FROM {l10n_community_string} WHERE sid = %d', $sid));
-  $translation = l10n_community_pack_string($translation);
-  $translation = l10n_community_trim($translation, $source_string);
-
-  // Don't store empty translations.
-  if ($translation === '') {
-    return NULL;
-  }
-
-  // Look for an existing active translation, if any.
-  $existing = db_fetch_object(db_query("SELECT tid FROM {l10n_community_translation} WHERE sid = %d AND language = '%s' AND translation = '%s'", $sid, $langcode, $translation));
-  if (!empty($existing)) {
-    // The translation is already in the db. Make it an active suggestion again.
-    db_query("UPDATE {l10n_community_translation} SET is_suggestion = 1, is_active = 1 WHERE tid = %d", $existing->tid);
-    $tid = $existing->tid;
-  }
-  else {
-    // This is a new translation.
-    l10n_community_mock_translation($langcode, $sid);
-    // Insert the new suggestion.
-    db_query("INSERT INTO {l10n_community_translation} (sid, translation, language, uid_entered, time_entered, is_suggestion, is_active) VALUES (%d, '%s', '%s', %d, %d, 1, 1)", $sid, $translation, $langcode, $user->uid, time());
-    $tid = db_last_insert_id('l10n_community_translation', 'tid');
-  }
-
-  // Mark the existing or mock translation as having suggestions.
-  l10n_community_update_suggestion_status($langcode, $sid);
-
-  return $tid;
-}
-
-/**
- * Marks a translation as declined.
- *
- * @param $langcode
- *   The language of the declined translation.
- * @param $sid
- *   The string ID the translation belongs to.
- * @param $tid
- *   The translation ID of the translation.
- */
-function l10n_community_decline_string($langcode, $sid, $tid) {
-  // Mark this translation as inactive.
-  db_query("UPDATE {l10n_community_translation} SET is_suggestion = 0, is_active = 0 WHERE tid = %d", $tid);
-
-  // Make sure the mock translation is there in case we declined the active translation.
-  l10n_community_mock_translation($langcode, $sid);
-  l10n_community_update_suggestion_status($langcode, $sid);
-}
-
-/**
- * Updates the has_suggestion flag for the active translation.
- *
- * @param $langcode
- *   The language of the string.
- * @param $sid
- *   The string ID that should be updated.
- */
-function l10n_community_update_suggestion_status($langcode, $sid) {
-  // Let's see if we have any suggestions remaining in this language.
-  $count = db_result(db_query("SELECT COUNT(*) FROM {l10n_community_translation} WHERE sid = %d AND is_suggestion = 1 AND is_active = 1 AND language = '%s'", $sid, $langcode));
-
-  // Update the status according to the number of suggestions.
-  db_query("UPDATE {l10n_community_translation} SET has_suggestion = %d WHERE sid = %d AND is_suggestion = 0 AND is_active = 1 AND language = '%s'", $count ? 1 : 0, $sid, $langcode);
-}
-
-/**
- * Mark a translation as approved.
- *
- * @param $langcode
- *   The language of the approved translation.
- * @param $sid
- *   The string ID the translation belongs to.
- * @param $tid
- *   The translation ID of the translation.
- */
-function l10n_community_approve_string($langcode, $sid, $tid) {
-  global $user;
-
-  // Remove placeholder translation record (which was there if
-  // first came suggestions, before an actual translation).
-  db_query("DELETE FROM {l10n_community_translation} WHERE sid = %d AND translation = '' AND language = '%s'", $sid, $langcode);
-
-  // Make the existing approved string a suggestion.
-  db_query("UPDATE {l10n_community_translation} SET is_suggestion = 1 WHERE sid = %d AND language = '%s' AND is_suggestion = 0 AND is_active = 1", $sid, $langcode);
-
-  // Mark this exact suggestion as active translation, and set approval time.
-  db_query("UPDATE {l10n_community_translation} SET time_approved = %d, uid_approved = %d, is_suggestion = 0, is_active = 1 WHERE tid = %d;", time(), $user->uid, $tid);
-  l10n_community_update_suggestion_status($langcode, $sid);
-}
-
-/**
  * Unpacks a string as retrieved from the database.
  *
  * Creates an array out of the string. If it was a single string, the array
Index: l10n_community/images/newline.png
===================================================================
RCS file: l10n_community/images/newline.png
diff -N l10n_community/images/newline.png
Binary files /tmp/cvshLg50K and /dev/null differ
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.15.2.2
diff -u -p -r1.1.2.15.2.2 l10n_community.test
--- l10n_community/tests/l10n_community.test	16 Feb 2010 16:22:34 -0000	1.1.2.15.2.2
+++ l10n_community/tests/l10n_community.test	17 Feb 2010 16:04:24 -0000
@@ -301,153 +301,52 @@ class L10nServerTestCase extends DrupalW
     $this->drupalGet('translate/languages/hu/translate', array('query' => 'status=4&limit=30'));
     $this->assertCount('<td class="source"', 21);
 
-    // MODERATION ====
-
-    $this->drupalGet('user/logout');
-    $this->drupalLogin($this->u_moderator);
-
-    $this->drupalGet('translate/languages/hu/moderate', array('query' => 'limit=30'));
-    $this->assertCount('<input type="checkbox"', 6);
-    $this->assertRaw('This is a test string. suggestion');
-    $this->assertRaw('Test string in context. suggestion-1');
-    $this->assertRaw('Test string in context. suggestion-2');
-
-    $this->drupalGet('translate/languages/hu/moderate', array('query' => 'limit=30&project='. $this->project_names[6]));
-    $this->assertCount('<input type="checkbox"', 4);
-    $this->assertRaw('This is a test string. suggestion');
-    $this->assertRaw('Test string in context. suggestion-1');
-    $this->assertNoRaw('Test string in context. suggestion-2');
-
-    $this->drupalGet('translate/languages/hu/moderate', array('query' => 'limit=30&project='. $this->project_names[7]));
-    $this->assertCount('<input type="checkbox"', 5);
-    $this->assertRaw('This is a test string. suggestion');
-    $this->assertNoRaw('Test string in context. suggestion-1');
-    $this->assertRaw('Test string in context. suggestion-2');
-
-    $this->drupalGet('translate/languages/hu/moderate', array('query' => 'limit=30&context=Test%20context'));
-    $this->assertCount('<input type="checkbox"', 2);
-    $this->assertRaw('Test string in context. suggestion-2');
-    $this->assertRaw('Test string in context. suggestion-3');
-
-    $this->drupalGet('translate/languages/hu/moderate', array('query' => 'limit=30&search=is%20a'));
-    $this->assertCount('<input type="checkbox"', 2);
-    $this->assertRaw('This is a test string. suggestion');
-    $this->assertRaw('This is a test string. suggestion-2');
-
-    $this->drupalGet('translate/languages/hu/moderate', array('query' => 'limit=30&author='. $this->u_member_1->name));
-    $this->assertCount('<input type="checkbox"', 4);
-    $this->assertRaw('This is a test string. suggestion');
-
-    $this->drupalGet('translate/languages/hu/moderate', array('query' => 'limit=30&author='. $this->u_member_2->name));
-    $this->assertCount('<input type="checkbox"', 2);
-    $this->assertRaw('This is a test string. suggestion-2');
-
-    $edit = array();
-    $tid1 = db_result(db_query("SELECT tid FROM {l10n_community_translation} WHERE translation = '%s' AND language = '%s'", 'This is a test string. suggestion', 'hu'));
-    $edit['strings[tid]['. $tid1 .']'] = 1;
-    $tid4 = db_result(db_query("SELECT tid FROM {l10n_community_translation} WHERE translation = '%s' AND language = '%s'", "1 test string suggestion\0@count test strings suggestion", 'hu'));
-    $edit['strings[tid]['. $tid4 .']'] = 1;
-    $this->drupalPost('translate/languages/hu/moderate', $edit, t('Approve all selected'), array('query' => 'limit=30'));
-    // Approving a translation removed two of them, since they were for the same
-    // source string.
-    $this->assertCount('<input type="checkbox"', 3);
-    $this->assertText(t('@count suggestions have been approved.', array('@count' => 2)));
-    $this->assertNoRaw('This is a test string. suggestion-2');
-
-    // Add 2 new suggestions and 1 translation for the fun of testing.
-    $edit = array();
-    $edit['strings['. $sid1 .'][new][value][0]'] = 'This is a test string. suggestion-3';
-    $edit['strings['. $sid3 .'][new][value][0]'] = 'Test string in context. suggestion-4';
-    $sid5 = db_result(db_query("SELECT sid FROM {l10n_community_string} WHERE value = '%s'", "This is a test menu item"));
-    $edit['strings['. $sid5 .'][new][value][0]'] = 'This is a test menu item translation';
-    // Mark this new suggestion as the active translation.
-    $edit['strings['. $sid5 .'][active]'] = 'new';
-    $this->drupalPost('translate/languages/hu/translate', $edit, t('Save changes'), array('query' => 'limit=30'));
-    $this->assertRaw(t('@count translation added', array('@count' => 1)));
-    $this->assertRaw(t('@count suggestions added', array('@count' => 2)));
-
-    // Add 1 more suggestion.
-    $edit = array();
-    $edit['strings['. $sid3 .'][new][value][0]'] = 'Test string in context. suggestion-5';
-    $this->drupalPost('translate/languages/hu/translate', $edit, t('Save changes'), array('query' => 'limit=30'));
-
-    // Three strings should have translations.
-    $this->drupalGet('translate/languages/hu/translate', array('query' => 'limit=30&status=2'));
-    $this->assertMsgID('This is a test string.');
-    $this->assertMsgID('This is a test menu item');
-    $this->assertMsgIDPlural('1 test string', '@count test strings');
-    $this->assertCount('<td class="source"', 3);
-
-    // 22 should not have translations
-    $this->drupalGet('translate/languages/hu/translate', array('query' => 'limit=30&status=1'));
-    $this->assertCount('<td class="source"', 22);
-
-    // Translated but lacks further suggestions.
-    $this->drupalGet('translate/languages/hu/translate', array('query' => 'limit=30&status='. (2 | 4)));
-    $this->assertMsgID('This is a test menu item');
-    $this->assertMsgIDPlural('1 test string', '@count test strings');
-    $this->assertCount('<td class="source"', 2);
-
-    // Translated and has further suggestions.
-    $this->drupalGet('translate/languages/hu/translate', array('query' => 'limit=30&status='. (2 | 8)));
-    $this->assertMsgID('This is a test string.');
-    $this->assertCount('<td class="source"', 1);
-
-    // Untranslated and has further suggestions.
-    $this->drupalGet('translate/languages/hu/translate', array('query' => 'limit=30&status='. (1 | 8)));
-    $this->assertMsgID('Test string in context');
-    $this->assertMsgIDContext('Test string in context', 'Test context');
-    $this->assertCount('<td class="source"', 2);
-
-    // Untranslated and no further suggestions.
-    $this->drupalGet('translate/languages/hu/translate', array('query' => 'limit=30&status='. (1 | 4)));
-    $this->assertCount('<td class="source"', 20);
-
     // Look for translations per user.
-    $this->drupalGet('translate/languages/hu/translate', array('query' => 'limit=30&author='. $this->u_moderator->name));
-    $this->assertMsgID('This is a test menu item translation');
-    $this->assertCount('<td class="source"', 1);
     $this->drupalGet('translate/languages/hu/translate', array('query' => 'limit=30&author='. $this->u_member_1->name));
     $this->assertMsgID('This is a test string. suggestion');
     $this->assertMsgIDPlural('1 test string', '@count test strings');
+    $this->assertCount('<td class="source"', 4);
+    $this->drupalGet('translate/languages/hu/translate', array('query' => 'limit=30&author='. $this->u_member_2->name));
+    $this->assertMsgID('This is a test string. suggestion-2');
     $this->assertCount('<td class="source"', 2);
 
-    // MASS DECLINE ====
+    // MODERATION ====
 
-    $edit = array();
-    $tid = db_result(db_query("SELECT tid FROM {l10n_community_translation} WHERE translation = '%s' AND language = '%s'", 'Test string in context. suggestion-1', 'hu'));
-    $edit['strings[tid]['. $tid .']'] = 1;
-    $tid = db_result(db_query("SELECT tid FROM {l10n_community_translation} WHERE translation = '%s' AND language = '%s'", 'Test string in context. suggestion-4', 'hu'));
-    $edit['strings[tid]['. $tid .']'] = 1;
-    $this->drupalPost('translate/languages/hu/moderate', $edit, t('Decline all selected'), array('query' => 'limit=30'));
-    $this->assertCount('<input type="checkbox"', 4);
-    $this->assertText(t('@count suggestions have been declined.', array('@count' => 2)));
+    $this->drupalGet('user/logout');
+    $this->drupalLogin($this->u_moderator);
 
     // INDIVIDUAL DECLINE ====
 
-    $tid = db_result(db_query("SELECT tid FROM {l10n_community_translation} WHERE translation = '%s' AND language = '%s'", 'Test string in context. suggestion-3', 'hu'));
+    $tid1 = db_result(db_query("SELECT tid FROM {l10n_community_translation} WHERE translation = '%s' AND language = '%s'", 'Test string in context. suggestion-1', 'hu'));
+    $tid2 = db_result(db_query("SELECT tid FROM {l10n_community_translation} WHERE translation = '%s' AND language = '%s'", 'Test string in context. suggestion-2', 'hu'));
+    $tid3 = db_result(db_query("SELECT tid FROM {l10n_community_translation} WHERE translation = '%s' AND language = '%s'", 'Test string in context. suggestion-3', 'hu'));
     $edit = array();
-    $edit['strings['. $sid3 .']['. $tid .'][declined]'] = 1;
+    $edit['strings['. $sid2 .']['. $tid1 .'][declined]'] = 1;
+    $edit['strings['. $sid3 .']['. $tid2 .'][declined]'] = 1;
+    $edit['strings['. $sid3 .']['. $tid3 .'][declined]'] = 1;
     $this->drupalPost('translate/languages/hu/translate', $edit, t('Save changes'), array('query' => 'limit=30'));
-    $this->assertRaw(t('1 suggestion declined'));
+    $this->assertRaw(t('3 suggestions declined'));
 
     // Now we should be down to 3 suggestions.
-    $this->drupalGet('translate/languages/hu/moderate', array('query' => 'limit=30'));
+    $this->drupalGet('translate/languages/hu/translate', array('query' => 'status=8&limit=30'));
     $this->assertCount('<input type="checkbox"', 3);
 
     // INDIVIDUAL APPROVE ====
 
-    $tid = db_result(db_query("SELECT tid FROM {l10n_community_translation} WHERE translation = '%s' AND language = '%s'", 'Test string in context. suggestion-2', 'hu'));
+    $tid1 = db_result(db_query("SELECT tid FROM {l10n_community_translation} WHERE translation = '%s' AND language = '%s'", 'This is a test string. suggestion', 'hu'));
+    $tid2 = db_result(db_query("SELECT tid FROM {l10n_community_translation} WHERE translation = '%s' AND language = '%s'", "1 test string suggestion\0@count test strings suggestion", 'hu'));
     $edit = array();
-    $edit['strings['. $sid3 .'][active]'] = $tid;
+    $edit['strings['. $sid1 .'][active]'] = $tid1;
+    $edit['strings['. $sid4 .'][active]'] = $tid2;
     $this->drupalPost('translate/languages/hu/translate', $edit, t('Save changes'), array('query' => 'limit=30'));
-    $this->assertRaw(t('1 translation approved'));
+    $this->assertRaw(t('2 translations approved'));
 
-    // Now we should be down to 2 suggestions.
-    $this->drupalGet('translate/languages/hu/moderate', array('query' => 'limit=30'));
+    // Now we should be down to 1 suggestion and 1 translation.
+    $this->drupalGet('translate/languages/hu/translate', array('query' => 'status=8&limit=30'));
+    $this->assertCount('<td class="source"', 1);
     $this->assertCount('<input type="checkbox"', 2);
-    $this->assertRaw('This is a test string. suggestion-3');
-    $this->assertRaw('Test string in context. suggestion-5');
+    $this->assertRaw('This is a test string. suggestion');
+    $this->assertRaw('This is a test string. suggestion-2');
   }
 
   /**
@@ -484,6 +383,10 @@ class L10nServerTestCase extends DrupalW
     $this->assertRaw(t('1 duplicate translation not saved'));
     $this->assertRaw(t('@count source strings not found; their translations were ignored', array('@count' => 2)));
 
+    // Log in with moderator, so we can check the suggestions easily.
+    $this->drupalGet('user/logout');
+    $this->drupalLogin($this->u_moderator);
+
     // Check whether it is there.
     $this->drupalGet('translate/languages/hu/translate', array('query' => 'limit=30&status=8'));
     $this->assertMsgID('This is a test string.');
@@ -503,8 +406,6 @@ class L10nServerTestCase extends DrupalW
     // IMPORT AS MODERATOR
 
     // Moderators can also choose to import translations directly.
-    $this->drupalGet('user/logout');
-    $this->drupalLogin($this->u_moderator);
     $this->drupalGet('translate/languages/hu/import');
     $this->assertRaw(t('Gettext .po file with translations'));
     $this->assertRaw(t('Store as approved translations'));
@@ -526,7 +427,7 @@ class L10nServerTestCase extends DrupalW
 
     // Importing the same file, but attribute to team (this can be done logged
     // in as anyone who can import, we don't need to log in as u_team).
-    $this->importPO($filename, $this->u_team->uid);
+    $this->importPO($filename, $this->u_team->uid, 1);
     $this->assertRaw(t('@count 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)));
@@ -557,18 +458,17 @@ class L10nServerTestCase extends DrupalW
 
     // Importing the same file, but ask to import as final translation.
     $this->importPO($filename, $this->u_moderator->uid, 0);
-    $this->assertRaw(t('@count suggestions added', array('@count' => 3)));
+    $this->assertRaw(t('@count translations 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)));
 
-    // Because we did not have an active translation but had outstanding
-    // suggestions, these translations should be diverted to suggestions.
+    // Should still have 3 strings with outstanding suggestions.
     $this->drupalGet('translate/languages/hu/translate', array('query' => 'limit=30&status=8'));
     $this->assertCount('<td class="source"', 3);
 
-    // Should still see no translations.
+    // Should now have 3 strings with approved translations.
     $this->drupalGet('translate/languages/hu/translate', array('query' => 'limit=30&status=2'));
-    $this->assertNoRaw('<td class="source"');
+    $this->assertCount('<td class="source"', 3);
 
     // IMPORT AS TRANSLATION
 
@@ -577,11 +477,10 @@ class L10nServerTestCase extends DrupalW
 
     $this->importPO($filename, $this->u_moderator->uid, 0);
     $this->assertRaw(t('@count translations added', array('@count' => 3)));
-    $this->assertRaw(t('1 translation unchanged'));
+    $this->assertRaw(t('1 duplicate translation not saved'));
     $this->assertRaw(t('@count source strings not found; their translations were ignored', array('@count' => 2)));
 
-    // Importing as translation should have kicked out the existing suggestions
-    // for these strings.
+    // No string should have suggestions submitted at this point.
     $this->drupalGet('translate/languages/hu/translate', array('query' => 'limit=30&status=8'));
     $this->assertNoRaw('<td class="source"');
 
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.7
diff -u -p -r1.1.2.7 l10n_remote.module
--- l10n_remote/l10n_remote.module	12 Jan 2010 08:43:26 -0000	1.1.2.7
+++ l10n_remote/l10n_remote.module	17 Feb 2010 16:04:24 -0000
@@ -164,15 +164,18 @@ function l10n_remote_xmlrpc_string_submi
     return array('status' => FALSE, 'reason' => 'Source string not found on server, translation not saved.');
   }
 
-  // Trim string to have the same whitespace as the source and check duplicates.
-  $translation = l10n_community_trim($translation, $source);
-  if (l10n_community_is_duplicate($translation, $sid, $langcode)) {
+  // Attempt to add this as a suggestion.
+  $tid = l10n_community_add_suggestion($sid, $translation, $langcode, $uid);
+  
+  if ($tid === FALSE) {
     //watchdog('l10n_community', 'Duplicate translation submitted.', NULL, WATCHDOG_WARNING);
     return array('status' => FALSE, 'reason' => 'Suggested translation already appears as active translation or suggestion.');
   }
+  elseif ($tid === NULL) {
+    //watchdog('l10n_community', 'Empty suggestions are not saved.', NULL, WATCHDOG_WARNING);
+    return array('status' => FALSE, 'reason' => 'Empty suggestions are not saved.');
+  }
 
-  // If we got this far, everything is fine and $translation contains the
-  // text as expected with whitespace correction. Signal success to the client.
-  l10n_community_target_save($sid, $translation, $langcode, $uid, TRUE, $inserted, $updated, $unchanged, $suggested);
+  // If we got this far, everything is fine. Signal success to the client.
   return array('status' => TRUE, 'sid' => $sid);
 }
