diff --git a/README.txt b/README.txt
index 18fcaf3..c06a382 100644
--- a/README.txt
+++ b/README.txt
@@ -32,7 +32,7 @@ visit update.php to update existing database tables.
 
 Links
 -----
-* Related links configuration: admin/settings/relatedlinks
+* Related links configuration: admin/config/search/relatedlinks
 * Block configuration: admin/build/block
 * Project URL: http://drupal.org/project/relatedlinks
 
diff --git a/relatedlinks.admin.inc b/relatedlinks.admin.inc
index a526098..ba8cd2a 100644
--- a/relatedlinks.admin.inc
+++ b/relatedlinks.admin.inc
@@ -28,15 +28,18 @@ be activated on the <a href="!block">blocks page</a>.</li>
         <li>Weight: Display order priority for links. Heavier types sink to the
 bottom.</li>
       </ul>
-    </p>', array('!block' => url('admin/build/block'))),
+    </p>', array('!block' => url('admin/structure/block'))),
     '#collapsible' => TRUE,
     '#collapsed' => FALSE
   );
 
   foreach ($types as $type_name => $type_values) {
+
     $form['relatedlinks_types'][$type_name] = array('#weight' => $type_values['weight']);
 
-    $form['relatedlinks_types'][$type_name]['info'] = array('#value' => $type_values['name']);
+    $form['relatedlinks_types'][$type_name]['info'] = array(
+      '#markup' => $type_values['name']
+    );
     // The name key is used during block configuration.
     $form['relatedlinks_types'][$type_name]['name'] = array(
       '#type' => 'value',
@@ -105,7 +108,7 @@ function _relatedlinks_settings_form_submit($form, &$form_state) {
   cache_clear_all();
 
   drupal_set_message(t('Configuration settings saved. If necessary, relatedlinks
-blocks can be configured via the <a href="!block-page">block configuration</a> page.', array('!block-page' => url('admin/build/block'))));
+blocks can be configured via the <a href="!block-page">block configuration</a> page.', array('!block-page' => url('admin/structure/block'))));
 }
 
 /**
@@ -117,7 +120,7 @@ blocks can be configured via the <a href="!block-page">block configuration</a> p
 function _relatedlinks_discovered_settings_form() {
   if (!module_exists('taxonomy') && !module_exists('search')) {
     drupal_set_message(t('The taxonomy and/or the search module are prerequisites
-for discovered links. Please use the <a href="!modules">modules page</a> to enable them.', array('!modules' => url('admin/build/modules'))), 'error');
+for discovered links. Please use the <a href="!modules">modules page</a> to enable them.', array('!modules' => url('admin/modules'))), 'error');
     drupal_goto('admin/settings/relatedlinks');
   }
   $form['relatedlinks_discovered'] = array('#tree' => TRUE);
@@ -276,20 +279,33 @@ function _relatedlinks_discovered_settings_form_submit($form, &$form_state) {
  *   Get rid of the inline styles.
  */
 function theme_relatedlinks_types_table($form) {
-  $types = element_children($form);
+
+  $types_form = $form['relatedlinks_types'];
+
+  $types = element_children($types_form);
+
   foreach ($types as $type) {
+
     $rows[] = array(
-      drupal_render($form[$type]['info']),
-      drupal_render($form[$type]['enabled']),
-      drupal_render($form[$type]['block']),
-      drupal_render($form[$type]['title']),
-      drupal_render($form[$type]['max']),
-      drupal_render($form[$type]['weight'])
+      drupal_render($types_form[$type]['info']),
+      drupal_render($types_form[$type]['enabled']),
+      drupal_render($types_form[$type]['block']),
+      drupal_render($types_form[$type]['title']),
+      drupal_render($types_form[$type]['max']),
+      drupal_render($types_form[$type]['weight'])
     );
   }
+
   $header = array(t('Link type'), t('Enabled'), t('Block'), t('Title'), t('Max'), t('Weight'));
 
-  $output = theme('table', $header, $rows, array('style' => 'width: 100%'));
+  $output = theme('table', array(
+    'header' => $header,
+    'rows' => $rows,
+    'attributes' => array(
+      'id' => 'relatedlinks_types_form',
+      'style' => 'width: 100%',
+    ),
+  ));
 
   return $output;
 }
diff --git a/relatedlinks.info b/relatedlinks.info
index 7e8df5a..18a48c2 100644
--- a/relatedlinks.info
+++ b/relatedlinks.info
@@ -1,3 +1,9 @@
 name = Related Links
 description = Displays links related to content.
-core = 6.x
+core = "7.x"
+
+configure = admin/config/relatedlinks/configure
+tags[] = filters/editors, search, taxonomy, utility
+version = "7.x-2.x-dev"
+project = "relatedlinks"
+
diff --git a/relatedlinks.install b/relatedlinks.install
index 9665b17..e3664af 100644
--- a/relatedlinks.install
+++ b/relatedlinks.install
@@ -10,7 +10,7 @@
  */
 function relatedlinks_install() {
   if (!module_exists('taxonomy') && !module_exists('search')) {
-    drupal_set_message(t('The discovered links feature of the relatedlinks module requires one of the following modules:
+    drupal_set_message(st('The discovered links feature of the relatedlinks module requires one of the following modules:
 <ul>
   <li>Taxonomy</li>
   <li>Search</li>
@@ -18,8 +18,6 @@ function relatedlinks_install() {
     return;
   }
 
-  drupal_install_schema('relatedlinks');
-
   $t = get_t();
   drupal_set_message($t('Relatedlinks module: Installation script complete.'));
 }
@@ -128,110 +126,7 @@ function relatedlinks_uninstall() {
   variable_del('relatedlinks_node_types');
   variable_del('relatedlinks_discovered');
 
-  drupal_uninstall_schema('relatedlinks');
-
   $t = get_t();
   drupal_set_message($t('Relatedlinks module: Uninstallation script complete.'));
 }
 
-/**
- * Convert existing variables from strings to internal constants used in the
- * module.
- */
-function relatedlinks_update_2() {
-  define('RELATEDLINKS_PARSED', 1);
-  define('RELATEDLINKS_MANUAL', 2);
-  // RELATEDLINKS_TAXONOMY is now named RELATEDLINKS_DISCOVERED.
-  define('RELATEDLINKS_TAXONOMY', 3);
-
-  $types = variable_get('relatedlinks_types', array());
-  $types_converted = array();
-  $i = 1;
-  foreach ($types as $type) {
-    $name = $type == 'taxonomy'? 'Discovered links' : ucfirst($type) .' links';
-    $types_converted[constant('RELATEDLINKS_'. strtoupper($type))] = array(
-      'name' => ucfirst($type) .' links',
-      'enabled' => TRUE,
-      'block' => TRUE,
-      'title' => ucfirst($type) .' links',
-      'max' => 5,
-      'weight' => $i++
-    );
-  }
-
-  variable_set('relatedlinks_types', $types_converted);
-
-  return array();
-}
-
-/**
- * Convert table to hold links as url, title.
- */
-function relatedlinks_update_3() {
-  $ret = array();
-
-  switch ($GLOBALS['db_type']) {
-    case 'mysqli':
-    case 'mysql':
-      $ret[] = update_sql("ALTER TABLE {relatedlinks} ADD lid int(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST");
-      $ret[] = update_sql("ALTER TABLE {relatedlinks} CHANGE link url varchar(255) NOT NULL default ''");
-      $ret[] = update_sql("ALTER TABLE {relatedlinks} ADD title varchar(255) DEFAULT NULL AFTER url");
-      // nid and type are UNSIGNED fields.
-      $ret[] = update_sql("ALTER TABLE {relatedlinks} CHANGE nid nid int(11) UNSIGNED NOT NULL DEFAULT 0");
-      $ret[] = update_sql("ALTER TABLE {relatedlinks} CHANGE type type tinyint(4) UNSIGNED NOT NULL DEFAULT 1");
-      $ret[] = update_sql("ALTER TABLE {relatedlinks} ADD INDEX nid_type (nid, type)");
-      break;
-    case 'pgsql':
-      // Needs testing. Field ordering needs to be added.
-      db_add_column($ret, 'relatedlinks', 'lid', 'serial');
-      db_change_column($ret, 'relatedlinks', 'link', 'url', 'varchar(255)', array('default' => "''", 'not null' => TRUE));
-      db_add_column($ret, 'relatedlinks', 'title', 'varchar(255)', array('default' => "NULL"));
-      $ret[] = update_sql("CREATE INDEX {relatedlinks}_nid_type_idx ON {relatedlinks}(nid, type)");
-      break;
-  }
-
-  $result = db_query("SELECT * FROM {relatedlinks}");
-  while ($link = db_fetch_array($result)) {
-    preg_match("!<\s*a\s*href\s*=\s*(?:\"([^\">]+)\"[^>]*|([^\" >]+?)[^>]*)>(.*)\s*<\s*/\s*a\s*>!Uis", $link['url'], $matches);
-    if (count($matches[1])) {
-      // Using db_query instead of update_sql to ease validation.
-      db_query("UPDATE {relatedlinks} SET url = '%s', title = '%s' WHERE lid = %d", $matches[1], trim($matches[3]), $link['lid']);
-    }
-  }
-
-  return $ret;
-}
-
-/**
- * Add relatedlinks_tracker table and initialise with existing relatedlink nids.
- */
-function relatedlinks_update_4() {
-  $ret = array();
-
-  switch ($GLOBALS['db_type']) {
-    case 'mysqli':
-    case 'mysql':
-      $ret[] = update_sql("CREATE TABLE {relatedlinks_tracker} (
-        nid int(11) UNSIGNED NOT NULL,
-        keywords varchar(255) NULL,
-        updated int(11) NOT NULL default 0,
-        PRIMARY KEY (nid),
-        KEY (updated))
-        COMMENT = 'Table keeping track of discovered links.'
-        /*!40100 default CHARACTER SET utf8 */"
-      );
-      break;
-    case 'pgsql':
-      $ret[] = update_sql("CREATE TABLE {relatedlinks_tracker} (
-        nid integer NOT NULL default 0,
-        keywords varchar(255) NULL,
-        updated integer NOT NULL default 0
-        PRIMARY KEY (nid))"
-      );
-      $ret[] = update_sql("CREATE INDEX {relatedlinks_tracker}_updated_idx ON {relatedlinks_tracker}(updated)");
-  }
-
-  $ret[] = update_sql("INSERT INTO {relatedlinks_tracker} (nid) SELECT DISTINCT(nid) FROM {relatedlinks}");
-
-  return $ret;
-}
diff --git a/relatedlinks.js b/relatedlinks.js
index da66179..309c3be 100644
--- a/relatedlinks.js
+++ b/relatedlinks.js
@@ -1,14 +1,18 @@
 
-Drupal.behaviors.relatedlinks = function() {
+(function ($) {
+
+Drupal.behaviors.relatedlinks = {
+  attach: function() {
+
   var autocomplete_path = Drupal.settings.relatedlinks_autocomplete[0];
   // Strip relatedlinks/autocomplete from the path.
   var path_prefix = autocomplete_path.substring(0, autocomplete_path.length - 25);
 
   // Trigger Drupal's autocomplete.
-  Drupal.behaviors.autocomplete();
+  Drupal.behaviors.autocomplete;
 
   var ta = $('#edit-relatedlinks-fieldset-relatedlinks');
-  $(ta).parents('div.form-item').css({'display' : 'none'});
+  $(ta).parents('div.form-textarea-wrapper').css({'display' : 'none'});
   var top = $(ta).parents('fieldset');
 
   // Parse textarea for links and store them in a global array.
@@ -200,10 +204,10 @@ Drupal.behaviors.relatedlinks = function() {
    */
   Drupal.jsAC.prototype.select = function (node) {
     if (this.input.id != 'field-relatedlinks-title') {
-      this.input.value = node.autocompleteValue;
+      this.input.value = $(node).data('autocompleteValue');
     }
     else {
-      $('#field-relatedlinks-url').val('node/' + node.autocompleteValue);
+      $('#field-relatedlinks-url').val('node/' + $(node).data('autocompleteValue'));
       // Title field is set in .hidePopup().
     }
   }
@@ -215,8 +219,8 @@ Drupal.behaviors.relatedlinks = function() {
     // Select item if the right key or mousebutton was pressed
     if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) {
       if (this.input.id == 'field-relatedlinks-title') {
-        var title = this.db.cache[this.input.value][this.selected.autocompleteValue];
-        $('#field-relatedlinks-url').val('node/' + this.selected.autocompleteValue);
+        var title = this.db.cache[this.input.value][$(this.selected).data('autocompleteValue')];
+        $('#field-relatedlinks-url').val('node/' + $(this.selected).data('autocompleteValue'));
         // Replace with title instead of nid.
         this.selected.autocompleteValue = title;
       }
@@ -232,3 +236,5 @@ Drupal.behaviors.relatedlinks = function() {
     this.selected = false;
   }
 }
+};
+}(jQuery));
\ No newline at end of file
diff --git a/relatedlinks.module b/relatedlinks.module
index 891aa90..97497be 100644
--- a/relatedlinks.module
+++ b/relatedlinks.module
@@ -45,7 +45,7 @@ of relatedlinks or creating blocks for a combination of link types.</p>
 Links:
 <ul>
   <li>Related links configuration: admin/settings/relatedlinks</li>
-  <li>Block configuration: admin/build/block</li>
+  <li>Block configuration: admin/structure/block</li>
   <li>Project URL: http://drupal.org/project/relatedlinks</li>
 </ul>');
   }
@@ -57,7 +57,7 @@ Links:
 function relatedlinks_menu() {
   $items = array();
 
-  $items['admin/settings/relatedlinks'] = array(
+  $items['admin/config/search/relatedlinks'] = array(
     'title' => 'Related links',
     'description' => 'Configure related links settings.',
     'page callback' => 'drupal_get_form',
@@ -65,7 +65,7 @@ function relatedlinks_menu() {
     'access arguments' => array('administer related links'),
     'file' => 'relatedlinks.admin.inc'
   );
-  $items['admin/settings/relatedlinks/configure'] = array(
+  $items['admin/config/search/relatedlinks/configure'] = array(
     'title' => 'Settings',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('_relatedlinks_settings_form'),
@@ -73,7 +73,7 @@ function relatedlinks_menu() {
     'type' => MENU_DEFAULT_LOCAL_TASK,
     'file' => 'relatedlinks.admin.inc'
   );
-  $items['admin/settings/relatedlinks/discovered'] = array(
+  $items['admin/config/search/relatedlinks/discovered'] = array(
     'title' => 'Discovered links',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('_relatedlinks_discovered_settings_form'),
@@ -92,232 +92,298 @@ function relatedlinks_menu() {
 }
 
 /**
- * Implementation of hook_perm().
+ * Implementation of hook_permission().
  */
-function relatedlinks_perm() {
-  return array('administer related links', 'add related links');
+function relatedlinks_permission() {
+  return array(
+    'administer related links' => array(
+      'title' => t('Administer related links'),
+    ),
+    'add related links' => array(
+      'title' => t('Add related links'),
+    ),
+  );
 }
 
 /**
  * Implementation of hook_theme().
  */
 function relatedlinks_theme() {
+
   return array(
-    'relatedlinks_types_table' => array('form' => NULL),
-    'relatedlinks' => array('arguments' => array('links' => array()))
+    'relatedlinks_types_table' => array(
+      'render element' => 'relatedlinks_types',
+      'form' => NULL
+    ),
+    'relatedlinks' => array(
+      'variables' => array(
+        'links' => array(),
+      ),
+    ),
   );
 }
 
 /**
- * Implementation of hook_block().
- * Provide blocks to list the related links.
- *
- * @todo Add caching support.
+ * Implementation of hook_block_info()
  */
-function relatedlinks_block($op = 'list', $delta = 0) {
-  switch ($op) {
-    case 'list':
-      $blocks = array();
+function relatedlinks_block_info() {
+  $blocks = array();
+
+  $link_types = variable_get('relatedlinks_types', _relatedlinks_get_type_defaults());
+
+  $top = TRUE;
+  foreach ($link_types as $type => $config) {
+    if ($config['enabled'] && ($config['block'] || $top)) {
+      $top = FALSE;
+      $blocks[$type]['info'] = t('Related links:') . ' ' . check_plain($config['name']);
+    }
+  }
+
+  return $blocks;
+}
 
+/**
+ * Implementation of hook_block_view()
+ */
+function relatedlinks_block_view($delta = '') {
+  // This is only valid if we're looking at a node now.
+  if (is_numeric($delta)) {
+    $block = array();
+    $node = node_load(arg(1));
+    // Only display if node type is associated with this module.
+    if ($node && in_array($node->type, variable_get('relatedlinks_node_types', _relatedlinks_node_get_types(TRUE)))) {
       $link_types = variable_get('relatedlinks_types', _relatedlinks_get_type_defaults());
-      $top = TRUE;
+      $append = FALSE;
+      $links = array();
       foreach ($link_types as $type => $config) {
-        if ($config['enabled'] && ($config['block'] || $top)) {
-          $top = FALSE;
-          $blocks[$type]['info'] = t('Related links: ') . check_plain($config['name']);
-        }
-      }
+        // Only worry about enabled types.
+        if ($config['enabled']) {
+          if ($type == RELATEDLINKS_DISCOVERED && (($delta == $type) || ($append && !$config['block'])) &&
+              !isset($node->relatedlinks[RELATEDLINKS_DISCOVERED])) {
+            // Refresh discovered links. This is being done here rather than
+            // 'hook_node_load' due to weight conflicts with the taxonomy
+            // module.
+            $node->relatedlinks[RELATEDLINKS_DISCOVERED] = _relatedlinks_update_discovered_links($node);
+          }
 
-      return $blocks;
-    case 'view':
-      // This is only valid if we're looking at a node now.
-      if (arg(0) == 'node' && is_numeric(arg(1))) {
-        $block = array();
-        $node = node_load(arg(1));
-        // Only display if node type is associated with this module.
-        if ($node && in_array($node->type, variable_get('relatedlinks_node_types', _relatedlinks_node_get_types(TRUE)))) {
-          $link_types = variable_get('relatedlinks_types', _relatedlinks_get_type_defaults());
-          $append = FALSE;
-          $links = array();
-          foreach ($link_types as $type => $config) {
-            // Only worry about enabled types.
-            if ($config['enabled']) {
-              if ($type == RELATEDLINKS_DISCOVERED && (($delta == $type) || ($append && !$config['block'])) &&
-                  !isset($node->relatedlinks[RELATEDLINKS_DISCOVERED])) {
-                // Refresh discovered links. This is being done here rather than
-                // 'hook_nodeapi load' due to weight conflicts with the taxonomy
-                // module.
-                $node->relatedlinks[RELATEDLINKS_DISCOVERED] = _relatedlinks_update_discovered_links($node);
-              }
-
-              // Current block.
-              if ($delta == $type) {
-                $block['subject'] = check_plain($config['title']);
-                $links = isset($node->relatedlinks[$type]) ? array_merge($links, $node->relatedlinks[$type]) : $links;
-                // Allow other types to append links to this block if needed.
-                $append = TRUE;
-              }
-              else if ($append) {
-                if ($config['block']) {
-                  break;
-                }
-                else {
-                  $links = isset($node->relatedlinks[$type]) ? array_merge($links, $node->relatedlinks[$type]) : $links;
-                }
-              }
-            }
+          // Current block.
+          if ($delta == $type) {
+            $block['subject'] = check_plain($config['title']);
+            $links = isset($node->relatedlinks[$type]) ? array_merge($links, $node->relatedlinks[$type]) : $links;
+            // Allow other types to append links to this block if needed.
+            $append = TRUE;
           }
-          $links = _relatedlinks_filter($links);
-          if (!empty($links)) {
-            $block['content'] = theme('relatedlinks', $links);
+          elseif ($append) {
+            if ($config['block']) {
+              break;
+            }
+            else {
+              $links = isset($node->relatedlinks[$type]) ? array_merge($links, $node->relatedlinks[$type]) : $links;
+            }
           }
-
-          return $block;
         }
       }
+
+      $links = _relatedlinks_filter($links);
+      if (!empty($links)) {
+        $block['content'] = theme('relatedlinks', array('links' => $links));
+      }
+
+      return $block;
+    }
+  }
+}
+
+
+/**
+ * Implementation of hook_node_load()
+ */
+function relatedlinks_node_load($nodes, $types) {
+  foreach ($nodes as $node) {
+    $node->relatedlinks = _relatedlinks_get_links($node->nid);
   }
 }
 
 /**
- * Implementation of hook_nodeapi().
+ * Implementation of hook_node_delete()
  *
- * @TODO: Move the parsed links filtering into a filter_process(). The current
- * implementation is not PHP filter friendly.
+ * The delete op should be allowed to account for admins who do not have
+ * the 'add related links' permission.
+ */
+function relatedlinks_node_delete($node) {
+  _relatedlinks_delete_links($node->nid);
+  _relatedlinks_delete_tracker($node->nid);
+}
+
+/**
+ * Implementation of hook_node_update()
+ */
+function relatedlinks_node_update($node) {
+
+  _relatedlinks_delete_links($node->nid);
+  _relatedlinks_delete_tracker($node->nid);
+
+  module_invoke('relatedlinks', 'node_insert', $node);
+  //relatedlinks_node_insert($node);
+}
+
+/**
+ * Implementation of hook_node_insert()
  */
-function relatedlinks_nodeapi(&$node, $op, $arg) {
-  if (in_array($node->type, variable_get('relatedlinks_node_types', _relatedlinks_node_get_types(TRUE)))) {
+function relatedlinks_node_insert($node) {
+
+  // Handle manual links.
+  if (_relatedlinks_get_type_property(RELATEDLINKS_MANUAL, 'enabled')) {
+    preg_match_all('#\s*([^\ \s]+)\ *(.*)$#im', $node->relatedlinks_fieldset['relatedlinks'], $matches);
+    $links = _relatedlinks_check_links($matches[1], $matches[2]);
+    _relatedlinks_add_links($node->nid, $links, RELATEDLINKS_MANUAL);
+  }
+
+  // Handle any submitted keywords. Discovered links are calculated
+  // and cached (as necessary) in hook_block.
+  $discovered = variable_get('relatedlinks_discovered', _relatedlinks_get_discovered_defaults());
+  // Existing keywords are removed if the keywords functionality is
+  // disabled.
+  $keywords = '';
+
+  if (_relatedlinks_get_type_property(RELATEDLINKS_DISCOVERED, 'enabled') &&
+    in_array('search', $discovered['ranking']) && $discovered['keywords']) {
+    $keywords = $node->relatedlinks_fieldset['keywords'];
+  }
+  _relatedlinks_insert_tracker($node->nid, $keywords);
+
+  // Handle parsed links.
+  if (_relatedlinks_get_type_property(RELATEDLINKS_PARSED, 'enabled')) {
+    // Run node body through check_markup to ensure that only
+    // processed output is being parsed for links. This also means
+    // that the link text is already escaped.
+
+    $lang = $node->language;
+    $node_body = check_markup($node->body[$lang][0]['value']);
+
+    // Previous versions of this module attempted to retain other tag
+    // attributes. This has been curtailed due to changes in the DB
+    // structure.
+    // Regex adapted from syscrusher's links package, which in turn
+    // was adapted from a post on php.net by "martin at vertikal dot dk".
+    // Any updates to this regex also needs to be applied to the
+    // .install file update.
+    preg_match_all("!<\s*a\s*href\s*=\s*(?:\"([^\">]+)\"[^>]*|([^\" >]+?)[^>]*)>(.*)\s*<\s*/\s*a\s*>!Uis", $node_body, $matches);
+    $links = _relatedlinks_check_links($matches[1], $matches[3]);
+
+    _relatedlinks_add_links($node->nid, $links, RELATEDLINKS_PARSED);
+  }
+}
+
+/**
+ * Implements hook_node_access()
+ */
+function relatedlinks_node_access($node, $op, $account) {
+  if (_relatedlinks_isenabled_nodetype($node)) {
     switch ($op) {
-      case 'load':
-        $node->relatedlinks = _relatedlinks_get_links($node->nid);
-        break;
-      default:
-        // The delete op should be allowed to account for admins who do not have
-        // the 'add related links' permission.
-        if (user_access('add related links') || $op == 'delete') {
-          switch ($op) {
-            case 'delete':
-              _relatedlinks_delete_links($node->nid);
-              _relatedlinks_delete_tracker($node->nid);
-              break;
-            case 'update':
-              _relatedlinks_delete_links($node->nid);
-              _relatedlinks_delete_tracker($node->nid);
-              // Fall through.
-            case 'insert':
-              // Handle manual links.
-              if (_relatedlinks_get_type_property(RELATEDLINKS_MANUAL, 'enabled')) {
-                preg_match_all('#\s*([^\ \s]+)\ *(.*)$#im', $node->relatedlinks_fieldset['relatedlinks'], $matches);
-                $links = _relatedlinks_check_links($matches[1], $matches[2]);
-                _relatedlinks_add_links($node->nid, $links, RELATEDLINKS_MANUAL);
-              }
-
-              // Handle any submitted keywords. Discovered links are calculated
-              // and cached (as necessary) in hook_block.
-              $discovered = variable_get('relatedlinks_discovered', _relatedlinks_get_discovered_defaults());
-              // Existing keywords are removed if the keywords functionality is
-              // disabled.
-              $keywords = '';
-              if (_relatedlinks_get_type_property(RELATEDLINKS_DISCOVERED, 'enabled') &&
-                in_array('search', $discovered['ranking']) && $discovered['keywords']) {
-                $keywords = $node->relatedlinks_fieldset['keywords'];
-              }
-              _relatedlinks_insert_tracker($node->nid, $keywords);
-
-              // Handle parsed links.
-              if (_relatedlinks_get_type_property(RELATEDLINKS_PARSED, 'enabled')) {
-                // Run node body through check_markup to ensure that only
-                // processed output is being parsed for links. This also means
-                // that the link text is already escaped.
-                $node->body = check_markup($node->body, $node->format, FALSE);
-                // Previous versions of this module attempted to retain other tag
-                // attributes. This has been curtailed due to changes in the DB
-                // structure.
-                // Regex adapted from syscrusher's links package, which in turn
-                // was adapted from a post on php.net by "martin at vertikal dot dk".
-                // Any updates to this regex also needs to be applied to the
-                // .install file update.
-                preg_match_all("!<\s*a\s*href\s*=\s*(?:\"([^\">]+)\"[^>]*|([^\" >]+?)[^>]*)>(.*)\s*<\s*/\s*a\s*>!Uis", $node->body, $matches);
-                $links = _relatedlinks_check_links($matches[1], $matches[3]);
-                _relatedlinks_add_links($node->nid, $links, RELATEDLINKS_PARSED);
-              }
-          }
+      case 'create':
+      case 'update':
+      case 'insert': // TODO: will this one actually work?
+        if (user_access('add related links')) {
+          return NODE_ACCESS_ALLOW;
+        }
+        else {
+          return NODE_ACCESS_DENY;
         }
+        break;
     }
   }
 }
 
 /**
- * Implementation of hook_form_alter().
+ * Checks whether or not the given node's type is eligible for related links
+ * @param $node
+ * @return bool
  */
-function relatedlinks_form_alter(&$form, &$form_state, $form_id) {
-  if (user_access('add related links') && isset($form['type']) &&
-    ($form['type']['#value'] .'_node_form' == $form_id)) {
-
-    $node = $form['#node'];
-    if (!in_array($node->type, variable_get('relatedlinks_node_types', _relatedlinks_node_get_types(TRUE)))) {
-      return;
-    }
-    $form['relatedlinks_fieldset'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Related links'),
-      '#collapsible' => TRUE,
-      '#tree' => TRUE,
-      '#weight' => 30
-    );
+function _relatedlinks_isenabled_nodetype($node) {
+  $node_type = NULL;
+  if (is_object($node)) {
+    $node_type = (isset($node->type) ? $node->type : NULL);
+  }
+  elseif (is_string($node)) {
+    $node_type = $node;
+  }
+  return (!is_null($node_type) && in_array($node_type, variable_get('relatedlinks_node_types', _relatedlinks_node_get_types(TRUE))));
+}
 
-    if (_relatedlinks_get_type_property(RELATEDLINKS_MANUAL, 'enabled')) {
-      // Include JavaScript variables and files.
-      $path = drupal_get_path('module', 'relatedlinks');
-      drupal_add_css($path .'/relatedlinks.css');
-
-      // The url() call is wrapped in an array to workaround a bug where node
-      // previews lead to form_alters being called repeatedly. These repeated
-      // calls to drupal_add_js() lead to the setting being mangled from a
-      // simple variable to an array as the variable is repeatedly added to
-      // itself. The workaround is to make it an array in the first place
-      // leading to some warped consistency.
-      drupal_add_js(array('relatedlinks_autocomplete' => array(url('relatedlinks/autocomplete'))), 'setting');
-      drupal_add_js($path .'/relatedlinks.js');
-
-      // Include the autocomplete JS file in case the authoring field is altered
-      // out.
-      drupal_add_js('misc/autocomplete.js');
-
-      // Add existing manual links to the form.
-      $relatedlinks = '';
-      if (isset($node->relatedlinks[RELATEDLINKS_MANUAL])) {
-        foreach ($node->relatedlinks[RELATEDLINKS_MANUAL] as $link) {
-          $relatedlinks .= $link['url'] .' '. $link['title'] ."\n";
-        }
+/**
+ * Implementation of hook_form_alter()
+ */
+function relatedlinks_form_alter(&$form, &$form_state, $form_id) {
+  if (user_access('add related links') && isset($form['type']) && isset($form['type']['#value'])) {
+    if ($form['type']['#value'] . '_node_form' == $form_id) {
+      $node = $form['#node'];
+      if (!in_array($node->type, variable_get('relatedlinks_node_types', _relatedlinks_node_get_types(TRUE)))) {
+        return;
       }
-
-      $form['relatedlinks_fieldset']['relatedlinks'] = array(
-        '#type' => 'textarea',
+      $form['relatedlinks_fieldset'] = array(
+        '#type' => 'fieldset',
         '#title' => t('Related links'),
-        '#default_value' => $relatedlinks,
-        '#rows' => 3,
-        '#description' => t('To manually define links to related material, enter one URL and title (separated by a space) per line. For example: "<code>http://www.example.com Clickable Text</code>". Alternately, you can enter an internal site address. For example: "<code>about About Us</code>". If no title is submitted, the URL itself will be used as the title.')
+        '#collapsible' => TRUE,
+        '#tree' => TRUE,
+        '#weight' => 30
       );
 
-      $form['relatedlinks_fieldset']['#collapsed'] = empty($relatedlinks);
-    }
+      if (_relatedlinks_get_type_property(RELATEDLINKS_MANUAL, 'enabled')) {
+        // Include JavaScript variables and files.
+        $path = drupal_get_path('module', 'relatedlinks');
+        drupal_add_css($path . '/relatedlinks.css');
+
+        // The url() call is wrapped in an array to workaround a bug where node
+        // previews lead to form_alters being called repeatedly. These repeated
+        // calls to drupal_add_js() lead to the setting being mangled from a
+        // simple variable to an array as the variable is repeatedly added to
+        // itself. The workaround is to make it an array in the first place
+        // leading to some warped consistency.
+        drupal_add_js(array('relatedlinks_autocomplete' => array(url('relatedlinks/autocomplete'))), 'setting');
+        drupal_add_js($path . '/relatedlinks.js');
+
+        // Include the autocomplete JS file in case the authoring field is altered
+        // out.
+        drupal_add_js('misc/autocomplete.js');
+
+        // Add existing manual links to the form.
+        $relatedlinks = '';
+        if (isset($node->relatedlinks[RELATEDLINKS_MANUAL])) {
+          foreach ($node->relatedlinks[RELATEDLINKS_MANUAL] as $link) {
+            $relatedlinks .= $link->url . ' ' . $link->title . "\n";
+          }
+        }
 
-    $discovered = variable_get('relatedlinks_discovered', _relatedlinks_get_discovered_defaults());
-    if (_relatedlinks_get_type_property(RELATEDLINKS_DISCOVERED, 'enabled') && in_array('search', $discovered['ranking']) && $discovered['keywords']) {
-      $form['relatedlinks_fieldset']['keywords'] = array(
-        '#type' => 'textfield',
-        '#title' => t('Keywords'),
-        '#description' => t('Enter search keywords that will aid in finding related content.')
-      );
+        $form['relatedlinks_fieldset']['relatedlinks'] = array(
+          '#type' => 'textarea',
+          '#title' => t('Related links'),
+          '#default_value' => $relatedlinks,
+          '#rows' => 3,
+          '#description' => t('To manually define links to related material, enter one URL and title (separated by a space) per line. For example: "<code>http://www.example.com Clickable Text</code>". Alternately, you can enter an internal site address. For example: "<code>about About Us</code>". If no title is submitted, the URL itself will be used as the title.')
+        );
 
-      $track = isset($node->nid) ? _relatedlinks_get_tracker($node->nid) : FALSE;
-      if ($track) {
-        $form['relatedlinks_fieldset']['keywords']['#default_value'] = $track['keywords'];
-        $form['relatedlinks_fieldset']['#collapsed'] = $form['relatedlinks_fieldset']['#collapsed'] && empty($track['keywords']);
+        $form['relatedlinks_fieldset']['#collapsed'] = empty($relatedlinks);
+      }
+
+      $discovered = variable_get('relatedlinks_discovered', _relatedlinks_get_discovered_defaults());
+      if (_relatedlinks_get_type_property(RELATEDLINKS_DISCOVERED, 'enabled') && in_array('search', $discovered['ranking']) && $discovered['keywords']) {
+        $form['relatedlinks_fieldset']['keywords'] = array(
+          '#type' => 'textfield',
+          '#title' => t('Keywords'),
+          '#description' => t('Enter search keywords that will aid in finding related content.')
+        );
+
+        $track = isset($node->nid) ? _relatedlinks_get_tracker($node->nid) : FALSE;
+        if ($track) {
+
+          $form['relatedlinks_fieldset']['keywords']['#default_value'] = $track->keywords;
+          $form['relatedlinks_fieldset']['#collapsed'] = $form['relatedlinks_fieldset']['#collapsed'] && empty($track->keywords);
+        }
+      }
+      if (!isset($form['relatedlinks_fieldset']['relatedlinks']) && !isset($form['relatedlinks_fieldset']['keywords'])) {
+        unset($form['relatedlinks_fieldset']);
       }
-    }
-    if (!isset($form['relatedlinks_fieldset']['relatedlinks']) && !isset($form['relatedlinks_fieldset']['keywords'])) {
-      unset($form['relatedlinks_fieldset']);
     }
   }
 }
@@ -330,17 +396,35 @@ function relatedlinks_cron() {
   if (_relatedlinks_get_type_property(RELATEDLINKS_DISCOVERED, 'enabled') && $discovered['cron']) {
     $updated = time() - ($discovered['cron'] * 24 * 60 * 60);
     // Select all stale nodes.
-    $result = db_query("SELECT nid FROM {relatedlinks_tracker} WHERE updated <> 0 AND updated < %d", $updated);
+    $result = db_query(
+      "SELECT nid FROM {relatedlinks_tracker} WHERE updated <> 0 AND updated < :updated",
+      array(
+        ':updated' => $updated,
+      )
+    );
     $nids = array();
-    while ($node = db_fetch_array($result)) {
-      $nids[] = $node['nid'];
+    foreach ($result as $node) {
+      $nids[] = $node->nid;
     }
     if (!empty($nids)) {
       $nid_args = implode(', ', $nids);
       // Remove stale discovered links.
-      db_query("DELETE FROM {relatedlinks} WHERE nid IN (". $nid_args .") AND type = %d", RELATEDLINKS_DISCOVERED);
+      db_query(
+        "DELETE FROM {relatedlinks} WHERE nid IN (:nid_args)
+         AND type = :type",
+         array(
+           ":nid_args" => $nid_args,
+           ':type' => RELATEDLINKS_DISCOVERED,
+         )
+      );
       // Reset updated field to 0. This will force a recalculation of discovered links.
-      db_query("UPDATE {relatedlinks_tracker} SET updated = 0 WHERE nid IN (". $nid_args .")");
+      db_query(
+        "UPDATE {relatedlinks_tracker}
+         SET updated = 0 WHERE nid IN (:nid_args)",
+        array(
+          ":nid_args" => $nid_args,
+        )
+      );
     }
   }
 }
@@ -354,7 +438,7 @@ function relatedlinks_autocomplete($string = '') {
   foreach (_relatedlinks_node_candidates($string) as $nid => $title) {
     $matches[$nid] = $title;
   }
-  print drupal_to_js($matches);
+  print drupal_json_encode($matches);
   exit();
 }
 
@@ -462,7 +546,16 @@ function _relatedlinks_get_discovered_defaults() {
  */
 function _relatedlinks_add_links($nid, $links, $type) {
   foreach ($links as $link) {
-    db_query("INSERT INTO {relatedlinks} (nid, url, title, type) VALUES (%d, '%s', '%s', %d)", $nid, $link['url'], trim($link['title']), $type);
+    db_query(
+      "INSERT INTO {relatedlinks} (nid, url, title, type)
+       VALUES (:nid, :url, :title, :type)",
+       array(
+         ':nid' => $nid,
+         ':url' => $link['url'],
+         ':title' => trim($link['title']),
+         ':type' => $type,
+       )
+    );
   }
 }
 
@@ -477,10 +570,24 @@ function _relatedlinks_add_links($nid, $links, $type) {
  */
 function _relatedlinks_delete_links($nid, $type = NULL) {
   if (isset($type)) {
-    db_query('DELETE FROM {relatedlinks} WHERE nid = %d AND type = %d', $nid, $type);
+    db_query(
+      'DELETE FROM {relatedlinks}
+       WHERE nid = :nid
+       AND type = :type',
+       array(
+         ':nid' => $nid,
+         ':type' => $type,
+       )
+    );
   }
   else {
-    db_query('DELETE FROM {relatedlinks} WHERE nid = %d', $nid);
+    db_query(
+      'DELETE FROM {relatedlinks}
+       WHERE nid = :nid',
+       array(
+         ':nid' => $nid,
+       )
+    );
   }
 }
 
@@ -494,15 +601,20 @@ function _relatedlinks_delete_links($nid, $type = NULL) {
  *   An associative array of links arranged by link type.
  */
 function _relatedlinks_get_links($nid) {
-  $result = db_query('SELECT url, title, type FROM {relatedlinks} WHERE nid = %d ORDER BY lid', $nid);
+  $result = db_query(
+              'SELECT url, title, type FROM {relatedlinks} WHERE nid = :nid ORDER BY lid',
+              array(
+                ':nid' => $nid,
+              )
+            );
 
   $links = array();
-  while ($link = db_fetch_array($result)) {
-    if (!isset($links[$link['type']])) {
+  foreach ($result as $link) {
+    if (!isset($links[$link->type])) {
       // Initialise array.
-      $links[$link['type']] = array();
+      $links[$link->type] = array();
     }
-    $links[$link['type']][] = $link;
+    $links[$link->type][] = $link;
   }
 
   return $links;
@@ -513,7 +625,7 @@ function _relatedlinks_get_links($nid) {
  * of URLs.
  *
  * @param $links
- *   Associative array of links.
+ *   Associative array of links, or an object with link
  *
  * @return
  *   An array of HTML links.
@@ -527,16 +639,32 @@ function _relatedlinks_filter($links) {
   $url_cache = array();
 
   foreach ($links as $link) {
-    if (!in_array($link['url'], $url_cache)) {
-      if (empty($link['title'])) {
-        // URLs without a title display the URL.
-        $link['title'] = $link['url'];
+
+    if ($link) {
+      $link_title = $link_url = $link_type = '';
+
+      if (is_array($link)) {
+        $link_title = $link['title'];
+        $link_url = $link['url'];
+        $link_type = (isset($link['type']) ? $link['type'] : '');
+      }
+      elseif (is_object($link)) {
+        $link_title = $link->title;
+        $link_url = $link->url;
+        $link_type = (isset($link->type) ? $link->type : '');
       }
-      $url_cache[] = $link['url'];
 
-      // Parsed links have already been through the wringer prior to insert.
-      $title = $link['type'] == RELATEDLINKS_PARSED ? $link['title'] : check_plain($link['title']);
-      $urls[] = '<a href="'. url($link['url']) .'">'. $title .'</a>';
+      if (!in_array($link_url, $url_cache)) {
+        if (empty($link_title)) {
+          // URLs without a title display the URL.
+          $link_title = $link_url;
+        }
+        $url_cache[] = $link_url;
+
+        // Parsed links have already been through the wringer prior to insert.
+        $title = $link_type == RELATEDLINKS_PARSED ? $link_title : check_plain($link_title);
+        $urls[] = '<a href="' . url($link_url) . '">' . $title . '</a>';
+      }
     }
   }
 
@@ -557,8 +685,8 @@ function _relatedlinks_update_discovered_links($node) {
 
   $tracker = _relatedlinks_get_tracker($node->nid);
 
-  if (!$tracker || !$tracker['updated']) {
-    $links = _relatedlinks_get_discovered_links($node, $tracker['keywords']);
+  if (!$tracker || !$tracker->updated) {
+    $links = _relatedlinks_get_discovered_links($node, $tracker->keywords);
 
     _relatedlinks_add_links($node->nid, $links, RELATEDLINKS_DISCOVERED);
     if (!$tracker) {
@@ -584,9 +712,14 @@ function _relatedlinks_update_discovered_links($node) {
  * data could be retrieved.
  */
 function _relatedlinks_get_tracker($nid) {
-  $result = db_query('SELECT * FROM {relatedlinks_tracker} WHERE nid = %d', $nid);
+  $result = db_query(
+    'SELECT * FROM {relatedlinks_tracker} WHERE nid = :nid',
+    array(
+      ':nid' => $nid,
+    )
+  );
 
-  if ($track = db_fetch_array($result)) {
+  if ($track = $result->fetch()) {
     return $track;
   }
 
@@ -603,7 +736,14 @@ function _relatedlinks_get_tracker($nid) {
  * using the search module.
  */
 function _relatedlinks_insert_tracker($nid, $keywords) {
-  db_query("INSERT INTO {relatedlinks_tracker} (nid, keywords) VALUES (%d, '%s')", $nid, $keywords);
+  db_query(
+    "INSERT INTO {relatedlinks_tracker} (nid, keywords)
+     VALUES (:nid, :keywords)",
+     array(
+       ':nid' => $nid,
+       ':keywords' => $keywords,
+     )
+  );
 }
 
 /**
@@ -616,7 +756,15 @@ function _relatedlinks_insert_tracker($nid, $keywords) {
  * for the node in question. 0 signifies that an update is yet to occur.
  */
 function _relatedlinks_update_tracker_timestamp($nid, $updated) {
-  db_query("UPDATE {relatedlinks_tracker} SET updated = %d WHERE nid = %d", $updated, $nid);
+  db_query(
+    "UPDATE {relatedlinks_tracker}
+     SET updated = :updated
+     WHERE nid = :nid",
+     array(
+       ':updated' => $updated,
+       ':nid' => $nid,
+     )
+   );
 }
 
 /**
@@ -626,7 +774,13 @@ function _relatedlinks_update_tracker_timestamp($nid, $updated) {
  *   Node ID of the node whose tracker record is to be deleted.
  */
 function _relatedlinks_delete_tracker($nid) {
-  db_query('DELETE FROM {relatedlinks_tracker} WHERE nid = %d', $nid);
+  db_query('
+    DELETE FROM {relatedlinks_tracker}
+    WHERE nid = :nid',
+    array(
+      ':nid' => $nid,
+    )
+  );
 }
 
 /**
@@ -643,7 +797,7 @@ function _relatedlinks_node_get_types($keys = FALSE) {
   static $node_types = NULL, $key_array = NULL;
 
   if (!isset($node_types)) {
-    $node_types = node_get_types('names');
+    $node_types = node_type_get_names();
     foreach ($node_types as $type => $name) {
       $node_types[$type] = check_plain($name);
     }
@@ -701,7 +855,20 @@ function _relatedlinks_get_discovered_links($node, $keywords) {
   $taxonomy_links = $search_links = array();
   if (module_exists('taxonomy') && in_array('taxonomy', $discovered['ranking'])) {
     $vids = isset($discovered['vocabularies']) ? $discovered['vocabularies'] : array();
-    $tids = array_keys($node->taxonomy);
+
+    $tids = (isset($node->taxonomy) ? array_keys($node->taxonomy) : array());
+
+    $results = db_query(
+      "SELECT tid FROM {taxonomy_index} WHERE nid = :nid",
+      array(
+        ':nid' => $node->nid
+      )
+    );
+
+    foreach ($results as $result) {
+      $tids[] = $result->tid;
+    }
+
     $taxonomy_links = _relatedlinks_taxonomy_select_nodes($node->nid, $tids, $max, $vids, $node_types, $date, $user, $comments);
   }
   if (module_exists('search') && in_array('search', $discovered['ranking'])) {
@@ -747,11 +914,11 @@ function _relatedlinks_get_discovered_links($node, $keywords) {
  *   Handle term hierarchies (depth).
  *   Escape SQL input as an added precaution.
  */
-function _relatedlinks_taxonomy_select_nodes($nid, $tids, $limit, $vids, $node_types, $date, $comments) {
+function _relatedlinks_taxonomy_select_nodes($nid, $tids = array(), $limit, $vids, $node_types, $date, $comments) {
   $links = array();
 
   foreach ($tids as $index => $tid) {
-    $term = taxonomy_get_term($tid);
+    $term = taxonomy_term_load($tid);
     // Filter out terms that do not belong to the specified vids.
     if (!empty($vids) && !in_array($term->vid, $vids)) {
       unset($tids[$index]);
@@ -760,29 +927,36 @@ function _relatedlinks_taxonomy_select_nodes($nid, $tids, $limit, $vids, $node_t
 
   if (count($tids) > 0) {
     $str_tids = implode(',', $tids);
-    $where[] = 'tn.tid IN ('. $str_tids .')';
+    $where[] = 'tn.tid IN (' . $str_tids . ')';
     if (!empty($node_types)) {
-      $where[] = "n.type IN ('". implode("', '", $node_types) ."')";
+      $where[] = "n.type IN ('" . implode("', '", $node_types) . "')";
     }
 
     $where[] = 'n.status = 1';
-    $where[] = 'n.moderate = 0';
-    $where[] = 'n.nid != '. $nid;
+    $where[] = 'n.nid <> ' . $nid;
 
     $order[] = 'count DESC';
-    $order[] = 'sticky DESC';
+    $order[] = 'n.sticky DESC';
     if ($date) {
-      $order[] = 'created DESC';
+      $order[] = 'n.created DESC';
     }
     if ($comments) {
       $order[] = 'ncs.comment_count DESC';
     }
     $order[] = 'promote DESC';
 
-    $sql = 'SELECT n.nid, n.title, n.type, n.uid, COUNT(tn.tid) as count, ncs.comment_count FROM {node} n INNER JOIN {term_node} tn USING (nid) LEFT JOIN {node_comment_statistics} ncs USING (nid) WHERE '. implode(' AND ', $where) .' GROUP BY n.nid ORDER BY '. implode(', ', $order) .' LIMIT '. $limit;
+    $sql = '
+      SELECT n.nid, n.title, n.type, n.uid, COUNT(tn.tid) as count, ncs.comment_count
+      FROM {node} n
+      INNER JOIN {taxonomy_index} tn USING (nid)
+      LEFT JOIN {node_comment_statistics} ncs USING (nid)
+      WHERE ' . implode(' AND ', $where) . '
+      GROUP BY n.nid
+      ORDER BY ' . implode(', ', $order) . '
+      LIMIT ' . $limit;
 
     $result = db_query($sql);
-    while ($node = db_fetch_object($result)) {
+    foreach ($result as $node) {
       // Exclude the current nid.
       if ($node->nid != $nid) {
         $links[$node->nid] = $node;
@@ -822,10 +996,22 @@ function _relatedlinks_search_select_nodes($nid, $keywords, $limit, $node_types)
   $links = array();
 
   if ($keywords == '') {
-    $result = db_query_range("SELECT DISTINCT(si.word) FROM {search_index} si INNER JOIN {search_total} st USING (word) WHERE si.sid = %d AND length(si.word) > %d AND si.type = 'node' ORDER BY st.count DESC, length(si.word) DESC", $nid, variable_get('minimum_word_size', 3), 0, 6);
+    $result = db_query_range(
+      "SELECT DISTINCT(si.word) FROM {search_index} si INNER JOIN {search_total} st USING (word) WHERE si.sid = :sid AND length(si.word) > :length AND si.type = 'node' ORDER BY st.count DESC, length(si.word) DESC",
+      0,
+      6,
+      array(
+        ':sid' => $nid,
+        ':length' => variable_get('minimum_word_size', 3),
+      )
+    );
     $words = array();
-    while ($word = db_fetch_array($result)) {
-      $words[] = $word['word'];
+    foreach ($result as $word) {
+ 
+
+     if (is_array($word['word']) || is_string($word['word'])) {
+        $words[] = $word['word'];
+      }
     }
     $keywords = implode(' OR ', $words);
     // Node type restrictions are only applicable if keywords have not been
@@ -834,14 +1020,14 @@ function _relatedlinks_search_select_nodes($nid, $keywords, $limit, $node_types)
     // The !empty($keywords) has been added as just searching by type results in
     // a warning when hook_search is called.
     if (!empty($keywords) && !empty($node_types)) {
-      $keywords .= ' type:'. implode(',', $node_types);
+      $keywords .= ' type:' . implode(',', $node_types);
     }
   }
 
   $keywords = trim($keywords);
 
-  if (module_hook('node', 'search') && !empty($keywords)) {
-    $results = module_invoke('node', 'search', 'search', $keywords);
+  if (module_hook('node', 'search_execute') && !empty($keywords)) {
+    $results = node_search_execute($keywords);
 
     foreach ($results as $result) {
       $node = $result['node'];
@@ -893,7 +1079,7 @@ function _relatedlinks_collate_links($set1, $set2, $user, $limit) {
   }
 
   foreach ($set as $node) {
-    $link = array('url' => 'node/'. $node->nid, 'title' => $node->title);
+    $link = array('url' => 'node/' . $node->nid, 'title' => $node->title);
     if ($user && $node->uid == $user) {
       $user_links[$node->nid] = $link;
     }
@@ -913,11 +1099,20 @@ function _relatedlinks_collate_links($set1, $set2, $user, $limit) {
  * relatedlinks autocomplete form.
  */
 function _relatedlinks_node_candidates($string = '') {
-  $result = db_query_range(db_rewrite_sql("SELECT n.nid, n.title AS node_title, n.type AS node_type FROM {node} n WHERE n.title LIKE '%%%s%' AND n.status = 1 ORDER BY n.title, n.type"), $string, 0, 10);
+  $string = '%' . $string . '%';
+  $result = db_select('node', 'n')
+    ->fields('n', array('nid', 'title', 'type'))
+    ->condition('title', $string, 'LIKE')
+    ->condition('status', 1)
+    ->orderBy('title', 'DESC')
+    ->orderBy('type', 'DESC')
+    ->range(0, 10)
+    ->addTag('related_links_access')
+    ->execute();
 
   $rows = array();
-  while ($node = db_fetch_object($result)) {
-    $rows[$node->nid] = $node->node_title;
+  while ($node = $result->fetchAssoc()) {
+    $rows[$node['nid']] = $node['title'];
   }
 
   return $rows;
@@ -932,6 +1127,9 @@ function _relatedlinks_node_candidates($string = '') {
  * @return
  *   A themed list of links.
  */
-function theme_relatedlinks($links = array()) {
-  return theme('item_list', $links);
+function theme_relatedlinks($variables) {
+
+  $links = $variables['links'];
+
+  return theme('item_list', array('items' => $links));
 }
