diff -urpN composite/composite.module composite-working/composite.module
--- composite/composite.module 2009-01-14 21:41:13.000000000 -0500
+++ composite-working/composite.module 2009-06-26 12:44:18.000000000 -0400
@@ -1,989 +1,863 @@
-type, FALSE);
-}
-
-function composite_get_types($type = '') {
- static $types = null;
-
- if (empty($types)) {
- $types = array();
- foreach (module_implements('composite_types') as $module) {
- $data = module_invoke($module, 'composite_types');
-
- foreach ($data as $k => $unused) {
- // Fill in 'module' if not specified
- if (!$data[$k]['module']) {
- $data[$k]['module'] = $module;
- }
-
- // Prefix 'file' with module dir
- if ($data[$k]['file']) {
- $data[$k]['file'] = drupal_get_path('module', $module) .'/'. $data[$k]['file'];
- }
- }
- $types = array_merge_recursive($types, $data);
- }
-
- foreach (module_implements('composite_types_alter') as $module) {
- $function = $module . '_composite_types_alter';
- $function($types);
- }
- }
-
- if ($type == '')
- return $types;
- else if (array_key_exists($type, $types))
- return $types[$type];
-}
-
-// Includes the 'file' parameter of a reference type, if defined.
-function composite_include_file($type) {
- if ($type['file'] && is_file($type['file'])) {
- include_once $type['file'];
- }
-}
-
-function composite_invoke_referenceapi(&$reference, $op, $a3 = NULL, $a4 = NULL) {
- $type_def = composite_get_types($reference['type']);
- if ($type_def) {
- composite_include_file($type_def);
- $function = $type_def['module'] .'_composite_'. $type_def['type'] .'_api';
- if (function_exists($function)) {
- return $function($reference, $op, $a3, $a4);
- }
- }
-}
-
-function composite_get_layouts($type = '') {
- static $layouts = array();
- static $layouts_select = array();
-
- if (!$layouts) {
- // Would like to make layouts pluggable, but doesn't quite work
- // because of inability to specify template files from other modules.
- $layouts = module_invoke_all('composite_layouts');
-
- // Do some defaults and path processing
- // Construct a select list for convenience
- foreach ($layouts as $key => $v) {
- $key_dashed = strtr($key, '_', '-');
- if (!$layouts[$key]['template']) {
- $layouts[$key]['template'] = 'composite-layout-' . $key_dashed;
- }
- if (!$layouts[$key]['path']) {
- $layouts[$key]['path'] = drupal_get_path('module', 'composite') . '/theme';
- }
-
- $css = $layouts[$key]['css'] ? $layouts[$key]['css'] : 'composite-layout-' . $key_dashed . '.css';
- $css = $layouts[$key]['path'] . '/' . $css;
- if (is_file($css)) {
- $layouts[$key]['css'] = $css;
- }
-
- $icon = $layouts[$key]['icon'] ? $layouts[$key]['icon'] : 'composite-layout-' . $key_dashed . '.icon.png';
- $icon = $layouts[$key]['path'] . '/' . $icon;
- if (is_file($icon)) {
- $layouts[$key]['icon'] = $icon;
- }
-
- $layouts_select[$key] = $v['name'];
- }
- }
-
- if ($type == 'select') {
- return $layouts_select;
- }
- else {
- return $layouts;
- }
-}
-
-function composite_get_layout($layout = '') {
- $layouts = composite_get_layouts();
- return $layouts[$layout];
-}
-
-/***********************************************************
-* MENU *
-************************************************************/
-
-function composite_perm() {
- return array('edit composite layouts');
-}
-
-/**
- * Implementation of hook_menu().
- */
-function composite_menu() {
- $items = array();
-
- // Generate local task for each reference type that requests it
- $types = composite_get_types();
- foreach ($types as $type) {
- if ($type['local task']) {
- $items['node/%node/composite_' . $type['type']] = array(
- 'title' => $type['label']['plural'],
- 'page callback' => 'composite_general_select_page',
- 'page arguments' => array($type['type'], 1),
- 'access callback' => 'composite_access',
- 'access arguments' => array($type['type'], 'update', 1),
- 'type' => MENU_LOCAL_TASK,
- 'weight' => 3,
- 'file' => 'composite.pages.inc',
- );
- }
- }
- $items['node/%node/composite_zones'] = array(
- 'title' => t('Zones'),
- 'page callback' => 'composite_zones_page',
- 'page arguments' => array(1),
- 'access callback' => 'composite_access',
- 'access arguments' => array('zones', 'update', 1),
- 'type' => MENU_LOCAL_TASK,
- 'weight' => 3,
- 'file' => 'composite.pages.inc',
- );
-
- return $items;
-}
-
-/**
- * Access function used to turn tabs off, or defer to node_access
- */
-function composite_access($tab, $op, $node) {
- $access = FALSE;
-
- if ((user_access('edit composite layouts') || user_access('administer nodes')) && composite_enabled($node) && $node->composite_layout) {
- $access_function = 'node_access';
- // Change access function if this tab was generated for a reference type
- // and the optional task access function is defined
- $type = composite_get_types($tab);
- if ($type && $type['task access']) {
- composite_include_file($type);
- $function = $type['task access'];
- if (function_exists($function)) {
- $access_function = $function;
- }
- }
- $access = $access_function($op, $node);
- }
- return $access;
-}
-
-/***********************************************************
-* NODE MANIPULATIONS .ie load/save/view etc. *
-************************************************************/
-
-/**
- * Implementation of hook_form_alter().
- */
-function composite_form_alter(&$form, $form_state, $form_id) {
- if ($form['#id'] == 'node-form' && composite_enabled($form['#node'])) {
- // .../node/xx/edit or .../node/add/xx page
- $node = $form['#node'];
- $default_set = variable_get('composite_default_set_' . $node->type, '--');
-
- if ((user_access('edit composite layouts') || user_access('administer nodes'))) {
- $form['composite_settings'] = array(
- '#type' => 'fieldset',
- '#title' => t('Composite layout'),
- '#description' => t('Select the desired composite layout of this node.'),
- '#collapsible' => TRUE,
- '#collapsed' => !$node->composite_layout && $default_set == '--',
- '#weight' => 10,
- );
-
- $form['composite_settings']['composite_references'] = array(
- '#type' => 'value',
- '#value' => $node->composite_references,
- );
- $form['composite_settings']['composite_layout_data'] = array(
- '#type' => 'value',
- '#value' => $node->composite_layout_data,
- );
- $form['composite_settings']['composite_layout'] = array(
- '#title' => t('Layout'),
- '#type' => 'composite_layout_radios',
- '#options' => array('--' => t('No composite layout')) + composite_get_layouts('select'),
- '#default_value' => $node->composite_layout ? $node->composite_layout : '--',
- );
- $form['composite_settings']['composite_content_reference'] = array(
- '#type' => 'checkbox',
- '#title' => t('Node content should be included as an item available for composite layout.'),
- '#description' => t('This has no effect if no composite layout is selected, or if a layout set is selected.'),
- '#default_value' => isset($node->composite_references['content']['type']) && ($node->composite_references['content']['type'] == 'content'),
- );
-
- // Append layout sets options
- $layout_sets = composite_sets_select();
- if ($layout_sets) {
- $form['composite_settings']['composite_layout']['#options'] += $layout_sets;
- // Assign a default set only if this is a new node
- if ($default_set != '--' && !isset($node->nid)) {
- $form['composite_settings']['composite_layout']['#default_value'] = $default_set;
- }
- }
- }
- else {
- // Non privileged user, retain all composite layout related settings
- $form['composite_settings']['composite_layout'] = array(
- '#type' => 'value',
- '#value' => $node->composite_layout ? $node->composite_layout : '--',
- );
- $form['composite_settings']['composite_references'] = array(
- '#type' => 'value',
- '#value' => $node->composite_references,
- );
- $form['composite_settings']['composite_layout_data'] = array(
- '#type' => 'value',
- '#value' => $node->composite_layout_data,
- );
- $form['composite_settings']['composite_content_reference'] = array(
- '#type' => 'value',
- '#value' => isset($node->composite_references['content']['type']) && ($node->composite_references['content']['type'] == 'content'),
- );
- // Force a default set if this is a new node
- if ($default_set != '--' && !isset($node->nid)) {
- $form['composite_settings']['composite_layout']['#value'] = $default_set;
- }
- }
- }
- else if ($form_id == 'node_type_form' && isset($form['#node_type'])) {
- // .../admin/content/node-type/xx page
- $node_type = $form['#node_type']->type;
- $composite_enabled = variable_get('composite_enabled_' . $node_type, FALSE);
-
- // The code below borrows heavily from nodereference_field_settings()
- $form['composite_extra_config'] = array(
- '#type' => 'fieldset',
- '#title' => t('Composite layout'),
- '#collapsible' => TRUE,
- '#collapsed' => !$composite_enabled,
- );
- $form['composite_extra_config']['composite_enabled'] = array(
- '#title' => t('Enable composite layout for this content type.'),
- '#type' => 'checkbox',
- '#default_value' => $composite_enabled,
- );
-
- $form['composite_extra_config']['composite_nodes'] = array(
- '#title' => t('Nodes'),
- '#type' => 'fieldset',
- '#collapsible' => TRUE,
- '#description' => t('If you wish to display other nodes in your composite layouts, please select the content types to be made available.'),
- );
-
- $form['composite_extra_config']['composite_nodes']['composite_referenceable_types'] = array(
- '#title' => t('Content types that can be referenced'),
- '#type' => 'checkboxes',
- '#multiple' => TRUE,
- '#options' => node_get_types('names'),
- '#default_value' => variable_get('composite_referenceable_types_' . $node_type, array()),
- );
-
- if (module_exists('views')) {
- $views = array('--' => '--');
- $all_views = views_get_all_views();
- foreach ($all_views as $view) {
- // Only 'node' views that have fields will work for our purpose.
- if ($view->base_table == 'node' && !empty($view->display['default']->display_options['fields'])) {
- if ($view->type == 'Default') {
- $views[t('Default Views')][$view->name] = $view->name;
- }
- else {
- $views[t('Existing Views')][$view->name] = $view->name;
- }
- }
- }
-
- if (count($views) > 1) {
- $form['composite_extra_config']['composite_nodes']['advanced'] = array(
- '#type' => 'fieldset',
- '#title' => t('Advanced - Nodes that can be referenced (View)'),
- '#collapsible' => TRUE,
- '#collapsed' => variable_get('composite_advanced_view_' . $node_type, '--') == '--',
- );
- $form['composite_extra_config']['composite_nodes']['advanced']['composite_advanced_view'] = array(
- '#type' => 'select',
- '#title' => t('View used to select the nodes'),
- '#options' => $views,
- '#default_value' => variable_get('composite_advanced_view_' . $node_type, '--'),
- '#description' => t('Choose the "Views module" view that selects the nodes that can be referenced.
Note:
- Only views that have fields will work for this purpose.
- This will discard the "Content types" settings above. Use the view\'s "filters" section instead.
- Use the view\'s "fields" section to display additional informations about candidate nodes on node creation/edition form.
- Use the view\'s "sort criteria" section to determine the order in which candidate nodes will be displayed.
'),
- );
- $form['composite_extra_config']['composite_nodes']['advanced']['composite_advanced_view_args'] = array(
- '#type' => 'textfield',
- '#title' => t('View arguments'),
- '#default_value' => variable_get('composite_advanced_view_args_' . $node_type, ''),
- '#required' => FALSE,
- '#description' => t('Provide a comma separated list of arguments to pass to the view.'),
- );
- }
- }
- $layout_sets = composite_sets_select();
- if ($layout_sets) {
- $form['composite_extra_config']['composite_default_set'] = array(
- '#type' => 'select',
- '#title' => t('Default layout set'),
- '#options' => array('--' => t('')) + $layout_sets,
- '#default_value' => variable_get('composite_default_set_' . $node_type, '--'),
- '#description' => t('Select a default layout set for this content type. If selected, all new nodes will be automatically populated by this set.'),
- );
- }
- }
-}
-
-/**
- * Implementation of hook_nodeapi().
- */
-function composite_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
- if (!composite_enabled($node))
- return;
-
- switch ($op) {
- case 'delete':
- // Notice that we're matching all revisions, by using the node's nid.
- db_query("DELETE FROM {node_composite} WHERE nid = %d", $node->nid);
- db_query("DELETE FROM {node_composite_references} WHERE nid = %d", $node->nid);
- break;
-
- case 'delete revision':
- composite_delete_revision($node);
- break;
-
- case 'insert':
- case 'update':
- if (isset($node->composite_layout) && is_numeric($node->composite_layout)) {
- // A numeric composite_layout is actually a layout set
- // But we zero out composite_layout just in case the set isn't found.
- $sid = $node->composite_layout;
- $node->composite_layout = '';
- composite_sets_populate_node($node, $sid);
- }
- composite_update($node);
- break;
-
- case 'load':
- return composite_load($node);
- break;
-
- case 'view':
- // Only do something in full view, and if there is a layout defined.
- if (!$a3 /* !$teaser */ && $node->composite_layout && $node->composite_layout != '--') {
- $node->composite_content = TRUE;
- }
- break;
-
- case 'alter':
- if (isset($node->composite_content)) {
- // Have to do our 'view' stuff in the alter hook because we have to wait
- // for other modules to do their processing first.
- $layout = composite_get_layout($node->composite_layout);
- if ($layout) {
- $layout['data'] = $node->composite_layout_data;
- $node->composite_content = composite_view($node, $layout, $node->composite_references);
- foreach ($node->composite_content as $zone => $zone_item) {
- $node->composite_content[$zone] = drupal_render($zone_item);
- }
- $node->body = theme('composite_content', $layout, $node->composite_content);
- }
- }
- break;
- }
-}
-
-function composite_delete_revision($node) {
- db_query("DELETE FROM {node_composite} WHERE nid = %d AND vid = %d", $node->nid, $node->vid);
- db_query("DELETE FROM {node_composite_references} WHERE nid = %d AND vid = %d", $node->nid, $node->vid);
-}
-
-/**
- * Implementation of hook_update().
- *
- * Not really. It just happens to have a compatible function signature with hook_update
- *
- */
-function composite_update($node) {
- // Just delete everything, and then add back in later
- composite_delete_revision($node);
-
- if ($node->composite_layout && $node->composite_layout != '--') {
- // Treat the content_reference specially if we came in from an edit form.
- if (isset($node->composite_content_reference)) {
- if ($node->composite_content_reference) {
- // Add content_reference, but only if it doesn't already exist
- if (!$node->composite_references['content']) {
- $node->composite_references['content'] = array(
- 'type' => 'content',
- 'weight' => 0,
- 'id' => 'content',
- 'data' => '',
- 'zone' => COMPOSITE_ZONE_NONE,
- );
- }
- }
- else {
- // Unset content_reference
- unset($node->composite_references['content']);
- }
- }
-
- // Update node_composite
- db_query("INSERT INTO {node_composite} (nid, vid, layout, data) VALUES (%d, %d, '%s', '%s') ", $node->nid, $node->vid, $node->composite_layout, $node->composite_layout_data ? serialize($node->composite_layout_data) : '');
-
- // Update node_composite_references
- $args = array();
- $query_parts = array();
- foreach (element_children($node->composite_references) as $id) {
- $reference = $node->composite_references[$id];
- if (is_array($reference) && $reference['id']) {
- $query_parts[] = " (%d, %d, '%s', %d, '%s', '%s', '%s')";
- $args[] = $node->nid;
- $args[] = $node->vid;
- $args[] = $reference['type'];
- $args[] = $reference['weight'];
- $args[] = $id;
- $args[] = $reference['data'] ? serialize($reference['data']) : '';
- $args[] = $reference['zone'];
- }
- }
-
- if (count($query_parts)) {
- $query = "INSERT INTO {node_composite_references} (nid, vid, type, weight, id, data, zone) VALUES" . implode(', ', $query_parts);
- db_query($query, $args);
- }
- }
-}
-
-/**
- * Implementation of hook_load().
- *
- * Not really. It just happens to have a compatible function signature with hook_load
- */
-function composite_load($node) {
- $types = composite_get_types();
- $additions = array();
-
- $result = db_fetch_object(db_query('SELECT layout, data FROM {node_composite} WHERE vid = %d', $node->vid));
- $additions['composite_layout'] = $result->layout;
- $additions['composite_layout_data'] = unserialize($result->data);
-
- // Seed reference sublists - they must be arrays or empty arrays
- $sublists = array();
- foreach ($types as $type) {
- $sublists[$type['type']] = array();
- }
-
- $result = db_query("SELECT type, weight, id, data, zone FROM {node_composite_references} WHERE vid = %d", $node->vid);
- while ($object = db_fetch_object($result)) {
- // Some common manipulations
- $object = (array) $object;
- $object['data'] = unserialize($object['data']);
-
- // Some type specific manipulations
- composite_invoke_referenceapi($object, 'load');
-
- $additions['composite_references'][$object['id']] = $object;
- $sublists[$object['type']][$object['id']] = $object;
- }
-
- // Add reference sublists to the load
- foreach ($types as $type) {
- $additions['composite_references']['#'. $type['type'] .'_references'] = $sublists[$type['type']];
- }
-
- return $additions;
-}
-
-/**
- * Nodeapi view helper
- * Assemble the composite content into an array in a similar fashion to $node->content
- *
- * - $content: Original node content
- * - $layout: Layout definition
- * - $references: List of items for composite content
- */
-function composite_view($node, $layout = array(), $references = array()) {
- $composite_content = array();
- $references = _composite_references_preprocess($references, $layout['zones'], FALSE);
-
- foreach (element_children($references) as $id) {
- $reference = $references[$id];
- // Filter out items not in a displayable zone
- if (array_key_exists($reference['zone'], $layout['zones'])) {
- $output = composite_invoke_referenceapi($reference, 'view', $node);
- if ($output) {
- $composite_content[$reference['zone']][$reference['id']] = array(
- '#value' => $output,
- '#weight' => $reference['weight'],
- );
- }
- }
- }
- return $composite_content;
-}
-
-/***********************************************************
-* COMPOSITE LAYOUT SETS *
-************************************************************/
-
-// Helper function to retrieve a list of existing sets
-function composite_sets_select() {
- return composite_get_sets('select');
-}
-
-function composite_get_sets($type = 'sets') {
- static $options = null;
- static $layout_sets = null;
-
- if (!isset($options)) {
- $options = array();
- $layout_sets = array();
- $result = db_query("SELECT sid, name, data FROM {node_composite_sets}");
- while ($object = db_fetch_object($result)) {
- $options[$object->sid] = $object->name;
- // Don't unserialize data unnecessarily
- $layout_sets[$object->sid] = $object;
- }
- }
- return $type == 'select' ? $options : $layout_sets;
-}
-
-function composite_sets_load($sid) {
- $layout_sets = composite_get_sets();
- if (isset($layout_sets[$sid])) {
- $layout_set = $layout_sets[$sid];
- $layout_set->data = unserialize($layout_set->data);
- return $layout_set;
- }
- else {
- return array();
- }
-}
-
-// Returns true if populated, false otherwise
-function composite_sets_populate_node(&$node, $sid) {
- // Fetch the layout set
- $layout_set = composite_sets_load($sid);
-
- // Update the node
- if ($layout_set) {
- $node->composite_layout = $layout_set->data['composite_layout'];
- $node->composite_layout_data = $layout_set->data['composite_layout_data'];
- $node->composite_references = array();
-
- foreach ($layout_set->data['composite_references'] as $reference) {
- if (is_array($reference) && isset($reference['id'])) {
- composite_invoke_referenceapi($reference, 'load');
- $node->composite_references[$reference['id']] = $reference;
- }
- }
- return TRUE;
- }
- return FALSE;
-}
-
-/***********************************************************
-* CUSTOM LAYOUT SELECT ELEMENT *
-************************************************************/
-
-function composite_elements() {
- $type['composite_layout_radios'] = array('#input' => TRUE, '#process' => array('composite_layout_radios_process'));
- return $type;
-}
-
-function composite_layout_radios_process($element) {
- if (count($element['#options']) > 0) {
- // Do the normal radios thing
- $element = expand_radios($element);
-
- // And then add in our icons
- $layouts = composite_get_layouts();
- drupal_add_css(drupal_get_path('module', 'composite') . '/composite.css', 'module', 'all', FALSE);
-
- // Track the first option which is a layout set
- $first_set = FALSE;
-
-
- foreach (element_children($element) as $key) {
- if (array_key_exists($key, $layouts)) {
- $element[$key]['#prefix'] = '';
- $element[$key]['#suffix'] = '
';
-
- if ($layouts[$key]['icon']) {
- $element[$key]['#prefix'] .= theme('composite_layout_icon', $layouts[$key]['icon']);
- }
- }
- else if (is_numeric($key)) {
- // A numeric key means a layout set
- if ($first_set == FALSE) {
- $first_set = $key;
- $element[$key]['#prefix'] = '';
- }
- }
- }
-
- // Close off the sets div if there is one
- if ($first_set) {
- $element[$key]['#suffix'] = '
';
- }
-
- $element['#prefix'] = '';
- $element['#suffix'] = '
';
- return $element;
- }
-}
-
-/***********************************************************
-* THEME-ING *
-************************************************************/
-
-/**
- * Implementation of hook_theme()
- */
-function composite_theme() {
- return array(
- 'composite_layout_radios' => array(
- 'arguments' => array('element' => NULL),
- ),
- 'composite_layout_icon' => array(
- 'arguments' => array('file' => NULL),
- ),
- 'composite_zones_form' => array(
- 'template' => 'composite-zones-form',
- 'file' => 'composite.pages.inc',
- 'arguments' => array('form' => NULL),
- ),
- 'composite_zones_preview' => array(
- 'arguments' => array('node' => NULL),
- ),
- 'composite_content' => array(
- 'template' => 'composite-content',
- 'path' => drupal_get_path('module', 'composite') . '/theme',
- 'arguments' => array('layout' => array(), 'composite_content' => array()),
- ),
- 'composite_node_title' => array(
- 'arguments' => array('title' => ''),
- ),
- );
-}
-
-function theme_composite_layout_radios($element) {
- return theme('radios', $element);
-}
-
-function theme_composite_layout_icon($file) {
- return '';
-}
-
-function theme_composite_zones_preview($node) {
- if (isset($node->nid)) {
- $output = '' . node_view($node, FALSE, FALSE) .'
';
- return $output;
- }
-}
-
-function template_preprocess_composite_content(&$variables) {
- $layout = $variables['layout'];
- $composite_content = $variables['composite_content'];
- $key = strtr($layout['key'], '_', '-');
-
- // Always include the default template
- $variables['template_files'][] = 'composite-content';
- $variables['template_files'][] = $layout['template'];
-
- if ($layout['css']) {
- drupal_add_css($layout['css']);
- }
-
- // Extract out composite_content into separate variables
- $variables['content'] = $composite_content['content'];
- foreach ($layout['zones'] as $zone => $unused) {
- $variables[$zone] = $composite_content[$zone];
- }
-
- // Call a preprocess callback if defined
- if (isset($layout['preprocess callback'])) {
- $function = $layout['preprocess callback'];
- if (function_exists($function)) {
- $function($variables);
- }
- }
-}
-
-function theme_composite_node_title($title) {
- return '' . $title . '
';
-}
-
-/***********************************************************
-* REFERENCE TYPE DEFINITIONS *
-************************************************************/
-
-function composite_composite_types() {
- $types = array(
- // 'content' is treated specially in some places.
- 'content' => array(
- 'type' => 'content',
- ),
- 'block' => array(
- 'type' => 'block',
- // 'module' => 'composite', // optional value
- 'file' => 'composite.block.inc', // optional, path relative from directory of 'module'
- 'label' => array('singular' => t('Block'), 'plural' => t('Blocks')),
-
- // 'local task' - whether composite.module should generate a local task tab for this type
- 'local task' => TRUE,
- // 'task access' - optional menu access function for the local task tab
- 'task access' => '',
- // 'potentials callback' - callback to retrieve list of potential composite references
- 'potentials callback' => 'composite_composite_block_potentials',
- ),
- 'node' => array(
- 'type' => 'node',
- 'file' => 'composite.node.inc',
- 'label' => array('singular' => t('Node'), 'plural' => t('Nodes')),
- 'local task' => TRUE,
- 'task access' => 'composite_composite_node_access',
- 'potentials callback' => 'composite_composite_node_potentials',
- ),
- );
- return $types;
-}
-
-function composite_composite_content_api(&$reference, $op, $node = NULL, $a4 = NULL) {
- switch ($op) {
- case 'info':
- $reference['info'] = t('Node: Original content');
- break;
-
- // Return a rendering of the reference item
- case 'view':
- // Be careful of security here. The correct field (and whether to escape
- // malicious text) depends on who calls composite_view() which calls this,
- return $node->body;
- break;
- }
-}
-
-/***********************************************************
-* LAYOUT DEFINITIONS *
-************************************************************/
-
-function composite_composite_layouts() {
- // Try to keep zone names common across layouts (but obviously within
- // sensible limits), so there is minimal disruption if the layout changes.
- $layouts['onecol'] = array(
- 'key' => 'onecol',
- 'name' => t('Single column'),
- 'zones' => array(
- 'left' => t('Left column'),
- ),
- // The following three vars are optional. If not specified, they
- // will be derived from key, in an equivalent manner to what you see below.
- 'path' => drupal_get_path('module', 'composite') . '/theme',
- 'css' => 'composite-layout-onecol.css',
- 'template' => 'composite-layout-onecol',
- 'icon' => 'composite-layout-onecol.icon.png'
- );
- $layouts['twocol'] = array(
- 'key' => 'twocol',
- 'name' => t('Two columns'),
- 'zones' => array(
- 'top' => t('Top'),
- 'left' => t('Left column'),
- 'right' => t('Right column'),
- 'bottom' => t('Bottom'),
- ),
- );
- $layouts['twocol_bricks'] = array(
- 'key' => 'twocol_bricks',
- 'name' => t('Two columns bricks'),
- 'zones' => array(
- 'top' => t('Top'),
- 'left' => t('Left column (upper)'),
- 'right' => t('Right column (upper)'),
- 'middle_row' => t('Middle row'),
- 'left2' => t('Left column (lower)'),
- 'right2' => t('Right column (lower)'),
- 'bottom' => t('Bottom'),
- ),
- );
- $layouts['threecol_33_33_33'] = array(
- 'key' => 'threecol_33_33_33',
- 'name' => t('Three columns 33/33/33'),
- 'zones' => array(
- 'top' => t('Top'),
- 'left' => t('Left column'),
- 'middle' => t('Middle column'),
- 'right' => t('Right column'),
- 'bottom' => t('Bottom'),
- ),
- );
- $layouts['threecol_25_50_25'] = array(
- 'key' => 'threecol_25_50_25',
- 'name' => t('Three columns 25/50/25'),
- 'zones' => array(
- 'top' => t('Top'),
- 'left' => t('Left column'),
- 'middle' => t('Middle column'),
- 'right' => t('Right column'),
- 'bottom' => t('Bottom'),
- ),
- );
- $layouts['threecol_flexigrid'] = array(
- 'key' => 'threecol_flexigrid',
- 'name' => t('Three columns flexible grid'),
- 'zones' => array(
- 'left' => t('Row 1 left'),
- 'middle' => t('Row 1 middle'),
- 'right' => t('Row 1 right'),
- 'left2' => t('Row 2 left'),
- 'middle2' => t('Row 2 middle'),
- 'right2' => t('Row 2 right'),
- 'left3' => t('Row 3 left'),
- 'middle3' => t('Row 3 middle'),
- 'right3' => t('Row 3 right'),
- 'left4' => t('Row 4 left'),
- 'middle4' => t('Row 4 middle'),
- 'right4' => t('Row 4 right'),
- ),
- 'preprocess callback' => 'composite_preprocess_threecol_flexigrid',
- 'settings callback' => 'composite_settings_threecol_flexigrid',
- );
- return $layouts;
-}
-
-function composite_preprocess_threecol_flexigrid(&$variables) {
- $columncount_labels = array(0 => 'no-columns', 1 => 'one-column', 2 => 'two-columns', 3 => 'three-columns');
-
- $layout = $variables['layout'];
- $variables['composite_classes'] = implode('-', array('composite', strtr($layout['key'], '_', '-'), $layout['data']['column_proportions']));
- foreach (array('', '2', '3', '4') as $row) {
- // Pointers
- $left = 'left' . $row;
- $middle = 'middle' . $row;
- $right = 'right' . $row;
-
- $columns = 0;
- $has_content = FALSE;
- $rowclasses = array();
-
- if ($variables[$left]) {
- $rowclasses[] = 'with-left-column';
- $columns++;
- $has_content = TRUE;
- }
- if ($variables[$middle]) {
- $rowclasses[] = 'with-middle-column';
- $has_content = TRUE;
- $columns++;
- }
- else {
- $rowclasses[] = 'without-middle-column';
- }
- if ($variables[$right]) {
- $rowclasses[] = 'with-right-column';
- $columns++;
- $has_content = TRUE;
- }
-
- if ($variables[$middle] && $columns == 2) {
- if ($variables[$left]) {
- $rowclasses[] = 'with-left-middle-columns';
- }
- if ($variables[$right]) {
- $rowclasses[] = 'with-middle-right-columns';
- }
- }
-
- $rowclasses[] = $columncount_labels[$columns];
- $variables['row' . $row . '_classes'] = implode(' ', $rowclasses);
- $variables['row' . $row . '_has_content'] = $has_content;
- }
- // Make numbering consistent
- $variables['row1_classes'] = $variables['row_classes'];
- $variables['row1_has_content'] = $variables['row_has_content'];
-}
-
-function composite_settings_threecol_flexigrid($op, $edit = null) {
- switch ($op) {
- case 'form':
- $options = array(
- '33-33-33' => t('Three columns 33/33/33'),
- '25-50-25' => t('Three columns 25/50/25')
- );
- $default_value = isset($options[$edit['data']['column_proportions']]) ? $edit['data']['column_proportions'] : '33-33-33';
- $form['column_proportions'] = array(
- '#type' => 'select',
- '#title' => t('Column proportions'),
- '#description' => t('Select the width proportions of the columns. This affects every row.'),
- '#options' => $options,
- '#default_value' => $default_value,
- );
- return $form;
- break;
- }
-}
-
+type, FALSE);
+}
+
+function composite_get_types($type = '') {
+ static $types = null;
+
+ if (empty($types)) {
+ $types = array();
+ foreach (module_implements('composite_types') as $module) {
+ $data = module_invoke($module, 'composite_types');
+
+ foreach ($data as $k => $unused) {
+ // Fill in 'module' if not specified
+ if (!$data[$k]['module']) {
+ $data[$k]['module'] = $module;
+ }
+
+ // Prefix 'file' with module dir
+ if ($data[$k]['file']) {
+ $data[$k]['file'] = drupal_get_path('module', $module) .'/'. $data[$k]['file'];
+ }
+ }
+ $types = array_merge_recursive($types, $data);
+ }
+
+ foreach (module_implements('composite_types_alter') as $module) {
+ $function = $module . '_composite_types_alter';
+ $function($types);
+ }
+ }
+
+ if ($type == '')
+ return $types;
+ else if (array_key_exists($type, $types))
+ return $types[$type];
+}
+
+// Includes the 'file' parameter of a reference type, if defined.
+function composite_include_file($type) {
+ if ($type['file'] && is_file($type['file'])) {
+ include_once $type['file'];
+ }
+}
+
+function composite_invoke_referenceapi(&$reference, $op, $a3 = NULL, $a4 = NULL) {
+ $type_def = composite_get_types($reference['type']);
+ if ($type_def) {
+ composite_include_file($type_def);
+ $function = $type_def['module'] .'_composite_'. $type_def['type'] .'_api';
+ if (function_exists($function)) {
+ return $function($reference, $op, $a3, $a4);
+ }
+ }
+}
+
+function composite_get_layouts($type = '') {
+ static $layouts = array();
+ static $layouts_select = array();
+
+ if (!$layouts) {
+ // Would like to make layouts pluggable, but doesn't quite work
+ // because of inability to specify template files from other modules.
+ $layouts = module_invoke_all('composite_layouts');
+ // Do some defaults and path processing
+ // Construct a select list for convenience
+ foreach ($layouts as $key => $v) {
+ $key_dashed = strtr($key, '_', '-');
+ if (!$layouts[$key]['template']) {
+ $layouts[$key]['template'] = 'composite-layout-' . $key_dashed;
+ }
+ if (!$layouts[$key]['path']) {
+ $layouts[$key]['path'] = drupal_get_path('module', 'composite') . '/theme';
+ }
+
+ $css = $layouts[$key]['css'] ? $layouts[$key]['css'] : 'composite-layout-' . $key_dashed . '.css';
+ $css = $layouts[$key]['path'] . '/' . $css;
+ if (is_file($css)) {
+ $layouts[$key]['css'] = $css;
+ }
+
+ $icon = $layouts[$key]['icon'] ? $layouts[$key]['icon'] : 'composite-layout-' . $key_dashed . '.icon.png';
+ $icon = $layouts[$key]['path'] . '/' . $icon;
+ if (is_file($icon)) {
+ $layouts[$key]['icon'] = $icon;
+ }
+
+ $layouts_select[$key] = $v['name'];
+ }
+ }
+
+ if ($type == 'select') {
+ return $layouts_select;
+ }
+ else {
+ return $layouts;
+ }
+}
+
+function composite_get_layout($layout = '') {
+ $layouts = composite_get_layouts();
+ return $layouts[$layout];
+}
+
+/***********************************************************
+* MENU *
+************************************************************/
+
+function composite_perm() {
+ return array('edit composite layouts');
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function composite_menu() {
+ $items = array();
+
+ // Generate local task for each reference type that requests it
+ $types = composite_get_types();
+ foreach ($types as $type) {
+ if ($type['local task']) {
+ $items['node/%node/composite_' . $type['type']] = array(
+ 'title' => $type['label']['plural'],
+ 'page callback' => 'composite_general_select_page',
+ 'page arguments' => array($type['type'], 1),
+ 'access callback' => 'composite_access',
+ 'access arguments' => array($type['type'], 'update', 1),
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 3,
+ 'file' => 'composite.pages.inc',
+ );
+ }
+ }
+ $items['node/%node/composite_zones'] = array(
+ 'title' => t('Zones'),
+ 'page callback' => 'composite_zones_page',
+ 'page arguments' => array(1),
+ 'access callback' => 'composite_access',
+ 'access arguments' => array('zones', 'update', 1),
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 3,
+ 'file' => 'composite.pages.inc',
+ );
+
+ return $items;
+}
+
+/**
+ * Access function used to turn tabs off, or defer to node_access
+ */
+function composite_access($tab, $op, $node) {
+ $access = FALSE;
+
+ if ((user_access('edit composite layouts') || user_access('administer nodes')) && composite_enabled($node) && $node->composite_layout) {
+ $access_function = 'node_access';
+ // Change access function if this tab was generated for a reference type
+ // and the optional task access function is defined
+ $type = composite_get_types($tab);
+ if ($type && $type['task access']) {
+ composite_include_file($type);
+ $function = $type['task access'];
+ if (function_exists($function)) {
+ $access_function = $function;
+ }
+ }
+ $access = $access_function($op, $node);
+ }
+ return $access;
+}
+
+/***********************************************************
+* NODE MANIPULATIONS .ie load/save/view etc. *
+************************************************************/
+
+/**
+ * Implementation of hook_form_alter().
+ */
+function composite_form_alter(&$form, $form_state, $form_id) {
+ if ($form['#id'] == 'node-form' && composite_enabled($form['#node'])) {
+ // .../node/xx/edit or .../node/add/xx page
+ $node = $form['#node'];
+ $default_set = variable_get('composite_default_set_' . $node->type, '--');
+
+ if ((user_access('edit composite layouts') || user_access('administer nodes'))) {
+ $form['composite_settings'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Composite layout'),
+ '#description' => t('Select the desired composite layout of this node.'),
+ '#collapsible' => TRUE,
+ '#collapsed' => !$node->composite_layout && $default_set == '--',
+ '#weight' => 10,
+ );
+
+ $form['composite_settings']['composite_references'] = array(
+ '#type' => 'value',
+ '#value' => $node->composite_references,
+ );
+ $form['composite_settings']['composite_layout_data'] = array(
+ '#type' => 'value',
+ '#value' => $node->composite_layout_data,
+ );
+ $form['composite_settings']['composite_layout'] = array(
+ '#title' => t('Layout'),
+ '#type' => 'composite_layout_radios',
+ '#options' => array('--' => t('No composite layout')) + composite_get_layouts('select'),
+ '#default_value' => $node->composite_layout ? $node->composite_layout : '--',
+ );
+ $form['composite_settings']['composite_content_reference'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Node content should be included as an item available for composite layout.'),
+ '#description' => t('This has no effect if no composite layout is selected, or if a layout set is selected.'),
+ '#default_value' => isset($node->composite_references['content']['type']) && ($node->composite_references['content']['type'] == 'content'),
+ );
+
+ // Append layout sets options
+ $layout_sets = composite_sets_select();
+ if ($layout_sets) {
+ $form['composite_settings']['composite_layout']['#options'] += $layout_sets;
+ // Assign a default set only if this is a new node
+ if ($default_set != '--' && !isset($node->nid)) {
+ $form['composite_settings']['composite_layout']['#default_value'] = $default_set;
+ }
+ }
+ }
+ else {
+ // Non privileged user, retain all composite layout related settings
+ $form['composite_settings']['composite_layout'] = array(
+ '#type' => 'value',
+ '#value' => $node->composite_layout ? $node->composite_layout : '--',
+ );
+ $form['composite_settings']['composite_references'] = array(
+ '#type' => 'value',
+ '#value' => $node->composite_references,
+ );
+ $form['composite_settings']['composite_layout_data'] = array(
+ '#type' => 'value',
+ '#value' => $node->composite_layout_data,
+ );
+ $form['composite_settings']['composite_content_reference'] = array(
+ '#type' => 'value',
+ '#value' => isset($node->composite_references['content']['type']) && ($node->composite_references['content']['type'] == 'content'),
+ );
+ // Force a default set if this is a new node
+ if ($default_set != '--' && !isset($node->nid)) {
+ $form['composite_settings']['composite_layout']['#value'] = $default_set;
+ }
+ }
+ }
+ else if ($form_id == 'node_type_form' && isset($form['#node_type'])) {
+ // .../admin/content/node-type/xx page
+ $node_type = $form['#node_type']->type;
+ $composite_enabled = variable_get('composite_enabled_' . $node_type, FALSE);
+
+ // The code below borrows heavily from nodereference_field_settings()
+ $form['composite_extra_config'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Composite layout'),
+ '#collapsible' => TRUE,
+ '#collapsed' => !$composite_enabled,
+ );
+ $form['composite_extra_config']['composite_enabled'] = array(
+ '#title' => t('Enable composite layout for this content type.'),
+ '#type' => 'checkbox',
+ '#default_value' => $composite_enabled,
+ );
+
+ $form['composite_extra_config']['composite_nodes'] = array(
+ '#title' => t('Nodes'),
+ '#type' => 'fieldset',
+ '#collapsible' => TRUE,
+ '#description' => t('If you wish to display other nodes in your composite layouts, please select the content types to be made available.'),
+ );
+
+ $form['composite_extra_config']['composite_nodes']['composite_referenceable_types'] = array(
+ '#title' => t('Content types that can be referenced'),
+ '#type' => 'checkboxes',
+ '#multiple' => TRUE,
+ '#options' => node_get_types('names'),
+ '#default_value' => variable_get('composite_referenceable_types_' . $node_type, array()),
+ );
+
+ if (module_exists('views')) {
+ $views = array('--' => '--');
+ $all_views = views_get_all_views();
+ foreach ($all_views as $view) {
+ // Only 'node' views that have fields will work for our purpose.
+ if ($view->base_table == 'node' && !empty($view->display['default']->display_options['fields'])) {
+ if ($view->type == 'Default') {
+ $views[t('Default Views')][$view->name] = $view->name;
+ }
+ else {
+ $views[t('Existing Views')][$view->name] = $view->name;
+ }
+ }
+ }
+
+ if (count($views) > 1) {
+ $form['composite_extra_config']['composite_nodes']['advanced'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Advanced - Nodes that can be referenced (View)'),
+ '#collapsible' => TRUE,
+ '#collapsed' => variable_get('composite_advanced_view_' . $node_type, '--') == '--',
+ );
+ $form['composite_extra_config']['composite_nodes']['advanced']['composite_advanced_view'] = array(
+ '#type' => 'select',
+ '#title' => t('View used to select the nodes'),
+ '#options' => $views,
+ '#default_value' => variable_get('composite_advanced_view_' . $node_type, '--'),
+ '#description' => t('Choose the "Views module" view that selects the nodes that can be referenced.
Note:- Only views that have fields will work for this purpose.
- This will discard the "Content types" settings above. Use the view\'s "filters" section instead.
- Use the view\'s "fields" section to display additional informations about candidate nodes on node creation/edition form.
- Use the view\'s "sort criteria" section to determine the order in which candidate nodes will be displayed.
'),
+ );
+ $form['composite_extra_config']['composite_nodes']['advanced']['composite_advanced_view_args'] = array(
+ '#type' => 'textfield',
+ '#title' => t('View arguments'),
+ '#default_value' => variable_get('composite_advanced_view_args_' . $node_type, ''),
+ '#required' => FALSE,
+ '#description' => t('Provide a comma separated list of arguments to pass to the view.'),
+ );
+ }
+ }
+ $layout_sets = composite_sets_select();
+ if ($layout_sets) {
+ $form['composite_extra_config']['composite_default_set'] = array(
+ '#type' => 'select',
+ '#title' => t('Default layout set'),
+ '#options' => array('--' => t('')) + $layout_sets,
+ '#default_value' => variable_get('composite_default_set_' . $node_type, '--'),
+ '#description' => t('Select a default layout set for this content type. If selected, all new nodes will be automatically populated by this set.'),
+ );
+ }
+ }
+}
+
+/**
+ * Implementation of hook_nodeapi().
+ */
+function composite_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
+ if (!composite_enabled($node))
+ return;
+
+ switch ($op) {
+ case 'delete':
+ // Notice that we're matching all revisions, by using the node's nid.
+ db_query("DELETE FROM {node_composite} WHERE nid = %d", $node->nid);
+ db_query("DELETE FROM {node_composite_references} WHERE nid = %d", $node->nid);
+ break;
+
+ case 'delete revision':
+ composite_delete_revision($node);
+ break;
+
+ case 'insert':
+ case 'update':
+ if (isset($node->composite_layout) && is_numeric($node->composite_layout)) {
+ // A numeric composite_layout is actually a layout set
+ // But we zero out composite_layout just in case the set isn't found.
+ $sid = $node->composite_layout;
+ $node->composite_layout = '';
+ composite_sets_populate_node($node, $sid);
+ }
+ composite_update($node);
+ break;
+
+ case 'load':
+ return composite_load($node);
+ break;
+
+ case 'view':
+ // Only do something in full view, and if there is a layout defined.
+ if (!$a3 /* !$teaser */ && $node->composite_layout && $node->composite_layout != '--') {
+ $node->composite_content = TRUE;
+ }
+ break;
+
+ case 'alter':
+ if (isset($node->composite_content)) {
+ // Have to do our 'view' stuff in the alter hook because we have to wait
+ // for other modules to do their processing first.
+ $layout = composite_get_layout($node->composite_layout);
+ if ($layout) {
+ $layout['data'] = $node->composite_layout_data;
+ $node->composite_content = composite_view($node, $layout, $node->composite_references);
+ foreach ($node->composite_content as $zone => $zone_item) {
+ $node->composite_content[$zone] = drupal_render($zone_item);
+ }
+ $node->body = theme('composite_content', $layout, $node->composite_content);
+ }
+ }
+ break;
+ }
+}
+
+function composite_delete_revision($node) {
+ db_query("DELETE FROM {node_composite} WHERE nid = %d AND vid = %d", $node->nid, $node->vid);
+ db_query("DELETE FROM {node_composite_references} WHERE nid = %d AND vid = %d", $node->nid, $node->vid);
+}
+
+/**
+ * Implementation of hook_update().
+ *
+ * Not really. It just happens to have a compatible function signature with hook_update
+ *
+ */
+function composite_update($node) {
+ // Just delete everything, and then add back in later
+ composite_delete_revision($node);
+
+ if ($node->composite_layout && $node->composite_layout != '--') {
+ // Treat the content_reference specially if we came in from an edit form.
+ if (isset($node->composite_content_reference)) {
+ if ($node->composite_content_reference) {
+ // Add content_reference, but only if it doesn't already exist
+ if (!$node->composite_references['content']) {
+ $node->composite_references['content'] = array(
+ 'type' => 'content',
+ 'weight' => 0,
+ 'id' => 'content',
+ 'data' => '',
+ 'zone' => COMPOSITE_ZONE_NONE,
+ );
+ }
+ }
+ else {
+ // Unset content_reference
+ unset($node->composite_references['content']);
+ }
+ }
+
+ // Update node_composite
+ db_query("INSERT INTO {node_composite} (nid, vid, layout, data) VALUES (%d, %d, '%s', '%s') ", $node->nid, $node->vid, $node->composite_layout, $node->composite_layout_data ? serialize($node->composite_layout_data) : '');
+
+ // Update node_composite_references
+ $args = array();
+ $query_parts = array();
+ foreach (element_children($node->composite_references) as $id) {
+ $reference = $node->composite_references[$id];
+ if (is_array($reference) && $reference['id']) {
+ $query_parts[] = " (%d, %d, '%s', %d, '%s', '%s', '%s')";
+ $args[] = $node->nid;
+ $args[] = $node->vid;
+ $args[] = $reference['type'];
+ $args[] = $reference['weight'];
+ $args[] = $id;
+ $args[] = $reference['data'] ? serialize($reference['data']) : '';
+ $args[] = $reference['zone'];
+ }
+ }
+
+ if (count($query_parts)) {
+ $query = "INSERT INTO {node_composite_references} (nid, vid, type, weight, id, data, zone) VALUES" . implode(', ', $query_parts);
+ db_query($query, $args);
+ }
+ }
+}
+
+/**
+ * Implementation of hook_load().
+ *
+ * Not really. It just happens to have a compatible function signature with hook_load
+ */
+function composite_load($node) {
+ $types = composite_get_types();
+ $additions = array();
+
+ $result = db_fetch_object(db_query('SELECT layout, data FROM {node_composite} WHERE vid = %d', $node->vid));
+ $additions['composite_layout'] = $result->layout;
+ $additions['composite_layout_data'] = unserialize($result->data);
+
+ // Seed reference sublists - they must be arrays or empty arrays
+ $sublists = array();
+ foreach ($types as $type) {
+ $sublists[$type['type']] = array();
+ }
+
+ $result = db_query("SELECT type, weight, id, data, zone FROM {node_composite_references} WHERE vid = %d", $node->vid);
+ while ($object = db_fetch_object($result)) {
+ // Some common manipulations
+ $object = (array) $object;
+ $object['data'] = unserialize($object['data']);
+
+ // Some type specific manipulations
+ composite_invoke_referenceapi($object, 'load');
+
+ $additions['composite_references'][$object['id']] = $object;
+ $sublists[$object['type']][$object['id']] = $object;
+ }
+
+ // Add reference sublists to the load
+ foreach ($types as $type) {
+ $additions['composite_references']['#'. $type['type'] .'_references'] = $sublists[$type['type']];
+ }
+
+ return $additions;
+}
+
+/**
+ * Nodeapi view helper
+ * Assemble the composite content into an array in a similar fashion to $node->content
+ *
+ * - $content: Original node content
+ * - $layout: Layout definition
+ * - $references: List of items for composite content
+ */
+function composite_view($node, $layout = array(), $references = array()) {
+ $composite_content = array();
+ $references = _composite_references_preprocess($references, $layout['zones'], FALSE);
+
+ foreach (element_children($references) as $id) {
+ $reference = $references[$id];
+ // Filter out items not in a displayable zone
+ if (array_key_exists($reference['zone'], $layout['zones'])) {
+ $output = composite_invoke_referenceapi($reference, 'view', $node);
+ if ($output) {
+ $composite_content[$reference['zone']][$reference['id']] = array(
+ '#value' => $output,
+ '#weight' => $reference['weight'],
+ );
+ }
+ }
+ }
+ return $composite_content;
+}
+
+/***********************************************************
+* COMPOSITE LAYOUT SETS *
+************************************************************/
+
+// Helper function to retrieve a list of existing sets
+function composite_sets_select() {
+ return composite_get_sets('select');
+}
+
+function composite_get_sets($type = 'sets') {
+ static $options = null;
+ static $layout_sets = null;
+
+ if (!isset($options)) {
+ $options = array();
+ $layout_sets = array();
+ $result = db_query("SELECT sid, name, data FROM {node_composite_sets}");
+ while ($object = db_fetch_object($result)) {
+ $options[$object->sid] = $object->name;
+ // Don't unserialize data unnecessarily
+ $layout_sets[$object->sid] = $object;
+ }
+ }
+ return $type == 'select' ? $options : $layout_sets;
+}
+
+function composite_sets_load($sid) {
+ $layout_sets = composite_get_sets();
+ if (isset($layout_sets[$sid])) {
+ $layout_set = $layout_sets[$sid];
+ $layout_set->data = unserialize($layout_set->data);
+ return $layout_set;
+ }
+ else {
+ return array();
+ }
+}
+
+// Returns true if populated, false otherwise
+function composite_sets_populate_node(&$node, $sid) {
+ // Fetch the layout set
+ $layout_set = composite_sets_load($sid);
+
+ // Update the node
+ if ($layout_set) {
+ $node->composite_layout = $layout_set->data['composite_layout'];
+ $node->composite_layout_data = $layout_set->data['composite_layout_data'];
+ $node->composite_references = array();
+
+ foreach ($layout_set->data['composite_references'] as $reference) {
+ if (is_array($reference) && isset($reference['id'])) {
+ composite_invoke_referenceapi($reference, 'load');
+ $node->composite_references[$reference['id']] = $reference;
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/***********************************************************
+* CUSTOM LAYOUT SELECT ELEMENT *
+************************************************************/
+
+function composite_elements() {
+ $type['composite_layout_radios'] = array('#input' => TRUE, '#process' => array('composite_layout_radios_process'));
+ return $type;
+}
+
+function composite_layout_radios_process($element) {
+ if (count($element['#options']) > 0) {
+ // Do the normal radios thing
+ $element = expand_radios($element);
+
+ // And then add in our icons
+ $layouts = composite_get_layouts();
+ drupal_add_css(drupal_get_path('module', 'composite') . '/composite.css', 'module', 'all', FALSE);
+
+ // Track the first option which is a layout set
+ $first_set = FALSE;
+
+
+ foreach (element_children($element) as $key) {
+ if (array_key_exists($key, $layouts)) {
+ $element[$key]['#prefix'] = '';
+ $element[$key]['#suffix'] = '
';
+
+ if ($layouts[$key]['icon']) {
+ $element[$key]['#prefix'] .= theme('composite_layout_icon', $layouts[$key]['icon']);
+ }
+ }
+ else if (is_numeric($key)) {
+ // A numeric key means a layout set
+ if ($first_set == FALSE) {
+ $first_set = $key;
+ $element[$key]['#prefix'] = '';
+ }
+ }
+ }
+
+ // Close off the sets div if there is one
+ if ($first_set) {
+ $element[$key]['#suffix'] = '
';
+ }
+
+ $element['#prefix'] = '';
+ $element['#suffix'] = '
';
+ return $element;
+ }
+}
+
+/***********************************************************
+* THEME-ING *
+************************************************************/
+
+/**
+ * Implementation of hook_theme()
+ */
+function composite_theme() {
+ return array(
+ 'composite_layout_radios' => array(
+ 'arguments' => array('element' => NULL),
+ ),
+ 'composite_layout_icon' => array(
+ 'arguments' => array('file' => NULL),
+ ),
+ 'composite_zones_form' => array(
+ 'template' => 'composite-zones-form',
+ 'file' => 'composite.pages.inc',
+ 'arguments' => array('form' => NULL),
+ ),
+ 'composite_zones_preview' => array(
+ 'arguments' => array('node' => NULL),
+ ),
+ 'composite_content' => array(
+ 'template' => 'composite-content',
+ 'path' => drupal_get_path('module', 'composite') . '/theme',
+ 'arguments' => array('layout' => array(), 'composite_content' => array()),
+ ),
+ 'composite_node_title' => array(
+ 'arguments' => array('title' => ''),
+ ),
+ );
+}
+
+function theme_composite_layout_radios($element) {
+ return theme('radios', $element);
+}
+
+function theme_composite_layout_icon($file) {
+ return '';
+}
+
+function theme_composite_zones_preview($node) {
+ if (isset($node->nid)) {
+ $output = '' . node_view($node, FALSE, FALSE) .'
';
+ return $output;
+ }
+}
+
+function template_preprocess_composite_content(&$variables) {
+ $layout = $variables['layout'];
+ $composite_content = $variables['composite_content'];
+ $key = strtr($layout['key'], '_', '-');
+
+ // Always include the default template
+ $variables['template_files'][] = 'composite-content';
+ $variables['template_files'][] = $layout['template'];
+
+ if ($layout['css']) {
+ drupal_add_css($layout['css']);
+ }
+
+ // Extract out composite_content into separate variables
+ $variables['content'] = $composite_content['content'];
+ foreach ($layout['zones'] as $zone => $unused) {
+ $variables[$zone] = $composite_content[$zone];
+ }
+
+ // Call a preprocess callback if defined
+ if (isset($layout['preprocess callback'])) {
+ $function = $layout['preprocess callback'];
+ if (function_exists($function)) {
+ $function($variables);
+ }
+ }
+}
+
+function theme_composite_node_title($title) {
+ return '' . $title . '
';
+}
+
+/***********************************************************
+* REFERENCE TYPE DEFINITIONS *
+************************************************************/
+
+function composite_composite_types() {
+ $types = array(
+ // 'content' is treated specially in some places.
+ 'content' => array(
+ 'type' => 'content',
+ ),
+ 'block' => array(
+ 'type' => 'block',
+ // 'module' => 'composite', // optional value
+ 'file' => 'composite.block.inc', // optional, path relative from directory of 'module'
+ 'label' => array('singular' => t('Block'), 'plural' => t('Blocks')),
+
+ // 'local task' - whether composite.module should generate a local task tab for this type
+ 'local task' => TRUE,
+ // 'task access' - optional menu access function for the local task tab
+ 'task access' => '',
+ // 'potentials callback' - callback to retrieve list of potential composite references
+ 'potentials callback' => 'composite_composite_block_potentials',
+ ),
+ 'node' => array(
+ 'type' => 'node',
+ 'file' => 'composite.node.inc',
+ 'label' => array('singular' => t('Node'), 'plural' => t('Nodes')),
+ 'local task' => TRUE,
+ 'task access' => 'composite_composite_node_access',
+ 'potentials callback' => 'composite_composite_node_potentials',
+ ),
+ );
+ return $types;
+}
+
+function composite_composite_content_api(&$reference, $op, $node = NULL, $a4 = NULL) {
+ switch ($op) {
+ case 'info':
+ $reference['info'] = t('Node: Original content');
+ break;
+
+ // Return a rendering of the reference item
+ case 'view':
+ // Be careful of security here. The correct field (and whether to escape
+ // malicious text) depends on who calls composite_view() which calls this,
+ return $node->body;
+ break;
+ }
+}
+
+/***********************************************************
+* LAYOUT DEFINITIONS *
+************************************************************/
+
+
+
+function composite_load_includes($directory, $callback) {
+ // Load all our module 'on behalfs'.
+ $path = drupal_get_path('module', 'composite') . '/' . $directory;
+ $files = drupal_system_listing('inc$', $path, 'name', 0);
+
+ foreach($files as $file) {
+ require_once('./' . $file->filename);
+ $file->name = str_replace('-','_',$file->name);
+ }
+ $output = module_invoke_all($callback);
+ foreach ($files as $file) {
+ $function = 'composite_' . $file->name . '_' . $callback;
+ if (function_exists($function)) {
+ $result = $function();
+ if (isset($result) && is_array($result)) {
+ $output = array_merge($output, $result);
+ }
+ }
+ }
+ return $output;
+}
+
+function composite_composite_layouts() {
+ static $layout = NULL;
+ if (!$layout) {
+ $layouts = composite_load_includes('theme', 'composite_include');
+ }
+ return $layouts;
+}
diff -urpN composite/theme/composite-layout-onecol.inc composite-working/theme/composite-layout-onecol.inc
--- composite/theme/composite-layout-onecol.inc 1969-12-31 19:00:00.000000000 -0500
+++ composite-working/theme/composite-layout-onecol.inc 2009-06-26 12:44:18.000000000 -0400
@@ -0,0 +1,37 @@
+composite_layout)
+ *
+ * Layout specific variables:
+ * - $left: Content for the left zone.
+ *
+ * @see template_preprocess_composite_content
+ */
+
+/**
+ * implementation of hook_composite_layout
+ **
+*/
+function composite_composite_layout_onecol_composite_include() {
+ $layouts['onecol'] = array(
+ 'key' => 'onecol',
+ 'name' => t('Single column'),
+ 'zones' => array(
+ 'left' => t('Left column'),
+ ),
+ // The following three vars are optional. If not specified, they
+ // will be derived from key, in an equivalent manner to what you see below.
+ 'path' => drupal_get_path('module', 'composite') . '/theme',
+ 'css' => 'composite-layout-onecol.css',
+ 'template' => 'composite-layout-onecol',
+ 'icon' => 'composite-layout-onecol.icon.png'
+ );
+ return $layouts;
+}
+
diff -urpN composite/theme/composite-layout-threecol-25-50-25.inc composite-working/theme/composite-layout-threecol-25-50-25.inc
--- composite/theme/composite-layout-threecol-25-50-25.inc 1969-12-31 19:00:00.000000000 -0500
+++ composite-working/theme/composite-layout-threecol-25-50-25.inc 2009-06-26 12:44:18.000000000 -0400
@@ -0,0 +1,34 @@
+composite_layout)
+ *
+ * Layout specific variables:
+ * - $left: Content for the left zone.
+ * - $right: Content for the right zone.
+ *
+ * @see template_preprocess_composite_content
+ */
+
+/**
+ * implementation of hook_composite_layout
+ **
+*/
+function composite_composite_layout_threecol_25_50_25_composite_include() {
+ $layouts['threecol_25_50_25'] = array(
+ 'key' => 'threecol_25_50_25',
+ 'name' => t('Three columns 25/50/25'),
+ 'zones' => array(
+ 'top' => t('Top'),
+ 'left' => t('Left column'),
+ 'middle' => t('Middle column'),
+ 'right' => t('Right column'),
+ 'bottom' => t('Bottom'),
+ ),
+ );
+ return $layouts;
+}
diff -urpN composite/theme/composite-layout-threecol-33-33-33.inc composite-working/theme/composite-layout-threecol-33-33-33.inc
--- composite/theme/composite-layout-threecol-33-33-33.inc 1969-12-31 19:00:00.000000000 -0500
+++ composite-working/theme/composite-layout-threecol-33-33-33.inc 2009-06-26 12:44:18.000000000 -0400
@@ -0,0 +1,35 @@
+composite_layout)
+ *
+ * Layout specific variables:
+ * - $left: Content for the left zone.
+ * - $right: Content for the right zone.
+ *
+ * @see template_preprocess_composite_content
+ */
+
+
+/**
+ * implementation of hook_composite_layout
+ **
+*/
+function composite_composite_layout_threecol_33_33_33_composite_include() {
+ $layouts['threecol_33_33_33'] = array(
+ 'key' => 'threecol_33_33_33',
+ 'name' => t('Three columns 33/33/33'),
+ 'zones' => array(
+ 'top' => t('Top'),
+ 'left' => t('Left column'),
+ 'middle' => t('Middle column'),
+ 'right' => t('Right column'),
+ 'bottom' => t('Bottom'),
+ ),
+ );
+ return $layouts;
+}
diff -urpN composite/theme/composite-layout-threecol-flexigrid.inc composite-working/theme/composite-layout-threecol-flexigrid.inc
--- composite/theme/composite-layout-threecol-flexigrid.inc 1969-12-31 19:00:00.000000000 -0500
+++ composite-working/theme/composite-layout-threecol-flexigrid.inc 2009-06-26 12:44:18.000000000 -0400
@@ -0,0 +1,119 @@
+composite_layout)
+ *
+ * Layout specific variables:
+ * - $left: Content for the left zone.
+ * - $right: Content for the right zone.
+ *
+ * @see template_preprocess_composite_content
+ */
+
+
+/**
+ * implementation of hook_composite_layout
+ **
+*/
+
+function composite_composite_layout_threecol_flexigrid_composite_include() {
+ $layouts['threecol_flexigrid'] = array(
+ 'key' => 'threecol_flexigrid',
+ 'name' => t('Three columns flexible grid'),
+ 'zones' => array(
+ 'left' => t('Row 1 left'),
+ 'middle' => t('Row 1 middle'),
+ 'right' => t('Row 1 right'),
+ 'left2' => t('Row 2 left'),
+ 'middle2' => t('Row 2 middle'),
+ 'right2' => t('Row 2 right'),
+ 'left3' => t('Row 3 left'),
+ 'middle3' => t('Row 3 middle'),
+ 'right3' => t('Row 3 right'),
+ 'left4' => t('Row 4 left'),
+ 'middle4' => t('Row 4 middle'),
+ 'right4' => t('Row 4 right'),
+ ),
+ 'preprocess callback' => 'composite_preprocess_threecol_flexigrid',
+ 'settings callback' => 'composite_settings_threecol_flexigrid',
+ );
+ return $layouts;
+}
+
+function composite_preprocess_threecol_flexigrid(&$variables) {
+ $columncount_labels = array(0 => 'no-columns', 1 => 'one-column', 2 => 'two-columns', 3 => 'three-columns');
+
+ $layout = $variables['layout'];
+ $variables['composite_classes'] = implode('-', array('composite', strtr($layout['key'], '_', '-'), $layout['data']['column_proportions']));
+ foreach (array('', '2', '3', '4') as $row) {
+ // Pointers
+ $left = 'left' . $row;
+ $middle = 'middle' . $row;
+ $right = 'right' . $row;
+
+ $columns = 0;
+ $has_content = FALSE;
+ $rowclasses = array();
+
+ if ($variables[$left]) {
+ $rowclasses[] = 'with-left-column';
+ $columns++;
+ $has_content = TRUE;
+ }
+ if ($variables[$middle]) {
+ $rowclasses[] = 'with-middle-column';
+ $has_content = TRUE;
+ $columns++;
+ }
+ else {
+ $rowclasses[] = 'without-middle-column';
+ }
+ if ($variables[$right]) {
+ $rowclasses[] = 'with-right-column';
+ $columns++;
+ $has_content = TRUE;
+ }
+
+ if ($variables[$middle] && $columns == 2) {
+ if ($variables[$left]) {
+ $rowclasses[] = 'with-left-middle-columns';
+ }
+ if ($variables[$right]) {
+ $rowclasses[] = 'with-middle-right-columns';
+ }
+ }
+
+ $rowclasses[] = $columncount_labels[$columns];
+ $variables['row' . $row . '_classes'] = implode(' ', $rowclasses);
+ $variables['row' . $row . '_has_content'] = $has_content;
+ }
+ // Make numbering consistent
+ $variables['row1_classes'] = $variables['row_classes'];
+ $variables['row1_has_content'] = $variables['row_has_content'];
+}
+
+function composite_settings_threecol_flexigrid($op, $edit = null) {
+ switch ($op) {
+ case 'form':
+ $options = array(
+ '33-33-33' => t('Three columns 33/33/33'),
+ '25-50-25' => t('Three columns 25/50/25')
+ );
+ $default_value = isset($options[$edit['data']['column_proportions']]) ? $edit['data']['column_proportions'] : '33-33-33';
+ $form['column_proportions'] = array(
+ '#type' => 'select',
+ '#title' => t('Column proportions'),
+ '#description' => t('Select the width proportions of the columns. This affects every row.'),
+ '#options' => $options,
+ '#default_value' => $default_value,
+ );
+ return $form;
+ break;
+ }
+}
+
+
\ No newline at end of file
diff -urpN composite/theme/composite-layout-twocol-bricks.inc composite-working/theme/composite-layout-twocol-bricks.inc
--- composite/theme/composite-layout-twocol-bricks.inc 1969-12-31 19:00:00.000000000 -0500
+++ composite-working/theme/composite-layout-twocol-bricks.inc 2009-06-26 12:44:18.000000000 -0400
@@ -0,0 +1,37 @@
+body)
+ * - $layout: Layout definition (from $node->composite_layout)
+ *
+ * Layout specific variables:
+ * - $left: Content for the left zone.
+ *
+ * @see template_preprocess_composite_content
+ */
+
+
+/**
+ * implementation of hook_composite_layout
+ **
+*/
+function composite_composite_layout_twocol_bricks_composite_include() {
+ $layouts['twocol_bricks'] = array(
+ 'key' => 'twocol_bricks',
+ 'name' => t('Two columns bricks'),
+ 'zones' => array(
+ 'top' => t('Top'),
+ 'left' => t('Left column (upper)'),
+ 'right' => t('Right column (upper)'),
+ 'middle_row' => t('Middle row'),
+ 'left2' => t('Left column (lower)'),
+ 'right2' => t('Right column (lower)'),
+ 'bottom' => t('Bottom'),
+ ),
+ );
+ return $layouts;
+}
diff -urpN composite/theme/composite-layout-twocol.inc composite-working/theme/composite-layout-twocol.inc
--- composite/theme/composite-layout-twocol.inc 1969-12-31 19:00:00.000000000 -0500
+++ composite-working/theme/composite-layout-twocol.inc 2009-06-26 12:44:18.000000000 -0400
@@ -0,0 +1,36 @@
+body)
+ * - $layout: Layout definition (from $node->composite_layout)
+ *
+ * Layout specific variables:
+ * - $left: Content for the left zone.
+ *
+ * @see template_preprocess_composite_content
+ */
+
+
+/**
+ * implementation of hook_composite_layout
+ **
+*/
+
+function composite_composite_layout_twocol_composite_include() {
+ $layouts['twocol'] = array(
+ 'key' => 'twocol',
+ 'name' => t('Two columns'),
+ 'zones' => array(
+ 'top' => t('Top'),
+ 'left' => t('Left column'),
+ 'right' => t('Right column'),
+ 'bottom' => t('Bottom'),
+ ),
+ );
+ return $layouts;
+}
+