Read more »')); define('ED_READMORE_TITLE_DEFAULT', t('Read the whole post')); define('ED_READMORE_SEPARATOR_DEFAULT', ''); /** * Implementation of hook_menu(). */ function ed_readmore_menu() { $items['admin/settings/ed_readmore'] = array( 'title' => 'Read More link', 'description' => 'Configures the Read More link that appears in node teasers.', 'page callback' => 'drupal_get_form', 'page arguments' => array('ed_readmore_admin_settings'), 'access arguments' => array('administer site configuration'), 'type' => MENU_NORMAL_ITEM, ); return $items; } /** * Displays the settings form. */ function ed_readmore_admin_settings() { $form = array(); $elements = array( 'address' => '
', 'blockquote' => '', 'cite' => '', 'div' => '', 'h1' => '', 'h2' => '
', 'h3' => '
', 'h4' => '
', 'h5' => '
', 'h6' => '
', 'p' => '
', 'span' => '', ); $form['ed_readmore_display'] = array( '#type' => 'fieldset', '#title' => t('Read More link display options'), '#collapsible' => FALSE, ); $form['ed_readmore_display']['ed_readmore_remove'] = array( '#type' => 'checkbox', '#title' => t('Remove Read More link from links section'), '#default_value' => variable_get('ed_readmore_remove', TRUE), '#description' => t('Enabling this option will remove Drupal\'s default Read More link from the node links (the
$links
output).'), ); $form['ed_readmore_display']['ed_readmore_placement'] = array( '#type' => 'radios', '#title' => t('Link placement'), '#options' => array( 'inline' => t('Inline: Try to add the Read More link after the last word of the teaser. If this fails, add the link on a new line after the teaser.'), 'after' => t('On a new line: Add the Read More link on a new line after the teaser.'), 'disable' => t('Disable the link: Do not add a Read More link to the teaser.'), ), '#default_value' => variable_get('ed_readmore_placement', ED_READMORE_PLACEMENT_DEFAULT), '#description' => t('The inline option will attempt to add the Read More link after the last word of the teaser and before any CCK fields. The HTML elements into which the Read More link may be inserted can be chosen in the "Advanced options for inline placement" interface below.'), ); $form['ed_readmore_display']['ed_readmore_placement_advanced'] = array( '#type' => 'fieldset', '#title' => t('Advanced options for inline placement'), '#collapsible' => TRUE, '#collapsed' => TRUE, ); $form['ed_readmore_display']['ed_readmore_placement_advanced']['ed_readmore_elements'] = array( '#type' => 'select', '#title' => t('Elements eligible for inline placement'), '#description' => t('Select the elements into which the Read More link may be inserted. The "Inline" placement option must be selected above.'), '#multiple' => TRUE, '#options' => $elements, '#default_value' => variable_get('ed_readmore_elements', array('p')), '#size' => 10, ); $form['ed_readmore_formatting'] = array( '#type' => 'fieldset', '#title' => t('Read More link formatting and attributes'), '#collapsible' => FALSE, ); $form['ed_readmore_formatting']['ed_readmore_text'] = array( '#type' => 'textfield', '#title' => t('Link text'), '#default_value' => variable_get('ed_readmore_text', ED_READMORE_TEXT_DEFAULT), '#description' => t('Enter the text you wish to display in the Read More link. The following HTML is allowed:em
,i
,strong
,b
,cite
,code
,span
,font
. Special characters should be encoded (like %raquo or %amp). Spaces will be changed to non-breaking spaces (%nbsp).', array('%raquo' => '»', '%amp' => '&', '%nbsp' => ' ')), '#required' => TRUE, ); $form['ed_readmore_formatting']['ed_readmore_title'] = array( '#type' => 'textfield', '#title' => t('Link title'), '#default_value' => variable_get('ed_readmore_title', ED_READMORE_TITLE_DEFAULT), '#description' => t('Enter the text you wish to be used as the title for the Read More link (the value of the %title attribute). It is used for accessibility and search engine optimization purposes and appears as a tooltip in some browsers.', array('%title' => 'title=""')), '#required' => FALSE, ); if (module_exists('token')) { $form['ed_readmore_formatting']['tokens'] = array( '#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => TRUE, '#title' => t('Placeholder tokens'), '#description' => t('The following placeholder tokens can be used in the Read More link text and title. They will be replaced with the appropriate values.'), ); $form['ed_readmore_formatting']['tokens']['ed_readmore_tokens'] = array( '#type' => 'checkbox', '#title' => t('Enable placeholder tokens'), '#default_value' => variable_get('ed_readmore_tokens', TRUE), '#description' => t('This option will allow you to use the placeholder tokens listed below.'), ); $form['ed_readmore_formatting']['tokens']['help'] = array( '#value' => theme('token_help', 'node'), ); // Add Token-specific help text $token_description = t('Tokens are supported if you enable them below.', array('@tokens' => url('http://drupal.org/project/token'))); $form['ed_readmore_formatting']['ed_readmore_text']['#description'] .= ' ' . $token_description; $form['ed_readmore_formatting']['ed_readmore_title']['#description'] .= ' ' . $token_description; } $form['ed_readmore_formatting']['ed_readmore_nofollow'] = array( '#type' => 'checkbox', '#title' => t('Make link nofollow'), '#default_value' => variable_get('ed_readmore_nofollow', TRUE), '#description' => t('Adds %nofollow to the link\'s attributes. Often used for search engine optimization purposes.', array('%nofollow' => 'rel="nofollow"')), ); $form['ed_readmore_formatting']['ed_readmore_separator'] = array( '#type' => 'textfield', '#title' => t('Separator'), '#description' => t('Enter the text you wish to be inserted between the teaser and the Read More link when the link is displayed inline. If you enter nothing, the link will be separated using a space character. May contain HTML. Special characters should be encoded (like %nbsp or %raquo). ', array('%nbsp' => ' ', '%raquo' => '»')), '#default_value' => variable_get('ed_readmore_separator', ED_READMORE_SEPARATOR_DEFAULT), ); return system_settings_form($form); } /** * Implementation of hook_link_alter(). */ function ed_readmore_link_alter(&$links, $node) { // Remove the link from the node's $links output if the option is enabled if (variable_get('ed_readmore_remove', TRUE)) { unset($links['node_read_more']); } } /** * Implementation of template_preprocess_node(). */ function ed_readmore_preprocess_node(&$variables) { $display = variable_get('ed_readmore_placement', ED_READMORE_PLACEMENT_DEFAULT); // Don't do anything if placing the link is disabled if ($display != 'disable') { // Check to make sure that this is a teaser and there's actually more to read if ($variables['teaser'] && $variables['readmore']) { $variables['content'] = ed_readmore_link_place($variables['content'], $variables['node'], $display); } } } /** * Place the "Read more" link in the correct location in the teaser. * * @param $teaser * The teaser to place the "Read more" link into. * @param $link * The node containing information necessary to generate a link. * @param $display * Link display mode: inline, after, or disable. * If 'inline,' try to insert into teaser. If 'added,' just add after the teaser. */ function ed_readmore_link_place($teaser, $node, $display) { if ($display == 'inline') { $elements_array = variable_get('ed_readmore_elements', array('p')); $elements = '(?:'. implode('|', $elements_array) .')'; // Get last position of the last closing marker in teaser if (preg_match('!?'. $elements .'[^>]*>$!i', $node->content['body']['#value'], $match, PREG_OFFSET_CAPTURE)) { // Recalculate the position in $teaser. We do this because there may be extra CCK fields appended to the teaser. $insert_point = strpos($teaser, $node->content['body']['#value']) + $match[0][1]; // Insert the link $teaser = substr_replace($teaser, ed_readmore_link_render($node, $display), $insert_point, 0); } else { $display = 'after'; $teaser .= ed_readmore_link_render($node, $display); // Not found, so just append it } } else { $teaser .= ed_readmore_link_render($node, $display); // Not inline, so just append it } return $teaser; } /** * Prepares the link for theming and returns a rendered link. * * XSS checking and other safety measures are performed here to prevent * themers from omitting them. */ function ed_readmore_link_render($node, $display = 'inline') { // Filter link text for cross-site scripting (XSS) // Replace spaces with non-breaking entities to prevent a wrapping in the link $allowed_tags = array('em', 'i', 'strong', 'b', 'cite', 'code', 'span', 'font'); $link_text = t(filter_xss(variable_get('ed_readmore_text', ED_READMORE_TEXT_DEFAULT), $allowed_tags)); // Make sure the text is plain $link_title = t(check_plain(variable_get('ed_readmore_title', ED_READMORE_TITLE_DEFAULT))); // Replace tokens with values if the Token module and the token options are enabled if (module_exists('token') && variable_get('ed_readmore_tokens', TRUE)) { $link_text = token_replace($link_text, 'node', $node); $link_title = token_replace($link_title, 'node', $node); } // Replace double quotes with single quotes to prevent breaking of the title attribute $link_title = str_replace('"', '\'', $link_title); // Build link options array $link_options = array( 'attributes' => array( 'title' => $link_title, ), 'html' => TRUE, ); // Add rel="nofollow" to link if the option is enabled if (variable_get('ed_readmore_nofollow', TRUE)) { $link_options['attributes']['rel'] = 'nofollow'; } // Filter separator for cross-site scripting (XSS) $separator = filter_xss(variable_get('ed_readmore_separator', ED_READMORE_SEPARATOR_DEFAULT)); // Send prepared data to the theme function return theme('ed_readmore_link', $node, $link_text, $link_options, $separator, $display); } /** * Implementation of hook_theme(). */ function ed_readmore_theme($existing, $type, $theme, $path) { return array( 'ed_readmore_link' => array( 'arguments' => array( 'link_text' => NULL, 'link_destination' => NULL, 'link_options' => NULL, 'display' => NULL, ), ), ); } /** * Theme function that wraps the rendered link. */ function theme_ed_readmore_link($node, $link_text, $link_options, $separator, $display) { // Use a(block-level) element for links appended after the teaser if ($display == 'after') { $element = 'div'; $separator = ''; } else { // Use a (inline) element for links that appear inside the teaser $element = 'span'; if (empty($separator)) { $separator = ' '; } } return $separator . '<' . $element . ' class="read-more">' . l($link_text, 'node/'. $node->nid, $link_options) . '' . $element . '>'; }