Index: relatedlinks.module =================================================================== --- relatedlinks.module (.../trunk/newashoka/public/modules/relatedlinks/relatedlinks.module) (revision 1232) +++ relatedlinks.module (.../sandboxes/cschwartz/newashoka/public/modules/relatedlinks/relatedlinks.module) (working copy) @@ -3,31 +3,70 @@ /** * @file - * Related links are defined in 2 ways: - * 1) For certain content types, HTML links are automatically discovered and included. - * 2) The author may manually add HTML links that will appear at the top of the - * 'Related links' block. - * 3) An optional block can also list related taxonomy nodes as related links. - * - * When a node is viewed alone, a block is provided to authorized users that - * displays a complete list of links. If no links are defined, the block will - * disappear. + * Provides a block with related links. + * + * Implemented Drupal Hooks: relatedlinks_help() + * relatedlinks_menu() + * relatedlinks_perm() + * relatedlinks_nodeapi() + * relatedlinks_form_alter() + * relatedlinks_block() + * + * Theme Functions: theme_relatedlinks() + * theme_relatedlinks_types_table() + * + * Private Functions: _relatedlinks_settings_form() + * _relatedlinks_settings_form_submit() + * _relatedlinks_add_links() + * _relatedlinks_delete_links() + * _relatedlinks_get_db_links() + * _relatedlinks_get_taxonomy_links() + * _relatedlinks_get_type_property() + * _relatedlinks_get_type_defaults() + * _relatedlinks_sort() + * _relatedlinks_get_link_url() + * _relatedlinks_get_link_text() */ // Define mnemonics to indicate the types of links stored in the relatedlinks // table [type field]. -define('RELATEDLINKS_PARSED', 1); -define('RELATEDLINKS_MANUAL', 2); +define('RELATEDLINKS_PARSED', 1); +define('RELATEDLINKS_MANUAL', 2); +define('RELATEDLINKS_TAXONOMY', 3); /** * Implementation of hook_help(). */ function relatedlinks_help($section) { + + // Add some basic help text. + $helptext = t( + 'Related links are defined in 3 ways: ' . + '
When a node is viewed alone, a block is provided to authorized ' . + 'users that displays a list of related links. If no links are ' . + 'defined, the block will disappear.
' . + 'Parsed and manual links are updated when the node is edited. ' . + 'Taxonomy links are determined on the fly.
'); + switch ($section) { case 'admin/modules#description': return t('Provides a block with related links.'); case 'admin/help#relatedlinks': - return t('Related links are defined in 2 ways: in certain input formats, HTML links are automatically discovered and included; when publishing certain content types, the author may manually add HTML links that will appear at the top of the Related links block. When a node is viewed alone, a block is provided to authorized users that displays a complete list of links. If no links are defined, the block will disappear.'); + return $helptext . $helptext_extra; + case 'admin/relatedlinks': + return $helptext; } } @@ -67,10 +106,28 @@ * */ function relatedlinks_nodeapi(&$node, $op, $arg) { - if ((user_access('add related links') || user_access('administer related links') && in_array($node->type, variable_get('relatedlinks_node_types', array())))) { + if (user_access('add related links') || + user_access('administer related links') && + in_array($node->type, variable_get('relatedlinks_nodes', array()))) { switch ($op) { case 'load': - $links = _relatedlinks_get_links($node->nid, RELATEDLINKS_MANUAL); + $links = _relatedlinks_get_db_links($node->nid, RELATEDLINKS_MANUAL); + + // The links that come in from the database are HTML-formatted. + // This isn't very user friendly, so we're splitting each into + // the URL and the title, separated by a space. Or if there isn't + // a separate title, just list the URL. + foreach ($links as $index => $link) { + $url = _relatedlinks_get_link_url($link); + $title = _relatedlinks_get_link_text($link); + + if ($url == $title) { + $links[$index] = $url; + } else { + $links[$index] = $url . " " . $title; + } + } + $node->relatedlinks = !empty($links) ? implode("\n", $links) : ''; break; case 'delete': @@ -80,10 +137,16 @@ _relatedlinks_delete_links($node->nid); // Fall through. case 'insert': - if (in_array('manual', variable_get('relatedlinks_types', array('parsed')))) { - _relatedlinks_add_links($node->nid, explode("\n", $node->relatedlinks), RELATEDLINKS_MANUAL); + + if (_relatedlinks_get_type_property('Manual', 'enabled')) { + // Reverse the array to maintain the original ordering + // across multiple node updates. + _relatedlinks_add_links($node->nid, + array_reverse(explode("\n", $node->relatedlinks)), + RELATEDLINKS_MANUAL); } - if (in_array('parsed', variable_get('relatedlinks_types', array('parsed')))) { + + if (_relatedlinks_get_type_property('Parsed', 'enabled')) { // Rather than parsing out only the URI + link text, an attempt is // made to retain any other attributes present. preg_match_all('#(]+>[^<]+)#', $node->body, $matches); @@ -101,7 +164,8 @@ unset($matches[1][$index]); } } - _relatedlinks_add_links($node->nid, $matches[1], RELATEDLINKS_PARSED); + _relatedlinks_add_links($node->nid, $matches[1], + RELATEDLINKS_PARSED); } } } @@ -112,10 +176,16 @@ * Implementation of hook_form_alter(). */ function relatedlinks_form_alter($form_id, &$form) { - if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id && in_array('manual', variable_get('relatedlinks_types', array('parsed')))) { + if (isset($form['type']) && + ($form['type']['#value'] .'_node_form' == $form_id) && + _relatedlinks_get_type_property('Manual', 'enabled') && + (user_access('add related links') || + user_access('administer related links'))) { + $node = $form['#node']; - if (!in_array($node->type, variable_get('relatedlinks_node_types', array()))) { + if (!in_array($node->type, + variable_get('relatedlinks_nodes', array()))) { return; } @@ -132,7 +202,15 @@ '#type' => 'textarea', '#default_value' => $relatedlinks, '#rows' => 3, - '#description' => t('To manually define links to related material, enter 1 HTML-formatted link per line. Example: <a href="http://www.example.com">Clickable text</a>.'), + '#notinymce' => TRUE, + '#description' => t('To manually define links to related material, ' . + 'enter one URL and title (separated by a space) ' . + 'per line. Example: "http://www.example.com ' .
+ 'Clickable Text". Alternately, you can '.
+ 'enter an internal site address. Example: ' .
+ '"about About Us" '.
+ 'If no title is submitted, ' .
+ 'the URL itself will become the title.'),
);
}
}
@@ -144,9 +222,9 @@
* @todo Add caching support.
*/
function relatedlinks_block($op = 'list', $delta = 0) {
+
if ($op == 'list') {
- $blocks[0]['info'] = t('Related links');
- $blocks[1]['info'] = t('Related taxonomy terms');
+ $blocks[0]['info'] = t('Related Links');
return $blocks;
}
if ($op == 'view') {
@@ -155,22 +233,65 @@
if ($node = node_load(arg(1))) {
switch ($delta) {
case 0:
- if (in_array($node->type, variable_get('relatedlinks_node_types', array()))) {
- // _relatedlinks_get_links also takes care of links filtering and validation.
- $links = _relatedlinks_get_links($node->nid);
-
- if (!empty($links)) {
- $block['subject'] = t('Related links');
- $block['content'] = theme('relatedlinks', $links);
+ // Only display the block if the current node type has related
+ // links enabled.
+ if (in_array($node->type,
+ variable_get('relatedlinks_nodes', array()))) {
+
+ // We need to gather all of the taxonomies that we'll need
+ // in order to find taxonomy related links. The reason we're
+ // not gathering them all (i.e. array_keys($node->taxonomy))
+ // is because we may be restricted to certain vocabularies.
+ $taxonomies = array();
+ foreach ($node->taxonomy as $key => $taxonomy) {
+ if (in_array($taxonomy->vid,
+ variable_get('relatedlinks_vocabularies',
+ array($taxonomy->vid)))) {
+ $taxonomies[] = $key;
+ }
}
+
+ // Combine links arrays from all sources.
+ $links_data = array_merge(
+ _relatedlinks_get_db_links($node->nid),
+ _relatedlinks_get_taxonomy_links($taxonomies, $node->nid));
+
+ // Sort them.
+ uasort($links_data, '_relatedlinks_sort');
+
+ // If there are any links, display them.
+ if (count($links_data)) {
+ $block['subject'] = t('Related Links');
+
+ // Create an array that'll tell us how many of each link
+ // type we have. Each key is a link type, and each value
+ // is the number of that type in the $links_data array.
+ // While we're there, create a list containing only the
+ // links, with no other data.
+ $types = array();
+ $links_only = array();
+ foreach ($links_data as $key => $value) {
+
+ // Skip any elements that exceed the link type limit.
+ $limit =_relatedlinks_get_type_property($value['type'],'max');
+ if ((!$limit) || ($types[$value['type']] < $limit)) {
+ $types[$value['type']]++;
+ $links_only[] = $value['link'];
+ }
+ }
+
+ // Theme each sublist with a title.
+ $offset = 0;
+ $block['content'] = '';
+ foreach ($types as $link_type => $number_of_this_type) {
+ $block['content'] .= theme('relatedlinks',
+ array_slice($links_only, $offset, $number_of_this_type),
+ _relatedlinks_get_type_property($link_type, 'title'));
+ $offset += $number_of_this_type;
+ }
+ }
}
break;
- case 1:
- $links = _relatedlinks_get_terms(array_keys($node->taxonomy), $node->nid);
- if (!empty($links)) {
- $block['subject'] = t('Related terms');
- $block['content'] = theme('relatedlinks_terms', $links);
- }
}
}
}
@@ -181,15 +302,37 @@
/**
* Theme the relatedlinks block output.
*/
-function theme_relatedlinks($links = array()) {
- return theme('item_list', $links);
+function theme_relatedlinks($links = array(), $title) {
+ return theme('item_list', $links, $title);
}
/**
- * Theme the relatedlinks block output.
+ * Theme the relatedlinks settings form.
*/
-function theme_relatedlinks_terms($links = array()) {
- return theme('item_list', $links);
+function theme_relatedlinks_types_table($form) {
+
+ // Create an link type index for weight & then sort it.
+ $weights = array();
+ foreach (element_children($form) as $index){
+ $weights[$index] = $form[$index]['weight']['#value'];
+ }
+ asort($weights);
+
+ // Render the link type data in a table sorted by weight.
+ foreach (array_keys($weights) as $type) {
+ $rows[] = array(
+ form_render($form[$type]['name']),
+ form_render($form[$type]['enabled']),
+ form_render($form[$type]['weight']),
+ form_render($form[$type]['title']),
+ form_render($form[$type]['max']),
+ );
+ }
+ $header = array(t('Link Type'), t('Enabled'), t('Weight'), t('Title'),
+ t('Limit'));
+ $output = theme('table', $header, $rows, array('style' => 'width: 100%',
+ 'class' => 'form-item'));
+ return $output;
}
/**
@@ -197,29 +340,105 @@
* due to the checkboxes element.
*/
function _relatedlinks_settings_form() {
- $form['relatedlinks'] = array(
+
+ // Create the Link Types section.
+ $linktypes_data = variable_get('relatedlinks_types', array());
+ $form['relatedlinks_types'] = array(
'#type' => 'fieldset',
- '#title' => t('Related links'),
+ '#title' => t('Link Types'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
+ '#theme' => 'relatedlinks_types_table',
+ '#tree' => TRUE,
+ '#weight' => 1,
+ '#description' => t('These are the controls for specific link types. ' .
+ 'Heavier weighted link types will sink to the ' .
+ 'bottom. To remove titles for each type, simply ' .
+ 'leave those fields blank. For an unlimited number ' .
+ 'of links, leave the Limit field blank.'),
);
- $form['relatedlinks']['relatedlinks_node_types'] = array(
+ $defaults = _relatedlinks_get_type_defaults();
+ foreach ($defaults as $typename => $typedefaults) {
+ $form['relatedlinks_types'][$typename]['name'] = array(
+ '#value' => $typename,
+ );
+ $form['relatedlinks_types'][$typename]['enabled'] = array(
+ '#type' => 'checkbox',
+ '#return_value' => TRUE,
+ '#default_value' => $linktypes_data ?
+ $linktypes_data[$typename]['enabled'] : $typedefaults['enabled'],
+ );
+ $form['relatedlinks_types'][$typename]['weight'] = array(
+ '#type' => 'weight',
+ '#delta' => 10,
+ '#default_value' => $linktypes_data ?
+ $linktypes_data[$typename]['weight'] : $typedefaults['weight'],
+ );
+ $form['relatedlinks_types'][$typename]['title'] = array(
+ '#type' => 'textfield',
+ '#size' => 20,
+ '#maxlength' => 40,
+ '#default_value' => $linktypes_data ?
+ $linktypes_data[$typename]['title'] : $typedefaults['title'],
+ );
+ $form['relatedlinks_types'][$typename]['max'] = array(
+ '#type' => 'textfield',
+ '#size' => 3,
+ '#maxlength' => 3,
+ '#default_value' => $linktypes_data ?
+ $linktypes_data[$typename]['max'] : $typedefaults['max'],
+ );
+ }
+
+ // Create the Activated Content Types section.
+ $form['activated_content_types'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Activated Content Types'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#weight' => 2,
+ );
+ $form['activated_content_types']['relatedlinks_nodes'] = array(
'#type' => 'checkboxes',
- '#title' => t('Node associations'),
- '#description' => t('Select the node types to associate with the related links module [parsed and manually added links]. This is not applicable to the taxonomy terms block.'),
+ '#title' => t('Content Types'),
+ '#description' => t('Select the content types to enable for parsed and ' .
+ 'manual links.'),
'#options' => node_get_types(),
- '#default_value' => variable_get('relatedlinks_node_types', array()),
+ '#default_value' => variable_get('relatedlinks_nodes', array()),
);
- $form['relatedlinks']['relatedlinks_types'] = array(
- '#type' => 'checkboxes',
- '#title' => t('Link types'),
- '#description' => t('Select the link types to enable.'),
- '#options' => array('parsed' => t('Parsed links'),
- 'manual' => t('Manually added links')),
- '#default_value' => variable_get('relatedlinks_types', array('parsed')),
- );
- $form['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
+ // Create the Activated Taxonomy Vocabularies section.
+ if (module_exist('taxonomy')) {
+ $voptions = array();
+ $vocabularies = taxonomy_get_vocabularies();
+ foreach ($vocabularies as $vid => $vname) {
+ $voptions[$vid] = t($vname->name);
+ }
+ $form['activated_taxonomy_vocabularies'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Activated Taxonomy Vocabularies'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#weight' => 3,
+ );
+ $form['activated_taxonomy_vocabularies']['relatedlinks_vocabularies']=array(
+ '#type' => 'checkboxes',
+ '#title' => t('Taxonomy Vocabularies'),
+ '#description' => t('Select the taxonomy vocabularies to enable for ' .
+ 'taxonomy links.'),
+ '#options' => $voptions,
+ '#default_value' => variable_get('relatedlinks_vocabularies',
+ array_keys($vocabularies)),
+ );
+ }
+
+ // Set the form submissino information.
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save configuration'),
+ '#weight' => 4);
+
+ // Return the completed from.
return drupal_get_form('_relatedlinks_settings_form', $form);
}
@@ -227,12 +446,14 @@
* Process relatedlinks settings form submissions.
*/
function _relatedlinks_settings_form_submit($form_id, $form_values) {
- $types = array_filter($form_values['relatedlinks_types']);
- variable_set('relatedlinks_types', array_keys($types));
+ variable_set('relatedlinks_types', $form_values['relatedlinks_types']);
- $node_types = array_filter($form_values['relatedlinks_node_types']);
- variable_set('relatedlinks_node_types', array_keys($node_types));
+ $nodes = array_filter($form_values['relatedlinks_nodes']);
+ variable_set('relatedlinks_nodes', array_keys($nodes));
+ $vocabularies = array_filter($form_values['relatedlinks_vocabularies']);
+ variable_set('relatedlinks_vocabularies', array_keys($vocabularies));
+
drupal_set_message(t('Configuration settings saved.'));
cache_clear_all();
}
@@ -244,7 +465,27 @@
foreach ($links as $link) {
$link = trim($link);
if (!empty($link)) {
- db_query("INSERT INTO {relatedlinks} (nid, link, type) VALUES (%d, '%s', %d)", $nid, $link, $type);
+
+ // Manual links need to be converted from a user-friendly format
+ // to HTML format because they are entered by the user.
+ if ($type == RELATEDLINKS_MANUAL) {
+ // Get the URL and the title from the text.
+ $url_and_title = explode(" ", $link);
+ $url = array_shift($url_and_title);
+ $title = implode(" ", $url_and_title);
+
+ // If there is no title, the title will become the URL itself.
+ if (!$title) {
+ $title = $url;
+ }
+
+ // Construct the HTML-formatted link.
+ $link = "$title";
+ }
+
+ // Insert it into the database.
+ db_query("INSERT INTO {relatedlinks} (nid, link, type) " .
+ "VALUES (%d, '%s', %d)", $nid, $link, $type);
}
}
}
@@ -257,41 +498,219 @@
}
/**
- * Retrieve related links from the database.
+ * Retrieve related links from the database. Currently, these include
+ * parsed links and manual links. This function also takes care
+ * of links filtering and validation.
+ *
+ * Returns: If $type is specified, then a one-dimensional array is returned
+ * filled with links of that type.
+ *
+ * If $type is not specified, a two-dimensional array is returned.
+ * In this case, the 'link' field will contain the link, the
+ * 'type' field will contain the link type, and the 'weight'
+ * field will contain the link type's weight..
*/
-function _relatedlinks_get_links($nid, $type = NULL) {
+function _relatedlinks_get_db_links($nid, $type = NULL) {
if (!isset($type)) {
- // The ORDER BY clause ensures that manually added links are given preference.
- $result = db_query('SELECT link FROM {relatedlinks} WHERE nid = %d ORDER BY TYPE DESC', $nid);
+ // The ORDER BY clause ensures that manually added links are given
+ // preference.
+ $result = db_query('SELECT link, type FROM {relatedlinks} WHERE nid = %d ' .
+ 'ORDER BY TYPE DESC', $nid);
}
else {
- $result = db_query('SELECT link FROM {relatedlinks} WHERE nid = %d AND type = %d ORDER BY TYPE DESC', $nid, $type);
+ $result = db_query('SELECT link FROM {relatedlinks} ' .
+ 'WHERE nid = %d AND type = %d ORDER BY TYPE DESC',
+ $nid, $type);
}
+ // Fetch the data into the an array.
$links = array();
-
+ $link_number = 0;
while ($link = db_fetch_array($result)) {
- $links[] = filter_xss($link['link'], array('a'));
+ if (!isset($type)) {
+ // We need to get the link type information as well.
+ $links[$link_number]['link'] = filter_xss($link['link'], array('a'));
+ $links[$link_number]['type'] = $link['type'];
+ $links[$link_number]['weight'] =
+ _relatedlinks_get_type_property($link['type'], 'weight');
+ }
+ else {
+ $links[$link_number] = filter_xss($link['link'], array('a'));
+ }
+ $link_number++;
}
+ // Return it.
return $links;
}
/**
- * Retrieve related taxonomy terms.
+ * Retrieve taxonomy links. Rather than fetching these from the database,
+ * they are collected on the fly. They are dependant on the current taxonomy
+ * state, independant of the current node.
*/
-function _relatedlinks_get_terms($tids, $nid) {
- // The following query is likely to prove expensive when the numbers involved are large.
- // pgSQL compliant?
- $result = module_invoke('taxonomy', 'select_nodes', $tids, 'or', 0, FALSE, 'RAND()');
+function _relatedlinks_get_taxonomy_links($tids, $nid) {
+ $links = array();
- $links = array();
- while ($node = db_fetch_array($result)) {
- // Exclude the current nid.
- if ($node['nid'] != $nid) {
- $links[] = l($node['title'], 'node/'. $node['nid']);
+ // Only get the terms if this link type is enabled.
+ if (_relatedlinks_get_type_property('Taxonomy', 'enabled')) {
+
+ // The following query is likely to prove expensive when the numbers
+ // involved are large.
+ // pgSQL compliant?
+ $result = module_invoke('taxonomy', 'select_nodes', $tids, 'or', 0,
+ FALSE, 'RAND()');
+
+ // Fetch the data into an array.
+ $link_number = 0;
+ while ($node = db_fetch_array($result)) {
+ // Exclude the current nid.
+ if ($node['nid'] != $nid) {
+ $links[$link_number]['link'] = l($node['title'], 'node/'. $node['nid']);
+ $links[$link_number]['type'] = RELATEDLINKS_TAXONOMY;
+ $links[$link_number]['weight'] =
+ _relatedlinks_get_type_property(RELATEDLINKS_TAXONOMY, 'weight');
+ }
+ $link_number++;
}
}
+ // Return it.
return $links;
}
+
+/**
+ * Retrieve a link type property.
+ *
+ * @param $type
+ * The link type.
+ * @param $property
+ * The property to return.
+ * @return
+ * The property value.
+ */
+function _relatedlinks_get_type_property($type, $property) {
+
+ // If the type is an integer, convert it to a string.
+ if ($type == 1) $type = 'Parsed';
+ if ($type == 2) $type = 'Manual';
+ if ($type == 3) $type = 'Taxonomy';
+
+ // If the link types data structure has been set, get the property from there.
+ if ($linktypes_data = variable_get('relatedlinks_types', array())) {
+ return $linktypes_data[$type][$property];
+ }
+
+ // Otherwise, we have to look at the default values.
+ $defaults = _relatedlinks_get_type_defaults();
+
+ // And set them up for next time.
+ variable_set('relatedlinks_types', $defaults);
+
+ return $defaults[$type][$property];
+} // _relatedlinks_get_type_property
+
+/**
+ * Retrieve the link type defaults.
+ * This should probably be done with globals somehow, instead of in a function.
+ *
+ * @return
+ * An array containing defaults for each link type.
+ */
+function _relatedlinks_get_type_defaults() {
+
+ /*
+ * Create a data structure for link types data.
+ * To add a new link type, simply add a new array to the structure below.
+ */
+ $defaults = array();
+
+ // Parsed Links
+ $defaults['Parsed'] = array('enabled' => TRUE,
+ 'weight' => 2,
+ 'title' => t('In Content'),
+ 'max' => 5);
+ // Manual Links
+ $defaults['Manual'] = array('enabled' => FALSE,
+ 'weight' => 1,
+ 'title' => t('By Recommendation'),
+ 'max' => 5);
+ // Taxonomy Link
+ $defaults['Taxonomy'] = array('enabled' => FALSE,
+ 'weight' => 3,
+ 'title' => t('By Category'),
+ 'max' => 5);
+
+ return $defaults;
+} // _relatedlinks_get_type_defaults
+
+/**
+ * Compare two links for sorting.
+ * Links are sorted by weight, type, and then alphanumerically.
+ *
+ * @param $link_one
+ * The first link
+ * @param $link_two
+ * The second link
+ * @return
+ * +1 if $link_one is greater than $link_two
+ * 0 if $link_one is equal to $link_two
+ * -1 if $link_one is less than $link_two
+ */
+function _relatedlinks_sort($link_one, $link_two) {
+
+ /*
+ * w => weight
+ * t => type
+ * n => name
+ */
+ $w1 = $link_one['weight'];
+ $w2 = $link_two['weight'];
+ $t1 = $link_one['type'];
+ $t2 = $link_two['type'];
+ $n1 = _relatedlinks_get_link_text($link_one['link']);
+ $n2 = _relatedlinks_get_link_text($link_two['link']);
+
+ // Sort first by weight, then by type, and then finally alphabetically.
+ switch (TRUE) {
+ case ($w1 > $w2):
+ return 1;
+ case ($w1 < $w2):
+ return -1;
+ case ($w1 == $w2):
+ switch (TRUE) {
+ case ($t1 > $t2):
+ return 1;
+ case ($t1 < $t2):
+ return -1;
+ case ($t1 == $t2):
+ return strcasecmp($n1, $n2);
+ }
+ }
+} // _relatedlinks_sort
+
+/**
+ * Get the URL from a link.
+ *
+ * @param $link
+ * The link as stored in the DB, text surrounded by tags.
+ * @return
+ * The URL.
+ */
+function _relatedlinks_get_link_url($link) {
+ preg_match('#href="([^"]*)"#',$link,$matches);
+ return ($matches[1]);
+}
+
+/**
+ * Get the text from a link.
+ *
+ * @param $link
+ * The link as stored in the DB, text surrounded by tags.
+ * @return
+ * The link text.
+ */
+function _relatedlinks_get_link_text($link) {
+ preg_match('#>([^<]*)<#',$link,$matches);
+ return ($matches[1]);
+}