? .DS_Store
? hook_00.patch
? hook_01.patch
? hook_02.patch
? profile.patch
? similar.patch
? includes/.DS_Store
Index: atr.api.php
===================================================================
RCS file: atr.api.php
diff -N atr.api.php
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ atr.api.php	2 Jun 2009 18:12:07 -0000
@@ -0,0 +1,61 @@
+<?php
+// $Id: atr.module,v 1.1.2.2 2009/05/25 21:03:57 xano Exp $
+
+/**
+ * @file
+ *   API documentation.
+ */
+
+/**
+ * Define review methods.
+ *
+ * @return
+ *  An associative array.
+ */
+function hook_atr_review_info() {
+  return array(
+    'similarity' => array( // The review method.
+      'all' => array( // Two-character language code as defined in
+      // _locale_get_predefined_list or 'all' to define the languages this
+      // callback can review.
+        '#callback' => 'atr_review_similar', // The name of the function.
+        '#module' => 'atr', // The module this function is from.
+        '#file' => 'includes/atr.review.inc', // The file within the module's
+        // directory the callback is located in.
+      ),
+    ),
+    'blacklist' => array( // The review method.
+      'en' => array( // Two-character language code as defined in
+      // _locale_get_predefined_list or 'all' to define the languages this
+      // callback can review.
+        '#callback' => 'atr_review_blacklist', // The name of the function.
+        '#module' => 'atr', // The module this function is from.
+        '#file' => 'includes/atr.review.inc', // The file within the module's
+        // directory the callback is located in.
+      ),
+    ),
+  );
+}
+
+/**
+ * Extend a $profile object when it's loaded.
+ *
+ * @param $profile
+ *   A profile object.
+ *
+ * @return
+ *  An associative array that can be merged into the $profile object.
+ */
+function hook_atr_profile_load($profile) {
+  $reviews = array();
+  if (variable_get('atr_profile_' . $profile->pid . '_similarity', FALSE)) {
+    $reviews[] = 'similarity';
+  }
+  if (variable_get('atr_profile_' . $profile->pid . '_blacklist', FALSE)) {
+    $reviews[] = 'blacklist';
+  }
+
+  return array(
+    'reviews' => $reviews,
+  );
+}
\ No newline at end of file
Index: atr.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/atr/Attic/atr.install,v
retrieving revision 1.1.2.5
diff -u -p -r1.1.2.5 atr.install
--- atr.install	30 May 2009 23:32:30 -0000	1.1.2.5
+++ atr.install	2 Jun 2009 18:12:08 -0000
@@ -1,5 +1,5 @@
 <?php
-// $Id: atr.install,v 1.1.2.5 2009/05/30 23:32:30 xano Exp $
+// $Id: atr.install,v 1.1.2.4 2009/05/25 21:03:57 xano Exp $
 
 /**
  * @file
@@ -24,6 +24,12 @@ function atr_schema() {
           'length' => 255,
           'not null' => TRUE,
         ),
+        'language' => array(
+          'description' => 'Which language this profile has been configured for.',
+          'type' => 'varchar',
+          'length' => 7,
+          'not null' => TRUE,
+        ),
       ),
       'primary key' => array('pid'),
     ),
Index: atr.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/atr/Attic/atr.module,v
retrieving revision 1.1.2.5
diff -u -p -r1.1.2.5 atr.module
--- atr.module	31 May 2009 01:21:04 -0000	1.1.2.5
+++ atr.module	2 Jun 2009 18:12:08 -0000
@@ -1,5 +1,5 @@
 <?php
-// $Id: atr.module,v 1.1.2.5 2009/05/31 01:21:04 xano Exp $
+// $Id: atr.module,v 1.1.2.2 2009/05/25 21:03:57 xano Exp $
 
 /**
  * @file
@@ -106,6 +106,50 @@ function atr_theme() {
         'locations' => array(),
       ),
     ),
+    'atr_profile' => array(
+      'arguments' => array(
+        'profile' => new stdclass(),
+      ),
+    ),
+  );
+}
+
+/**
+ * Implementation of hook_atr_review_info().
+ */
+function atr_atr_review_info() {
+  return array(
+    'similarity' => array(
+      'all' => array(
+        '#callback' => 'atr_review_similar',
+        '#module' => 'atr',
+        '#file' => 'includes/atr.review.inc',
+      ),
+    ),
+    'blacklist' => array(
+      'all' => array(
+        '#callback' => 'atr_review_blacklist',
+        '#module' => 'atr',
+        '#file' => 'includes/atr.review.inc',
+      ),
+    ),
+  );
+}
+
+/**
+ * Implementation of hook_atr_profile_load().
+ */
+function atr_atr_profile_load($profile) {
+  $reviews = array();
+  if (variable_get('atr_profile_' . $profile->pid . '_similarity', FALSE)) {
+    $reviews[] = 'similarity';
+  }
+  if (variable_get('atr_profile_' . $profile->pid . '_blacklist', FALSE)) {
+    $reviews[] = 'blacklist';
+  }
+
+  return array(
+    'reviews' => $reviews,
   );
 }
 
@@ -116,10 +160,23 @@ function atr_theme() {
  *   The profile's ID.
  *
  * @return
- * A profile object or FALSE if no profile matched the provided $pid.
+ *   A profile object or FALSE if no profile matched the provided $pid.
  */
 function atr_profile_load($pid) {
   if ($profile = db_fetch_object(db_query("SELECT * FROM {atr_profile} WHERE pid = %d", $pid))) {
+    $profile = (object) array_merge((array) $profile, module_invoke_all('atr_profile_load', $profile));
+    $review_info = module_invoke_all('atr_review_info');
+    foreach ($profile->reviews as $i => &$review) {
+      if (isset($review_info[$review][$profile->language])) {
+        $review = $review_info[$review][$profile->language];
+      }
+      elseif (isset($review_info[$review]['all'])) {
+        $review = $review_info[$review]['all'];
+      }
+      else {
+        unset($profile->reviews[$i]);
+      }
+    }
     return $profile;
   }
   return FALSE;
Index: includes/atr.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/atr/includes/Attic/atr.admin.inc,v
retrieving revision 1.1.2.8
diff -u -p -r1.1.2.8 atr.admin.inc
--- includes/atr.admin.inc	30 May 2009 23:32:30 -0000	1.1.2.8
+++ includes/atr.admin.inc	2 Jun 2009 18:12:08 -0000
@@ -1,5 +1,5 @@
 <?php
-// $Id: atr.admin.inc,v 1.1.2.8 2009/05/30 23:32:30 xano Exp $
+// $Id: atr.admin.inc,v 1.1.2.6 2009/05/25 22:22:10 xano Exp $
 
 /**
  * @file
@@ -20,12 +20,13 @@ function atr_form_review() {
   $result = db_query("SELECT * FROM {atr_profile} ORDER BY title ASC");
   $profiles = array();
   while ($profile = db_fetch_object($result)) {
-    $profiles[$profile->pid] = $profile->title;
+    $title = theme('atr_profile', $profile);
+    $profiles[$profile->pid] = $title;
   }
   $count = count($profiles);
   if ($count > 1) {
     $form['pid'] = array(
-      '#type' => 'select',
+      '#type' => 'radios',
       '#title' => t('Profile'),
       '#description' => t('Which <a href="@settings">settings profile</a> to use for this review.', array('@settings' => url('atr/settings'))),
       '#options' => $profiles,
@@ -34,7 +35,7 @@ function atr_form_review() {
   elseif ($count == 1) {
     $form['pid'] = array(
       '#type' => 'value',
-      '#options' => array_pop(array_flip($profiles)),
+      '#value' => array_pop(array_flip($profiles)),
     );
     $form['profile_label'] = array(
       '#type' => 'item',
@@ -44,7 +45,7 @@ function atr_form_review() {
     );
   }
   else {
-    drupal_set_message(t('You have yet no <a href="@settings">settings profiles</a> to use for this review.', array('@settings' => url('atr/settings/add'))), 'warning');
+    drupal_set_message(t('You have yet no <a href="@settings">settings profile</a> to use for this review.', array('@settings' => url('atr/settings/add'))), 'warning');
   }
   $form['start'] = array(
     '#type' => 'submit',
@@ -74,9 +75,8 @@ function atr_form_review_submit($form, &
   if ($tar_gz) {
     // The path points to a *.tar.gz file, so fetch and extract it first.
     atr_process_tar_gz($path);
-  }variable_set('atr_pid', $values['pid']);
-  atr_review($path, (int) $values['pid']);
-  
+  }
+  atr_review($path, atr_profile_load((int) $values['pid']));
   drupal_set_message(t('The texts have been reviewed.'));
 }
 
@@ -87,7 +87,7 @@ function atr_form_settings_list() {
   $result = db_query("SELECT * FROM {atr_profile} ORDER BY title ASC");
   $rows = array();
   while ($profile = db_fetch_object($result)) {
-    $rows[] = array($profile->title, l(t('edit'), 'atr/settings/edit/' . $profile->pid), l(t('delete'), 'atr/settings/delete/' . $profile->pid));
+    $rows[] = array(theme('atr_profile', $profile), l(t('edit'), 'atr/settings/edit/' . $profile->pid), l(t('delete'), 'atr/settings/delete/' . $profile->pid));
   }
 
   if ($rows) {
@@ -108,6 +108,9 @@ function atr_form_settings_list() {
  * Form builder; add or edit a settings profile.
  */
 function atr_form_settings(&$form_state, $profile = NULL) {
+  include_once './includes/locale.inc';
+  drupal_add_js(drupal_get_path('module', 'atr') . '/js/atr.js');
+
   $form['#redirect'] = 'atr/settings';
   $form['title'] = array(
     '#type' => 'textfield',
@@ -115,9 +118,29 @@ function atr_form_settings(&$form_state,
     '#default_value' => $profile ? $profile->title : NULL,
     '#required' => TRUE,
   );
+  $form['language'] = array(
+    '#type' => 'select',
+    '#title' => t('Text language'),
+    '#options' => atr_languages(),
+    '#default_value' => $profile ? $profile->language : 'all',
+  );
+
+  // Blacklist settings.
   $form['blacklist'] = array(
     '#type' => 'fieldset',
-    '#title' => t('Blacklist'),
+    '#title' => t('Blackist'),
+  );
+  $form['blacklist']['atr_blacklist'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable blacklist review'),
+    '#default_value' => $profile ? variable_get('atr_profile_' . $profile->pid . '_blacklist', FALSE) : FALSE,
+    '#attributes' => array(
+      'class' => 'atr_settings_toggle',
+    ),
+  );
+  $form['blacklist']['settings'] = array(
+    '#prefix' => '<div id="atr_blacklist" class="js-hide">',
+    '#suffix' => '</div>',
   );
   $keywords = NULL;
   if ($profile) {
@@ -126,33 +149,48 @@ function atr_form_settings(&$form_state,
       $keywords .= "\n" . $keyword;
     }
   }
-  $form['blacklist']['blacklist_keywords'] = array(
+  $form['blacklist']['settings']['blacklist_keywords'] = array(
     '#type' => 'textarea',
     '#title' => t('Keywords'),
-    '#description' => t('Any words or phrases that should not exist in the text.') . ' ' . t('Put every word or phrase on a new line.'),
+    '#description' => t('Any words or phrases that should not exist in the texts.') . ' ' . t('Put every word or phrase on a new line.'),
     '#default_value' => $keywords,
   );
+
+  // Similarity settings.
   $form['similarity'] = array(
     '#type' => 'fieldset',
     '#title' => t('Similarity'),
   );
+  $form['similarity']['atr_similarity'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable similarity review'),
+    '#default_value' => $profile ? variable_get('atr_profile_' . $profile->pid . '_similarity', FALSE) : FALSE,
+    '#attributes' => array(
+      'class' => 'atr_settings_toggle',
+    ),
+  );
+  $form['similarity']['settings'] = array(
+    '#prefix' => '<div id="atr_similarity" class="js-hide">',
+    '#suffix' => '</div>',
+  );
   $percentages = range(0, 100);
   foreach ($percentages as &$percentage) {
     $percentage .= '%';
   }
-  $form['similarity']['similarity_threshold'] = array(
+  $form['similarity']['settings']['similarity_threshold'] = array(
     '#type' => 'select',
     '#title' => t('Threshold'),
     '#options' => $percentages,
     '#description' => t('How similar two strings should be to be considered entirely similar.'),
     '#default_value' => $profile ? variable_get('atr_profile_' . $profile->pid . '_similarity_threshold', 90) : 90,
   );
-  $form['similarity']['similarity_ignore'] = array(
+  $form['similarity']['settings']['similarity_ignore'] = array(
     '#type' => 'textarea',
     '#title' => t('Ignore list'),
     '#description' => t('Any words or phrases that should be ignored during the similarity check.') . ' ' . t('Put every word or phrase on a new line.'),
     '#default_value' => $profile ? implode("\n", variable_get('atr_profile_' . $profile->pid . '_similarity_ignore', array())) : NULL,
   );
+
   $form['save'] = array(
     '#type' => 'submit',
     '#value' => t('Save profile'),
@@ -174,25 +212,32 @@ function atr_form_settings_submit($form,
   // Build and save the $profile object.
   if ($profile = $values['profile']) {
     $profile->title = $values['title'];
+    $profile->language = $values['language'];
     $update = 'pid';
     drupal_set_message(t('%profile has been updated.', array('%profile' => $profile->title)));
   }
   else {    
     $profile = (object) array(
       'title' => $values['title'],
+      'language' => $values['language'],
     );
     $update = array();
     drupal_set_message(t('%profile has been added.', array('%profile' => $profile->title)));
   }
   drupal_write_record('atr_profile', $profile, $update);
+  // Put the saved profile back in $form_state, so it can be used by other
+  // submit handlers.
+  $form_state['values']['profile'] = $profile;
 
   // Save all preferences.
+  variable_set('atr_profile_' . $profile->pid . '_similarity', $values['atr_similarity']);
   variable_set('atr_profile_' . $profile->pid . '_similarity_threshold', $values['similarity_threshold']);
   variable_set('atr_profile_' . $profile->pid . '_similarity_ignore', preg_split("#[\r\n]+#", $values['similarity_ignore']));
+  variable_set('atr_profile_' . $profile->pid . '_blacklist', $values['atr_blacklist']);
   // Blacklisted keywords are saved in a table of their own. Delete possible old
   // keywords and save the new ones.
-  if ($pid) {
-    db_query("DELETE FROM {atr_blacklist} WHERE pid = %d", $pid);
+  if ($profile->pid) {
+    db_query("DELETE FROM {atr_blacklist} WHERE pid = %d", $profile->pid);
   }
   $keywords = preg_split("#[\r\n]+#", $values['blacklist_keywords']);
   $values = array();
@@ -226,9 +271,68 @@ function atr_form_settings_delete_submit
   db_query("DELETE FROM {atr_profile} WHERE pid = %d", $profile->pid);
 
   // Delete all preferences.
+  variable_del('atr_profile_' . $profile->pid . '_similarity');
   variable_del('atr_profile_' . $profile->pid . '_similarity_threshold');
   variable_del('atr_profile_' . $profile->pid . '_similarity_ignore');
+  variable_del('atr_profile_' . $profile->pid . '_blacklist');
   db_query("DELETE FROM {atr_blacklist} WHERE pid = %d", $profile->pid);
 
   drupal_set_message(t('%profile has been deleted.', array('%profile' => $profile->title)));
+}
+
+/**
+ * Return a list languages together with "Any language" as the first item.
+ *
+ * @return
+ *   Array where the keys are languages codes and values are language names.
+ */
+function atr_languages() {
+  static $languages = NULL;
+  include_once './includes/locale.inc';
+
+  if(!$languages) {
+    $languages = _locale_get_predefined_list();
+    foreach ($languages as $code => $names) {
+      // Include native name in output, if possible
+      $name = t($names[0]);
+      if (count($names) > 1) {
+        $languages[$code] = ($name == $names[1]) ? $name : $name . ' (' . $names[1] . ')';
+      }
+      else {
+        $languages[$code] = $name;
+      }
+    }
+    asort($languages);
+    $languages = array_merge(array('all' => t('Any language')), $languages);
+  }
+
+  return $languages;
+}
+
+/**
+ * Return a language name based on a language code.
+ *
+ * @param $code
+ *   A language code as defined in _locale_get_predefined_list().
+ *
+ * @return
+ *   The language name.
+ */
+function atr_language($code) {
+  $languages = atr_languages();
+
+  return $languages[$code];
+}
+
+/**
+ * Theme a settings profile title.
+ *
+ * @param $profile
+ *   The profile to theme the title from.
+ *
+ * @return
+ *   String.
+ */
+function theme_atr_profile($profile) {
+  return $profile->title . '<div class="description">' . t('Text language') . ': ' . atr_language($profile->language) . '</div>';
 }
\ No newline at end of file
Index: includes/atr.review.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/atr/includes/Attic/atr.review.inc,v
retrieving revision 1.1.2.13
diff -u -p -r1.1.2.13 atr.review.inc
--- includes/atr.review.inc	30 May 2009 23:32:30 -0000	1.1.2.13
+++ includes/atr.review.inc	2 Jun 2009 18:12:08 -0000
@@ -1,9 +1,9 @@
 <?php
-// $Id: atr.review.inc,v 1.1.2.13 2009/05/30 23:32:30 xano Exp $
+// $Id: atr.review.inc,v 1.1.2.11 2009/05/29 10:47:54 xano Exp $
 
 /**
  * @file
- *   Review previously extracted strings.
+ *   Extract and review strings.
  */
 
 /**
@@ -33,29 +33,50 @@ function atr_process_tar_gz(&$path) {
  *
  * @param $path
  *   The path to the directory with the files that need to be reviewed.
+ * @param $profile
+ *   The settings profile used for this review.
  */
-function atr_review($path, $pid) {
-  batch_set(array(
-    'title' => t('Reviewing texts'),
-    'operations' => array(
-      array(
-        'atr_extract',
-        array($path),
-      ),
-      array(
-        'atr_review_blacklist',
-        array($pid),
-      ),
+function atr_review($path, $profile) {
+  $operations = array(
+    array(
+      'atr_extract',
+      array($path),
+    ),
+  );
+  foreach($profile->reviews as $review) {
+    $operations[] = array(
+      'atr_review_execute_method',
       array(
-        'atr_review_similar',
-        array($pid),
+        $review,
+        $profile,
       ),
-    ),
+    );
+  }
+  batch_set(array(
+    'title' => t('Reviewing texts'),
+    'operations' => $operations,
     'file' => drupal_get_path('module', 'atr') .'/includes/atr.review.inc',
   ));
 }
 
 /**
+ * Execute a review method.
+ *
+ * @param $review
+ *   An associative array containing callback information as defined in
+ *   hook_atr_review_info().
+ * @param $profile
+ *   The settings profile used for this review.
+ * @param $context
+ *   A batch operation context.
+ */
+function atr_review_execute_method($review, $profile, &$context) {
+  require_once('./'. drupal_get_path('module', $review['#module']) . '/' . $review['#file']);
+
+  call_user_func_array($review['#callback'], array($profile, $context));
+}
+
+/**
  * Extract and store all strings found in files in a certain directory.
  *
  * @param $path
@@ -75,7 +96,7 @@ function atr_extract($path, &$context) {
   potx_status('set', POTX_STATUS_SILENT);
   $files = _potx_explore_dir($path);
   foreach ($files as $file) {
-    _potx_process_file($file, 0, '_atr_save_string', '_atr_save_version', POTX_API_7);
+    _potx_process_file($file, 0, '_atr_save_string', '_atr_save_version', POTX_API_6);
   }
 
   $path_length = strlen($path);
@@ -146,9 +167,9 @@ function _atr_save_version($version = NU
  * @param $context
  *   A batch operation context.
  */
-function atr_review_blacklist($pid, &$context) {
+function atr_review_blacklist($profile, &$context) {
   $context['message'] = t('Checking for blacklisted words or phrases.');
-  db_query("INSERT INTO {atr_string_blacklist} SELECT s.sid, b.keyword FROM {atr_string} s, {atr_blacklist} b WHERE s.string_lower LIKE CONCAT('%%', b.keyword, '%%') AND b.pid = %d", $pid);
+  db_query("INSERT INTO {atr_string_blacklist} SELECT s.sid, b.keyword FROM {atr_string} s, {atr_blacklist} b WHERE s.string_lower LIKE CONCAT('%%', b.keyword, '%%') AND b.pid = %d", $profile->pid);
 }
 
 /**
@@ -157,7 +178,7 @@ function atr_review_blacklist($pid, &$co
  * @param $context
  *   A batch operation context.
  */
-function atr_review_similar($pid, &$context) {
+function atr_review_similar($profile, &$context) {
   // Get and set values necessary for this operation.
   if (!isset($context['sandbox']['from'])) {
     $context['sandbox']['from'] = 0;
@@ -199,7 +220,7 @@ function atr_review_similar($pid, &$cont
   $threshold = (int) variable_get('atr_similarity_threshold', 90);
   for ($sid_a = $from; $sid_a < $max; $sid_a++) {
     for ($sid_b = $sid_a + 1; $sid_b < $total; $sid_b++) {
-      $similarity = atr_similar($strings[$sid_a], $strings[$sid_b], $pid);
+      $similarity = atr_similar($strings[$sid_a], $strings[$sid_b], $profile->pid);
       if ($similarity > $threshold) {
         $values[] = $sid_a;
         $values[] = $sid_b;
@@ -233,7 +254,7 @@ function atr_review_similar($pid, &$cont
  * @return
  *   The strings' similarity in percents.
  */
-function atr_similar($string_a, $string_b) {
+function atr_similar($string_a, $string_b, $pid) {
   $words_a = atr_similar_preprocess($string_a, $pid);
   $words_b = atr_similar_preprocess($string_b, $pid);
   $word_count = count(array_unique(array_merge($words_a, $words_b)));
Index: js/atr.js
===================================================================
RCS file: js/atr.js
diff -N js/atr.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ js/atr.js	2 Jun 2009 18:12:08 -0000
@@ -0,0 +1,11 @@
+// $Id$
+
+Drupal.behaviors.atr = function (context) {
+  $('.atr_settings_toggle').each(function(){
+    if(this.checked) {
+      $('#' + this.name).show();
+    }
+  }).click(function(){
+    $('#' + this.name).toggle();
+  });
+};
