| 1 |
<?php
|
| 2 |
|
| 3 |
// $Id$
|
| 4 |
// Modeline for drupal
|
| 5 |
// vim: set expandtab tabstop=2 shiftwidth=2 autoindent smartindent filetype=php:
|
| 6 |
|
| 7 |
/*
|
| 8 |
* Copyright 2008 ThinkLeft (thinkleft.com.au)
|
| 9 |
* Copyright 2008 ProsePoint (www.prosepoint.org)
|
| 10 |
*/
|
| 11 |
|
| 12 |
/**
|
| 13 |
* Denotes that a reference is not enabled in any zone and should not
|
| 14 |
* be shown.
|
| 15 |
*/
|
| 16 |
define('COMPOSITE_ZONE_NONE', -1);
|
| 17 |
|
| 18 |
function _composite_compare($a = 'reset-zones', $b) {
|
| 19 |
static $zones;
|
| 20 |
|
| 21 |
if (is_string($a)) {
|
| 22 |
// $a is a command
|
| 23 |
if ($a == 'reset-zones') {
|
| 24 |
// Store zones information from $b
|
| 25 |
$zones = array_flip(array_keys($b));
|
| 26 |
}
|
| 27 |
// If $a is a string, return regardless.
|
| 28 |
return;
|
| 29 |
}
|
| 30 |
|
| 31 |
// Sort by zone (in the order defined by the layout).
|
| 32 |
if ((!empty($a['zone']) && !empty($b['zone'])) && ($place = ($zones[$a['zone']] - $zones[$b['zone']]))) {
|
| 33 |
return $place;
|
| 34 |
}
|
| 35 |
// Sort by weight.
|
| 36 |
$weight = $a['weight'] - $b['weight'];
|
| 37 |
if ($weight) {
|
| 38 |
return $weight;
|
| 39 |
}
|
| 40 |
// Sort by title.
|
| 41 |
return strcmp($a['info'], $b['info']);
|
| 42 |
}
|
| 43 |
|
| 44 |
// Helper function: Filter out properties, fill in 'info' field
|
| 45 |
function _composite_references_preprocess($original, $zones) {
|
| 46 |
// Build a list of references, also generating labels in the process
|
| 47 |
$references = array();
|
| 48 |
foreach (element_children($original) as $id) {
|
| 49 |
$reference = $original[$id];
|
| 50 |
composite_invoke_referenceapi($reference, 'info');
|
| 51 |
$references[$id] = $reference;
|
| 52 |
}
|
| 53 |
|
| 54 |
return $references;
|
| 55 |
}
|
| 56 |
|
| 57 |
/***********************************************************
|
| 58 |
* CONFIGURATION FUNCTIONS *
|
| 59 |
************************************************************/
|
| 60 |
|
| 61 |
/**
|
| 62 |
* Helper function to see if a node is a composite node
|
| 63 |
*/
|
| 64 |
function composite_enabled($node) {
|
| 65 |
return variable_get('composite_enabled_' . $node->type, FALSE);
|
| 66 |
}
|
| 67 |
|
| 68 |
function composite_get_types($type = '') {
|
| 69 |
static $types = null;
|
| 70 |
|
| 71 |
if (empty($types)) {
|
| 72 |
$types = array();
|
| 73 |
foreach (module_implements('composite_types') as $module) {
|
| 74 |
$data = module_invoke($module, 'composite_types');
|
| 75 |
|
| 76 |
foreach ($data as $k => $unused) {
|
| 77 |
// Fill in 'module' if not specified
|
| 78 |
if (!$data[$k]['module']) {
|
| 79 |
$data[$k]['module'] = $module;
|
| 80 |
}
|
| 81 |
|
| 82 |
// Prefix 'file' with module dir
|
| 83 |
if ($data[$k]['file']) {
|
| 84 |
$data[$k]['file'] = drupal_get_path('module', $module) .'/'. $data[$k]['file'];
|
| 85 |
}
|
| 86 |
}
|
| 87 |
$types = array_merge_recursive($types, $data);
|
| 88 |
}
|
| 89 |
|
| 90 |
foreach (module_implements('composite_types_alter') as $module) {
|
| 91 |
$function = $module . '_composite_types_alter';
|
| 92 |
$function($types);
|
| 93 |
}
|
| 94 |
}
|
| 95 |
|
| 96 |
if ($type == '')
|
| 97 |
return $types;
|
| 98 |
else if (array_key_exists($type, $types))
|
| 99 |
return $types[$type];
|
| 100 |
}
|
| 101 |
|
| 102 |
// Includes the 'file' parameter of a reference type, if defined.
|
| 103 |
function composite_include_file($type) {
|
| 104 |
if ($type['file'] && is_file($type['file'])) {
|
| 105 |
include_once $type['file'];
|
| 106 |
}
|
| 107 |
}
|
| 108 |
|
| 109 |
function composite_invoke_referenceapi(&$reference, $op, $a3 = NULL, $a4 = NULL) {
|
| 110 |
$type_def = composite_get_types($reference['type']);
|
| 111 |
if ($type_def) {
|
| 112 |
composite_include_file($type_def);
|
| 113 |
$function = $type_def['module'] .'_composite_'. $type_def['type'] .'_api';
|
| 114 |
if (function_exists($function)) {
|
| 115 |
return $function($reference, $op, $a3, $a4);
|
| 116 |
}
|
| 117 |
}
|
| 118 |
}
|
| 119 |
|
| 120 |
function composite_get_layouts($type = '') {
|
| 121 |
static $layouts = array();
|
| 122 |
static $layouts_select = array();
|
| 123 |
|
| 124 |
if (!$layouts) {
|
| 125 |
// Would like to make layouts pluggable, but doesn't quite work
|
| 126 |
// because of inability to specify template files from other modules.
|
| 127 |
$layouts = module_invoke_all('composite_layouts');
|
| 128 |
|
| 129 |
// Do some defaults and path processing
|
| 130 |
// Construct a select list for convenience
|
| 131 |
foreach ($layouts as $key => $v) {
|
| 132 |
$key_dashed = strtr($key, '_', '-');
|
| 133 |
if (!$layouts[$key]['template']) {
|
| 134 |
$layouts[$key]['template'] = 'composite-layout-' . $key_dashed;
|
| 135 |
}
|
| 136 |
if (!$layouts[$key]['path']) {
|
| 137 |
$layouts[$key]['path'] = drupal_get_path('module', 'composite') . '/theme';
|
| 138 |
}
|
| 139 |
|
| 140 |
$css = $layouts[$key]['css'] ? $layouts[$key]['css'] : 'composite-layout-' . $key_dashed . '.css';
|
| 141 |
$css = $layouts[$key]['path'] . '/' . $css;
|
| 142 |
if (is_file($css)) {
|
| 143 |
$layouts[$key]['css'] = $css;
|
| 144 |
}
|
| 145 |
|
| 146 |
$icon = $layouts[$key]['icon'] ? $layouts[$key]['icon'] : 'composite-layout-' . $key_dashed . '.icon.png';
|
| 147 |
$icon = $layouts[$key]['path'] . '/' . $icon;
|
| 148 |
if (is_file($icon)) {
|
| 149 |
$layouts[$key]['icon'] = $icon;
|
| 150 |
}
|
| 151 |
|
| 152 |
$layouts_select[$key] = $v['name'];
|
| 153 |
}
|
| 154 |
}
|
| 155 |
|
| 156 |
if ($type == 'select') {
|
| 157 |
return $layouts_select;
|
| 158 |
}
|
| 159 |
else {
|
| 160 |
return $layouts;
|
| 161 |
}
|
| 162 |
}
|
| 163 |
|
| 164 |
function composite_get_layout($layout = '', $type = '') {
|
| 165 |
$layouts = composite_get_layouts($type);
|
| 166 |
return $layouts[$layout];
|
| 167 |
}
|
| 168 |
|
| 169 |
/***********************************************************
|
| 170 |
* MENU *
|
| 171 |
************************************************************/
|
| 172 |
|
| 173 |
/**
|
| 174 |
* Implementation of hook_menu().
|
| 175 |
*/
|
| 176 |
function composite_menu() {
|
| 177 |
$items = array();
|
| 178 |
|
| 179 |
// Generate local task for each reference type that requests it
|
| 180 |
$types = composite_get_types();
|
| 181 |
foreach ($types as $type) {
|
| 182 |
if ($type['local task']) {
|
| 183 |
$items['node/%node/composite_' . $type['type']] = array(
|
| 184 |
'title' => $type['label']['plural'],
|
| 185 |
'page callback' => 'composite_general_select_page',
|
| 186 |
'page arguments' => array($type['type'], 1),
|
| 187 |
'access callback' => 'composite_access',
|
| 188 |
'access arguments' => array($type['type'], 'update', 1),
|
| 189 |
'type' => MENU_LOCAL_TASK,
|
| 190 |
'weight' => 3,
|
| 191 |
'file' => 'composite.pages.inc',
|
| 192 |
);
|
| 193 |
}
|
| 194 |
}
|
| 195 |
$items['node/%node/composite_zones'] = array(
|
| 196 |
'title' => t('Zones'),
|
| 197 |
'page callback' => 'composite_zones_page',
|
| 198 |
'page arguments' => array(1),
|
| 199 |
'access callback' => 'composite_access',
|
| 200 |
'access arguments' => array('zones', 'update', 1),
|
| 201 |
'type' => MENU_LOCAL_TASK,
|
| 202 |
'weight' => 3,
|
| 203 |
'file' => 'composite.pages.inc',
|
| 204 |
);
|
| 205 |
|
| 206 |
return $items;
|
| 207 |
}
|
| 208 |
|
| 209 |
/**
|
| 210 |
* Access function used to turn tabs off, or defer to node_access
|
| 211 |
*/
|
| 212 |
function composite_access($tab, $op, $node) {
|
| 213 |
$access = FALSE;
|
| 214 |
|
| 215 |
if (composite_enabled($node) && $node->composite_layout) {
|
| 216 |
$access_function = 'node_access';
|
| 217 |
// Change access function if this tab was generated for a reference type
|
| 218 |
// and the optional task access function is defined
|
| 219 |
$type = composite_get_types($tab);
|
| 220 |
if ($type && $type['task access']) {
|
| 221 |
composite_include_file($type);
|
| 222 |
$function = $type['task access'];
|
| 223 |
if (function_exists($function)) {
|
| 224 |
$access_function = $function;
|
| 225 |
}
|
| 226 |
}
|
| 227 |
$access = $access_function($op, $node);
|
| 228 |
}
|
| 229 |
return $access;
|
| 230 |
}
|
| 231 |
|
| 232 |
/***********************************************************
|
| 233 |
* NODE MANIPULATIONS .ie load/save/view etc. *
|
| 234 |
************************************************************/
|
| 235 |
|
| 236 |
/**
|
| 237 |
* Implementation of hook_form_alter().
|
| 238 |
*/
|
| 239 |
function composite_form_alter(&$form, $form_state, $form_id) {
|
| 240 |
if ($form['#id'] == 'node-form' && composite_enabled($form['#node'])) {
|
| 241 |
// .../node/xx/edit or .../node/add/xx page
|
| 242 |
$node = $form['#node'];
|
| 243 |
$form['composite_settings'] = array(
|
| 244 |
'#type' => 'fieldset',
|
| 245 |
'#title' => t('Composite layout'),
|
| 246 |
'#description' => t('Select the desired composite layout of this node.'),
|
| 247 |
'#collapsible' => TRUE,
|
| 248 |
'#collapsed' => !$node->composite_layout,
|
| 249 |
'#weight' => 10,
|
| 250 |
);
|
| 251 |
|
| 252 |
$form['composite_settings']['composite_references'] = array(
|
| 253 |
'#type' => 'value',
|
| 254 |
'#value' => $node->composite_references,
|
| 255 |
);
|
| 256 |
$form['composite_settings']['composite_layout'] = array(
|
| 257 |
'#title' => t('Layout'),
|
| 258 |
'#type' => 'composite_layout_radios',
|
| 259 |
'#options' => array('--' => t('No composite layout')) + composite_get_layouts('select'),
|
| 260 |
'#default_value' => $node->composite_layout ? $node->composite_layout : '--',
|
| 261 |
);
|
| 262 |
$form['composite_settings']['composite_content_reference'] = array(
|
| 263 |
'#type' => 'checkbox',
|
| 264 |
'#title' => t('Node content should be included as an item available for composite layout.'),
|
| 265 |
'#description' => t('This has no effect if no composite layout is selected.'),
|
| 266 |
'#default_value' => isset($node->composite_references['content']['type']) && ($node->composite_references['content']['type'] == 'content'),
|
| 267 |
);
|
| 268 |
}
|
| 269 |
if ($form_id == 'node_type_form' && isset($form['#node_type'])) {
|
| 270 |
// .../admin/content/node-type/xx page
|
| 271 |
$node_type = $form['#node_type']->type;
|
| 272 |
$composite_enabled = variable_get('composite_enabled_' . $node_type, FALSE);
|
| 273 |
|
| 274 |
// The code below borrows heavily from nodereference_field_settings()
|
| 275 |
$form['composite_extra_config'] = array(
|
| 276 |
'#type' => 'fieldset',
|
| 277 |
'#title' => t('Composite node'),
|
| 278 |
'#collapsible' => TRUE,
|
| 279 |
'#collapsed' => !$composite_enabled,
|
| 280 |
);
|
| 281 |
$form['composite_extra_config']['composite_enabled'] = array(
|
| 282 |
'#title' => t('Make this type a composite node type'),
|
| 283 |
'#type' => 'checkbox',
|
| 284 |
'#default_value' => $composite_enabled,
|
| 285 |
);
|
| 286 |
|
| 287 |
$form['composite_extra_config']['composite_referenceable_types'] = array(
|
| 288 |
'#title' => t('Content types that can be referenced'),
|
| 289 |
'#type' => 'checkboxes',
|
| 290 |
'#multiple' => TRUE,
|
| 291 |
'#options' => node_get_types('names'),
|
| 292 |
'#default_value' => variable_get('composite_referenceable_types_' . $node_type, array()),
|
| 293 |
);
|
| 294 |
|
| 295 |
if (module_exists('views')) {
|
| 296 |
$views = array('--' => '--');
|
| 297 |
$all_views = views_get_all_views();
|
| 298 |
foreach ($all_views as $view) {
|
| 299 |
// Only 'node' views that have fields will work for our purpose.
|
| 300 |
if ($view->base_table == 'node' && !empty($view->display['default']->display_options['fields'])) {
|
| 301 |
if ($view->type == 'Default') {
|
| 302 |
$views[t('Default Views')][$view->name] = $view->name;
|
| 303 |
}
|
| 304 |
else {
|
| 305 |
$views[t('Existing Views')][$view->name] = $view->name;
|
| 306 |
}
|
| 307 |
}
|
| 308 |
}
|
| 309 |
|
| 310 |
if (count($views) > 1) {
|
| 311 |
$form['composite_extra_config']['advanced'] = array(
|
| 312 |
'#type' => 'fieldset',
|
| 313 |
'#title' => t('Advanced - Nodes that can be referenced (View)'),
|
| 314 |
'#collapsible' => TRUE,
|
| 315 |
'#collapsed' => variable_get('composite_advanced_view_' . $node_type, '--') == '--',
|
| 316 |
);
|
| 317 |
$form['composite_extra_config']['advanced']['composite_advanced_view'] = array(
|
| 318 |
'#type' => 'select',
|
| 319 |
'#title' => t('View used to select the nodes'),
|
| 320 |
'#options' => $views,
|
| 321 |
'#default_value' => variable_get('composite_advanced_view_' . $node_type, '--'),
|
| 322 |
'#description' => t('Choose the "Views module" view that selects the nodes that can be referenced.<br />Note:<ul><li>Only views that have fields will work for this purpose.</li><li>This will discard the "Content types" settings above. Use the view\'s "filters" section instead.</li><li>Use the view\'s "fields" section to display additional informations about candidate nodes on node creation/edition form.</li><li>Use the view\'s "sort criteria" section to determine the order in which candidate nodes will be displayed.</li></ul>'),
|
| 323 |
);
|
| 324 |
$form['composite_extra_config']['advanced']['composite_advanced_view_args'] = array(
|
| 325 |
'#type' => 'textfield',
|
| 326 |
'#title' => t('View arguments'),
|
| 327 |
'#default_value' => variable_get('composite_advanced_view_args_' . $node_type, ''),
|
| 328 |
'#required' => FALSE,
|
| 329 |
'#description' => t('Provide a comma separated list of arguments to pass to the view.'),
|
| 330 |
);
|
| 331 |
}
|
| 332 |
}
|
| 333 |
}
|
| 334 |
}
|
| 335 |
|
| 336 |
/**
|
| 337 |
* Implementation of hook_nodeapi().
|
| 338 |
*/
|
| 339 |
function composite_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
|
| 340 |
if (!composite_enabled($node))
|
| 341 |
return;
|
| 342 |
|
| 343 |
switch ($op) {
|
| 344 |
case 'delete':
|
| 345 |
// Notice that we're matching all revisions, by using the node's nid.
|
| 346 |
db_query("DELETE FROM {node_composite} WHERE nid = %d", $node->nid);
|
| 347 |
db_query("DELETE FROM {node_composite_references} WHERE nid = %d", $node->nid);
|
| 348 |
break;
|
| 349 |
|
| 350 |
case 'delete revision':
|
| 351 |
composite_delete_revision($node);
|
| 352 |
break;
|
| 353 |
|
| 354 |
case 'insert':
|
| 355 |
case 'update':
|
| 356 |
composite_update($node);
|
| 357 |
break;
|
| 358 |
|
| 359 |
case 'load':
|
| 360 |
return composite_load($node);
|
| 361 |
break;
|
| 362 |
|
| 363 |
case 'view':
|
| 364 |
// Only do something in full view, and if there is a layout defined.
|
| 365 |
if (!$a3 /* !$teaser */ && $node->composite_layout) {
|
| 366 |
$node->composite_content = TRUE;
|
| 367 |
}
|
| 368 |
break;
|
| 369 |
|
| 370 |
case 'alter':
|
| 371 |
if (isset($node->composite_content)) {
|
| 372 |
// Have to do our 'view' stuff in the alter hook because we have to wait
|
| 373 |
// for other modules to do their processing first.
|
| 374 |
$node->composite_content = composite_view($node, composite_get_layout($node->composite_layout), $node->composite_references);
|
| 375 |
foreach ($node->composite_content as $zone => $zone_item) {
|
| 376 |
$node->composite_content[$zone] = drupal_render($zone_item);
|
| 377 |
}
|
| 378 |
$node->body = theme('composite_content', composite_get_layout($node->composite_layout), $node->composite_content);
|
| 379 |
}
|
| 380 |
break;
|
| 381 |
}
|
| 382 |
}
|
| 383 |
|
| 384 |
function composite_delete_revision($node) {
|
| 385 |
db_query("DELETE FROM {node_composite} WHERE nid = %d AND vid = %d", $node->nid, $node->vid);
|
| 386 |
db_query("DELETE FROM {node_composite_references} WHERE nid = %d AND vid = %d", $node->nid, $node->vid);
|
| 387 |
}
|
| 388 |
|
| 389 |
/**
|
| 390 |
* Implementation of hook_update().
|
| 391 |
*
|
| 392 |
* Not really. It just happens to have a compatible function signature with hook_update
|
| 393 |
*
|
| 394 |
*/
|
| 395 |
function composite_update($node) {
|
| 396 |
// Just delete everything, and then add back in later
|
| 397 |
composite_delete_revision($node);
|
| 398 |
|
| 399 |
if ($node->composite_layout && $node->composite_layout != '--') {
|
| 400 |
// Treat the content_reference specially if we came in from an edit form.
|
| 401 |
if (isset($node->composite_content_reference)) {
|
| 402 |
if ($node->composite_content_reference) {
|
| 403 |
// Add content_reference, but only if it doesn't already exist
|
| 404 |
if (!$node->composite_references['content']) {
|
| 405 |
$node->composite_references['content'] = array(
|
| 406 |
'type' => 'content',
|
| 407 |
'weight' => 0,
|
| 408 |
'id' => 'content',
|
| 409 |
'data' => '',
|
| 410 |
'zone' => COMPOSITE_ZONE_NONE,
|
| 411 |
);
|
| 412 |
}
|
| 413 |
}
|
| 414 |
else {
|
| 415 |
// Unset content_reference
|
| 416 |
unset($node->composite_references['content']);
|
| 417 |
}
|
| 418 |
}
|
| 419 |
|
| 420 |
// Update node_composite
|
| 421 |
db_query("INSERT INTO {node_composite} (nid, vid, layout) VALUES (%d, %d, '%s') ", $node->nid, $node->vid, $node->composite_layout);
|
| 422 |
|
| 423 |
// Update node_composite_references
|
| 424 |
$args = array();
|
| 425 |
$query_parts = array();
|
| 426 |
foreach (element_children($node->composite_references) as $id) {
|
| 427 |
$reference = $node->composite_references[$id];
|
| 428 |
if (is_array($reference) && $reference['id']) {
|
| 429 |
$query_parts[] = " (%d, %d, '%s', %d, '%s', '%s', '%s')";
|
| 430 |
$args[] = $node->nid;
|
| 431 |
$args[] = $node->vid;
|
| 432 |
$args[] = $reference['type'];
|
| 433 |
$args[] = $reference['weight'];
|
| 434 |
$args[] = $id;
|
| 435 |
$args[] = $reference['data'] ? serialize($reference['data']) : '';
|
| 436 |
$args[] = $reference['zone'];
|
| 437 |
}
|
| 438 |
}
|
| 439 |
|
| 440 |
if (count($query_parts)) {
|
| 441 |
$query = "INSERT INTO {node_composite_references} (nid, vid, type, weight, id, data, zone) VALUES" . implode(', ', $query_parts);
|
| 442 |
db_query($query, $args);
|
| 443 |
}
|
| 444 |
}
|
| 445 |
}
|
| 446 |
|
| 447 |
/**
|
| 448 |
* Implementation of hook_load().
|
| 449 |
*
|
| 450 |
* Not really. It just happens to have a compatible function signature with hook_load
|
| 451 |
*/
|
| 452 |
function composite_load($node) {
|
| 453 |
$types = composite_get_types();
|
| 454 |
$additions = array();
|
| 455 |
|
| 456 |
$result = db_fetch_object(db_query('SELECT layout FROM {node_composite} WHERE vid = %d', $node->vid));
|
| 457 |
$additions['composite_layout'] = $result->layout;
|
| 458 |
|
| 459 |
// Seed reference sublists - they must be arrays or empty arrays
|
| 460 |
$sublists = array();
|
| 461 |
foreach ($types as $type) {
|
| 462 |
$sublists[$type['type']] = array();
|
| 463 |
}
|
| 464 |
|
| 465 |
$result = db_query("SELECT type, weight, id, data, zone FROM {node_composite_references} WHERE vid = %d", $node->vid);
|
| 466 |
while ($object = db_fetch_object($result)) {
|
| 467 |
// Some common manipulations
|
| 468 |
$object = (array) $object;
|
| 469 |
$object['data'] = unserialize($object['data']);
|
| 470 |
|
| 471 |
// Some type specific manipulations
|
| 472 |
composite_invoke_referenceapi($object, 'load');
|
| 473 |
|
| 474 |
$additions['composite_references'][$object['id']] = $object;
|
| 475 |
$sublists[$object['type']][$object['id']] = $object;
|
| 476 |
}
|
| 477 |
|
| 478 |
// Add reference sublists to the load
|
| 479 |
foreach ($types as $type) {
|
| 480 |
$additions['composite_references']['#'. $type['type'] .'_references'] = $sublists[$type['type']];
|
| 481 |
}
|
| 482 |
|
| 483 |
return $additions;
|
| 484 |
}
|
| 485 |
|
| 486 |
/**
|
| 487 |
* Nodeapi view helper
|
| 488 |
* Assemble the composite content into an array in a similar fashion to $node->content
|
| 489 |
*
|
| 490 |
* - $content: Original node content
|
| 491 |
* - $layout: Layout definition
|
| 492 |
* - $references: List of items for composite content
|
| 493 |
*/
|
| 494 |
function composite_view($node, $layout = array(), $references = array()) {
|
| 495 |
$composite_content = array();
|
| 496 |
$references = _composite_references_preprocess($references, $layout['zones'], FALSE);
|
| 497 |
|
| 498 |
foreach (element_children($references) as $id) {
|
| 499 |
$reference = $references[$id];
|
| 500 |
// Filter out items not in a displayable zone
|
| 501 |
if (array_key_exists($reference['zone'], $layout['zones'])) {
|
| 502 |
$output = composite_invoke_referenceapi($reference, 'view', $node);
|
| 503 |
if ($output) {
|
| 504 |
$composite_content[$reference['zone']][$reference['id']] = array(
|
| 505 |
'#value' => $output,
|
| 506 |
'#weight' => $reference['weight'],
|
| 507 |
);
|
| 508 |
}
|
| 509 |
}
|
| 510 |
}
|
| 511 |
return $composite_content;
|
| 512 |
}
|
| 513 |
|
| 514 |
/***********************************************************
|
| 515 |
* CUSTOM LAYOUT SELECT ELEMENT *
|
| 516 |
************************************************************/
|
| 517 |
|
| 518 |
function composite_elements() {
|
| 519 |
$type['composite_layout_radios'] = array('#input' => TRUE, '#process' => array('composite_layout_radios_process'));
|
| 520 |
return $type;
|
| 521 |
}
|
| 522 |
|
| 523 |
function composite_layout_radios_process($element) {
|
| 524 |
if (count($element['#options']) > 0) {
|
| 525 |
// Do the normal radios thing
|
| 526 |
$element = expand_radios($element);
|
| 527 |
|
| 528 |
// And then add in our icons
|
| 529 |
$layouts = composite_get_layouts();
|
| 530 |
drupal_add_css(drupal_get_path('module', 'composite') . '/composite.css', 'module', 'all', FALSE);
|
| 531 |
|
| 532 |
foreach (element_children($element) as $key) {
|
| 533 |
if (array_key_exists($key, $layouts)) {
|
| 534 |
$element[$key]['#prefix'] = '<div class="composite-layout-option">';
|
| 535 |
$element[$key]['#suffix'] = '</div>';
|
| 536 |
|
| 537 |
if ($layouts[$key]['icon']) {
|
| 538 |
$element[$key]['#prefix'] .= theme('composite_layout_icon', $layouts[$key]['icon']);
|
| 539 |
}
|
| 540 |
}
|
| 541 |
}
|
| 542 |
|
| 543 |
$element['#prefix'] = '<div class="clear-block">';
|
| 544 |
$element['#suffix'] = '</div>';
|
| 545 |
return $element;
|
| 546 |
}
|
| 547 |
}
|
| 548 |
|
| 549 |
/***********************************************************
|
| 550 |
* THEME-ING *
|
| 551 |
************************************************************/
|
| 552 |
|
| 553 |
/**
|
| 554 |
* Implementation of hook_theme()
|
| 555 |
*/
|
| 556 |
function composite_theme() {
|
| 557 |
return array(
|
| 558 |
'composite_layout_radios' => array(
|
| 559 |
'arguments' => array('element' => NULL),
|
| 560 |
),
|
| 561 |
'composite_layout_icon' => array(
|
| 562 |
'arguments' => array('file' => NULL),
|
| 563 |
),
|
| 564 |
'composite_zones_form' => array(
|
| 565 |
'template' => 'composite-zones-form',
|
| 566 |
'file' => 'composite.pages.inc',
|
| 567 |
'arguments' => array('form' => NULL),
|
| 568 |
),
|
| 569 |
'composite_zones_preview' => array(
|
| 570 |
'arguments' => array('node' => NULL),
|
| 571 |
),
|
| 572 |
'composite_content' => array(
|
| 573 |
'template' => 'composite-content',
|
| 574 |
'path' => drupal_get_path('module', 'composite') . '/theme',
|
| 575 |
'arguments' => array('layout' => array(), 'composite_content' => array()),
|
| 576 |
),
|
| 577 |
'composite_node_title' => array(
|
| 578 |
'arguments' => array('title' => ''),
|
| 579 |
),
|
| 580 |
);
|
| 581 |
}
|
| 582 |
|
| 583 |
function theme_composite_layout_radios($element) {
|
| 584 |
return theme('radios', $element);
|
| 585 |
}
|
| 586 |
|
| 587 |
function theme_composite_layout_icon($file) {
|
| 588 |
return '<img src="' . url($file) .'" class="composite-layout-icon" />';
|
| 589 |
}
|
| 590 |
|
| 591 |
function theme_composite_zones_preview($node) {
|
| 592 |
if (isset($node->nid)) {
|
| 593 |
$output = '<div class="preview">' . node_view($node, FALSE, FALSE) .'</div>';
|
| 594 |
return $output;
|
| 595 |
}
|
| 596 |
}
|
| 597 |
|
| 598 |
function template_preprocess_composite_content(&$variables) {
|
| 599 |
$layout = $variables['layout'];
|
| 600 |
$composite_content = $variables['composite_content'];
|
| 601 |
$key = strtr($layout['key'], '_', '-');
|
| 602 |
|
| 603 |
// Always include the default template
|
| 604 |
$variables['template_files'][] = 'composite-content';
|
| 605 |
$variables['template_files'][] = $layout['template'];
|
| 606 |
|
| 607 |
if ($layout['css']) {
|
| 608 |
drupal_add_css($layout['css']);
|
| 609 |
}
|
| 610 |
|
| 611 |
// Extract out composite_content into separate variables
|
| 612 |
$variables['content'] = $composite_content['content'];
|
| 613 |
foreach ($layout['zones'] as $zone => $unused) {
|
| 614 |
$variables[$zone] = $composite_content[$zone];
|
| 615 |
}
|
| 616 |
}
|
| 617 |
|
| 618 |
function theme_composite_node_title($title) {
|
| 619 |
return '<div>' . $title . '</div>';
|
| 620 |
}
|
| 621 |
|
| 622 |
/***********************************************************
|
| 623 |
* REFERENCE TYPE DEFINITIONS *
|
| 624 |
************************************************************/
|
| 625 |
|
| 626 |
function composite_composite_types() {
|
| 627 |
$types = array(
|
| 628 |
// 'content' is treated specially in some places.
|
| 629 |
'content' => array(
|
| 630 |
'type' => 'content',
|
| 631 |
),
|
| 632 |
'block' => array(
|
| 633 |
'type' => 'block',
|
| 634 |
// 'module' => 'composite', // optional value
|
| 635 |
'file' => 'composite.block.inc', // optional, path relative from directory of 'module'
|
| 636 |
'label' => array('singular' => t('Block'), 'plural' => t('Blocks')),
|
| 637 |
|
| 638 |
// 'local task' - whether composite.module should generate a local task tab for this type
|
| 639 |
'local task' => TRUE,
|
| 640 |
// 'task access' - optional menu access function for the local task tab
|
| 641 |
'task access' => '',
|
| 642 |
// 'potentials callback' - callback to retrieve list of potential composite references
|
| 643 |
'potentials callback' => 'composite_composite_block_potentials',
|
| 644 |
),
|
| 645 |
'node' => array(
|
| 646 |
'type' => 'node',
|
| 647 |
'file' => 'composite.node.inc',
|
| 648 |
'label' => array('singular' => t('Node'), 'plural' => t('Nodes')),
|
| 649 |
'local task' => TRUE,
|
| 650 |
'task access' => 'composite_composite_node_access',
|
| 651 |
'potentials callback' => 'composite_composite_node_potentials',
|
| 652 |
),
|
| 653 |
);
|
| 654 |
return $types;
|
| 655 |
}
|
| 656 |
|
| 657 |
function composite_composite_content_api(&$reference, $op, $node = NULL, $a4 = NULL) {
|
| 658 |
switch ($op) {
|
| 659 |
case 'info':
|
| 660 |
$reference['info'] = t('Node: Original content');
|
| 661 |
break;
|
| 662 |
|
| 663 |
// Return a rendering of the reference item
|
| 664 |
case 'view':
|
| 665 |
// Be careful of security here. The correct field (and whether to escape
|
| 666 |
// malicious text) depends on who calls composite_view() which calls this,
|
| 667 |
return $node->body;
|
| 668 |
break;
|
| 669 |
}
|
| 670 |
}
|
| 671 |
|
| 672 |
/***********************************************************
|
| 673 |
* LAYOUT DEFINITIONS *
|
| 674 |
************************************************************/
|
| 675 |
|
| 676 |
function composite_composite_layouts() {
|
| 677 |
// Try to keep zone names common across layouts (but obviously within
|
| 678 |
// sensible limits), so there is minimal disruption if the layout changes.
|
| 679 |
$layouts['onecol'] = array(
|
| 680 |
'key' => 'onecol',
|
| 681 |
'name' => t('Single column'),
|
| 682 |
'zones' => array(
|
| 683 |
'left' => t('Left column'),
|
| 684 |
),
|
| 685 |
// The following three vars are optional. If not specified, they
|
| 686 |
// will be derived from key, in an equivalent manner to what you see below.
|
| 687 |
'path' => drupal_get_path('module', 'composite') . '/theme',
|
| 688 |
'css' => 'composite-layout-onecol.css',
|
| 689 |
'template' => 'composite-layout-onecol',
|
| 690 |
'icon' => 'composite-layout-onecol.icon.png'
|
| 691 |
);
|
| 692 |
$layouts['twocol'] = array(
|
| 693 |
'key' => 'twocol',
|
| 694 |
'name' => t('Two columns'),
|
| 695 |
'zones' => array(
|
| 696 |
'top' => t('Top'),
|
| 697 |
'left' => t('Left column'),
|
| 698 |
'right' => t('Right column'),
|
| 699 |
'bottom' => t('Bottom'),
|
| 700 |
),
|
| 701 |
);
|
| 702 |
$layouts['twocol_bricks'] = array(
|
| 703 |
'key' => 'twocol_bricks',
|
| 704 |
'name' => t('Two columns bricks'),
|
| 705 |
'zones' => array(
|
| 706 |
'top' => t('Top'),
|
| 707 |
'left' => t('Left column (upper)'),
|
| 708 |
'right' => t('Right column (upper)'),
|
| 709 |
'middle_row' => t('Middle row'),
|
| 710 |
'left2' => t('Left column (lower)'),
|
| 711 |
'right2' => t('Right column (lower)'),
|
| 712 |
'bottom' => t('Bottom'),
|
| 713 |
),
|
| 714 |
);
|
| 715 |
$layouts['threecol_33_33_33'] = array(
|
| 716 |
'key' => 'threecol_33_33_33',
|
| 717 |
'name' => t('Three columns 33/33/33'),
|
| 718 |
'zones' => array(
|
| 719 |
'top' => t('Top'),
|
| 720 |
'left' => t('Left column'),
|
| 721 |
'middle' => t('Middle column'),
|
| 722 |
'right' => t('Right column'),
|
| 723 |
'bottom' => t('Bottom'),
|
| 724 |
),
|
| 725 |
);
|
| 726 |
$layouts['threecol_25_50_25'] = array(
|
| 727 |
'key' => 'threecol_25_50_25',
|
| 728 |
'name' => t('Three columns 25/50/25'),
|
| 729 |
'zones' => array(
|
| 730 |
'top' => t('Top'),
|
| 731 |
'left' => t('Left column'),
|
| 732 |
'middle' => t('Middle column'),
|
| 733 |
'right' => t('Right column'),
|
| 734 |
'bottom' => t('Bottom'),
|
| 735 |
),
|
| 736 |
);
|
| 737 |
return $layouts;
|
| 738 |
}
|
| 739 |
|