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,21 +3,26 @@ /** * @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. + * Related links are defined in 3 ways: + * 1) Parsed Links: For certain content types, HTML links are + * automatically discovered and included. + * 2) Manual Links: The author may manually add HTML links that will + * appear at the top of the 'Related Links' block. + * 3) Taxonomy Links: Related taxonomy nodes can also be listed. * * 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. + * + * Parsed and manual links are updated when the node is edited. Taxonomy + * links are determined on the fly. */ // 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(). @@ -27,7 +32,20 @@ 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 t('Related links are defined in 3 ways: ' . + '
' . + '
Parsed Links
' . + '
For certain content types, HTML links ' . + 'are automatically discovered and included.
' . + '
Manual Links
' . + '
The author may manually add HTML links ' . + 'that will appear at the top of the "Related Links" ' . + 'block.
' . + '
Taxonomy Links
' . + '
Related taxonomy nodes can also be listed.
' . + '
' . + 'Parsed and manual links are updated when the node is edited. ' . + 'Taxonomy links are determined on the fly.'); } } @@ -67,10 +85,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_node_types', array()))) { switch ($op) { case 'load': $links = _relatedlinks_get_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) { + $html_chunks = explode('"', $link); + $url = $html_chunks[1]; + $title = substr($html_chunks[2], 1, -4); + if ($url == $title) { + $links[$index] = $url; + } else { + $links[$index] = $url . " " . $title; + } + } + $node->relatedlinks = !empty($links) ? implode("\n", $links) : ''; break; case 'delete': @@ -80,10 +116,14 @@ _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 (in_array('manual', + variable_get('relatedlinks_types', array('parsed')))) { + _relatedlinks_add_links($node->nid, + explode("\n", $node->relatedlinks), + RELATEDLINKS_MANUAL); } - if (in_array('parsed', variable_get('relatedlinks_types', array('parsed')))) { + if (in_array('parsed', + variable_get('relatedlinks_types', array('parsed')))) { // 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 +141,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 +153,13 @@ * 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 && + in_array('manual', variable_get('relatedlinks_types', array('parsed')))) { $node = $form['#node']; - if (!in_array($node->type, variable_get('relatedlinks_node_types', array()))) { + if (!in_array($node->type, + variable_get('relatedlinks_node_types', array()))) { return; } @@ -132,7 +176,11 @@ '#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>.'), + '#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". If no title is submitted,' . + ' the URL itself will become the title.'), ); } } @@ -144,9 +192,14 @@ * @todo Add caching support. */ function relatedlinks_block($op = 'list', $delta = 0) { + + // Define the subheadings for each link type. + $headings[RELATEDLINKS_PARSED] = t('In Content'); + $headings[RELATEDLINKS_MANUAL] = t('By Recommendation'); + $headings[RELATEDLINKS_TAXONOMY] = t('By Taxonomy Terms'); + 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 +208,45 @@ 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_node_types', array()))) { + + // Combine links arrays from all sources. + $links_data = array_merge( + _relatedlinks_get_links($node->nid), + _relatedlinks_get_terms(array_keys($node->taxonomy), + $node->nid)); + + // 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) { + $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), + $headings[$link_type]); + $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,18 +257,11 @@ /** * 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. - */ -function theme_relatedlinks_terms($links = array()) { - return theme('item_list', $links); -} - -/** * Menu Callback: relatedlinks module settings form. Not using hook_settings * due to the checkboxes element. */ @@ -206,7 +275,7 @@ $form['relatedlinks']['relatedlinks_node_types'] = 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.'), + '#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 listing.'), '#options' => node_get_types(), '#default_value' => variable_get('relatedlinks_node_types', array()), ); @@ -214,8 +283,9 @@ '#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')), + '#options' => array('parsed' => t('Parsed links'), + 'manual' => t('Manually added links'), + 'taxonomy' => t('Taxonomy terms')), '#default_value' => variable_get('relatedlinks_types', array('parsed')), ); $form['submit'] = array('#type' => 'submit', '#value' => t('Save configuration')); @@ -244,7 +314,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,23 +347,45 @@ } /** - * Retrieve related links from the database. + * Retrieve related links from the database. 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, and the + * 'type' field will contain the link type. */ function _relatedlinks_get_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']; + } + else { + $links[$link_number] = filter_xss($link['link'], array('a')); + } + $link_number++; } + // Return it. return $links; } @@ -281,17 +393,30 @@ * Retrieve related taxonomy terms. */ 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()'); + $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 (in_array('taxonomy', + variable_get('relatedlinks_types', array('parsed')))) { + + // 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; + } + $link_number++; } } + // Return it. return $links; }