Index: unique_field.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/unique_field/unique_field.module,v
retrieving revision 1.6.2.6
diff -u -p -r1.6.2.6 unique_field.module
--- unique_field.module	13 Nov 2009 01:30:15 -0000	1.6.2.6
+++ unique_field.module	28 Nov 2009 21:44:40 -0000
@@ -24,6 +24,12 @@ define('UNIQUE_FIELD_SCOPE_TYPE', 'type'
 define('UNIQUE_FIELD_SCOPE_LANGUAGE', 'language');
 define('UNIQUE_FIELD_SCOPE_ALL', 'all');
 
+// additional link definitions
+define('UNIQUE_FIELD_LINK_OPTIONS_WWW', 'www');
+define('UNIQUE_FIELD_LINK_OPTIONS_HTTP', 'http');
+define('UNIQUE_FIELD_LINK_OPTIONS_TRAILING_SLASH', 'trailing-slash');
+define('UNIQUE_FIELD_LINK_OPTIONS_TRIM', 'trim');
+
 // query comparison definitions
 define('UNIQUE_FIELD_COMP_EACH', 'each');
 define('UNIQUE_FIELD_COMP_ALL', 'all');
@@ -72,6 +78,7 @@ function unique_field_nodeapi(&$node, $o
 
     // get list of unique fields for node type
     $fields = variable_get('unique_field_fields_'. $node->type, array());
+    $fieldsLink = variable_get('unique_field_link_fields_'. $node->type, array());
 
     // check if there are unique fields for this node type
     if (count($fields)) {
@@ -154,6 +161,13 @@ function unique_field_nodeapi(&$node, $o
             foreach ($f as $index => $value) {
               if (is_numeric($index) && is_array($value)) {
                 $values[] = $value;
+                
+                // Add additional values for link fields
+                if(in_array($field, $fieldsLink)) {
+                  // Generate other similar URLs to additionally check for
+                  $additionalLinkValues = generate_additional_url_values($value, variable_get('unique_field_link_options_'. $node->type, array()));
+                  $values = array_merge($values, $additionalLinkValues);
+                }
               }
             }
           }
@@ -322,6 +336,7 @@ function unique_field_match_value($field
     $q .= "JOIN {". $qtbl ."} AS ". $qtbl ." USING (vid) ";
   }
   $q .= "WHERE ". $qwhere;
+  //print '<pre>';var_dump($q);print '</pre>';die();
   $res = db_query($q);
   $nids = array();
   while ($obj = db_fetch_object($res)) {
@@ -345,11 +360,16 @@ function unique_field_node_settings_form
   if (module_exists('locale') && variable_get('language_content_type_'. $ntype, 0)) {
     $fieldopts[UNIQUE_FIELD_FIELDS_LANGUAGE] = t('Language');
   }
+  $fieldLinkOptions = array();
   if (module_exists('content')) {
     $ctype = content_types($ntype);
     if (is_array($ctype) && is_array($ctype['fields'])) {
       foreach ($ctype['fields'] as $field => $info) {
         $fieldopts[$field] = $info['widget']['label'] .' ('. $field .')';
+        // Create list of fields that are of type 'link'
+        if(module_exists('link') && $info['type'] == 'link') {
+          $fieldLinkOptions[$field] = $info['widget']['label'] .' ('. $field .')';
+        }
       }
     }
   }
@@ -397,6 +417,29 @@ function unique_field_node_settings_form
     '#options' => array(UNIQUE_FIELD_SHOW_MATCHES => t('Enabled')),
     '#default_value' => variable_get('unique_field_show_matches_'. $ntype, array()),
   );
+  // Additional options for Link fields
+  if($fieldLinkOptions) {
+    $form['unique_field']['unique_field_link_fields'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Choose the fields to do additional URL checking'),
+      '#options' => $fieldLinkOptions,
+      '#default_value' => variable_get('unique_field_link_fields_'. $ntype, array()),
+      '#description' => t('Additional URL checks will go into making sure URL values are truely unique.  For example http://example.com/ will match www.example.com/.  Notice: This probably does not work with node scope and has not been tested with multiple URLs per field.'),
+    );
+    
+    $form['unique_field']['unique_field_link_options'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Choose the additional checks for URLs'),
+    '#options' => array(
+      UNIQUE_FIELD_LINK_OPTIONS_WWW => t('With or without www.'),
+      UNIQUE_FIELD_LINK_OPTIONS_HTTP => t('With or without http://'),
+      UNIQUE_FIELD_LINK_OPTIONS_TRAILING_SLASH => t('With or without trailing /'),
+      UNIQUE_FIELD_LINK_OPTIONS_TRIM => t('Trim whitespace before and after URL'),
+    ),
+    '#default_value' => variable_get('unique_field_link_options_'. $ntype, array()),
+    '#description' => t('Choose the additional checks to be performed on the Link fields selected above.'),
+    );
+  }
 
   // add validation function
   $form['#validate'][] = 'unique_field_node_settings_form_validate';
@@ -415,3 +458,190 @@ function unique_field_node_settings_form
     }
   }
 }
+
+/**
+ * Generates and returns addition url values that can be used by unique_field_match_value
+ * to check for unique truely unique URLs
+ * 
+ * @param array $value Should be in the form
+ * @param array List of options for do additional checks
+ * @return array Array of new $value arrays
+ */
+function generate_additional_url_values($value, $link_options) {
+
+  $url = array();
+  $additionalValues = array();
+  
+  $urlMatches = get_url_sections($value['url']);
+  
+  if($urlMatches = null) {
+    // URL isn't valid so return
+    // TODO: Some kind of message here?
+    return($additionalValues);
+  }
+  
+  /* define('UNIQUE_FIELD_LINK_OPTIONS_WWW', 'www');
+define('UNIQUE_FIELD_LINK_OPTIONS_HTTP', 'http');
+define('UNIQUE_FIELD_LINK_OPTIONS_TRAILING_SLASH', 'trailing-slash');
+define('UNIQUE_FIELD_LINK_OPTIONS_TRIM', 'trim'); */
+  
+  // Add additional URL checks here
+  if(in_array(UNIQUE_FIELD_LINK_OPTIONS_HTTP, $link_options)) { // HTTP check
+    $additionalValues = array_merge($additionalValues, create_http_values($value));
+  }
+  
+  // WWW check
+  if(in_array(UNIQUE_FIELD_LINK_OPTIONS_WWW, $link_options)) {
+    // Add check to additional URLs created
+    foreach($additionalValues as $additionalValue) {
+      $additionalValues = array_merge($additionalValues, create_www_values($additionalValue));
+    }
+    $additionalValues = array_merge($additionalValues, create_www_values($value));
+  }
+  
+  // Trailing slashes check
+  if(in_array(UNIQUE_FIELD_LINK_OPTIONS_TRAILING_SLASH, $link_options)) {
+    // Add check to additional URLs created
+    foreach($additionalValues as $additionalValue) {
+      $additionalValues = array_merge($additionalValues, create_trailing_slash_values($additionalValue));
+    }
+    $additionalValues = array_merge($additionalValues, create_trailing_slash_values($value));
+  }
+  
+  // Create trimmed URLs
+  if(in_array(UNIQUE_FIELD_LINK_OPTIONS_TRIM, $link_options)) {
+    // Add check to additional URLs created
+    foreach($additionalValues as $additionalValue) {
+      $additionalValues = array_merge($additionalValues, create_trim_values($additionalValue));
+    }
+    $additionalValues = array_merge($additionalValues, create_trim_values($value));
+  }
+  
+  return($additionalValues);
+}
+
+/**
+ * Create new URLs for HTTP checks from the $value param
+ */
+function create_http_values($value){
+  $urlSections = get_url_sections($value['url']);
+  $newValues = array();
+  
+  if(!$urlSections) {
+    // URL isn't valid so return
+    // TODO: Some kind of message here?
+    return($newValues);
+  }
+  
+  // Add HTTP checks
+  if(strcasecmp($urlSections['scheme'], 'http://') == 0) {
+    // Create url without http://  
+    $newValues[] = create_new_link_value($urlSections['all_except_scheme'], $value);
+  } else if(!$urlSections['scheme']) {
+    // Create url with http://  
+    $newValues[] = create_new_link_value('http://' . $urlSections['all_except_scheme'], $value);
+  }
+  
+  return($newValues);
+}
+
+/**
+ * Create new URLs for WWW checks from the $value param
+ */
+function create_www_values($value){
+  $urlSections = get_url_sections($value['url']);
+  $newValues = array();
+  
+  if(!$urlSections) {
+    // URL isn't valid so return
+    // TODO: Some kind of message here?
+    return($newValues);
+  }
+  
+  //break apart hostname
+  $hostname_sections = explode('.', $urlSections['all_except_scheme']);
+  
+  // Add WWW checks
+  if(strcasecmp($hostname_sections[0],'www') == 0) {
+    // Create url without www
+    $newURL = implode('.', array_slice($hostname_sections, 1));    
+
+    $newValues[] = create_new_link_value($urlSections['scheme']. $newURL, $value);
+  } else {
+    // Create url with www 
+    $newValues[] = create_new_link_value($urlSections['scheme'] . 'www.' . $urlSections['all_except_scheme'], $value);
+  }
+  
+  //print '<pre>';var_dump($newValues);print '</pre>';//die();
+  
+  return($newValues);
+}
+
+/**
+ * Create new URLs for trailing slash checks from the $value param
+ */
+function create_trailing_slash_values($value){
+  $newValues = array();
+  
+  if(preg_match('/\/(\s*)$/', $value['url'])) {
+    // Remove trailing slash    
+    $newValues[] = create_new_link_value(preg_replace('/\/(\s*)$/', '$1', $value['url']), $value);
+  } else {
+    // Add a trailing slash
+    if(preg_match('/(\s*)$/', $value['url'])) {
+      $newValues[] = create_new_link_value(preg_replace('/(\s*)$/', '/$1', $value['url']), $value);
+    } else {
+      $newValues[] = create_new_link_value($value['url'] . '/', $value);
+    }
+  }
+  
+  return($newValues);
+}
+
+/**
+ * Create new URLs for trimmed string checks from the $value param
+ */
+function create_trim_values($value) {
+  $newValues = array();
+  
+  $trimmedUrl = trim($value['url']);
+  
+  if($trimmedUrl != $value['url']) {
+    $newValues[] = create_new_link_value($trimmedUrl, $value);
+  }
+  
+  return($newValues);
+}
+
+/**
+ * Helper to get the URL sections
+ * @param string The url to break apart
+ * @return array The sections pulled out of the URL
+ */
+function get_url_sections($url){
+  // Match different sections of the URL  ( Copied and modified from http://www.php.net/manual/en/function.preg-match.php#93824 )
+  $regex = "(?P<scheme>(https?|ftp)\:\/\/)?"; // SCHEME
+  $regex .= "(?P<all_except_scheme>([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)?"; // User and Pass
+  $regex .= "(?P<host_ip>([a-z0-9-.]*)\.([a-z]{2,3}))"; // Host or IP
+  $regex .= "(\:[0-9]{2,5})?"; // Port
+  $regex .= "(\/([a-z0-9+\$_-]\.?)+)*\/?"; // Path
+  $regex .= "(\?[a-z+&\$_.-][a-z0-9;:@&%=+\/\$_.-]*)?"; // GET Query
+  $regex .= "(#[a-z_.-][a-z0-9+\$_.-]*)?)"; // Anchor
+  
+  if(!preg_match("/^$regex$/i", $url, $urlMatches)){
+    // URL isn't valid so return
+    // TODO: Some kind of message here?
+    return(null);
+  }
+  
+  return($urlMatches);
+}
+
+
+/**
+ * Helper to create new values to ultimately be used in unique_field_match_value
+ */
+function create_new_link_value($url, $original_value) {
+  $original_value['url'] = $url;
+  return($original_value);
+}
