Index: l10n_community/editor.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_community/Attic/editor.css,v
retrieving revision 1.1.2.14
diff -u -p -r1.1.2.14 editor.css
--- l10n_community/editor.css	6 Dec 2010 12:20:18 -0000	1.1.2.14
+++ l10n_community/editor.css	9 Jan 2011 16:07:52 -0000
@@ -69,6 +69,7 @@ td.source {
     color: #000;
     opacity: 0.4;
   }
+  td.translation .l10n-byline .l10n-more-link:before,
   td.source .l10n-usage .l10n-more-link:before {
     content: "▾ ";
   }
@@ -182,9 +183,6 @@ li.translation > .author {
   li.translation > .author a {
     color: #666;
   }
-  li.translation > .author span[title] {
-    cursor: pointer;
-  }
 
 
   li.new-translation:not(.focused):not(.has-content):not(.is-active) > .l10n-string {
Index: l10n_community/editor.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_community/Attic/editor.js,v
retrieving revision 1.1.2.10
diff -u -p -r1.1.2.10 editor.js
--- l10n_community/editor.js	24 Jun 2010 16:00:59 -0000	1.1.2.10
+++ l10n_community/editor.js	9 Jan 2011 16:07:52 -0000
@@ -33,7 +33,7 @@
   $(function () {
 
     // Replace "More information" link with AJAX output.
-    $('.l10n-more-link').click(function() {
+    $('.l10n-usage .l10n-more-link').click(function() {
       if ($(this).siblings('.l10n-more-info').css('display') == 'none') {
         // Was shown before but is currently hidden.
         $(this).html(Drupal.t('Hide related projects')).siblings('.l10n-more-info').toggle();
@@ -51,6 +51,17 @@
       return false;
     });
 
+    // Provide more history about string submissions.
+    $('.l10n-byline .l10n-more-link').click(function() {
+      if (!$(this).siblings('.l10n-more-info').html()) {
+        // Was not yet loaded, we want to load the information fresh from the server.
+        // Append /1 to the href, telling the server we want AHAH targeted output.
+        $(this).html(Drupal.t('Loading...')).siblings('.l10n-more-info').load(this.href + '/1', function(){$(this).siblings('.l10n-more-link').hide();});
+      }
+      // Prevent the actual link click from happening.
+      return false;
+    });
+
     // Callback to show all filters.
     var showAllFilters = function() {
       $('#l10n-community-filter-form .reveal-link').hide();
@@ -179,11 +190,6 @@
           setStatus(translation, 'declined', this.checked);
         });
 
-        translation.find('> .author span[title]').click(function() {
-          var $this = $(this), html = $this.html();
-          $this.html($this.attr('title')).attr('title', html);
-        });
-
         if (isTranslation) {
           // Add doubleclick behavior to decline all other suggestions.
           translation.filter('.is-selectable').find('.l10n-string').dblclick(function() {
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.8.2.1
diff -u -p -r1.1.2.5.2.18.2.8.2.1 import.inc
--- l10n_community/import.inc	22 Dec 2010 19:12:54 -0000	1.1.2.5.2.18.2.8.2.1
+++ l10n_community/import.inc	9 Jan 2011 16:07:52 -0000
@@ -120,8 +120,6 @@ function l10n_community_import_form_vali
  * Process the locale import form submission.
  */
 function l10n_community_import_form_submit($form, &$form_state) {
-  global $user;
-
   // Save file in the local file system.
   if ($file = file_save_upload('file')) {
 
@@ -183,7 +181,7 @@ function _l10n_community_import_one_stri
       $value['msgstr'] = is_array($value['msgstr']) ? join("\0", $value['msgstr']) : $value['msgstr'];
 
       // Add this as a suggestion first.
-      $tid = l10n_community_add_suggestion($sid, $value['msgstr'], $langcode, $uid, L10N_SERVER_ACTION_ADD_IMPORT);
+      $tid = l10n_community_add_suggestion($sid, $value['msgstr'], $langcode, $uid, $user->uid, L10N_SERVER_MEDIUM_IMPORT);
 
       if ($tid) {
         if ($is_suggestion) {
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.2.14.2.1
diff -u -p -r1.1.2.11.2.15.2.14.2.1 l10n_community.install
--- l10n_community/l10n_community.install	22 Dec 2010 19:12:54 -0000	1.1.2.11.2.15.2.14.2.1
+++ l10n_community/l10n_community.install	9 Jan 2011 16:07:53 -0000
@@ -467,7 +467,7 @@ function l10n_community_update_6021() {
 function l10n_community_update_6022() {
   $ret = array();
 
-  db_create_table($ret, 'l10n_community_translation_history', array(
+  db_create_table($ret, 'l10n_server_translation_history', array(
     'fields' => array(
       'tid' => array(
         'description' => 'References {l10n_server_translation}.tid',
@@ -497,25 +497,52 @@ function l10n_community_update_6022() {
         'default' => 0,
         'disp-width' => '11'
       ),
+      'medium_action' => array(
+        'description' => 'Numeric identifier of the medium the action happened through.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'disp-width' => '11'
+      ),
     ),
     'indexes' => array(
       'tid' => array('tid'),
     ),
   ));
 
-  // Save submission history data with L10N_SERVER_ACTION_ADD_UNKNOWN (0), since
-  // we don't know how was the string added really.
-  $ret[] = update_sql("INSERT INTO {l10n_server_translation_history} (tid, uid_action, type_action, time_action) SELECT tid, uid_entered, 0, time_entered FROM {l10n_server_translation}");
+  // Save submission history data with L10N_SERVER_ACTION_ADD (1), and
+  // L10N_SERVER_MEDIUM_UNKNOWN (0) since we don't know how was the string
+  // added really.
+  $ret[] = update_sql("INSERT INTO {l10n_server_translation_history} (tid, uid_action, type_action, time_action, medium_action) SELECT tid, uid_entered, 0, time_entered, 0 FROM {l10n_server_translation}");
 
-  // Save approval history data with L10N_SERVER_ACTION_APPROVE (4).
-  $ret[] = update_sql("INSERT INTO {l10n_server_translation_history} (tid, uid_action, type_action, time_action) SELECT tid, uid_approved, 4, time_approved FROM {l10n_server_translation} WHERE is_active = 1 AND is_suggestion = 0");
+  // Save approval history data with L10N_SERVER_ACTION_APPROVE (2) and
+  // L10N_SERVER_MEDIUM_WEB (1).
+  $ret[] = update_sql("INSERT INTO {l10n_server_translation_history} (tid, uid_action, type_action, time_action, medium_action) SELECT tid, uid_approved, 2, time_approved, 1 FROM {l10n_server_translation} WHERE is_active = 1 AND is_suggestion = 0");
   
+  // Save somewhat padded decline data with L10N_SERVER_ACTION_DECLINE (3) and
+  // L10N_SERVER_MEDIUM_WEB (1). Use a second later time based on submission
+  // time, so in time ordering of events, it will appear later. Actual display
+  // of this action will not consider the time, so it is known to be fake
+  // based on the uid being zero.
+  $ret[] = update_sql("INSERT INTO {l10n_server_translation_history} (tid, uid_action, type_action, time_action, medium_action) SELECT tid, 0, 3, time_entered + 1, 1 FROM {l10n_server_translation} WHERE is_active = 0");
+
+  // Do nothing for L10N_SERVER_ACTION_DEMOTE (4) and
+  // L10N_SERVER_ACTION_READD (5). We don't know if these happened to any of the
+  // strings, since there is not yet lasting indication of these actions in the
+  // database.
+
   // Add the time_changed field.
   db_add_field($ret, 'l10n_server_translation', 'time_changed', array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'disp-width' => '11'));
   
-  // The last change date we know is either the submission or approval time.
-  $ret[] = update_sql("UPDATE {l10n_server_translation} SET time_changed = GREATEST(time_entered, time_approved)");
+  // The last change date we know is either the submission or approval time
+  // for active strings.
+  $ret[] = update_sql("UPDATE {l10n_server_translation} SET time_changed = GREATEST(time_entered, time_approved) WHERE is_active = 1");
   
+  // The last change date for declined strings is faked above to time_entered + 1,
+  // so use that for consistency. That is sure to be later to time_entered and
+  // will be consistent with the last history action on the string.
+  $ret[] = update_sql("UPDATE {l10n_server_translation} SET time_changed = time_entered + 1 WHERE is_active = 0");
+
   // Drop fields not needed anymore in the translation table, now we track these separately.
   db_drop_field($ret, 'l10n_server_translation', 'uid_approved');
   db_drop_field($ret, 'l10n_server_translation', 'time_approved');
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.25.2.1
diff -u -p -r1.1.2.23.2.66.2.25.2.1 l10n_community.module
--- l10n_community/l10n_community.module	22 Dec 2010 19:12:54 -0000	1.1.2.23.2.66.2.25.2.1
+++ l10n_community/l10n_community.module	9 Jan 2011 16:07:53 -0000
@@ -136,14 +136,16 @@ function l10n_community_menu() {
     'access arguments' => array('access localization community'),
     'weight' => -5,
   );
-  // Unique path to avoid clashing with other items.
+
+  // Autocomplete callback for project textfields.
   $items['translate/project-autocomplete'] = array(
     'title' => 'Project autocomplete',
     'page callback' => 'l10n_community_projects_autocomplete',
     'access arguments' => array('access localization community'),
     'type' => MENU_CALLBACK
   );
-  // Serve a list of releases for a given project.
+
+  // Form API AHAH release selector.
   $items['translate/releases-ahah'] = array(
     'title' => 'AHAH release picker callback',
     'page callback' => 'l10n_community_ahah_releases',
@@ -151,13 +153,21 @@ function l10n_community_menu() {
     'type' => MENU_CALLBACK
   );
 
-
-  // AJAX callbacks for easy translation management. These are expected to be
-  // used only onsite (not as remote API endpoints), so they have no versioning.
-  $items['translate/details/%l10n_community_language/%'] = array(
+  // Callback to provide details about a certain source string.
+  $items['translate/source-details/%'] = array(
     'title' => 'Source string usage details',
     'page callback' => 'l10n_community_string_details',
-    'page arguments' => array(2, 3),
+    'page arguments' => array(2),
+    'file' => 'translate.inc',
+    'access arguments' => array('browse translations'),
+    'type' => MENU_CALLBACK,
+  );
+
+  // Callback to provide details about translation history.
+  $items['translate/translation-details/%'] = array(
+    'title' => 'Translation history information',
+    'page callback' => 'l10n_community_translation_details',
+    'page arguments' => array(2),
     'file' => 'translate.inc',
     'access arguments' => array('browse translations'),
     'type' => MENU_CALLBACK,
@@ -971,12 +981,14 @@ function l10n_community_ahah_releases() 
  *   String representing the new translation.
  * @param $langcode
  *   The language of the new translation.
- * @param $uid
+ * @param $uid_attribution
  *   User ID to use to save the string.
- * @param $type
- *   Action type constant L10N_SERVER_ACTION_*
+ * @param $uid_user
+ *   User ID to use to keep history of.
+ * @param $medium
+ *   Medium type constant L10N_SERVER_MEDIUM_*
  */
-function l10n_community_add_suggestion($sid, $translation, $langcode, $uid, $type) {
+function l10n_community_add_suggestion($sid, $translation, $langcode, $uid_attribution, $uid_user, $medium) {
 
   // Load source string and adjust translation whitespace based on source.
   $source_string = db_result(db_query('SELECT value FROM {l10n_server_string} WHERE sid = %d', $sid));
@@ -997,6 +1009,7 @@ function l10n_community_add_suggestion($
       // If the existing item is not active, make it an active suggestion and clean up its possible previous approval information.
       db_query("UPDATE {l10n_server_translation} SET is_suggestion = 1, is_active = 1, time_changed = %d WHERE tid = %d", $time, $existing->tid);
       $tid = $existing->tid;
+      $type = L10N_SERVER_ACTION_READD;
     }
     else {
       return FALSE;
@@ -1004,10 +1017,11 @@ function l10n_community_add_suggestion($
   }
   else {
     // Insert the new suggestion.
-    db_query("INSERT INTO {l10n_server_translation} (sid, translation, language, uid_entered, time_entered, time_changed, is_suggestion, is_active) VALUES (%d, '%s', '%s', %d, %d, %d, 1, 1)", $sid, $translation, $langcode, $uid, $time, $time);
+    db_query("INSERT INTO {l10n_server_translation} (sid, translation, language, uid_entered, time_entered, time_changed, is_suggestion, is_active) VALUES (%d, '%s', '%s', %d, %d, %d, 1, 1)", $sid, $translation, $langcode, $uid_attribution, $time, $time);
     $tid = db_last_insert_id('l10n_community_translation', 'tid');
+    $type = L10N_SERVER_ACTION_ADD;
   }
-  db_query("INSERT INTO {l10n_server_translation_history} (tid, uid_action, type_action, time_action) VALUES (%d, %d, %d, %d)", $tid, $uid, $type, $time);
+  db_query("INSERT INTO {l10n_server_translation_history} (tid, uid_action, type_action, time_action, medium_action) VALUES (%d, %d, %d, %d, %d)", $tid, $uid_user, $type, $time, $medium);
 
   // Mark the existing or mock translation as having suggestions.
   l10n_community_update_string_status($langcode, $sid);
@@ -1030,7 +1044,8 @@ function l10n_community_add_suggestion($
 function l10n_community_decline_string($langcode, $sid, $tid, $uid) {
   $time = time();
   db_query("UPDATE {l10n_server_translation} SET is_active = 0, time_changed = %d WHERE tid = %d", $time, $tid);
-  db_query("INSERT INTO {l10n_server_translation_history} (tid, uid_action, type_action, time_action) VALUES (%d, %d, %d, %d)", $tid, $uid, L10N_SERVER_ACTION_DECLINE, $time);
+  // ATM we only support this through the web, so always save the web medium.
+  db_query("INSERT INTO {l10n_server_translation_history} (tid, uid_action, type_action, time_action, medium_action) VALUES (%d, %d, %d, %d, %d)", $tid, $uid, L10N_SERVER_ACTION_DECLINE, $time, L10N_SERVER_MEDIUM_WEB);
   l10n_community_update_string_status($langcode, $sid);
 }
 
@@ -1076,12 +1091,14 @@ function l10n_community_approve_string($
   // There should only ever be one string like this.
   if ($existing_tid = db_result(db_query("SELECT tid FROM {l10n_server_translation} WHERE sid = %d AND language = '%s' AND is_suggestion = 0 AND is_active = 1", $sid, $langcode))) {
     db_query("UPDATE {l10n_server_translation} SET is_suggestion = 1, time_changed = %d WHERE tid = %d", $time, $existing_tid);
-    db_query("INSERT INTO {l10n_server_translation_history} (tid, uid_action, type_action, time_action) VALUES (%d, %d, %d, %d)", $existing_tid, $user->uid, L10N_SERVER_ACTION_DEMOTE, $time);
+    // ATM we only support this through the web, so always save the web medium.
+    db_query("INSERT INTO {l10n_server_translation_history} (tid, uid_action, type_action, time_action, medium_action) VALUES (%d, %d, %d, %d, %d)", $existing_tid, $user->uid, L10N_SERVER_ACTION_DEMOTE, $time, L10N_SERVER_MEDIUM_WEB);
   }
 
   // Mark this exact suggestion as active translation, and set approval time.
   db_query("UPDATE {l10n_server_translation} SET time_changed = %d, is_suggestion = 0, is_active = 1 WHERE tid = %d;", $time, $tid);
-  db_query("INSERT INTO {l10n_server_translation_history} (tid, uid_action, type_action, time_action) VALUES (%d, %d, %d, %d)", $tid, $user->uid, L10N_SERVER_ACTION_APPROVE, $time);
+  // ATM we only support this through the web, so always save the web medium.
+  db_query("INSERT INTO {l10n_server_translation_history} (tid, uid_action, type_action, time_action, medium_action) VALUES (%d, %d, %d, %d, %d)", $tid, $user->uid, L10N_SERVER_ACTION_APPROVE, $time, L10N_SERVER_MEDIUM_WEB);
 
   l10n_community_update_string_status($langcode, $sid);
 }
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.28.2.1
diff -u -p -r1.1.2.7.2.31.2.28.2.1 translate.inc
--- l10n_community/translate.inc	22 Dec 2010 19:12:54 -0000	1.1.2.7.2.31.2.28.2.1
+++ l10n_community/translate.inc	9 Jan 2011 16:07:53 -0000
@@ -513,9 +513,12 @@ function _l10n_community_translate_trans
         );
       }
       if (isset($string->username)) {
-        $title = l10n_community_translate_byline($string);
+        $title = l10n_community_translate_byline($string->username, $string->uid_entered, $string->time_entered, -1, -1, FALSE);
+        
         $form['author'] = array(
           '#value' => $title,
+          '#prefix' => '<div class="l10n-byline"><a href="'. url('translate/translation-details/'. $string->tid) .'" class="l10n-more-link" title="'. t('Show full history for translation.') .'">',
+          '#suffix' => '</a><div class="l10n-more-info"></div></div>',
         );
       }
     }
@@ -526,13 +529,50 @@ function _l10n_community_translate_trans
 /**
  * Generates the byline containing meta information about a string.
  */
-function l10n_community_translate_byline($string) {
+function l10n_community_translate_byline($name, $uid, $time, $medium, $type, $link_user = TRUE) {
   $params = array(
-    '!author' => theme('username', (object)array('name' => $string->username, 'uid' => $string->uid_entered)),
-    '@date' => format_date($string->time_entered),
-    '@ago' => t('@time ago', array('@time' => format_interval(time() - $string->time_entered))),
+    '!author' => $uid ? ($link_user ? theme('username', (object)array('name' => $name, 'uid' => $uid)) : check_plain($name)) : t('uknown user'),
+    // Also skip handling time if uid was not specified (for decline entries
+    // in the update, which have time for ordering reasons, but no uid).
+    '@date' => $time && $uid ? format_date($time) : t('unknown time'),
+    '@ago' => $time ? t('@time ago', array('@time' => format_interval(time() - $time))) : t('no time record available'),
   );
-  return t('suggested by !author <span title="@ago">on @date</span>', $params);
+  switch ($type) {
+    case L10N_SERVER_ACTION_ADD:
+      switch ($medium) {
+        case L10N_SERVER_MEDIUM_IMPORT:
+          return t('imported by !author <span title="@ago">on @date</span>', $params);
+        case L10N_SERVER_MEDIUM_REMOTE:
+          return t('remotely submitted by !author <span title="@ago">on @date</span>', $params);
+        case L10N_SERVER_MEDIUM_WEB:
+          return t('suggested on the web by !author <span title="@ago">on @date</span>', $params);
+        case L10N_SERVER_MEDIUM_UNKNOWN:
+          return t('suggested by !author <span title="@ago">on @date</span> (source unknown)', $params);
+      }
+      return;
+    case L10N_SERVER_ACTION_READD:
+      switch ($medium) {
+        case L10N_SERVER_MEDIUM_IMPORT:
+          return t('re-imported by !author <span title="@ago">on @date</span>', $params);
+        case L10N_SERVER_MEDIUM_REMOTE:
+          return t('remotely re-submitted by !author <span title="@ago">on @date</span>', $params);
+        case L10N_SERVER_MEDIUM_WEB:
+          return t('re-suggested on the web by !author <span title="@ago">on @date</span>', $params);
+        // L10N_SERVER_MEDIUM_UNKNOWN does not apply, because we only have that
+        // for backwards compatibility and L10N_SERVER_ACTION_READD did not
+        // happen with data migrated (at least we did not know about it).
+      }
+      return;
+    case L10N_SERVER_ACTION_APPROVE:
+      return t('approved by !author <span title="@ago">on @date</span>', $params);
+    case L10N_SERVER_ACTION_DECLINE:
+      return t('declined by !author <span title="@ago">on @date</span>', $params);
+    case L10N_SERVER_ACTION_DEMOTE:
+      return t('demoted by !author <span title="@ago">on @date</span>', $params);
+    default:
+      // Default byline that work as a click-target to get more information.
+      return t('by !author <span title="@ago">on @date</span>', $params);
+  }
 }
 
 // = Translation form theming ==================================================
@@ -566,7 +606,7 @@ function theme_l10n_community_translate_
   $output .= '<label class="l10n-string'. ($element['filters_match']['#value'] ? ' filter-match' : ' filter-no-match') .'">'. drupal_render($element['source']['string']) .'</label>';
   $output .= theme('l10n_community_in_context', $element['#string']);
   $output .= '<div class="l10n-sid js-hide">'. l(t('ID: @id', array('@id' => $element['#string']->sid)), $_GET['q'], array('query' => array('sid' => $element['#string']->sid))) .'</div>';
-  $output .= '<div class="l10n-usage"><a href="'. url('translate/details/'. $element['#langcode'] .'/'. $element['#string']->sid) .'" class="l10n-more-link" title="'. t('Show list of projects and releases where this text is used.') .'">'. t('Show related projects') .'</a><div class="l10n-more-info"></div></div>';
+  $output .= '<div class="l10n-usage"><a href="'. url('translate/source-details/'. $element['#string']->sid) .'" class="l10n-more-link" title="'. t('Show list of projects and releases where this text is used.') .'">'. t('Show related projects') .'</a><div class="l10n-more-info"></div></div>';
   return $output;
 }
 
@@ -715,7 +755,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($sid, l10n_community_pack_string($options['value']), $langcode, $user->uid, L10N_SERVER_ACTION_ADD_WEB);
+          $tid = l10n_community_add_suggestion($sid, l10n_community_pack_string($options['value']), $langcode, $user->uid, $user->uid, L10N_SERVER_MEDIUM_WEB);
           if ($tid) {
             if ($string['active'] === 'new') {
               // This new string was selected to be approved, so remember $tid
@@ -927,24 +967,22 @@ function l10n_community_pack_string($str
 // = AJAX callbacks ============================================================
 
 /**
- * Return a HTML list of projects, releases and counts of where strings
- * appear in the managed projects.
+ * Generate list of projects and releases of where a string appears.
  *
- * We could provide much more information, but usability should also be kept
- * in mind. It is possible to investigate hidden information sources though,
- * like tooltips on the release titles presented.
+ * We could provide much more information (down to line numbers of files), but
+ * usability should also be kept in mind. It is possible to investigate hidden
+ * information sources though, like tooltips on the release titles presented.
  *
  * This callback is invoked from JavaScript and is used as an AHAH provider.
  *
- * @param $langcode
- *   Language code.
  * @param $sid
  *   Source string id.
  * @param $ahah
  *   Whether the request came through AHAH, in which case no page
- *   theming should be applied.
+ *   theming should be applied. Non-AHAH support is a fallback if JS is
+ *   turned off.
  */
-function l10n_community_string_details($langcode = NULL, $sid = 0, $ahah = 0) {
+function l10n_community_string_details($sid = 0, $ahah = 0) {
 
   $output = '';
   if ($ahah) {
@@ -989,3 +1027,61 @@ function l10n_community_string_details($
     return $output;
   }
 }
+
+/**
+ * Provide full history information about translation.
+ *
+ * @param $tid
+ *   Translation id.
+ * @param $ahah
+ *   Whether the request came through AHAH, in which case no page
+ *   theming should be applied. Non-AHAH support is a fallback if JS is
+ *   turned off.
+ */
+function l10n_community_translation_details($tid = 0, $ahah = 0) {
+
+  // Get data about this translation.
+  $string = db_fetch_object(db_query('SELECT * FROM {l10n_server_translation} WHERE tid = %d', $tid));
+
+  $output = '';
+  if ($ahah) {
+    // Prevent devel module information if doing AHAH.
+    $GLOBALS['devel_shutdown'] = FALSE;
+  }
+  else {
+    $unpacked = l10n_community_unpack_string($string->translation);
+    $output .= '<h3>'. t('Translation') .'</h3>'. theme('item_list', $unpacked) . '<h3>'. t('History') .'</h3>';
+  }
+
+  // List of project releases, where this string is used.
+  $result = db_query('SELECT h.*, u.name FROM {l10n_server_translation_history} h INNER JOIN {users} u ON h.uid_action = u.uid WHERE tid = %d ORDER BY time_action DESC, type_action DESC', $tid);
+
+  $uid_submitted = 0;
+  $history_list = array();
+  while ($item = db_fetch_object($result)) {
+    $history_list[] = l10n_community_translate_byline($item->name, $item->uid_action, $item->time_action, $item->medium_action, $item->type_action);
+    if ($item->type_action == L10N_SERVER_ACTION_ADD) {
+      // Remember the first uid who submitted this. This will get overwritten, and since we are going backwards in time,
+      // the last value kept will be the first uid who submitted this string. We need this in case it is different to
+      // the user we used for attribution, so we can display the difference to the translator/moderator.
+      $uid_submitted = $item->uid_action;
+    }
+  }
+  if (!empty($uid_submitted) && ($uid_submitted != $string->uid_entered)) {
+    // Turns out the user used for attribution is different to the user used for
+    // the string storage.
+    $account = user_load($string->uid_entered);
+    $author = theme('username', $account);
+    array_unshift($history_list, t('by !author', array('!author' => $author)));
+  }
+  if (count($history_list)) {
+    $output .= theme('item_list', $history_list);
+  }
+  if ($ahah) {
+    print $output;
+    exit;
+  }
+  else {
+    return $output;
+  }
+}
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.2.2.2.1
diff -u -p -r1.1.2.7.2.2.2.1 l10n_remote.module
--- l10n_remote/l10n_remote.module	22 Dec 2010 19:12:54 -0000	1.1.2.7.2.2.2.1
+++ l10n_remote/l10n_remote.module	9 Jan 2011 16:07:53 -0000
@@ -165,7 +165,7 @@ function l10n_remote_xmlrpc_string_submi
   }
 
   // Attempt to add this as a suggestion.
-  $tid = l10n_community_add_suggestion($sid, $translation, $langcode, $uid, L10N_SERVER_ACTION_ADD_REMOTE);
+  $tid = l10n_community_add_suggestion($sid, $translation, $langcode, $uid, $uid, L10N_SERVER_MEDIUM_REMOTE);
   
   if ($tid === FALSE) {
     //watchdog('l10n_community', 'Duplicate translation submitted.', NULL, WATCHDOG_WARNING);
Index: l10n_server/l10n_server.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_server/Attic/l10n_server.install,v
retrieving revision 1.1.2.3.2.1
diff -u -p -r1.1.2.3.2.1 l10n_server.install
--- l10n_server/l10n_server.install	22 Dec 2010 19:12:54 -0000	1.1.2.3.2.1
+++ l10n_server/l10n_server.install	9 Jan 2011 16:07:53 -0000
@@ -395,6 +395,13 @@ function l10n_server_schema() {
         'default' => 0,
         'disp-width' => '11'
       ),
+      'medium_action' => array(
+        'description' => 'Numeric identifier of the medium the action happened through.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'disp-width' => '11'
+      ),
     ),
     'indexes' => array(
       'tid' => array('tid'),
Index: l10n_server/l10n_server.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_server/Attic/l10n_server.module,v
retrieving revision 1.1.2.9.2.1
diff -u -p -r1.1.2.9.2.1 l10n_server.module
--- l10n_server/l10n_server.module	22 Dec 2010 19:12:54 -0000	1.1.2.9.2.1
+++ l10n_server/l10n_server.module	9 Jan 2011 16:07:53 -0000
@@ -7,39 +7,49 @@
  */
 
 /**
- * Action flag for backwards compatibility.
+ * Action flag for string addition.
  */
-define('L10N_SERVER_ACTION_ADD_UNKNOWN', 0);
+define('L10N_SERVER_ACTION_ADD', 1);
 
 /**
- * Action flag for web based addition.
+ * Action flag for approval.
  */
-define('L10N_SERVER_ACTION_ADD_WEB', 1);
+define('L10N_SERVER_ACTION_APPROVE', 2);
 
 /**
- * Action flag for web based import.
+ * Action flag for denial.
  */
-define('L10N_SERVER_ACTION_ADD_IMPORT', 2);
+define('L10N_SERVER_ACTION_DECLINE', 3);
 
 /**
- * Action flag for remote addition.
+ * Action flag for demotes.
  */
-define('L10N_SERVER_ACTION_ADD_REMOTE', 3);
+define('L10N_SERVER_ACTION_DEMOTE', 4);
 
 /**
- * Action flag for approval.
+ * Action flag for re-additions.
  */
-define('L10N_SERVER_ACTION_APPROVE', 4);
+define('L10N_SERVER_ACTION_READD', 5);
 
 /**
- * Action flag for denial.
+ * Action medium flag for unknown sources.
  */
-define('L10N_SERVER_ACTION_DECLINE', 3);
+define('L10N_SERVER_MEDIUM_UNKNOWN', 0);
 
 /**
- * Action flag for demotes.
+ * Action medium flag for web based actions.
+ */
+define('L10N_SERVER_MEDIUM_WEB', 1);
+
+/**
+ * Action medium flag for web based import.
+ */
+define('L10N_SERVER_MEDIUM_IMPORT', 2);
+
+/**
+ * Action medium flag for remote action.
  */
-define('L10N_SERVER_ACTION_DEMOTE', 5);
+define('L10N_SERVER_MEDIUM_REMOTE', 3);
 
 // = Core hooks ================================================================
 
Index: l10n_server/l10n_server.projects.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/l10n_server/l10n_server/Attic/l10n_server.projects.inc,v
retrieving revision 1.1.2.4
diff -u -p -r1.1.2.4 l10n_server.projects.inc
--- l10n_server/l10n_server.projects.inc	10 Nov 2010 11:14:44 -0000	1.1.2.4
+++ l10n_server/l10n_server.projects.inc	9 Jan 2011 16:07:53 -0000
@@ -297,7 +297,7 @@ function l10n_server_admin_projects_edit
   if (!isset($project_uri)) {
     $connector_list = l10n_server_get_add_enabled_project_connectors();
     // Provide sensible default for project if adding new one.
-    $project = (object) array('pid' => NULL, 'uri' => '', 'title' => '', 'home_link' => '', 'connector_module' => array_shift(array_keys($connector_list)), 'status' => 1);
+    $project = (object) array('pid' => NULL, 'uri' => '', 'title' => '', 'home_link' => '', 'connector_module' => array_shift(array_keys($connector_list)), 'status' => 1, 'weight' => 0);
   }
   else {
     $project = l10n_server_get_projects(array('uri' => $project_uri));
@@ -394,7 +394,7 @@ function l10n_server_admin_projects_edit
 function l10n_server_admin_projects_release_edit_form(&$form_state, $project_uri, $release_rid = NULL) {
   $project = l10n_server_get_projects(array('uri' => $project_uri));
   if (empty($release_rid)) {
-    $release = (object) array('pid' => $project->pid, 'title' => '', 'rid' => NULL, 'download_link' => '');
+    $release = (object) array('pid' => $project->pid, 'title' => '', 'rid' => NULL, 'download_link' => '', 'weight' => 0);
   }
   else {
     $releases = l10n_server_get_releases($project_uri, FALSE);
