Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.132 diff -u -F^f -r1.132 form.inc --- includes/form.inc 5 Aug 2006 00:26:35 -0000 1.132 +++ includes/form.inc 7 Aug 2006 01:18:28 -0000 @@ -648,7 +648,15 @@ function form_render(&$elements) { if (isset($content) && $content !== '') { $prefix = isset($elements['#prefix']) ? $elements['#prefix'] : ''; $suffix = isset($elements['#suffix']) ? $elements['#suffix'] : ''; - return $prefix . $content . $suffix; + $content = $prefix . $content . $suffix; + + if (isset($elements['#after_render'])) { + foreach ($elements['#after_render'] as $function) { + $function($elements, $content); + } + } + + return $content; } } Index: modules/blog/blog.module =================================================================== RCS file: /cvs/drupal/drupal/modules/blog/blog.module,v retrieving revision 1.254 diff -u -F^f -r1.254 blog.module --- modules/blog/blog.module 6 Aug 2006 23:00:41 -0000 1.254 +++ modules/blog/blog.module 7 Aug 2006 01:18:28 -0000 @@ -235,7 +235,7 @@ function blog_form(&$node) { /** * Implementation of hook_view(). */ -function blog_view(&$node, $teaser = FALSE, $page = FALSE) { +function blog_view($node, $teaser = FALSE, $page = FALSE) { if ($page) { // Breadcrumb navigation $breadcrumb[] = array('path' => 'blog', 'title' => t('blogs')); @@ -243,7 +243,7 @@ function blog_view(&$node, $teaser = FAL $breadcrumb[] = array('path' => 'node/'. $node->nid); menu_set_location($breadcrumb); } - $node = node_prepare($node, $teaser); + return node_prepare($node, $teaser); } /** Index: modules/book/book.module =================================================================== RCS file: /cvs/drupal/drupal/modules/book/book.module,v retrieving revision 1.378 diff -u -F^f -r1.378 book.module --- modules/book/book.module 6 Aug 2006 23:00:41 -0000 1.378 +++ modules/book/book.module 7 Aug 2006 01:18:28 -0000 @@ -440,16 +440,6 @@ function book_content($node, $teaser = F } /** - * Implementation of hook_view(). - * - * If not displayed on the main page, we render the node as a page in the - * book with extra links to the previous and next pages. - */ -function book_view(&$node, $teaser = FALSE, $page = FALSE) { - $node = node_prepare($node, $teaser); -} - -/** * Implementation of hook_nodeapi(). * * Appends book navigation to all nodes in the book. @@ -476,7 +466,10 @@ function book_nodeapi(&$node, $op, $teas } $node->breadcrumb[] = array('path' => 'node/'. $node->nid); - $node->body .= theme('book_navigation', $node); + $node->content['book_navigation'] = array( + '#value' => theme('book_navigation', $node), + '#weight' => 100, + ); if ($page) { menu_set_location($node->breadcrumb); @@ -811,7 +804,7 @@ function book_node_visitor_html_pre($nod $output .= "

". check_plain($node->title) ."

\n"; if ($node->body) { - $output .= $node->body; + $output .= form_render($node->content); } return $output; } Index: modules/forum/forum.module =================================================================== RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v retrieving revision 1.344 diff -u -F^f -r1.344 forum.module --- modules/forum/forum.module 6 Aug 2006 23:00:42 -0000 1.344 +++ modules/forum/forum.module 7 Aug 2006 01:18:28 -0000 @@ -313,8 +313,11 @@ function forum_view(&$node, $teaser = FA } $node = node_prepare($node, $teaser); - - $node->body .= theme('forum_topic_navigation', $node); + $node->content['forum_navigation'] = array( + '#value' => theme('forum_topic_navigation', $node), + '#weight' => 100, + ); + return $node; } /** Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.667 diff -u -F^f -r1.667 node.module --- modules/node/node.module 6 Aug 2006 23:00:42 -0000 1.667 +++ modules/node/node.module 7 Aug 2006 01:18:28 -0000 @@ -636,24 +636,6 @@ function node_save(&$node) { function node_view($node, $teaser = FALSE, $page = FALSE, $links = TRUE) { $node = (object)$node; - // Remove the delimiter (if any) that separates the teaser from the body. - // TODO: this strips legitimate uses of '' also. - $node->body = str_replace('', '', $node->body); - - if ($node->log != '' && !$teaser) { - $node->body .= '
'. t('Log') .':
'. filter_xss($node->log) .'
'; - } - - // The 'view' hook can be implemented to overwrite the default function - // to display nodes. - if (node_hook($node, 'view')) { - node_invoke($node, 'view', $teaser, $page); - } - else { - $node = node_prepare($node, $teaser); - } - // Allow modules to change $node->body before viewing. - node_invoke_nodeapi($node, 'view', $teaser, $page); if ($links) { $node->links = module_invoke_all('link', 'node', $node, !$page); @@ -662,11 +644,18 @@ function node_view($node, $teaser = FALS $function($node, $node->links); } } - // unset unused $node part so that a bad theme can not open a security hole + + $node = node_build_content($node, $teaser, $page); + + // Set the proper node part, then unset unused $node part so that a bad + // theme can not open a security hole. + $content = form_render($node->content); if ($teaser) { + $node->teaser = $content; unset($node->body); } else { + $node->body = $content; unset($node->teaser); } @@ -674,16 +663,62 @@ function node_view($node, $teaser = FALS } /** - * Apply filters to a node in preparation for theming. + * Apply filters and build the node's standard elements. */ function node_prepare($node, $teaser = FALSE) { - $node->readmore = (strlen($node->teaser) < strlen($node->body)); - if ($teaser == FALSE) { - $node->body = check_markup($node->body, $node->format, FALSE); + $node->content['body'] = array( + '#value' => check_markup($teaser ? $node->teaser : $node->body, $node->format, FALSE), + '#weight' => 0, + ); + + if ($node->log != '' && !$teaser) { + $node->content['log_message'] = array( + '#value' => theme('node_log_message', filter_xss($node->log)), + '#weight' => 20, + ); + } + + if (strlen($node->teaser) < strlen($node->body)) { + $node->readmore = TRUE; + } + + return $node; +} + +/** + * Builds a structured array representing the node's content. + * + * @param $node + * A node object. + * @param $teaser + * Whether to display the teaser only, as on the main page. + * @param $page + * Whether the node is being displayed by itself as a page. + * + * @return + * An structured array containing the individual elements + * of the node's body. + */ +function node_build_content($node, $teaser = FALSE, $page = FALSE) { + // Remove the delimiter (if any) that separates the teaser from the body. + // TODO: this strips legitimate uses of '' also. + $node->body = str_replace('', '', $node->body); + + // The 'view' hook can be implemented to overwrite the default function + // to display nodes. + if (node_hook($node, 'view')) { + $node = node_invoke($node, 'view', $teaser, $page); } else { - $node->teaser = check_markup($node->teaser, $node->format, FALSE); + $node = node_prepare($node, $teaser); } + + // Allow modules to make their own additions to the node. + node_invoke_nodeapi($node, 'view', $teaser, $page); + + // Allow modules to modify the fully-built node. + node_invoke_nodeapi($node, 'alter', $teaser, $page); + return $node; } @@ -838,17 +873,10 @@ function node_search($op = 'search', $ke // Load results $results = array(); foreach ($find as $item) { + // Build the node body. $node = node_load($item->sid); - - // Get node output (filtered and with module-specific fields). - if (node_hook($node, 'view')) { - node_invoke($node, 'view', FALSE, FALSE); - } - else { - $node = node_prepare($node, FALSE); - } - // Allow modules to change $node->body before viewing. - node_invoke_nodeapi($node, 'view', FALSE, FALSE); + $node = node_build_content($node, FALSE, FALSE); + $node->body = form_render($node->content); // Fetch comments for snippet $node->body .= module_invoke('comment', 'nodeapi', $node, 'update index'); @@ -2049,6 +2077,10 @@ function theme_node_preview($node) { return $output; } +function theme_node_log_message($log) { + return '
'. t('Log') .':
'. $log .'
'; +} + function node_form_submit($form_id, $edit) { global $user; @@ -2328,15 +2360,10 @@ function node_update_index() { $last_nid = $node->nid; $node = node_load($node->nid); - // Get node output (filtered and with module-specific fields). - if (node_hook($node, 'view')) { - node_invoke($node, 'view', FALSE, FALSE); - } - else { - $node = node_prepare($node, FALSE); - } - // Allow modules to change $node->body before viewing. - node_invoke_nodeapi($node, 'view', FALSE, FALSE); + // Build the node body. + $node = node_load($item->sid); + $node = node_build_content($node, FALSE, FALSE); + $node->body = form_render($node->content); $text = '

'. check_plain($node->title) .'

'. $node->body; Index: modules/poll/poll.module =================================================================== RCS file: /cvs/drupal/drupal/modules/poll/poll.module,v retrieving revision 1.205 diff -u -F^f -r1.205 poll.module --- modules/poll/poll.module 6 Aug 2006 23:00:42 -0000 1.205 +++ modules/poll/poll.module 7 Aug 2006 01:18:28 -0000 @@ -56,12 +56,11 @@ function poll_block($op = 'list', $delta $poll = node_load(array('type' => 'poll', 'created' => $timestamp, 'status' => 1)); if ($poll->nid) { - // poll_view() dumps the output into $poll->body. - poll_view($poll, 1, 0, 1); + $poll = poll_view($poll, TRUE, FALSE, TRUE); } } $block['subject'] = t('Poll'); - $block['content'] = $poll->body; + $block['content'] = form_render($poll->content); return $block; } } @@ -541,14 +540,14 @@ function poll_cancel(&$node) { * An extra parameter that adapts the hook to display a block-ready * rendering of the poll. */ -function poll_view(&$node, $teaser = FALSE, $page = FALSE, $block = FALSE) { +function poll_view($node, $teaser = FALSE, $page = FALSE, $block = FALSE) { global $user; $output = ''; // Special display for side-block if ($block) { // No 'read more' link - $node->body = $node->teaser = ''; + $node->readmore = FALSE; $links = module_invoke_all('link', 'node', $node, 1); $links[] = array('title' => t('older polls'), 'href' => 'poll', 'attributes' => array('title' => t('View the list of polls on this site.'))); @@ -560,13 +559,16 @@ function poll_view(&$node, $teaser = FAL } if ($node->allowvotes && ($block || arg(2) != 'results')) { - $output .= poll_view_voting($node, $teaser, $page, $block); + $node->content['body'] = array( + '#value' => poll_view_voting($node, $teaser, $page, $block), + ); } else { - $output .= poll_view_results($node, $teaser, $page, $block); + $node->content['body'] = array( + '#value' => poll_view_results($node, $teaser, $page, $block), + ); } - - $node->body = $node->teaser = $output; + return $node; } /** Index: modules/upload/upload.module =================================================================== RCS file: /cvs/drupal/drupal/modules/upload/upload.module,v retrieving revision 1.116 diff -u -F^f -r1.116 upload.module --- modules/upload/upload.module 6 Aug 2006 23:00:42 -0000 1.116 +++ modules/upload/upload.module 7 Aug 2006 01:18:28 -0000 @@ -484,32 +484,36 @@ function upload_nodeapi(&$node, $op, $te case 'view': if (isset($node->files) && user_access('view uploaded files')) { - // Manipulate so that inline references work in preview - if (!variable_get('clean_url', 0)) { - $previews = array(); - foreach ($node->files as $file) { - if (strpos($file->fid, 'upload') !== FALSE) { - $previews[] = $file; - } + // Add the attachments list to node body with a heavy + // weight to ensure they're below other elements + if (count($node->files)) { + if (!$teaser && user_access('view uploaded files')) { + $node->content['files'] = array( + '#value' => theme('upload_attachments', $node->files), + '#weight' => 50, + ); } - - // URLs to files being previewed are actually Drupal paths. When Clean - // URLs are disabled, the two do not match. We perform an automatic - // replacement from temporary to permanent URLs. That way, the author - // can use the final URL in the body before having actually saved (to - // place inline images for example). - foreach ($previews as $file) { - $old = file_create_filename($file->filename, file_create_path()); - $new = url($old); - $node->body = str_replace($old, $new, $node->body); - $node->teaser = str_replace($old, $new, $node->teaser); + // Manipulate so that inline references work in preview + if (!variable_get('clean_url', 0)) { + $previews = array(); + foreach ($node->files as $file) { + $file = (object)$file; + if (strpos($file->fid, 'upload') !== FALSE) { + $previews[] = $file; + } + } + // URLs to files being previewed are actually Drupal paths. When Clean + // URLs are disabled, the two do not match. We perform an automatic + // replacement from temporary to permanent URLs. That way, the author + // can use the final URL in the body before having actually saved (to + // place inline images for example). + foreach ($previews as $file) { + $old = file_create_filename($file->filename, file_create_path()); + $node->content['#upload_urls'][$old] = url($old); + } + $node->content['#after_render'][] = 'upload_fix_preview_urls'; } } - - // Add the attachments list to node body - if (count($node->files) && !$teaser) { - $node->body .= theme('upload_attachments', $node->files); - } } break; @@ -558,6 +562,18 @@ function upload_nodeapi(&$node, $op, $te } } +function upload_fix_preview_urls($elements, &$content) { + if (is_array($elements['#upload_urls'])) { + $old_list = array(); + $new_list = array(); + foreach ($elements['#upload_urls'] as $old => $new) { + $old_list[] = $old; + $new_list[] = $new; + } + $content = str_replace($old_list, $new_list, $content); + } +} + /** * Displays file attachments in table */