diff --git a/includes/theme.inc b/includes/theme.inc index 52725b9df23d892b197beab9a1160802b9a03b75..1580a437b8615a3ece42d64a5099c6dd7ad2c701 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -1978,29 +1978,29 @@ function template_preprocess_page(&$variables) { */ function template_preprocess_node(&$variables) { $node = $variables['node']; - if (module_exists('taxonomy')) { - $variables['taxonomy'] = taxonomy_link('taxonomy terms', $node); - } - else { - $variables['taxonomy'] = array(); - } - - if ($variables['teaser'] && $node->teaser) { - $variables['content'] = $node->teaser; - } - elseif (isset($node->body)) { - $variables['content'] = $node->body; - } - else { - $variables['content'] = ''; - } $variables['date'] = format_date($node->created); - $variables['links'] = !empty($node->links) ? theme('links', $node->links, array('class' => 'links inline')) : ''; $variables['name'] = theme('username', $node); $variables['node_url'] = url('node/' . $node->nid); - $variables['terms'] = theme('links', $variables['taxonomy'], array('class' => 'links inline')); $variables['title'] = check_plain($node->title); + + if ($node->build_mode == NODE_BUILD_PREVIEW) { + unset($node->content['links']); + } + + // Render taxonomy links separately. + $variables['terms'] = !empty($node->content['links']['terms']) ? drupal_render($node->content['links']['terms']) : ''; + + // Render all remaining node links. + $variables['links'] = !empty($node->content['links']) ? drupal_render($node->content['links']) : ''; + + // Render the rest of the node into $content. + if (!empty($node->content['teaser'])) { + $variables['content'] = drupal_render($node->content['teaser']); + } + else { + $variables['content'] = drupal_render($node->content); + } // Flatten the node object's member fields. $variables = array_merge((array)$node, $variables); diff --git a/modules/blog/blog.module b/modules/blog/blog.module index 4e22962e6c7eb2dd72bbd75cf5cd6ac1876c6434..e9b036c130d3b1b2ba9d7150c92bb77d29b30fd5 100644 --- a/modules/blog/blog.module +++ b/modules/blog/blog.module @@ -94,22 +94,22 @@ function blog_view($node, $teaser, $page) { } /** - * Implementation of hook_link(). + * Implementation of hook_nodeapi_view. */ -function blog_link($type, $node = NULL, $teaser = FALSE) { - $links = array(); - - if ($type == 'node' && $node->type == 'blog') { +function blog_nodeapi_view($node, $teaser = FALSE) { + if ($node->type == 'blog') { if (arg(0) != 'blog' || arg(1) != $node->uid) { $links['blog_usernames_blog'] = array( 'title' => t("!username's blog", array('!username' => $node->name)), 'href' => "blog/$node->uid", 'attributes' => array('title' => t("Read !username's latest blog entries.", array('!username' => $node->name))), ); + $node->content['links']['blog'] = array( + '#type' => 'node_links', + '#value' => $links, + ); } } - - return $links; } /** diff --git a/modules/book/book.module b/modules/book/book.module index c5b0d17d5dd19dbd15267a5707b1ed15b1eda661..294076d305cfb42e7329a202b0fbcbbbfdd0bc18 100644 --- a/modules/book/book.module +++ b/modules/book/book.module @@ -61,12 +61,12 @@ function book_perm() { } /** - * Implementation of hook_link(). + * Inject links into $node as needed. */ -function book_link($type, $node = NULL, $teaser = FALSE) { +function book_nodeapi_view_link($node, $teaser, $page) { $links = array(); - - if ($type == 'node' && isset($node->book)) { + + if (isset($node->book['depth'])) { if (!$teaser) { $child_type = variable_get('book_child_type', 'book'); if ((user_access('add content to books') || user_access('administer book outlines')) && node_access('create', $child_type) && $node->status == 1 && $node->book['depth'] < MENU_MAX_DEPTH) { @@ -87,7 +87,12 @@ function book_link($type, $node = NULL, $teaser = FALSE) { } } - return $links; + if (!empty($links)) { + $node->content['links']['book'] = array( + '#type' => 'node_links', + '#value' => $links, + ); + } } /** @@ -732,6 +737,8 @@ function book_nodeapi_view($node, $teaser, $page) { } } } + + book_nodeapi_view_link($node, $teaser, $page); } /** diff --git a/modules/comment/comment.module b/modules/comment/comment.module index 96fd3680bae470946574a25ff708899524eee553..eb088a95e19bb942b82f27cbc25985f29c086475 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -412,16 +412,16 @@ function theme_comment_block() { } /** - * Implementation of hook_link(). + * An implementation of hook_nodeapi_view(). */ -function comment_link($type, $node = NULL, $teaser = FALSE) { +function comment_nodeapi_view($node, $teaser, $page) { $links = array(); - if ($type == 'node' && $node->comment) { + if ($node->comment) { if ($teaser) { // Main page: display the number of comments that have been posted. if (user_access('access comments')) { - if ($node->comment_count) { + if (!empty($node->comment_count)) { $links['comment_comments'] = array( 'title' => format_plural($node->comment_count, '1 comment', '@count comments'), 'href' => "node/$node->nid", @@ -476,17 +476,16 @@ function comment_link($type, $node = NULL, $teaser = FALSE) { } } } - } - - if ($type == 'comment') { - $links = comment_links($node, $teaser); - } + + if (isset($links['comment_forbidden'])) { + $links['comment_forbidden']['html'] = TRUE; + } - if (isset($links['comment_forbidden'])) { - $links['comment_forbidden']['html'] = TRUE; + $node->content['links']['comment'] = array( + '#type' => 'node_links', + '#value' => $links, + ); } - - return $links; } /** @@ -864,6 +863,14 @@ function comment_save($edit) { } } +// An implementation of hook_link(). +function comment_link($type, $object, $teaser) { + if ($type == 'comment') { + $links = comment_links($object, FALSE); + return $links; + } +} + /** * Build command links for a comment (e.g.\ edit, reply, delete) with respect to the current user's access permissions. * diff --git a/modules/node/node.module b/modules/node/node.module index 634da3987cd402b5b1df310ae9d0fd701991c929..27ff4be3f89ffdb2b099f9eee2d478853b1a3acf 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -138,6 +138,9 @@ function node_theme() { 'node_admin_overview' => array( 'arguments' => array('name' => NULL, 'type' => NULL), ), + 'node_links' => array( + 'arguments' => array('element' => NULL), + ), ); } @@ -1134,31 +1137,11 @@ function node_delete($nid) { * @return * An HTML representation of the themed node. */ -function node_view($node, $teaser = FALSE, $page = FALSE, $links = TRUE) { +function node_view($node, $teaser = FALSE, $page = FALSE) { $node = (object)$node; $node = node_build_content($node, $teaser, $page); - if ($links) { - $node->links = module_invoke_all('link', 'node', $node, $teaser); - drupal_alter('link', $node->links, $node); - } - - // Set the proper node part, then unset unused $node part so that a bad - // theme can not open a security hole. - $content = drupal_render($node->content); - if ($teaser) { - $node->teaser = $content; - unset($node->body); - } - else { - $node->body = $content; - unset($node->teaser); - } - - // Allow modules to modify the fully-built node. - node_invoke_nodeapi($node, 'alter', $teaser, $page); - return theme('node', $node, $teaser, $page); } @@ -1221,6 +1204,9 @@ function node_build_content($node, $teaser = FALSE, $page = FALSE) { // Allow modules to make their own additions to the node. node_invoke_nodeapi($node, 'view', $teaser, $page); + + // Allow modules to modify the structured node. + drupal_alter('node_view', $node, $teaser, $page); return $node; } @@ -1232,6 +1218,7 @@ function node_show($node, $cid, $message = FALSE) { if ($message) { drupal_set_title(t('Revision of %title from %date', array('%title' => $node->title, '%date' => format_date($node->revision_timestamp))), PASS_THROUGH); } + $output = node_view($node, FALSE, TRUE); if (function_exists('comment_render') && $node->comment) { @@ -3000,3 +2987,27 @@ function node_list_permissions($type) { return $perms; } + +/** + * Implementation of hook_elements(). + */ +function node_elements() { + $type['node_links'] = array(); + + return $type; +} + +/** + * Format a set of node links. + * + * @param $element + * An associative array containing the properties of the element. + * Properties used: value + * @return + * A themed HTML string representing the links. + * + * @ingroup themeable + */ +function theme_node_links($element) { + return theme('links', $element['#value'], array('class' => 'links inline')); +} \ No newline at end of file diff --git a/modules/node/node.pages.inc b/modules/node/node.pages.inc index 83a59680c473a20dfb84b8ee7e896a31bbd1a439..e05485b72a1ddbcd2fdcd98c4100579115c00221 100644 --- a/modules/node/node.pages.inc +++ b/modules/node/node.pages.inc @@ -415,12 +415,12 @@ function theme_node_preview($node) { if ($preview_trimmed_version) { drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication.<span class="no-js"> You can insert the delimiter "<!--break-->" (without the quotes) to fine-tune where your post gets split.</span>')); $output .= '<h3>' . t('Preview trimmed version') . '</h3>'; - $output .= node_view(clone $node, 1, FALSE, 0); + $output .= node_view(clone $node, 1, FALSE); $output .= '<h3>' . t('Preview full version') . '</h3>'; - $output .= node_view($node, 0, FALSE, 0); + $output .= node_view($node, 0, FALSE); } else { - $output .= node_view($node, 0, FALSE, 0); + $output .= node_view($node, 0, FALSE); } $output .= "</div>\n"; diff --git a/modules/statistics/statistics.module b/modules/statistics/statistics.module index dfb98bda2712b0b421bb5887eae58698da01f1aa..98dab7531903e600346d45987a1ce00ee1ba864a 100644 --- a/modules/statistics/statistics.module +++ b/modules/statistics/statistics.module @@ -96,20 +96,23 @@ function statistics_perm() { } /** - * Implementation of hook_link(). + * Implementation of hook_nodeapi_view(). */ -function statistics_link($type, $node = NULL, $teaser = FALSE) { +function statistics_nodeapi_view($node, $teaser, $page) { global $id; $links = array(); - if ($type != 'comment' && user_access('view post access counter')) { + if (user_access('view post access counter')) { $statistics = statistics_get($node->nid); if ($statistics) { $links['statistics_counter']['title'] = format_plural($statistics['totalcount'], '1 read', '@count reads'); } } - - return $links; + + $node->content['links']['statistics'] = array( + '#type' => 'node_links', + '#value' => $links, + ); } /** @@ -244,9 +247,8 @@ function statistics_get($nid) { if ($nid > 0) { // Retrieve an array with both totalcount and daycount. $statistics = db_fetch_array(db_query('SELECT totalcount, daycount, timestamp FROM {node_counter} WHERE nid = %d', $nid)); + return $statistics; } - - return $statistics; } /** diff --git a/modules/system/system.api.php b/modules/system/system.api.php index faac9a1749fc23fc1feca5bff830f04b18213dcc..5160fe19aa06d13db90cf73f779ab782fa2a8ebd 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -322,7 +322,7 @@ function hook_image_toolkits() { * Define internal Drupal links. * * This hook enables modules to add links to many parts of Drupal. Links - * may be added in nodes or in the navigation block, for example. + * may be added in the navigation block, for example. * * The returned array should be a keyed array of link entries. Each link can * be in one of two formats. @@ -345,11 +345,10 @@ function hook_image_toolkits() { * An identifier declaring what kind of link is being requested. * Possible values: * - comment: Links to be placed below a comment being viewed. - * - node: Links to be placed below a node being viewed. * @param $object - * A node object or a comment object according to the $type. + * A comment object. * @param $teaser - * In case of node link: a 0/1 flag depending on whether the node is + * A 0/1 flag depending on whether the node is * displayed with its teaser or its full form. * @return * An array of the requested links. @@ -358,48 +357,17 @@ function hook_image_toolkits() { function hook_link($type, $object, $teaser = FALSE) { $links = array(); - if ($type == 'node' && isset($object->parent)) { - if (!$teaser) { - if (book_access('create', $object)) { - $links['book_add_child'] = array( - 'title' => t('add child page'), - 'href' => "node/add/book/parent/$object->nid", - ); - } - if (user_access('see printer-friendly version')) { - $links['book_printer'] = array( - 'title' => t('printer-friendly version'), - 'href' => 'book/export/html/' . $object->nid, - 'attributes' => array('title' => t('Show a printer-friendly version of this book page and its sub-pages.')) - ); - } - } - } - - $links['sample_link'] = array( - 'title' => t('go somewhere'), - 'href' => 'node/add', - 'query' => 'foo=bar', - 'fragment' => 'anchorname', - 'attributes' => array('title' => t('go to another page')), - ); - - // Example of a link that's not an anchor - if ($type == 'video') { - if (variable_get('video_playcounter', 1) && user_access('view play counter')) { - $links['play_counter'] = array( - 'title' => format_plural($object->play_counter, '1 play', '@count plays'), - ); - } + if ($type == 'comment') { + $links = comment_links($object, FALSE); + return $links; } return $links; } /** - * Perform alterations before links on a node are rendered. - * - * One popular use of this hook is to add/delete links from other modules. + * Perform alterations before links on a comment are rendered. One popular use of + * this hook is to add/delete links from other modules. * * @param $links * Nested array of links for the node keyed by providing module. diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module index ebb91459c8611dfc977c786e2e15ed6086028ff0..219451cfb5eb707f02af851477d34ba7918409b9 100644 --- a/modules/taxonomy/taxonomy.module +++ b/modules/taxonomy/taxonomy.module @@ -39,57 +39,45 @@ function taxonomy_theme() { } /** - * Implementation of hook_link(). - * - * This hook is extended with $type = 'taxonomy terms' to allow themes to - * print lists of terms associated with a node. Themes can print taxonomy - * links with: - * - * if (module_exists('taxonomy')) { - * $terms = taxonomy_link('taxonomy terms', $node); - * print theme('links', $terms); - * } + * An implementation of hook_nodeapi_view(). */ -function taxonomy_link($type, $node = NULL) { - if ($type == 'taxonomy terms' && $node != NULL) { - $links = array(); - // If previewing, the terms must be converted to objects first. - if (isset($node->build_mode) && $node->build_mode == NODE_BUILD_PREVIEW) { - $node->taxonomy = taxonomy_preview_terms($node); - } - if (!empty($node->taxonomy)) { - foreach ($node->taxonomy as $term) { - // During preview the free tagging terms are in an array unlike the - // other terms which are objects. So we have to check if a $term - // is an object or not. - if (is_object($term)) { - $links['taxonomy_term_' . $term->tid] = array( - 'title' => $term->name, - 'href' => taxonomy_term_path($term), - 'attributes' => array('rel' => 'tag', 'title' => strip_tags($term->description)) - ); - } - // Previewing free tagging terms; we don't link them because the - // term-page might not exist yet. - else { - foreach ($term as $free_typed) { - $typed_terms = drupal_explode_tags($free_typed); - foreach ($typed_terms as $typed_term) { - $links['taxonomy_preview_term_' . $typed_term] = array( - 'title' => $typed_term, - ); - } +function taxonomy_nodeapi_view($node) { + $links = array(); + // If previewing, the terms must be converted to objects first. + if (isset($node->build_mode) && $node->build_mode == NODE_BUILD_PREVIEW) { + $node->taxonomy = taxonomy_preview_terms($node); + } + if (!empty($node->taxonomy)) { + foreach ($node->taxonomy as $term) { + // During preview the free tagging terms are in an array unlike the + // other terms which are objects. So we have to check if a $term + // is an object or not. + if (is_object($term)) { + $links['taxonomy_term_' . $term->tid] = array( + 'title' => $term->name, + 'href' => taxonomy_term_path($term), + 'attributes' => array('rel' => 'tag', 'title' => strip_tags($term->description)) + ); + } + // Previewing free tagging terms; we don't link them because the + // term-page might not exist yet. + else { + foreach ($term as $free_typed) { + $typed_terms = drupal_explode_tags($free_typed); + foreach ($typed_terms as $typed_term) { + $links['taxonomy_preview_term_' . $typed_term] = array( + 'title' => $typed_term, + ); } } } } - - // We call this hook again because some modules and themes - // call taxonomy_link('taxonomy terms') directly. - drupal_alter('link', $links, $node); - - return $links; } + + $node->content['links']['terms'] = array( + '#type' => 'node_links', + '#value' => $links + ); } /** diff --git a/modules/translation/translation.module b/modules/translation/translation.module index 801e556e036682a9c948988c6aa895f512542472..0057dfb821426d6325694c0b82cd385a9be05ac3 100644 --- a/modules/translation/translation.module +++ b/modules/translation/translation.module @@ -158,14 +158,13 @@ function translation_form_alter(&$form, $form_state, $form_id) { } /** - * Implementation of hook_link(). + * Implementation of hook_nodeapi_view(). * * Display translation links with native language names, if this node * is part of a translation set. */ -function translation_link($type, $node = NULL, $teaser = FALSE) { - $links = array(); - if ($type == 'node' && ($node->tnid) && $translations = translation_node_get_translations($node->tnid)) { +function translation_nodeapi_view(&$node, $teaser = FALSE) { + if (isset($node->tnid) && $translations = translation_node_get_translations($node->tnid)) { // Do not show link to the same node. unset($translations[$node->language]); $languages = language_list(); @@ -177,10 +176,13 @@ function translation_link($type, $node = NULL, $teaser = FALSE) { 'language' => $language, 'attributes' => array('title' => $translations[$langcode]->title, 'class' => 'translation-link') ); + $node->content['links']['translation'] = array( + '#type' => 'node_links', + '#value' => $links, + ); } } } - return $links; } /** diff --git a/modules/upload/upload.module b/modules/upload/upload.module index 518c9011596144c15c533268255526e4b7ed4e2c..65cd225bdf6e2b2107b3652b060be78045b42604 100644 --- a/modules/upload/upload.module +++ b/modules/upload/upload.module @@ -56,13 +56,13 @@ function upload_perm() { } /** - * Implementation of hook_link(). + * Inject links into $node for attachments. */ -function upload_link($type, $node = NULL, $teaser = FALSE) { +function upload_nodeapi_links($node, $teaser) { $links = array(); // Display a link with the number of attachments - if ($teaser && $type == 'node' && isset($node->files) && user_access('view uploaded files')) { + if ($teaser && isset($node->files) && user_access('view uploaded files')) { $num_files = 0; foreach ($node->files as $file) { if ($file->list) { @@ -76,10 +76,12 @@ function upload_link($type, $node = NULL, $teaser = FALSE) { 'attributes' => array('title' => t('Read full article to view attachments.')), 'fragment' => 'attachments' ); + $node->content['links']['upload_attachments'] = array( + '#type' => 'node_links', + '#value' => $links, + ); } } - - return $links; } /** @@ -323,6 +325,8 @@ function upload_nodeapi_view($node, $teaser, $page) { ); } } + + upload_nodeapi_links($node, $teaser); } } diff --git a/modules/upload/upload.test b/modules/upload/upload.test index 1a343884417af697bb6c418b6d85f53a77deba8c..a39f0ac96b6b9d1113602d619e4449961b77af09 100644 --- a/modules/upload/upload.test +++ b/modules/upload/upload.test @@ -44,12 +44,17 @@ class UploadTestCase extends DrupalWebTestCase { $this->uploadFile($node, $files[0]); $this->uploadFile($node, $files[1]); - // Check to see that uploaded file is listed and actually accessible. + // Check to see that uploaded file is listed in detail page and actually accessible. $this->assertText(basename($files[0]), basename($files[0]) . ' found on node.'); $this->assertText(basename($files[1]), basename($files[1]) . ' found on node.'); $this->checkUploadedFile(basename($files[0])); $this->checkUploadedFile(basename($files[1])); + + // Assure that the attachment link appears on teaser view and has correct count. + $node = node_load($node->nid); + $teaser = node_view($node, TRUE); + $this->assertTrue(strpos($teaser, format_plural(2, '1 attachment', '@count attachments')), 'Attachments link found on node teaser.'); // Fetch db record and use fid to rename and delete file. $upload = db_fetch_object(db_query('SELECT fid, description FROM {upload} WHERE nid = %d', array($node->nid))); diff --git a/themes/chameleon/chameleon.theme b/themes/chameleon/chameleon.theme index 43630a9ae273b34c03ef34038e2f5ec869ce892d..3e72f8097cc7ad97c18f7df72018664248359766 100644 --- a/themes/chameleon/chameleon.theme +++ b/themes/chameleon/chameleon.theme @@ -133,21 +133,23 @@ function chameleon_node($node, $teaser = 0, $page = 0) { $output .= " </div>\n"; - $submitted['node_submitted'] = theme_get_setting("toggle_node_info_$node->type") ? array( - 'title' => t("By !author at @date", array('!author' => theme('username', $node), '@date' => format_date($node->created, 'small'))), - 'html' => TRUE) : array(); - - $terms = array(); - if (module_exists('taxonomy')) { - $terms = taxonomy_link("taxonomy terms", $node); + $submitted = ''; + if (theme_get_setting("toggle_node_info_$node->type")) { + $submitted = t("By !author at @date", array('!author' => theme('username', $node), '@date' => format_date($node->created, 'small'))); } - $links = array_merge($submitted, $terms); - if (isset($node->links)) { - $links = array_merge($links, $node->links); + $terms = ''; + if (!empty($node->content['links']['taxonomy'])) { + $terms = drupal_render($node->content['links']['taxonomy']); + } + + $links = ''; + if ($node->content['links']) { + $links = drupal_render($node->content['links']); } - if (count($links)) { - $output .= '<div class="links">' . theme('links', $links, array('class' => 'links inline')) . "</div>\n"; + + if (!empty($terms) || !empty($links)) { + $output .= '<div class="links">' . $terms . $links . $submitted . "</div>\n"; } $output .= "</div>\n"; @@ -160,7 +162,7 @@ function chameleon_comment($comment, $node, $links = array()) { 'title' => t('By !author at @date', array('!author' => theme('username', $comment), '@date' => format_date($comment->timestamp, 'small'))), 'html' => TRUE); - $output = "<div class=\"comment" . ' ' . $status . "\">\n"; + $output = "<div class=\"comment" . ' ' . $comment->status . "\">\n"; $output .= " <h3 class=\"title\">" . l($comment->subject, $_GET['q'], array('fragment' => "comment-$comment->cid")) . "</h3>\n"; $output .= " <div class=\"content\">" . $comment->comment; if (!empty($signature)) {