| 1 |
<?php
|
| 2 |
|
| 3 |
/* $Id: relatedcontent.module,v 1.16.2.2 2008/01/09 21:41:48 tbarregren Exp $
|
| 4 |
*
|
| 5 |
* Copyright (C) 2007-2008 Thomas Barregren.
|
| 6 |
*
|
| 7 |
* This program is free software; you can redistribute it and/or modify
|
| 8 |
* it under the terms of the GNU General Public License as published by
|
| 9 |
* the Free Software Foundation; either version 2 of the License, or
|
| 10 |
* (at your option) any later version.
|
| 11 |
*
|
| 12 |
* This program is distributed in the hope that it will be useful,
|
| 13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 15 |
* GNU General Public License for more details.
|
| 16 |
*
|
| 17 |
* You should have received a copy of the GNU General Public License along
|
| 18 |
* with this program; if not, write to the Free Software Foundation, Inc.,
|
| 19 |
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
| 20 |
*/
|
| 21 |
|
| 22 |
|
| 23 |
/**
|
| 24 |
* @file
|
| 25 |
* RelatedContent – a Drupal module that Allows privileged users to associate
|
| 26 |
* a node with related nodes that can be displayed along with the node.
|
| 27 |
*
|
| 28 |
* Author:
|
| 29 |
* Thomas Barregren at Webbredaktören <http://drupal.org/user/16678>.
|
| 30 |
*/
|
| 31 |
|
| 32 |
|
| 33 |
/******************************************************************************
|
| 34 |
* RELATEDCONTENT API THAT CAN BE USED BY THEMES AND OTHER MODULES
|
| 35 |
******************************************************************************/
|
| 36 |
|
| 37 |
/**
|
| 38 |
* Gets the id numbers (nid) of nodes that are related to $node.
|
| 39 |
*
|
| 40 |
* @param $node
|
| 41 |
* is the node object, as loaded through node_load(), for which the related
|
| 42 |
* content is requested.
|
| 43 |
* @return
|
| 44 |
* an array of id numbers (nid) of nodes that are related to $node. The array
|
| 45 |
* is sorted as described in the documentation of the module.
|
| 46 |
*/
|
| 47 |
function relatedcontent_get_nodes(&$node) {
|
| 48 |
$nodes = $node->nodes; // Keeps $node immutable.
|
| 49 |
asort($nodes, SORT_NUMERIC);
|
| 50 |
return array_keys($nodes);
|
| 51 |
}
|
| 52 |
|
| 53 |
/**
|
| 54 |
* Sets the id numbers (nid) of nodes that are related to $node.
|
| 55 |
*
|
| 56 |
* @param $node
|
| 57 |
* is the node object, as loaded through node_load(), for which the related
|
| 58 |
* content is to be set.
|
| 59 |
* @param $nodes
|
| 60 |
* is an array of id numbers (nid) of nodes that are related to $node. The
|
| 61 |
* array should be sorted as described in the documentation of the module.
|
| 62 |
*/
|
| 63 |
function relatedcontent_set_nodes(&$node, $nodes) {
|
| 64 |
$ordinal = -count($nodes);
|
| 65 |
foreach ($nodes as $nid) {
|
| 66 |
$node_nodes[$nid] = $ordinal++;
|
| 67 |
}
|
| 68 |
$node->nodes = $node_nodes;
|
| 69 |
}
|
| 70 |
|
| 71 |
/**
|
| 72 |
* Loads, transform and group the related content of a given node.
|
| 73 |
*
|
| 74 |
* This function takes either a node id or a node object ($node); load those
|
| 75 |
* nodes that contains its related content; transform them by passing them
|
| 76 |
* through a callback function ($content_function), which may take additional
|
| 77 |
* arguments ($content_function_args); and finally group ($output_grouped).
|
| 78 |
*
|
| 79 |
* Example 1: After executing
|
| 80 |
*
|
| 81 |
* $node = 3;
|
| 82 |
* $output = relatedcontent($node);
|
| 83 |
*
|
| 84 |
* $output['all'] is an array of all node objects, as loaded through
|
| 85 |
* node_load(), with related content or the node with the id (3) in $node.
|
| 86 |
*
|
| 87 |
* Example 2: After executing
|
| 88 |
*
|
| 89 |
* $node = node_load(7);
|
| 90 |
* $output = relatedcontent($node, 'type', 'node_view', array(true));
|
| 91 |
*
|
| 92 |
* $output[$type][], where $type is the name of an content type, is an array of
|
| 93 |
* teasers, as provided by node_view(), with related content for the node
|
| 94 |
* with the node object in $node.
|
| 95 |
*
|
| 96 |
* Example 3: Following code print the full bodies of nodes with related
|
| 97 |
* content of the node with id 205. The output is grouped by the authors. Each
|
| 98 |
* group is preceded with a heading stating the author's name.
|
| 99 |
*
|
| 100 |
* $output = relatedcontent(205, 'name', 'node_view', array(false));
|
| 101 |
* foreach ($output as $group => $contents) {
|
| 102 |
* $author = $group ? $group : t('Anonymous');
|
| 103 |
* echo "<h3>$author</h3>";
|
| 104 |
* echo implode('', $contents);
|
| 105 |
* }
|
| 106 |
*
|
| 107 |
* @param $node
|
| 108 |
* is either the id of a node (nid) or a node object as loaded by node_load().
|
| 109 |
* @param $output_grouped
|
| 110 |
* is the name of the node field by which the content should be grouped, e.g.
|
| 111 |
* 'type', 'name', 'uid' and 'status', or FALSE if the output should not be
|
| 112 |
* grouped.
|
| 113 |
* @param $content_function
|
| 114 |
* is a callback function that transforms a node object, passed in as its
|
| 115 |
* first argument, to the desired representation, e.g. the teaser or body.
|
| 116 |
* If omitted or empty, the default transform is just returning the node
|
| 117 |
* object itself. If omitted or empty, grouping s not performed.
|
| 118 |
* @param $content_function_args
|
| 119 |
* is an optional array with values that are passed in as argument 2, 3, and
|
| 120 |
* so forth, when calling the callback function $content_function.
|
| 121 |
* @returns
|
| 122 |
* An array whose keys are the names by which the output should be grouped,
|
| 123 |
* i.e. names of content types or authors, or 'all', depending on
|
| 124 |
* $output_grouped, and whose values are arrays with the return values of
|
| 125 |
* calling $content_function for the nodes with related content.
|
| 126 |
*/
|
| 127 |
function relatedcontent($node, $output_grouped = false, $content_function = '', $content_function_args = array()) {
|
| 128 |
|
| 129 |
// Load the node if only the node id was given.
|
| 130 |
if (is_numeric($node)) {
|
| 131 |
$node = node_load($node);
|
| 132 |
}
|
| 133 |
|
| 134 |
// Abort if RelatedContent is disabled for the node's content type.
|
| 135 |
if (!relatedcontent_variable_enabled($node->type)) return;
|
| 136 |
|
| 137 |
// Handle missing arguments.
|
| 138 |
if (!$content_function) {
|
| 139 |
$content_function = create_function('$n', 'return $n;');
|
| 140 |
}
|
| 141 |
if (!isset($content_function_args)) {
|
| 142 |
$content_function_args = array();
|
| 143 |
}
|
| 144 |
|
| 145 |
// Get the job done.
|
| 146 |
return _relatedcontent($node, $output_grouped, $content_function, $content_function_args);
|
| 147 |
|
| 148 |
}
|
| 149 |
|
| 150 |
|
| 151 |
/******************************************************************************
|
| 152 |
* THEMEABLE FUNCTIONS
|
| 153 |
******************************************************************************/
|
| 154 |
|
| 155 |
/**
|
| 156 |
* Themeable function for the related content.
|
| 157 |
*
|
| 158 |
* @ingroup themeable
|
| 159 |
*/
|
| 160 |
function theme_relatedcontent($output, $grouped = null, $teaser = null, $page = null) {
|
| 161 |
|
| 162 |
global $theme_engine;
|
| 163 |
|
| 164 |
// If the current theme engine is PHPTemplate and the current theme has a
|
| 165 |
// RelatedContent template file, process the provided template file, and
|
| 166 |
// return the resulting output. Otherwise, return the default theming.
|
| 167 |
if ($theme_engine == 'phptemplate' && file_exists(path_to_theme() .'/relatedcontent.tpl.php')) {
|
| 168 |
$variables = array('output' => $output, 'grouped' => $grouped, 'teaser' => $teaser, 'page' => $page);
|
| 169 |
$out = _phptemplate_callback('relatedcontent', $variables);
|
| 170 |
}
|
| 171 |
else {
|
| 172 |
foreach ($output as $group => $contents) {
|
| 173 |
$out .= "<div class=\"relatedcontent-nodes $group\">";
|
| 174 |
if ($grouped) {
|
| 175 |
$out .= "<h3>$group</h3>";
|
| 176 |
}
|
| 177 |
$out .= implode('', $contents);
|
| 178 |
$out .= '</div>';
|
| 179 |
}
|
| 180 |
}
|
| 181 |
|
| 182 |
return $out;
|
| 183 |
|
| 184 |
}
|
| 185 |
|
| 186 |
/**
|
| 187 |
* Themeable function for displaying the table with the nodes to select from.
|
| 188 |
*
|
| 189 |
* @ingroup themeable
|
| 190 |
*/
|
| 191 |
function theme_relatedcontent_form($form) {
|
| 192 |
|
| 193 |
// Prepare the table for rendering.
|
| 194 |
$header = array(theme('table_select_header_cell'), t('Title'), t('Type'), t('Created'), t('Author'));
|
| 195 |
if (isset($form['title']) && is_array($form['title'])) {
|
| 196 |
foreach (element_children($form['title']) as $nid) {
|
| 197 |
$row = array();
|
| 198 |
$row[] = drupal_render($form['nodes'][$nid]);
|
| 199 |
$row[] = drupal_render($form['title'][$nid]);
|
| 200 |
$row[] = drupal_render($form['name'][$nid]);
|
| 201 |
$row[] = drupal_render($form['created'][$nid]);
|
| 202 |
$row[] = drupal_render($form['username'][$nid]);
|
| 203 |
$rows[] = $row;
|
| 204 |
}
|
| 205 |
}
|
| 206 |
else {
|
| 207 |
$rows[] = array(array('data' => t('No nodes available.'), 'colspan' => count($header)));
|
| 208 |
}
|
| 209 |
|
| 210 |
// Render the form.
|
| 211 |
$output .= drupal_render($form['intro']);
|
| 212 |
$output .= theme('table', $header, $rows);
|
| 213 |
$output .= drupal_render($form);
|
| 214 |
|
| 215 |
return $output;
|
| 216 |
|
| 217 |
}
|
| 218 |
|
| 219 |
|
| 220 |
/******************************************************************************
|
| 221 |
* HOOKS
|
| 222 |
******************************************************************************/
|
| 223 |
|
| 224 |
/**
|
| 225 |
* Implementation of hook_menu().
|
| 226 |
*/
|
| 227 |
function relatedcontent_menu($may_cache) {
|
| 228 |
$items = array();
|
| 229 |
if (!$may_cache) {
|
| 230 |
_relatedcontent_menu_css($items);
|
| 231 |
_relatedcontent_menu_relatedcontent($items);
|
| 232 |
}
|
| 233 |
return $items;
|
| 234 |
}
|
| 235 |
|
| 236 |
/**
|
| 237 |
* Implementation of hook_form_alter().
|
| 238 |
*/
|
| 239 |
function relatedcontent_form_alter($form_id, &$form) {
|
| 240 |
if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
|
| 241 |
_relatedcontent_form_alter_node_type($form);
|
| 242 |
}
|
| 243 |
}
|
| 244 |
|
| 245 |
/**
|
| 246 |
* Implementation of hook_nodeapi().
|
| 247 |
*/
|
| 248 |
function relatedcontent_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
|
| 249 |
if (relatedcontent_variable_enabled($node->type)) {
|
| 250 |
$function = "_relatedcontent_node_$op";
|
| 251 |
if (function_exists($function)) {
|
| 252 |
return $function($node, $a3, $a4);
|
| 253 |
}
|
| 254 |
}
|
| 255 |
}
|
| 256 |
|
| 257 |
/**
|
| 258 |
* Implementation of hook_help().
|
| 259 |
*/
|
| 260 |
function relatedcontent_help($section='admin/help#relatedcontent') {
|
| 261 |
switch ($section) {
|
| 262 |
case 'admin/help#relatedcontent':
|
| 263 |
return _relatedcontent_help_help();
|
| 264 |
}
|
| 265 |
}
|
| 266 |
|
| 267 |
|
| 268 |
/******************************************************************************
|
| 269 |
* IMPLEMENTATION OF THE MENU HOOK
|
| 270 |
******************************************************************************/
|
| 271 |
|
| 272 |
/**
|
| 273 |
* Add the module's CSS-file.
|
| 274 |
*/
|
| 275 |
function _relatedcontent_menu_css(&$items) {
|
| 276 |
$base = drupal_get_path('module', 'relatedcontent');
|
| 277 |
drupal_add_css("$base/relatedcontent.css");
|
| 278 |
}
|
| 279 |
|
| 280 |
/**
|
| 281 |
* If viewing a node of a type for which RelatedContent is enabled,
|
| 282 |
* provide paths and tabs for adding and removing related content of the
|
| 283 |
* node.
|
| 284 |
*/
|
| 285 |
function _relatedcontent_menu_relatedcontent(&$items) {
|
| 286 |
|
| 287 |
// Abort if the requested page is not a node.
|
| 288 |
if (arg(0) != 'node' || !is_numeric($nid = arg(1))) return;
|
| 289 |
|
| 290 |
// Abort if the module is not enabled for the content type of the node.
|
| 291 |
if (!relatedcontent_variable_enabled(_relatedcontent_db_content_type($nid))) return;
|
| 292 |
|
| 293 |
// Load the node.
|
| 294 |
$node = node_load($nid);
|
| 295 |
|
| 296 |
// Determine whether the user has right to update the node.
|
| 297 |
$node_access = node_access('update', $node);
|
| 298 |
|
| 299 |
// Add the primary tab for RelatedContent.
|
| 300 |
$items[] = array(
|
| 301 |
'path' => "node/$nid/relatedcontent",
|
| 302 |
'type' => MENU_LOCAL_TASK,
|
| 303 |
'title' => t('RelatedContent'),
|
| 304 |
'callback' => 'drupal_get_form',
|
| 305 |
'callback arguments' => array('_relatedcontent_form_list', $node),
|
| 306 |
'access' => $node_access,
|
| 307 |
'weight' => 5,
|
| 308 |
);
|
| 309 |
|
| 310 |
// Add the secondary tab listing all nodes with related content.
|
| 311 |
// Make it default.
|
| 312 |
$items[] = array(
|
| 313 |
'path' => "node/$nid/relatedcontent/list",
|
| 314 |
'type' => MENU_DEFAULT_LOCAL_TASK,
|
| 315 |
'title' => t('Overview'),
|
| 316 |
'callback' => 'drupal_get_form',
|
| 317 |
'callback arguments' => array('_relatedcontent_form_list', $node),
|
| 318 |
'access' => $node_access,
|
| 319 |
'weight' => -11,
|
| 320 |
);
|
| 321 |
|
| 322 |
// Add the secondary tab listing all nodes provided by the view.
|
| 323 |
$views = _relatedcontent_db_views();
|
| 324 |
$view = relatedcontent_variable_view($node->type);
|
| 325 |
$items[] = array(
|
| 326 |
'path' => "node/$nid/relatedcontent/$view",
|
| 327 |
'type' => MENU_LOCAL_TASK,
|
| 328 |
'title' => $views[$view],
|
| 329 |
'callback' => 'drupal_get_form',
|
| 330 |
'callback arguments' => array('_relatedcontent_form_view', $node, $view),
|
| 331 |
'access' => $node_access,
|
| 332 |
'weight' => 0,
|
| 333 |
);
|
| 334 |
|
| 335 |
}
|
| 336 |
|
| 337 |
|
| 338 |
/******************************************************************************
|
| 339 |
* IMPLEMENTATION OF THE NODEAPI HOOK
|
| 340 |
******************************************************************************/
|
| 341 |
|
| 342 |
/**
|
| 343 |
* A node is being viewed.
|
| 344 |
*/
|
| 345 |
function _relatedcontent_node_view(&$node, $teaser, $page) {
|
| 346 |
|
| 347 |
// Get settings.
|
| 348 |
$output_placing = relatedcontent_variable_output_placing($node->type);
|
| 349 |
$output_teasers = $teaser || relatedcontent_variable_output_teasers($node->type);
|
| 350 |
$output_grouped = relatedcontent_variable_output_grouped($node->type);
|
| 351 |
|
| 352 |
// Abort if output isn't wanted in general, or when viewing a teaser.
|
| 353 |
if (!$output_placing || $teaser && relatedcontent_variable_exclude_teasers($node->type)) return;
|
| 354 |
|
| 355 |
// Get the related content. Abort if there is no content.
|
| 356 |
if (!($output = _relatedcontent($node, $output_grouped, 'node_view', array($output_teasers)))) return;
|
| 357 |
|
| 358 |
// Theme the related content.
|
| 359 |
$output = theme('relatedcontent', $output, $output_grouped, $teaser, $page);
|
| 360 |
|
| 361 |
// Add the themed output to the node's body.
|
| 362 |
switch ($output_placing) {
|
| 363 |
case 'beginning':
|
| 364 |
$node->content['body']['#value'] = $output . $node->content['body']['#value'];
|
| 365 |
break;
|
| 366 |
case 'end':
|
| 367 |
$node->content['body']['#value'] = $node->content['body']['#value'] . $output;
|
| 368 |
break;
|
| 369 |
}
|
| 370 |
|
| 371 |
}
|
| 372 |
|
| 373 |
/**
|
| 374 |
* A node is being loaded.
|
| 375 |
*/
|
| 376 |
function _relatedcontent_node_load(&$node) {
|
| 377 |
return array('nodes' => _relatedcontent_db_load($node->nid));
|
| 378 |
}
|
| 379 |
|
| 380 |
/**
|
| 381 |
* A node is being deleted.
|
| 382 |
*/
|
| 383 |
function _relatedcontent_node_delete(&$node) {
|
| 384 |
_relatedcontent_db_delete($node->nid);
|
| 385 |
}
|
| 386 |
|
| 387 |
|
| 388 |
/******************************************************************************
|
| 389 |
* IMPLEMENTATION OF THE HELP HOOK
|
| 390 |
*****************************************************************************/
|
| 391 |
|
| 392 |
function _relatedcontent_help_help() {
|
| 393 |
|
| 394 |
// Load the help text.
|
| 395 |
$help = file_get_contents('relatedcontent.help', FILE_USE_INCLUDE_PATH);
|
| 396 |
|
| 397 |
// Abort if we can't find the file.
|
| 398 |
if (!$help) return;
|
| 399 |
|
| 400 |
// Substitute variables and translate.
|
| 401 |
$version = str_replace(array('$Re'.'vision:', ' $'), array('', ''), '$Revision: 1.16.2.2 $');
|
| 402 |
$year = substr('$Date: 2008/01/09 21:41:48 $', 7, 4);
|
| 403 |
$help = t($help, array('!version' => $version, '!year' => $year));
|
| 404 |
|
| 405 |
// Add some style. (This is really dirty, but...)
|
| 406 |
$style = <<<EOT
|
| 407 |
<style type="text/css" media="all">
|
| 408 |
/*<![CDATA[*/
|
| 409 |
code, kbd, pre { padding: 1px; font-family: "Bitstream Vera Sans Mono", Monaco, "Lucida Console", monospace; background-color: #EDF1F3; }
|
| 410 |
/*]]>*/
|
| 411 |
</style>
|
| 412 |
EOT;
|
| 413 |
$help = $style . $help;
|
| 414 |
|
| 415 |
return $help;
|
| 416 |
|
| 417 |
}
|
| 418 |
|
| 419 |
|
| 420 |
/******************************************************************************
|
| 421 |
* ALTER NODE TYPE FORM
|
| 422 |
******************************************************************************/
|
| 423 |
|
| 424 |
/**
|
| 425 |
* Add RelatedContent settings to the node type forms.
|
| 426 |
*/
|
| 427 |
function _relatedcontent_form_alter_node_type(&$form) {
|
| 428 |
|
| 429 |
// Get the node type
|
| 430 |
$type = $form['#node_type']->type;
|
| 431 |
|
| 432 |
// Insert RelatedContent's fieldset into the form.
|
| 433 |
$form['relatedcontent'] = array(
|
| 434 |
'#type' => 'fieldset',
|
| 435 |
'#title' => t('RelatedContent settings'),
|
| 436 |
'#description' => t(
|
| 437 |
'Configure how RelatedContent should work on nodes of the type <em>'. $type .'</em>. For more information, see the !help.',
|
| 438 |
array('!help' => l(t('help page'), 'admin/help/relatedcontent'))
|
| 439 |
),
|
| 440 |
'#collapsible' => true,
|
| 441 |
'#collapsed' => !relatedcontent_variable_enabled($type),
|
| 442 |
'#weight' => $form['submit']['#weight'],
|
| 443 |
);
|
| 444 |
|
| 445 |
// Ask if related content should be enabled on this content type.
|
| 446 |
$form['relatedcontent']['relatedcontent_enabled'] = array(
|
| 447 |
'#type' => 'checkbox',
|
| 448 |
'#title' => t('Enable'),
|
| 449 |
'#description' => t('Check to enable RelatedContent on nodes of the content type <em>'. $type .'</em>.'),
|
| 450 |
'#default_value' => relatedcontent_variable_enabled($type),
|
| 451 |
);
|
| 452 |
|
| 453 |
// Ask which views, if any, to use as node list in the related content select form.
|
| 454 |
$form['relatedcontent']['relatedcontent_view'] = array(
|
| 455 |
'#type' => 'select',
|
| 456 |
'#title' => t('Source of nodes'),
|
| 457 |
'#description' => t(
|
| 458 |
'Select the view that will provide the nodes to select from. If no view is selected, the table with nodes to select from is disabled. Any previously selected nodes will remain selected.',
|
| 459 |
array('!view' => l('view' , 'admin/build/views'))
|
| 460 |
),
|
| 461 |
'#options' => _relatedcontent_db_views(),
|
| 462 |
'#default_value' => relatedcontent_variable_view($type),
|
| 463 |
);
|
| 464 |
|
| 465 |
// Ask how many nodes to list in the related content select form.
|
| 466 |
$form['relatedcontent']['relatedcontent_length'] = array(
|
| 467 |
'#type' => 'select',
|
| 468 |
'#title' => t('Length of node table'),
|
| 469 |
'#description' => t('The number of nodes to shown on each page of the table with nodes to select from.'),
|
| 470 |
'#options' => array(10 => 10, 20 => 20, 50 => 50, 100 => 100, 0 => t('All nodes')),
|
| 471 |
'#default_value' => relatedcontent_variable_length($type),
|
| 472 |
);
|
| 473 |
|
| 474 |
// Ask where the related nodes are going to be placed in the body.
|
| 475 |
$form['relatedcontent']['relatedcontent_exclude_teasers'] = array(
|
| 476 |
'#type' => 'radios',
|
| 477 |
'#title' => t('Teasers'),
|
| 478 |
'#description' => t('Choose if related content should be shown in teasers.'),
|
| 479 |
'#options' => array(false => t('Include'), true => t('Exclude')),
|
| 480 |
'#default_value' => relatedcontent_variable_exclude_teasers($type),
|
| 481 |
);
|
| 482 |
|
| 483 |
// Ask where the related nodes are going to be placed in the body.
|
| 484 |
$form['relatedcontent']['relatedcontent_output_placing'] = array(
|
| 485 |
'#type' => 'radios',
|
| 486 |
'#title' => t('Placing'),
|
| 487 |
'#description' => t('Choose where the related content should be outputted.'),
|
| 488 |
'#options' => array(false => 'No output', 'beginning' => t('Beginning'), 'end' => t('End')),
|
| 489 |
'#default_value' => relatedcontent_variable_output_placing($type),
|
| 490 |
);
|
| 491 |
|
| 492 |
// Ask the kind of grouping, if any.
|
| 493 |
$form['relatedcontent']['relatedcontent_output_grouped'] = array(
|
| 494 |
'#type' => 'radios',
|
| 495 |
'#title' => t('Grouping'),
|
| 496 |
'#description' => t('Choose how the related content should be grouped.'),
|
| 497 |
'#options' => array(false => t('No grouping'), 'type' => t('Content type'), 'name' => t('Author')),
|
| 498 |
'#default_value' => relatedcontent_variable_output_grouped($type),
|
| 499 |
);
|
| 500 |
|
| 501 |
// Ask if the the related nodes are going to be output as teasers or bodies.
|
| 502 |
$form['relatedcontent']['relatedcontent_output_teasers'] = array(
|
| 503 |
'#type' => 'radios',
|
| 504 |
'#title' => t('Format'),
|
| 505 |
'#description' => t('Choose whether the related content should be viewed as teasers or bodies of the selected nodes.'),
|
| 506 |
'#options' => array(true => t('Teaser'), false => t('Body')),
|
| 507 |
'#default_value' => relatedcontent_variable_output_teasers($type),
|
| 508 |
);
|
| 509 |
|
| 510 |
// Move down the submit and delete/reset buttons.
|
| 511 |
$form['submit']['#weight'] += 1;
|
| 512 |
if ($form['delete']) {
|
| 513 |
$form['delete']['#weight'] = $form['submit']['#weight'];
|
| 514 |
}
|
| 515 |
else {
|
| 516 |
$form['reset']['#weight'] = $form['submit']['#weight'];
|
| 517 |
}
|
| 518 |
|
| 519 |
}
|
| 520 |
|
| 521 |
|
| 522 |
/******************************************************************************
|
| 523 |
* RELATED CONTENT LIST FORM
|
| 524 |
******************************************************************************/
|
| 525 |
|
| 526 |
/**
|
| 527 |
* Builds the related content list form.
|
| 528 |
*/
|
| 529 |
function _relatedcontent_form_list(&$node) {
|
| 530 |
|
| 531 |
$form = array();
|
| 532 |
|
| 533 |
// Build the form.
|
| 534 |
_relatedcontent_form_list_introduction($form, $node->type);
|
| 535 |
_relatedcontent_form_list_table($form, $node->nodes);
|
| 536 |
_relatedcontent_form_list_buttons($form);
|
| 537 |
_relatedcontent_form_list_directives($form);
|
| 538 |
|
| 539 |
return $form;
|
| 540 |
|
| 541 |
}
|
| 542 |
|
| 543 |
/**
|
| 544 |
* Helper function to _relatedcontent_form_list(). Adds a introductory text
|
| 545 |
* to the form.
|
| 546 |
*/
|
| 547 |
function _relatedcontent_form_list_introduction(&$form, $type) {
|
| 548 |
$form['intro'] = array(
|
| 549 |
'#value' => t(
|
| 550 |
"Select nodes to be removed from this <em>$type</em>. For more information, see the !help.",
|
| 551 |
array('!help' => l(t('help page'), 'admin/help/relatedcontent'))
|
| 552 |
),
|
| 553 |
);
|
| 554 |
}
|
| 555 |
|
| 556 |
/**
|
| 557 |
* Helper function to _relatedcontent_form_list(). Adds button to the form.
|
| 558 |
*/
|
| 559 |
function _relatedcontent_form_list_table(&$form, $selected_nodes) {
|
| 560 |
foreach ($selected_nodes as $nid => $ordinal) {
|
| 561 |
$node = node_load($nid);
|
| 562 |
$form['nodes'][$node->nid] = array(
|
| 563 |
'#type' => 'checkbox',
|
| 564 |
'#return_value' => $ordinal,
|
| 565 |
'#default_value' => true,
|
| 566 |
);
|
| 567 |
$form['title'][$node->nid] = array('#value' => l($node->title, 'node/'. $node->nid) .' '. theme('mark', node_mark($node->nid, $node->changed)));
|
| 568 |
$form['name'][$node->nid] = array('#value' => node_get_types('name', $node));
|
| 569 |
$form['created'][$node->nid] = array('#value' => format_date($node->created, 'small'));
|
| 570 |
$form['username'][$node->nid] = array('#value' => theme('username', $node));
|
| 571 |
}
|
| 572 |
}
|
| 573 |
|
| 574 |
/**
|
| 575 |
* Helper function to _relatedcontent_form_list(). Adds the table of nodes with
|
| 576 |
* related content to the form.
|
| 577 |
*/
|
| 578 |
function _relatedcontent_form_list_buttons(&$form) {
|
| 579 |
$form['buttons']['update'] = array(
|
| 580 |
'#type' => 'submit',
|
| 581 |
'#value' => t('Update'),
|
| 582 |
'#weight' => 5,
|
| 583 |
);
|
| 584 |
}
|
| 585 |
|
| 586 |
|
| 587 |
/**
|
| 588 |
* Helper function to _relatedcontent_form_list(). Adds form processing
|
| 589 |
* directives.
|
| 590 |
*/
|
| 591 |
function _relatedcontent_form_list_directives(&$form) {
|
| 592 |
$form['#tree'] = true;
|
| 593 |
$form['#theme'] = 'relatedcontent_form';
|
| 594 |
}
|
| 595 |
|
| 596 |
/**
|
| 597 |
* Handles the submission of the related content list form.
|
| 598 |
*/
|
| 599 |
function _relatedcontent_form_list_submit($form_id, &$form_values) {
|
| 600 |
|
| 601 |
// Get the nid of the current node.
|
| 602 |
$nid = arg(1);
|
| 603 |
|
| 604 |
// We are saving. Store the selected nodes in the node.
|
| 605 |
$nodes = array_filter($form_values['nodes']);
|
| 606 |
_relatedcontent_db_delete($nid);
|
| 607 |
_relatedcontent_db_insert($nid, $nodes);
|
| 608 |
|
| 609 |
// Return to the regular view of the node.
|
| 610 |
return "node/$nid/relatedcontent";
|
| 611 |
|
| 612 |
}
|
| 613 |
|
| 614 |
|
| 615 |
/******************************************************************************
|
| 616 |
* RELATED CONTENT VIEW FORM
|
| 617 |
******************************************************************************/
|
| 618 |
|
| 619 |
/**
|
| 620 |
* Builds the related content view form.
|
| 621 |
*/
|
| 622 |
function _relatedcontent_form_view(&$node, $view_id, $form_values = null) {
|
| 623 |
|
| 624 |
$form = array();
|
| 625 |
|
| 626 |
// Get the view that will provide the node list. Abort if the the view has
|
| 627 |
// been removed and the content type hasn't been updated accordingly.
|
| 628 |
if (!($view = _relatedcontent_form_get_view($view_id, $node->type))) return;
|
| 629 |
|
| 630 |
// Determine some data needed in the build process.
|
| 631 |
$page_length = relatedcontent_variable_length($node->type);
|
| 632 |
$page_number = _relatedcontent_form_get_next_page_number($form, $form_values['page_number'], $form_values['op']);
|
| 633 |
$db_result = views_build_view('result', $view, array(), false, $page_length, $page_number);
|
| 634 |
$max_page_number = _relatedcontent_form_get_max_page_number($form, $form_values['max_page_number'], $page_length, $db_result['countquery'], $db_result['rewrite_args']);
|
| 635 |
|
| 636 |
// If this is the initial call, initialize the selected node tracker with the
|
| 637 |
// nodes of related content already stored within the node.
|
| 638 |
if ($form_values == null) {
|
| 639 |
_relatedcontent_track_initialize($node->nid, $node->nodes);
|
| 640 |
}
|
| 641 |
|
| 642 |
// Build the form.
|
| 643 |
_relatedcontent_form_view_introduction($form, $node->type);
|
| 644 |
_relatedcontent_form_view_table($form, $db_result['result'], $node->nid);
|
| 645 |
_relatedcontent_form_view_buttons($form, $page_number, $max_page_number);
|
| 646 |
_relatedcontent_form_view_hidden_values($form, $view_id, $page_length, $page_number, $max_page_number);
|
| 647 |
_relatedcontent_form_view_directives($form);
|
| 648 |
|
| 649 |
return $form;
|
| 650 |
|
| 651 |
}
|
| 652 |
|
| 653 |
/**
|
| 654 |
* Load the view. Display an error message if it has been removed.
|
| 655 |
*/
|
| 656 |
function _relatedcontent_form_get_view($view, $type) {
|
| 657 |
|
| 658 |
// Load the named view and return it.
|
| 659 |
if ($view = views_get_view($view)) return $view;
|
| 660 |
|
| 661 |
// No view was given, or the named view didn't exist. Log and display
|
| 662 |
// an error message.
|
| 663 |
$message = t(
|
| 664 |
'RelatedContent uses <a href="!views-url">views</a> as its source of nodes. The specified view <em>@view-name</em> doesn\'t exist any more. Please, go to the <a href="!settings-url">settings of the content type "@content-type"</a> and select another view.',
|
| 665 |
array(
|
| 666 |
'!views-url' => url('admin/build/views'),
|
| 667 |
'!settings-url' => url("admin/content/types/$type"),
|
| 668 |
'@view-name' => $view_name,
|
| 669 |
'@content-type' => $type,
|
| 670 |
)
|
| 671 |
);
|
| 672 |
watchdog('relatedcontent', $message, WATCHDOG_ERROR);
|
| 673 |
drupal_set_message($message, 'error');
|
| 674 |
|
| 675 |
}
|
| 676 |
|
| 677 |
/**
|
| 678 |
* Advance the page. If $page_number is not set, we are on the first page.
|
| 679 |
*/
|
| 680 |
function _relatedcontent_form_get_next_page_number(&$form, $page_number, $op) {
|
| 681 |
if (!isset($page_number)) return 0;
|
| 682 |
switch ($op) {
|
| 683 |
case t('Next'):
|
| 684 |
return ++$page_number;
|
| 685 |
case t('Previous'):
|
| 686 |
return --$page_number;
|
| 687 |
}
|
| 688 |
}
|
| 689 |
|
| 690 |
/**
|
| 691 |
* Calculate the number of the very last page.
|
| 692 |
*/
|
| 693 |
function _relatedcontent_form_get_max_page_number(&$form, $max_page_number, $page_length, $count_query, $rewrite_args) {
|
| 694 |
if (!isset($max_page_number)) {
|
| 695 |
if ($page_length) {
|
| 696 |
$count = db_rewrite_sql($count_query, 'node', 'nid', $rewrite_args);
|
| 697 |
$count = db_result(db_query($count));
|
| 698 |
$max_page_number = ceil($count / $page_length) - 1;
|
| 699 |
}
|
| 700 |
else {
|
| 701 |
$max_page_number = 0;
|
| 702 |
}
|
| 703 |
}
|
| 704 |
return $max_page_number;
|
| 705 |
}
|
| 706 |
|
| 707 |
/**
|
| 708 |
* Adds a introductory text to the form.
|
| 709 |
*/
|
| 710 |
function _relatedcontent_form_view_introduction(&$form, $type) {
|
| 711 |
$form['intro'] = array(
|
| 712 |
'#value' => t(
|
| 713 |
"Select nodes to be added to this <em>$type</em>. For more information, see the !help.",
|
| 714 |
array('!help' => l(t('help page'), 'admin/help/relatedcontent'))
|
| 715 |
),
|
| 716 |
);
|
| 717 |
}
|
| 718 |
|
| 719 |
/**
|
| 720 |
* Adds the table of nodes with related content to the form.
|
| 721 |
*/
|
| 722 |
function _relatedcontent_form_view_table(&$form, $db_result, $nid) {
|
| 723 |
$ordinal = 0;
|
| 724 |
$selected_nodes = _relatedcontent_track_get_selected_nodes($nid);
|
| 725 |
while ($node = db_fetch_object($db_result)) {
|
| 726 |
$node = node_load($node->nid);
|
| 727 |
$form['nodes'][$node->nid] = array(
|
| 728 |
'#type' => 'checkbox',
|
| 729 |
'#return_value' => ++$ordinal,
|
| 730 |
'#attributes' => $selected_nodes[$node->nid] ? array('checked' => 'checked') : null, // See http://drupal.org/node/187413.
|
| 731 |
);
|
| 732 |
$form['title'][$node->nid] = array('#value' => l($node->title, 'node/'. $node->nid) .' '. theme('mark', node_mark($node->nid, $node->changed)));
|
| 733 |
$form['name'][$node->nid] = array('#value' => node_get_types('name', $node));
|
| 734 |
$form['created'][$node->nid] = array('#value' => format_date($node->created, 'small'));
|
| 735 |
$form['username'][$node->nid] = array('#value' => theme('username', $node));
|
| 736 |
}
|
| 737 |
}
|
| 738 |
|
| 739 |
/**
|
| 740 |
* Adds buttons to the form.
|
| 741 |
*/
|
| 742 |
function _relatedcontent_form_view_buttons(&$form, $page_number, $max_page_number) {
|
| 743 |
|
| 744 |
// On all pages, except the first page, add a previous button.
|
| 745 |
if ($page_number > 0) {
|
| 746 |
$form['buttons']['previous'] = array(
|
| 747 |
'#type' => 'submit',
|
| 748 |
'#value' => t('Previous'),
|
| 749 |
'#weight' => -5,
|
| 750 |
);
|
| 751 |
}
|
| 752 |
|
| 753 |
// On all pages, except the last page, add a next button.
|
| 754 |
if ($page_number < $max_page_number) {
|
| 755 |
$form['buttons']['next'] = array(
|
| 756 |
'#type' => 'submit',
|
| 757 |
'#value' => t('Next'),
|
| 758 |
'#weight' => 0,
|
| 759 |
);
|
| 760 |
}
|
| 761 |
|
| 762 |
// On all pages, add an update button.
|
| 763 |
$form['buttons']['update'] = array(
|
| 764 |
'#type' => 'submit',
|
| 765 |
'#value' => t('Update'),
|
| 766 |
'#weight' => 5,
|
| 767 |
);
|
| 768 |
|
| 769 |
}
|
| 770 |
|
| 771 |
/**
|
| 772 |
* Adds hidden fields with information needed later.
|
| 773 |
*/
|
| 774 |
function _relatedcontent_form_view_hidden_values(&$form, $view_id, $page_length, $page_number, $max_page_number) {
|
| 775 |
|
| 776 |
// Store the view's id.
|
| 777 |
$form['view_id'] = array(
|
| 778 |
'#type' => 'hidden',
|
| 779 |
'#value' => $view_id,
|
| 780 |
);
|
| 781 |
|
| 782 |
// Store the number of nodes shown on a page.
|
| 783 |
$form['page_length'] = array(
|
| 784 |
'#type' => 'hidden',
|
| 785 |
'#value' => $page_length,
|
| 786 |
);
|
| 787 |
|
| 788 |
// Store the number of the current page.
|
| 789 |
$form['page_number'] = array(
|
| 790 |
'#type' => 'hidden',
|
| 791 |
'#value' => $page_number,
|
| 792 |
);
|
| 793 |
|
| 794 |
// Store the number of the very last page.
|
| 795 |
$form['max_page_number'] = array(
|
| 796 |
'#type' => 'hidden',
|
| 797 |
'#value' => $max_page_number,
|
| 798 |
);
|
| 799 |
|
| 800 |
}
|
| 801 |
|
| 802 |
/**
|
| 803 |
* Adds form processing directives.
|
| 804 |
*/
|
| 805 |
function _relatedcontent_form_view_directives(&$form) {
|
| 806 |
$form['#multistep'] = true;
|
| 807 |
$form['#tree'] = true;
|
| 808 |
$form['#theme'] = 'relatedcontent_form';
|
| 809 |
}
|
| 810 |
|
| 811 |
/**
|
| 812 |
* Handles the submission of the related content form.
|
| 813 |
*/
|
| 814 |
function _relatedcontent_form_view_submit($form_id, &$form_values) {
|
| 815 |
|
| 816 |
// Get the nid of the current node.
|
| 817 |
$nid = arg(1);
|
| 818 |
|
| 819 |
// Track changes done on the current form page.
|
| 820 |
$offset = $form_values['page_number'] * $form_values['page_length'];
|
| 821 |
_relatedcontent_track_update_selected_nodes($nid, $form_values['nodes'], $offset);
|
| 822 |
|
| 823 |
// If we are not saving, return to the form again.
|
| 824 |
if ($form_values['op'] != t('Update')) return false;
|
| 825 |
|
| 826 |
// We are saving. Finalize the tracking; and store the tracked changes.
|
| 827 |
$nodes = _relatedcontent_track_finalize($nid, $form_values['view_id'], $form_values['page_length'], $form_values['max_page_number']);
|
| 828 |
_relatedcontent_db_delete($nid);
|
| 829 |
_relatedcontent_db_insert($nid, $nodes);
|
| 830 |
|
| 831 |
// Return to the regular view of the node.
|
| 832 |
return "node/$nid/relatedcontent";
|
| 833 |
|
| 834 |
}
|
| 835 |
|
| 836 |
/******************************************************************************
|
| 837 |
* SELECTED NODES TRACKING
|
| 838 |
******************************************************************************/
|
| 839 |
|
| 840 |
/**
|
| 841 |
* Initalize the selected nodes tracker.
|
| 842 |
*
|
| 843 |
* @see _relatedcontent_track_finalize() for details.
|
| 844 |
*/
|
| 845 |
function _relatedcontent_track_initialize($nid, $nodes) {
|
| 846 |
|
| 847 |
// Tracks selected nodes as we move from on epage to another.
|
| 848 |
$_SESSION['relatedcontent'][$nid]['nodes'] = $nodes;
|
| 849 |
|
| 850 |
// Tracks the highets page number visited.
|
| 851 |
$_SESSION['relatedcontent'][$nid]['highest_page_number'] = 0;
|
| 852 |
|
| 853 |
}
|
| 854 |
|
| 855 |
/**
|
| 856 |
* Add $nodes to the selected nodes tracker.
|
| 857 |
*
|
| 858 |
* @see _relatedcontent_track_finalize() for details.
|
| 859 |
*/
|
| 860 |
function _relatedcontent_track_update_selected_nodes($nid, $nodes, $offset) {
|
| 861 |
|
| 862 |
// Keep track of the highest page number visited.
|
| 863 |
if ($_SESSION['relatedcontent'][$nid]['highest_page_number'] < $page_number) {
|
| 864 |
$_SESSION['relatedcontent'][$nid]['highest_page_number'] = $page_number;
|
| 865 |
}
|
| 866 |
|
| 867 |
// The ordinal numbers of the passed must be added by an offset to keep
|
| 868 |
// the sort order from one page to another.
|
| 869 |
foreach ($nodes as $id => $ordinal) {
|
| 870 |
if ($ordinal) {
|
| 871 |
$nodes[$id] += $offset;
|
| 872 |
}
|
| 873 |
}
|
| 874 |
|
| 875 |
// For keys in $_SESSION['relatedcontent'][$nid]['nodes'] for which there is no key
|
| 876 |
// in $nodes, append corresponding value $_SESSION['relatedcontent'][$nid]['nodes']
|
| 877 |
// to $node.
|
| 878 |
$nodes += $_SESSION['relatedcontent'][$nid]['nodes'];
|
| 879 |
|
| 880 |
// Remove all non-selcted nodes.
|
| 881 |
$nodes = array_filter($nodes);
|
| 882 |
|
| 883 |
// Store the nodes in the session.
|
| 884 |
$_SESSION['relatedcontent'][$nid]['nodes'] = $nodes;
|
| 885 |
|
| 886 |
}
|
| 887 |
|
| 888 |
/**
|
| 889 |
* Returns the traced selected nodes.
|
| 890 |
*
|
| 891 |
* @see _relatedcontent_track_finalize() for details.
|
| 892 |
*/
|
| 893 |
function _relatedcontent_track_get_selected_nodes($nid) {
|
| 894 |
return $_SESSION['relatedcontent'][$nid]['nodes'];
|
| 895 |
}
|
| 896 |
|
| 897 |
/**
|
| 898 |
* Finalizing the tracking, and returns the selected nodes.
|
| 899 |
*
|
| 900 |
* Nodes with related content get those loaded into an array where the keys are
|
| 901 |
* the nodes' id and the values are ordinal numbers implying an order by which
|
| 902 |
* they should be displayed. These initial ordinal numbers are negative.
|
| 903 |
*
|
| 904 |
* Upon first display of the table with nodes to select from, the tracker is
|
| 905 |
* initialized with these tuples of node id and negative ordinal numbers.
|
| 906 |
* Each time the user moves from one page of the table to another, or click on
|
| 907 |
* the Update button, the tracker updates the ordinal number of those nodes
|
| 908 |
* displayed on the page the user is leaving. The updated numbers are positive,
|
| 909 |
* and chosen to reflect the sort order among the seen nodes.
|
| 910 |
*
|
| 911 |
* We have a contract with the user to not break the sort order of already
|
| 912 |
* selected nodes if they are available in the view. We must therefore
|
| 913 |
* "visit" all pages that has not yet been displayed, and update the ordinal
|
| 914 |
* numbers of those remaining nodes which we find on these pages. In many
|
| 915 |
* applications of RelatedContent it is likely to find the remaining nodes
|
| 916 |
* among the first non-visited pages. This given, and the potential that
|
| 917 |
* there might be many unvisited pages, which translates into many round-
|
| 918 |
* trips to the database, we abort the search as soon as possible. However,
|
| 919 |
* if there are orphan nodes (see the on-line help), we will visit all pages
|
| 920 |
* in vain.
|
| 921 |
*/
|
| 922 |
function _relatedcontent_track_finalize($nid, $view_id, $page_length, $max_page_number) {
|
| 923 |
|
| 924 |
// Get the tracked information.
|
| 925 |
$nodes = $_SESSION['relatedcontent'][$nid]['nodes'];
|
| 926 |
$page = $_SESSION['relatedcontent'][$nid]['highest_page_number'];
|
| 927 |
|
| 928 |
// Get the node id of those nodes which haven't been seen. These are at the
|
| 929 |
// end of the array of tracked nodes and are recognized by negative ordinal
|
| 930 |
// numbers.
|
| 931 |
for (end($nodes); current($nodes) < 0 ; prev($nodes)) {
|
| 932 |
$remaining_nodes[key($nodes)] = true;
|
| 933 |
}
|
| 934 |
|
| 935 |
// Visit all pages that has not been displayed, and update the ordinal
|
| 936 |
// numbers of those remaining nodes which we find on these pages.
|
| 937 |
$view = views_get_view($view_id); // Get the view.
|
| 938 |
$ordinal = ($page + 1) * $page_length; // The last used ordinal number.
|
| 939 |
while (++$page <= $max_page_number) { // For each page not yet visited...
|
| 940 |
$info = views_build_view('result', $view, array(), false, $page_length, $page); // Get the nodes on the current page.
|
| 941 |
while ($node = db_fetch_object($info['result'])) { // For each node on the current page...
|
| 942 |
++$ordinal; // Next ordinal number.
|
| 943 |
if ($remaining_nodes[$node->nid]) { // If current node is among the remaining ones...
|
| 944 |
$nodes[$node->nid] = $ordinal; // Assign an ordinal number.
|
| 945 |
unset($remaining_nodes[$node->nid]); // Remove it from the list of remaining nodes.
|
| 946 |
if (count($remaining_nodes) == 0) break 2; // Abort as soon as possible.
|
| 947 |
}
|
| 948 |
}
|
| 949 |
}
|
| 950 |
|
| 951 |
// Clean up.
|
| 952 |
unset($_SESSION['relatedcontent'][$nid]);
|
| 953 |
|
| 954 |
return $nodes;
|
| 955 |
|
| 956 |
}
|
| 957 |
|
| 958 |
/******************************************************************************
|
| 959 |
* IMPLEMENTATION OF THE RELATEDCONTENT API
|
| 960 |
*****************************************************************************/
|
| 961 |
|
| 962 |
/**
|
| 963 |
* For each node with related content and which the user is allowed to view,
|
| 964 |
* append the related content to the end of the output buffer of the relevant
|
| 965 |
* group.
|
| 966 |
*/
|
| 967 |
function _relatedcontent(&$node, $output_grouped, $content_function, $args) {
|
| 968 |
array_unshift($args, 0);
|
| 969 |
foreach ($node->nodes as $nid => $ordinal) {
|
| 970 |
if ($nid && ($n = node_load($nid)) && node_access ('view', $n)) {
|
| 971 |
$args[0] = $n;
|
| 972 |
$key = $output_grouped ? $n->$output_grouped : 'all';
|
| 973 |
$output[$key][] = call_user_func_array($content_function, $args);
|
| 974 |
}
|
| 975 |
}
|
| 976 |
return $output;
|
| 977 |
}
|
| 978 |
|
| 979 |
|
| 980 |
/******************************************************************************
|
| 981 |
* PERSISTED VARIABLES
|
| 982 |
*****************************************************************************/
|
| 983 |
|
| 984 |
/**
|
| 985 |
* The persisted variable 'enabled' which is true/false whether the
|
| 986 |
* related content is enabled or not, respectively, for the content type.
|
| 987 |
* Default value, if the variable does not exists, is FALSE.
|
| 988 |
*/
|
| 989 |
function relatedcontent_variable_enabled($type, $enabled = null) {
|
| 990 |
return _relatedcontent_variable("relatedcontent_enabled_$type", $enabled, false);
|
| 991 |
}
|
| 992 |
|
| 993 |
/**
|
| 994 |
* The persisted variable 'view' containing the name of the view to feed the
|
| 995 |
* assembler for a node of the given type. Default value, if the variable does
|
| 996 |
* not exists, is ''.
|
| 997 |
*/
|
| 998 |
function relatedcontent_variable_view($type, $view = null) {
|
| 999 |
return _relatedcontent_variable("relatedcontent_view_$type", $view, '');
|
| 1000 |
}
|
| 1001 |
|
| 1002 |
/**
|
| 1003 |
* The persisted variable 'length' containing the number of nodes to show in
|
| 1004 |
* the assembler for a node of the given type. Default value, if the variable
|
| 1005 |
* does not exists, is 50.
|
| 1006 |
*/
|
| 1007 |
function relatedcontent_variable_length($type, $page_length = null) {
|
| 1008 |
return _relatedcontent_variable("relatedcontent_length_$type", $page_length, 50);
|
| 1009 |
}
|
| 1010 |
|
| 1011 |
/**
|
| 1012 |
* The persisted variable 'exclude_teasers' which is true/false whether the
|
| 1013 |
* related content should excluded in teasers or not respectively. Default
|
| 1014 |
* value, if the variable does not exists, is TRUE.
|
| 1015 |
*/
|
| 1016 |
function relatedcontent_variable_exclude_teasers($type, $exclude_teasers = null) {
|
| 1017 |
return _relatedcontent_variable("relatedcontent_exclude_teasers_$type", $exclude_teasers, true);
|
| 1018 |
}
|
| 1019 |
|
| 1020 |
/**
|
| 1021 |
* The persisted variable 'teasers' which is true/false whether the related
|
| 1022 |
* content should be viewed as teasers or full bodies respectively. Default
|
| 1023 |
* value, if the variable does not exists, is TRUE.
|
| 1024 |
*/
|
| 1025 |
function relatedcontent_variable_output_teasers($type, $teasers = null) {
|
| 1026 |
return _relatedcontent_variable("relatedcontent_output_teasers_$type", $teasers, true);
|
| 1027 |
}
|
| 1028 |
|
| 1029 |
/**
|
| 1030 |
* The persisted variable 'grouped' which is false or a string with the name
|
| 1031 |
* of the node attribute, e.g. 'type' and 'name', by which the related content
|
| 1032 |
* should be grouped by. Default value, if the variable does not exists, is
|
| 1033 |
* 'type'.
|
| 1034 |
*/
|
| 1035 |
function relatedcontent_variable_output_grouped($type, $grouped = null) {
|
| 1036 |
return _relatedcontent_variable("relatedcontent_output_grouped_$type", $grouped, 'type');
|
| 1037 |
}
|
| 1038 |
|
| 1039 |
/**
|
| 1040 |
* The persisted variable 'after' which is false if related content should not
|
| 1041 |
* be outputted, 'beginning' or 'end' if the output should be at the beginning
|
| 1042 |
* or end respectively. Default value, if the variable does not exists, is
|
| 1043 |
* 'end'.
|
| 1044 |
*/
|
| 1045 |
function relatedcontent_variable_output_placing($type, $after = null) {
|
| 1046 |
return _relatedcontent_variable("relatedcontent_output_placing_$type", $after, 'end');
|
| 1047 |
}
|
| 1048 |
|
| 1049 |
/**
|
| 1050 |
* Sets and gets the named persisted variable.
|
| 1051 |
*/
|
| 1052 |
function _relatedcontent_variable($name, $value = null, $default = null) {
|
| 1053 |
if (isset($value)) {
|
| 1054 |
variable_set($name, $value);
|
| 1055 |
}
|
| 1056 |
return variable_get($name, $default);
|
| 1057 |
}
|
| 1058 |
|
| 1059 |
|
| 1060 |
/******************************************************************************
|
| 1061 |
* DATABASE
|
| 1062 |
*****************************************************************************/
|
| 1063 |
|
| 1064 |
/**
|
| 1065 |
* Returns the content type of the node with the provided node id.
|
| 1066 |
*/
|
| 1067 |
function _relatedcontent_db_content_type($nid) {
|
| 1068 |
$db_result = db_query('SELECT type FROM {node} WHERE nid = %d', $nid);
|
| 1069 |
$db_result = db_fetch_object($db_result);
|
| 1070 |
return $db_result->type;
|
| 1071 |
}
|
| 1072 |
|
| 1073 |
/**
|
| 1074 |
* Returns the available views provided by the Views module.
|
| 1075 |
*/
|
| 1076 |
function _relatedcontent_db_views() {
|
| 1077 |
$views = array('' => '');
|
| 1078 |
$db_result = db_query('SELECT vid, name FROM {view_view} ORDER BY name');
|
| 1079 |
while ($view = db_fetch_object($db_result)) {
|
| 1080 |
$views[$view->vid] = $view->name;
|
| 1081 |
}
|
| 1082 |
return $views;
|
| 1083 |
}
|
| 1084 |
|
| 1085 |
/**
|
| 1086 |
* Insert nid of included nodes into database.
|
| 1087 |
*/
|
| 1088 |
function _relatedcontent_db_insert($nid, $nodes) {
|
| 1089 |
|
| 1090 |
// Build the VALUES clause for inserting multiple rows.
|
| 1091 |
foreach ($nodes as $include_nid => $ordinal) {
|
| 1092 |
$values .= "($nid, $include_nid, $ordinal),";
|
| 1093 |
}
|
| 1094 |
$values = substr($values, 0, -1);
|
| 1095 |
|
| 1096 |
// Insert the values, if any, into the RelatedContent table.
|
| 1097 |
if ($values) {
|
| 1098 |
db_query('INSERT INTO {relatedcontent} (nid, include_nid, ordinal_number) VALUES %s', $values);
|
| 1099 |
}
|
| 1100 |
|
| 1101 |
}
|
| 1102 |
|
| 1103 |
/**
|
| 1104 |
* Load nid of included nodes from database.
|
| 1105 |
*
|
| 1106 |
* Get the related content of $nid, and put it into a map. The keys are the id
|
| 1107 |
* of the nodes with the related content. The values are ordinal numbers. The
|
| 1108 |
* ordinal numbers are negative, going from -N, where N is the count of pairs
|
| 1109 |
* in the map, to -1. The reason is to flag that these numbers doesn't come
|
| 1110 |
* from the user selecting nodes, but from the database. The ordinal number
|
| 1111 |
* itself is irrelevant; it's only their relation to each other that matters.
|
| 1112 |
*/
|
| 1113 |
function _relatedcontent_db_load($nid) {
|
| 1114 |
$nodes = array();
|
| 1115 |
$db_result = db_query('SELECT include_nid FROM {relatedcontent} WHERE nid = %d GROUP BY ordinal_number', $nid);
|
| 1116 |
$ordinal = -db_num_rows($db_result);
|
| 1117 |
while ($row = db_fetch_object($db_result)) {
|
| 1118 |
$nodes[$row->include_nid] = $ordinal++;
|
| 1119 |
}
|
| 1120 |
return $nodes;
|
| 1121 |
}
|
| 1122 |
|
| 1123 |
/**
|
| 1124 |
* Delete nid of included nodes in database.
|
| 1125 |
*/
|
| 1126 |
function _relatedcontent_db_delete($nid) {
|
| 1127 |
db_query('DELETE FROM {relatedcontent} WHERE nid = %d', $nid);
|
| 1128 |
}
|