t('Fields: Render back reference views as content fields'), NODERELATIONSHIPS_BACKREF_REGION_PAGE => t('Page: Render back reference views grouped on a single fieldset on main node view'), NODERELATIONSHIPS_BACKREF_REGION_TAB => t('Tab: Render back reference views in separate relationships tab'), ); } /** * Get a list of relationship settings matching the given conditions. * * @param $where * Expression for the WHERE clause of the query. * @param $args * A variable list of arguments for the query. */ function noderelationships_settings_list($where) { $args = func_get_args(); array_shift($args); // Check for 'All arguments in one array' syntax. if (isset($args[0]) && is_array($args[0])) { $args = $args[0]; } $result = db_query('SELECT type_name, relation_type, related_type, field_name, settings FROM {noderelationships_settings} WHERE '. $where, $args); $rows = array(); while ($row = db_fetch_object($result)) { $row->settings = (!empty($row->settings) ? (array)unserialize($row->settings) : array()); $rows[] = $row; } return $rows; } /** * Get relationship settings for the given content type. * * @param $nodetype * The node type. * @param $settings_group * The group of settings the caller is interested in. * Optional, options: 'noderef', 'backref', 'all' (default). * * @return array * The requested settings array. */ function noderelationships_settings_load($nodetype, $settings_group = 'all') { // Build default settings structure. $settings = array( 'noderef' => array( 'search_and_reference_view' => array(), 'create_and_reference' => array(), 'translate_and_reference' => array(), ), 'backref' => array( 'regions' => array(), ), ); // Read settings from database. $regions = noderelationships_get_back_reference_regions(); foreach (noderelationships_settings_list("type_name = '%s'", $nodetype) as $row) { $relation_key = $row->related_type .':'. $row->field_name; if ($row->relation_type == 'noderef') { if (!empty($row->settings['search_and_reference_view'])) { $settings['noderef']['search_and_reference_view'][$row->field_name] = $row->settings['search_and_reference_view']; } if (!empty($row->settings['create_and_reference'])) { $settings['noderef']['create_and_reference'][$row->field_name] = $row->field_name; } if (!empty($row->settings['translate_and_reference'])) { $settings['noderef']['translate_and_reference'][$row->field_name] = $row->field_name; } } elseif ($row->relation_type == 'backref') { $region = $row->settings['region']; if (isset($regions[$region])) { if (!isset($settings['backref']['regions'][$region])) { $settings['backref']['regions'][$region] = array(); } $settings['backref']['regions'][$region][$relation_key] = array( 'weight' => $row->settings['weight'], 'back_reference_view' => $row->settings['back_reference_view'], ); } } } // Sort back reference regions by weight. foreach (array_keys($settings['backref']['regions']) as $region) { noderelationships_settings_region_sort($settings['backref']['regions'][$region]); } if ($settings_group == 'noderef') { return $settings['noderef']; } elseif ($settings_group == 'backref') { return $settings['backref']; } return $settings; } /** * Set relationship settings for the given content type. */ function noderelationships_settings_save($nodetype, $settings) { $old_settings_hash = md5(serialize(noderelationships_settings_load($nodetype))); $new_settings_hash = md5(serialize($settings)); // Quit if settings have not been changed. if ($old_settings_hash == $new_settings_hash) { return FALSE; } // Prepare the settings for database storage. $settings_rows = array(); if (!empty($settings['noderef'])) { $noderef_settings = $settings['noderef']; if (!empty($noderef_settings) && is_array($noderef_settings)) { foreach ($noderef_settings as $option_name => $option_values) { if (!empty($option_values) && is_array($option_values)) { foreach ($option_values as $field_name => $value) { $settings_key = 'noderef::'. $field_name; if (!isset($settings_rows[$settings_key])) { $settings_rows[$settings_key] = array(); } $settings_rows[$settings_key][$option_name] = $value; } } } } } if (!empty($settings['backref']) && !empty($settings['backref']['regions'])) { $backref_settings = $settings['backref']['regions']; if (!empty($backref_settings) && is_array($backref_settings)) { foreach ($backref_settings as $region => $region_relations) { if (!empty($region_relations) && is_array($region_relations)) { foreach ($region_relations as $relation_key => $relation_info) { $settings_key = 'backref:'. $relation_key; $relation_info['region'] = $region; $settings_rows[$settings_key] = $relation_info; } } } } } // Update the settings in the database. db_query("DELETE FROM {noderelationships_settings} WHERE type_name = '%s'", $nodetype); foreach ($settings_rows as $settings_key => $settings_row) { list($relation_type, $related_type, $field_name) = explode(':', $settings_key); $args = array($nodetype, $relation_type, $related_type, $field_name, serialize($settings_row)); db_query("INSERT INTO {noderelationships_settings} (type_name, relation_type, related_type, field_name, settings) VALUES ('%s', '%s', '%s', '%s', '%s')", $args); } // Synchronize back reference settings with back reference fields. noderelationships_cck_backref_sync_fields($nodetype); return TRUE; } /** * Delete relationship settings matching the given conditions. * * @param $where * Expression for the WHERE clause of the delete query. * @param $args * A variable list of arguments for the delete query. */ function noderelationships_settings_delete($where) { $args = func_get_args(); array_shift($args); // Check for 'All arguments in one array' syntax. if (isset($args[0]) && is_array($args[0])) { $args = $args[0]; } db_query('DELETE FROM {noderelationships_settings} WHERE '. $where, $args); } /** * Update relationship settings when a content type has been renamed. */ function noderelationships_settings_rename_type($oldname, $newname) { db_query("UPDATE {noderelationships_settings} SET type_name = '%s' WHERE type_name = '%s'", array($newname, $oldname)); db_query("UPDATE {noderelationships_settings} SET related_type = '%s' WHERE related_type = '%s'", array($newname, $oldname)); // Load the list of potentially affected back reference related types. $types_to_sync = array($newname => $newname); $result = db_query("SELECT DISTINCT type_name FROM {noderelationships_settings} WHERE related_type = '%s'", $newname); while ($row = db_fetch_object($result)) { if (!isset($types_to_sync[$row->type_name])) { $types_to_sync[$row->type_name] = $row->type_name; } } // Synchronize back reference settings with back reference fields. noderelationships_cck_backref_sync_fields($types_to_sync); } /** * Sort back reference regions by weight. */ function noderelationships_settings_region_sort(&$region) { uasort($region, 'noderelationships_settings_element_sort'); } /** * Helper function to sort relations by 'weight' attribute. * * Based on element_sort(), which acts on '#weight' keys. */ function noderelationships_settings_element_sort($a, $b) { $a_weight = (is_array($a) && isset($a['weight'])) ? $a['weight'] : 0; $b_weight = (is_array($b) && isset($b['weight'])) ? $b['weight'] : 0; if ($a_weight == $b_weight) { return 0; } return ($a_weight < $b_weight) ? -1 : 1; } /** * Get the list of all content types that have some kind of relation. */ function noderelationships_get_relationed_types($reset = FALSE) { $relationships = noderelationships_get_relationships($reset); $relationed_types = array_keys($relationships['referrer']); foreach (array_keys($relationships['referred']) as $type) { if (!isset($relationships['referrer'][$type])) { $relationed_types[] = $type; } } sort($relationed_types); return $relationed_types; } /** * Get the list of content types related to the given type that can be created * by the current user. */ function noderelationships_get_creatable_types($nodetype, $reset = FALSE) { static $creatable_types; if (!isset($creatable_types) || $reset) { $creatable_types = array(); } if (!isset($creatable_types[$nodetype])) { $creatable_types[$nodetype] = array(); foreach (array_keys(noderelationships_get_referred_types($nodetype, $reset)) as $referred_type) { if (node_access('create', $referred_type)) { $creatable_types[$nodetype][$referred_type] = $referred_type; } } } return $creatable_types[$nodetype]; } /** * Get the list of node reference fields for the given type. */ function noderelationships_get_reference_fields($nodetype, $reset = FALSE) { static $reference_fields; if (!isset($reference_fields)) { $reference_fields = array(); } if (!isset($reference_fields[$nodetype]) || $reset) { $reference_fields[$nodetype] = array(); foreach (noderelationships_get_nodereferences($reset) as $field_name => $info) { if (isset($info[$nodetype])) { $reference_fields[$nodetype][$field_name] = $info[$nodetype]; } } ksort($reference_fields[$nodetype]); } return $reference_fields[$nodetype]; } /** * Get the list of referrer types for the given referred type. */ function noderelationships_get_referrer_types($referred_type, $reset = FALSE) { $relationships = noderelationships_get_relationships($reset); if (isset($relationships['referred'][$referred_type])) { return $relationships['referred'][$referred_type]['referrer_types']; } return array(); } /** * Get the list of referred types for the given referrer type. */ function noderelationships_get_referred_types($referrer_type, $reset = FALSE) { $relationships = noderelationships_get_relationships($reset); if (isset($relationships['referrer'][$referrer_type])) { return $relationships['referrer'][$referrer_type]['referred_types']; } return array(); } /** * Build the list of node relationships on the system. * * This function is basically the heart of this module. We build the * list of all relationed content types by parsing all nodereference * fields. * * Non-standard requirement: * The 'referenceable_types' attribute of nodereference fields must * be populated explicitly by the user, regardless of the use of views * for nodereference widgets. * * We cannot use content_types() because we could be invoked while * the data content_types() generates is being computed. * Note that noderelationships_content_extra_fields() is invoked * by content_types() to process hook_content_extra_fields() and * noderelationships_content_extra_fields() needs the information * we generate here. * So we read field settings directly from database using the same * method as content_field_instance_read(), just focussing on data * we're interested in. */ function noderelationships_get_relationships($reset = FALSE) { static $relationships; // Provide the data from static or cached storage when possible. if (!$reset) { // Do we have static data? if (isset($relationships)) { return $relationships; } // Do we have cached data? if ($cached = cache_get('noderelationships_relationships', content_cache_tablename())) { $relationships = $cached->data; return $relationships; } } // Build the relationships structure. $relationships = array( 'referrer' => array(), 'referred' => array(), 'nodereferences' => noderelationships_get_nodereferences(TRUE), ); foreach ($relationships['nodereferences'] as $field_name => $field_instances) { foreach ($field_instances as $referrer_type => $referenceable_types) { // Build the list of referrers by child type and field name. if (!isset($relationships['referrer'][$referrer_type])) { $relationships['referrer'][$referrer_type] = array('referred_types' => array(), 'fields' => array($field_name => array())); } foreach ($referenceable_types as $referred_type) { if (!isset($relationships['referrer'][$referrer_type]['referred_types'][$referred_type])) { $relationships['referrer'][$referrer_type]['referred_types'][$referred_type] = array(); } $relationships['referrer'][$referrer_type]['referred_types'][$referred_type][] = $field_name; if (!isset($relationships['referrer'][$referrer_type]['fields'][$field_name])) { $relationships['referrer'][$referrer_type]['fields'][$field_name] = array(); } $relationships['referrer'][$referrer_type]['fields'][$field_name][] = $referred_type; // Build the list of referred types by parent type and field name. if (!isset($relationships['referred'][$referred_type])) { $relationships['referred'][$referred_type] = array('referrer_types' => array(), 'fields' => array()); } if (!isset($relationships['referred'][$referred_type]['referrer_types'][$referrer_type])) { $relationships['referred'][$referred_type]['referrer_types'][$referrer_type] = array(); } $relationships['referred'][$referred_type]['referrer_types'][$referrer_type][] = $field_name; if (!isset($relationships['referred'][$referred_type]['fields'][$field_name])) { $relationships['referred'][$referred_type]['fields'][$field_name] = array(); } $relationships['referred'][$referred_type]['fields'][$field_name][] = $referrer_type; } } } // Sort phase. foreach (array_keys($relationships['referrer']) as $referrer_type) { ksort($relationships['referrer'][$referrer_type]['fields']); ksort($relationships['referrer'][$referrer_type]['referred_types']); } ksort($relationships['referrer']); foreach (array_keys($relationships['referred']) as $referred_type) { ksort($relationships['referred'][$referred_type]['fields']); ksort($relationships['referred'][$referred_type]['referrer_types']); } ksort($relationships['referred']); // Cache a copy of the relationships structure. cache_set('noderelationships_relationships', $relationships, content_cache_tablename()); return $relationships; } /** * Get a list of all 'nodereference' fields in the system. */ function noderelationships_get_nodereferences($reset = FALSE) { static $nodereferences; if (!isset($nodereferences) || $reset) { $nodereferences = array(); $db_result = db_query("SELECT nfi.field_name, nfi.type_name, nf.global_settings FROM {". content_instance_tablename() ."} nfi INNER JOIN {". content_field_tablename() ."} nf ON nfi.field_name = nf.field_name WHERE nf.type = 'nodereference' AND nf.active = 1 AND nf.locked = 0 ORDER BY nfi.weight ASC, nfi.label ASC"); while ($instance = db_fetch_object($db_result)) { $global_settings = (!empty($instance->global_settings) ? (array)unserialize($instance->global_settings) : array()); $referenceable_types = (!empty($global_settings['referenceable_types']) ? array_keys(array_filter($global_settings['referenceable_types'])) : array()); $field_name = $instance->field_name; $referrer_type = $instance->type_name; if (!isset($nodereferences[$field_name])) { $nodereferences[$field_name] = array(); } $nodereferences[$field_name][$referrer_type] = $referenceable_types; } } return $nodereferences; } /** * Get a list of all 'noderelationships_backref' fields in the system. * * @param $nodetype * If set, return information for this content type. * @param $reset * If TRUE, discard static information and load information from database. */ function noderelationships_get_backreferences($nodetype = NULL, $reset = FALSE) { static $backreferences; if (!isset($backreferences) || $reset) { $backreferences = array(); $db_result = db_query("SELECT nfi.field_name, nfi.type_name, nfi.label, nf.global_settings FROM {". content_instance_tablename() ."} nfi INNER JOIN {". content_field_tablename() ."} nf ON nfi.field_name = nf.field_name WHERE nf.type = 'noderelationships_backref' AND nf.active = 1 AND nf.locked = 0 ORDER BY nfi.weight ASC, nfi.label ASC"); while ($instance = db_fetch_object($db_result)) { $global_settings = (!empty($instance->global_settings) ? (array)unserialize($instance->global_settings) : array()); $field_name = $instance->field_name; $type_name = $instance->type_name; if (!isset($backreferences[$type_name])) { $backreferences[$type_name] = array(); } $backreferences[$type_name][$field_name] = array( 'widget_label' => $instance->label, 'referrer_type' => $global_settings['referrer_type'], ); } } if (isset($nodetype)) { return (isset($backreferences[$nodetype]) ? $backreferences[$nodetype] : array()); } return $backreferences; } /** * Clear cached information about relationships. * * Note that we use the cache table provided by CCK itself, and CCK will * clear the whole cache (including our objects) when it needs to. * * We may still need to force a rebuild of the relationships structure, * therefore we can use this function, or load the data with the $reset * option enabled. * * @see noderelationships_get_relationships() */ function noderelationships_cache_clear() { cache_clear_all('noderelationships_relationships', content_cache_tablename()); } /** * Clear content cache and (optionally) rebuild menus. */ function noderelationships_cache_clear_all($rebuild_menu = FALSE) { cache_clear_all('*', content_cache_tablename(), TRUE); if ($rebuild_menu) { menu_rebuild(); } } /** * Synchronize back reference settings with back reference fields. */ function noderelationships_cck_backref_sync_fields($types_to_sync) { if (!is_array($types_to_sync)) { $types_to_sync = array($types_to_sync); } $rebuild = FALSE; foreach ($types_to_sync as $nodetype) { // Load back reference settings for fields region. $backref_settings = noderelationships_settings_load($nodetype, 'backref'); $region_backrefs = (!empty($backref_settings['regions'][NODERELATIONSHIPS_BACKREF_REGION_FIELD]) ? $backref_settings['regions'][NODERELATIONSHIPS_BACKREF_REGION_FIELD] : array()); // Get the list of back reference fields already defined for this content type. $backref_fields = noderelationships_get_backreferences($nodetype, TRUE); // Walk through all back reference settings to create new back reference fields. foreach ($region_backrefs as $relation_key => $relation_info) { list($referrer_type, $field_name) = explode(':', $relation_key); // Find backref to $nodetype from $field_name in $referrer_type. $backref_name = noderelationships_cck_backref_build_name($nodetype, $referrer_type, $field_name); if (isset($backref_fields[$backref_name])) { // So far so good, this back reference field already exists. unset($backref_fields[$backref_name]); } else { // This back reference field does not exist, so we should create one. $referrer_field = content_fields($field_name, $referrer_type); $field_label = t('Back references from @referrer-label in @referrer-type', array( '@referrer-label' => $referrer_field['widget']['label'], '@referrer-type' => noderelationships_get_localized_content_type_name($referrer_type), )); noderelationships_cck_backref_create(array( 'type_name' => $nodetype, 'field_name' => $backref_name, 'label' => $field_label, 'referrer_type' => $referrer_type, 'referrer_field' => $field_name, )); $rebuild = TRUE; drupal_set_message(t('Back reference field created: %field', array('%field' => $field_label))); } } // Remove any back reference field that is not enabled in back reference settings. foreach ($backref_fields as $field_name => $field_info) { noderelationships_cck_backref_delete($nodetype, $field_name); $rebuild = TRUE; drupal_set_message(t('Back reference field removed: %field', array('%field' => $field_info['widget_label']))); } } // Clear caches and rebuild menu only if any field has been created or removed. if ($rebuild) { content_clear_type_cache(TRUE); menu_rebuild(); } } /** * Build a unique back reference field name. */ function noderelationships_cck_backref_build_name($referred_type, $referrer_type, $field_name) { return substr('field_backref_'. md5($referred_type .':'. $referrer_type .':'. $field_name), 0, 32); // Coder note: This is just fine. } /** * Create a back reference field. * * @param $field_options * An array with the following elements: * - type_name The content type where the is about to be created. * - field_name The name of the new field. * - label The label of the new field. * - referrer_type The referrer content type. * - referrer_field The name of the nodereference field in the referrer type. */ function noderelationships_cck_backref_create($field_options) { // Load the Create/Read/Update/Delete library for CCK objects. module_load_include('inc', 'content', 'includes/content.crud'); // Build the field structure. $field = array( 'label' => $field_options['label'], 'field_name' => $field_options['field_name'], 'type_name' => $field_options['type_name'], 'type' => 'noderelationships_backref', 'widget_type' => 'noderelationships_backref', 'module' => 'noderelationships', 'widget_module' => 'noderelationships', 'required' => 0, 'multiple' => 0, 'description' => '', 'default_value' => NULL, 'default_value_php' => NULL, 'default_value_widget' => NULL, 'columns' => array(), 'weight' => noderelationships_get_element_weight($field_options['type_name']), // Global field settings. 'referrer_type' => $field_options['referrer_type'], 'referrer_field' => $field_options['referrer_field'], ); // Create the field. content_field_instance_create($field, FALSE); } /** * Delete a back reference field. */ function noderelationships_cck_backref_delete($type_name, $field_name) { // Load the Create/Read/Update/Delete library for CCK objects. module_load_include('inc', 'content', 'includes/content.crud'); // Make sure the field exists and it belongs to us. $read_params = array( 'field_name' => $field_name, 'type_name' => $type_name, 'widget_module' => 'noderelationships', ); $instances = content_field_instance_read($read_params, TRUE); if (!empty($instances)) { content_field_instance_delete($field_name, $type_name, FALSE); } } /** * Obtain the default weight for a new CCK "Manage fields" element. * * See content_field_overview_form() in cck/includes/content.admin.inc. * * @param $nodetype * The node type. * * @return * An integer with the element weight. */ function noderelationships_get_element_weight($nodetype) { $weights = array(); if (module_exists('fieldgroup')) { foreach (fieldgroup_groups($nodetype) as $group) { $weights[] = $group['weight']; } } $content_type = content_types($nodetype); foreach ($content_type['fields'] as $field) { $weights[] = $field['widget']['weight']; } return !empty($weights) ? (max($weights) + 1) : 1; } /** * Allow external modules alter noderelationships labels. * * Hook prototype: * @code * hook_noderelationships_label_alter(&$label, $label_id, $context, $arguments) * @endcode * * @param $label * The default value for the label. * @param $label_id * An string that identifies whice label it is. * @param $context * An array with information about the context. * @param $arguments * An array with arguments that the label depends on. */ function noderelationships_alter_label(&$label, $label_id, $context, $arguments) { drupal_alter('noderelationships_label', $label, $label_id, $context, $arguments); } /** * Get the list of views in the system. */ function noderelationships_get_views($relation_type) { $views = array(); if ($relation_type == 'backref') { $views[''] = '['. t('Default view') .']'; } foreach (views_get_all_views() as $view) { // Ignore disabled views. if ($view->disabled) { continue; } // Ignore views that are not based on node table. if ($view->base_table != 'node') { continue; } // Filter views depending on relation type. if ($relation_type == 'backref' && ($view->tag != NODERELATIONSHIPS_BACKREF_VIEW_TAG || $view->name == NODERELATIONSHIPS_BACKREF_VIEW_NAME)) { continue; } elseif ($relation_type == 'noderef' && $view->tag != NODERELATIONSHIPS_NODEREF_VIEW_TAG) { continue; } // Let's look at all displays defined for the view. $view_displays = array(); foreach ($view->display as $display_id => $display_info) { if ($relation_type == 'backref') { // For backref views we are interested only in default display. if ($display_info->display_plugin != 'default') { continue; } } elseif ($relation_type == 'noderef') { // For noderef views we are interested only in page displays. if ($display_info->display_plugin != 'page') { continue; } // For the moment, we can only guarantee support for the following // style pulgins: $supported_style_plugins = array('table', 'grid'); $view->set_display($display_id); if (!in_array($view->display_handler->get_option('style_plugin'), $supported_style_plugins)) { continue; } } $view_displays[$display_id] = $display_info->display_title; } if (!empty($view_displays)) { $view_displays_count = count($view_displays); foreach ($view_displays as $display_id => $display_title) { $view_label = $view_name = ($view->name == NODERELATIONSHIPS_NODEREF_VIEW_NAME ? t('Default view') : $view->name); if ($view_displays_count > 1) { $view_label .= ' ('. $display_title .')'; } if ($view->name == NODERELATIONSHIPS_NODEREF_VIEW_NAME) { $view_label = '['. $view_label .']'; } if ($view_displays_count > 1) { if (!isset($views[$view_name])) { $views[$view_name] = array(); } $views[$view_name][$view->name .':'. $display_id] = $display_title; } else { $views[$view->name .':'. $display_id] = $view_label; } } } } // Sort views alphabetically. uksort($views, '_noderelationships_sort_strncmp'); return $views; } /** * Case-insensitive string compare function. */ function _noderelationships_sort_strncmp($a, $b) { $a = drupal_strtolower($a); $b = drupal_strtolower($b); return ($a == $b ? 0 : ($a < $b ? -1 : 1)); } /** * Abort a custom view with page not found. */ function noderelationships_abort_view(&$view, &$view_args) { $view_args = array(NULL); $view->display_handler->override_option('arguments', array( 'nid' => array( 'id' => 'nid', 'table' => 'node', 'field' => 'nid', 'default_action' => 'not found', ) )); } /** * Customize node title and body labels for fields in the main table. */ function noderelationships_customize_views_node_fields(&$view, $display_id, $type_info) { if (($item = $view->get_item($display_id, 'field', 'title'))) { if ($item['table'] == 'node' && $item['relationship'] == 'none') { $item['label'] = $type_info->title_label; $view->set_item($display_id, 'field', 'title', $item); } } if ($referrer_type->has_body && ($item = $view->get_item($display_id, 'field', 'body'))) { if ($item['table'] == 'node' && $item['relationship'] == 'none') { $item['label'] = $type_info->body_label; $view->set_item($display_id, 'field', 'body', $item); } } } /** * Get overrides for back reference views. * * @param $view * @param $referrer_field * * @see noderelationships_customize_backref_view() * @see content_views_data() */ function noderelationships_get_backref_view_overrides($view, $referrer_field) { // Obtain database and views information about the field. if (!function_exists('content_views_field_views_data')) { module_load_include('inc', 'content', 'includes/views/content.views'); } $module = $referrer_field['module']; $views_data = module_invoke($module, 'field_settings', 'views data', $referrer_field); $table_alias = content_views_tablename($referrer_field); $db_info = content_database_info($referrer_field); $nodereference_column = $db_info['columns']['nid']['column']; $view_overrides = array(); // Build relationship overrides. $view_overrides['relationships'] = array( $nodereference_column => array( 'label' => $views_data[$table_alias][$nodereference_column]['relationship']['label'], 'id' => $nodereference_column, 'table' => $table_alias, 'field' => $nodereference_column, 'required' => 1, 'delta' => -1, 'relationship' => 'none', ), ); $existing_view_relationships = $view->display_handler->get_option('relationships'); if (!empty($existing_view_relationships)) { $view_overrides['relationships'] = array_merge($view_overrides['relationships'], $existing_view_relationships); } // Build argument overrides. $common_argument_options = array( 'default_action' => 'empty', 'default_argument_type' => 'fixed', 'default_argument' => '', 'validate_type' => 'none', 'validate_fail' => 'not found', 'relationship' => 'none', ); $view_overrides['arguments'] = array( $nodereference_column => array_merge($common_argument_options, array( 'id' => $nodereference_column, 'table' => $table_alias, 'field' => $nodereference_column, )), 'type' => array_merge($common_argument_options, array( 'id' => 'type', 'table' => 'node', 'field' => 'type', )), ); return $view_overrides; } /** * Apply custom configuration to the given back reference view. */ function noderelationships_customize_backref_view(&$view, $display_id, &$view_args, $reset_current_display = TRUE) { // Nothing to do if the view has already been processed. if (!empty($view->noderelationships_processed)) { return; } // Mark the view as being already processed. $view->noderelationships_processed = TRUE; // If the view is not executed with the expected number of arguments, then // we should abort this view. if (count($view_args) < 3 || !is_numeric($view_args[0])) { noderelationships_abort_view($view, $view_args); return FALSE; } // We expect to see the following arguments: // 0 - nid of the referred node. // 1 - type of the referring node. // 2 - name of the nodereference field in the referring type. list($referred_nid, $type_name, $field_name) = $view_args; // Load the referred node. if (!($referred_node = node_load($referred_nid))) { noderelationships_abort_view($view, $view_args); return FALSE; } // Load settings of the referring field. $referrer_field = content_fields($field_name, $type_name); if (empty($referrer_field)) { drupal_set_message(t('Could not load field information to properly customize the view %view-name.', array('%view-name' => $view->name)), 'error'); watchdog('noderelationships', 'Could not load field information to properly customize the view %view-name (field: @field-name, type: @type-name).', array('%view-name' => $view->name, '@field-name' => $field_name, '@type-name' => $type_name), WATCHDOG_ERROR); noderelationships_abort_view($view, $view_args); return FALSE; } drupal_add_css(drupal_get_path('module', 'noderelationships') .'/css/noderelationships.backref_views.css'); // Load the related node types. $referrer_type = noderelationships_get_localized_content_type($type_name); $referred_type = noderelationships_get_localized_content_type($referred_node->type); // Get view overrides for the given nodereference field. $view_overrides = noderelationships_get_backref_view_overrides($view, $referrer_field); // Customization related to the page display. if ($view->display_handler->display->display_plugin == 'page') { $view_title = $view->get_title(); $context = array( 'referred_node' => $referred_node, 'referred_type' => $referred_type, 'referrer_type' => $referrer_type, 'referrer_field' => $referrer_field, 'field_name' => $field_name, ); // Build custom title, if nothing exists. if (empty($view_title)) { // Prepare default view title. $arguments = array( '%node-title' => $referred_node->title, '%referrer-type-name' => $referrer_type->name, '%referred-type-name' => $referred_type->name, '%referrer-label' => $referrer_field['widget']['label'], ); $view_title = t('Back references from %referrer-label in %referrer-type-name for %referred-type-name: %node-title', $arguments); // Allow external modules alter the view title. noderelationships_alter_label($view_title, 'backref_referred_page_title', $context, $arguments); $view_overrides['title'] = $view_title; } // Build custom breadcrumb. $view->noderelationships_breadcrumb = array( l(t('Home'), NULL), l($referred_node->title, 'node/'. $referred_node->nid), ); // Allow external modules alter the breadcrumb. drupal_alter('noderelationships_breadcrumb', $view->noderelationships_breadcrumb, $view, $view_args, $context); } // Reset the current page display so that the changes take effect. // This is necessary for AJAX requests and page displays managed by // the views menu callback. if ($reset_current_display && isset($view->current_display)) { unset($view->current_display); } // Activate the specified display. $view->set_display($display_id); // Allow external modules alter the view. drupal_alter('noderelationships_view', $view_overrides, $view, $display_id, $view_args); // Apply dynamic customization to the view display. foreach ($view_overrides as $option => $definition) { $view->display_handler->override_option($option, $definition); } // If this is the default backref view AND it has not been overridden, then // we want to customize the node title and body labels for fields in the main // table to match those defined in the content type settings page. if ($view->name == NODERELATIONSHIPS_BACKREF_VIEW_NAME && $view->type == t('Default')) { noderelationships_customize_views_node_fields($view, $display_id, $referrer_type); } return TRUE; } /** * Get overrides for search and reference views. * * @param $view * @param $referrer_field * * @see noderelationships_customize_noderef_view() */ function noderelationships_get_noderef_view_overrides($view, $referrer_field) { $view_overrides = array(); // Customize view fields. $new_view_fields = array(); $new_view_fields['noderelationships_nid'] = array( 'id' => 'nid', 'table' => 'node', 'field' => 'nid', 'label' => 'X-NodeRelationships-Nid', 'relationship' => 'none', ); $new_view_fields['noderelationships_title'] = array( 'id' => 'title', 'table' => 'node', 'field' => 'title', 'label' => 'X-NodeRelationships-Title', 'relationship' => 'none', ); $existing_view_fields = $view->display_handler->get_option('fields'); if (!empty($existing_view_fields)) { $new_view_fields = array_merge($new_view_fields, $existing_view_fields); } $view_overrides['fields'] = $new_view_fields; // Customize view filters. $referenceable_types = (isset($referrer_field['referenceable_types']) ? array_filter($referrer_field['referenceable_types']) : array()); if (!empty($referenceable_types)) { $new_view_filters = $view->display_handler->get_option('filters'); if (empty($new_view_filters)) { $new_view_filters = array(); } // Make sure we have a filter by content type. if (isset($new_view_filters['type'])) { $new_view_filters['type']['value'] = $referenceable_types; // If this is the default noderef view AND it has not been overridden, // then we want to control if the filter is exposed or not depending // on number of referenceable types. if ($view->name == NODERELATIONSHIPS_NODEREF_VIEW_NAME && $view->type == t('Default')) { $new_view_filters['type']['exposed'] = (count($referenceable_types) > 1 ? TRUE : FALSE); } } else { $new_view_filters['type'] = array( 'id' => 'type', 'table' => 'node', 'field' => 'type', 'operator' => 'in', 'value' => $referenceable_types, 'group' => '0', 'exposed' => FALSE, 'relationship' => 'none', ); if (count($referenceable_types) > 1) { $new_view_filters['type']['exposed'] = TRUE; $new_view_filters['type']['expose'] = array( 'use_operator' => 0, 'operator' => 'type_op', 'identifier' => 'type', 'label' => 'Type', 'optional' => 0, 'single' => 0, 'remember' => 1, 'reduce' => 1, ); } } $view_overrides['filters'] = $new_view_filters; } // Dynamic customization dependent on active style plugin. $style_plugin = $view->display_handler->get_option('style_plugin'); if ($style_plugin == 'table') { $style_options = $view->display_handler->get_option('style_options'); $new_style_columns = array( 'noderelationships_nid' => 'noderelationships_nid', 'noderelationships_title' => 'noderelationships_title', ); if (!empty($style_options['columns'])) { $new_style_columns = array_merge($new_style_columns, $style_options['columns']); } $style_options['columns'] = $new_style_columns; $new_style_info = array( 'noderelationships_nid' => array('sortable' => 0, 'separator' => ':'), 'noderelationships_title' => array('sortable' => 0, 'separator' => ''), ); if (!empty($style_options['info'])) { $new_style_info = array_merge($new_style_info, $style_options['info']); } $style_options['info'] = $new_style_info; $view_overrides['style_options'] = $style_options; } // Make sure Views AJAX is enabled, also disable the Views "more" button. $view_overrides['use_ajax'] = TRUE; $view_overrides['use_more'] = 0; // Customize the view path. $view_overrides['path'] = 'noderelationships/search/%/%'; return $view_overrides; } /** * Apply custom configuration to the given search and reference view. */ function noderelationships_customize_noderef_view(&$view, $display_id, &$view_args, $reset_current_display = TRUE) { // Nothing to do if the view has already been processed. if (!empty($view->noderelationships_processed)) { return; } // Mark the view as being already processed. $view->noderelationships_processed = TRUE; // If the view is not executed with the expected number of arguments, then // we should abort this view. if (count($view_args) < 2) { noderelationships_abort_view($view, $view_args); return FALSE; } // We expect to see the following arguments: // 0 - type of the referrer node. // 1 - name of the nodereference field in the referrer type. list($type_name, $field_name) = $view_args; // Load settings of the referrer field. $referrer_field = content_fields($field_name, $type_name); if (empty($referrer_field)) { drupal_set_message(t('Could not load field information to properly customize the view %view-name.', array('%view-name' => $view->name)), 'error'); watchdog('noderelationships', 'Could not load field information to properly customize the view %view-name (field: @field-name, type: @type-name).', array('%view-name' => $view->name, '@field-name' => $field_name, '@type-name' => $type_name), WATCHDOG_ERROR); noderelationships_abort_view($view, $view_args); return FALSE; } // Load the related node type. $referrer_type = noderelationships_get_localized_content_type($type_name); // Reset the current page display so that the changes take effect. // This is necessary for AJAX requests and page displays. if ($reset_current_display && isset($view->current_display)) { unset($view->current_display); } // Activate the specified display. $view->set_display($display_id); // Get view overrides for the given nodereference field. $view_overrides = noderelationships_get_noderef_view_overrides($view, $referrer_field); // Build custom title, if nothing exists. $view_title = $view->get_title(); if (empty($view_title)) { // Prepare default view title. $arguments = array( '%referrer-label' => $referrer_field['widget']['label'], ); $view_title = t('Search and reference %referrer-label', $arguments); // Allow external modules alter the view title. $context = array( 'referrer_type' => $referrer_type, 'referrer_field' => $referrer_field, 'field_name' => $field_name, ); noderelationships_alter_label($view_title, 'noderef_search_page_title', $context, $arguments); $view_overrides['title'] = $view_title; } // Allow external modules alter the view. drupal_alter('noderelationships_view', $view_overrides, $view, $display_id, $view_args); // Apply dynamic customization to the view display. foreach ($view_overrides as $option => $definition) { $view->display_handler->override_option($option, $definition); } return TRUE; } /** * Get the localized version of the given content type. */ function noderelationships_get_localized_content_type($nodetype) { $type = node_get_types('type', $nodetype); if ($type && module_exists('i18ncontent')) { $type->name = tt("nodetype:type:$nodetype:name", $type->name); if (!empty($type->description)) { $type->description = tt("nodetype:type:$nodetype:description", $type->description); } if (!empty($type->title_label)) { $type->title_label = tt("nodetype:type:$nodetype:title", $type->title_label); } if (!empty($type->body_label)) { $type->body_label = tt("nodetype:type:$nodetype:body", $type->body_label); } if (!empty($type->help)) { $type->help = tt("nodetype:type:$nodetype:help", $type->help); } } return $type; } /** * Get the localized name of the given content type. */ function noderelationships_get_localized_content_type_name($nodetype) { $name = node_get_types('name', $nodetype); if (module_exists('i18ncontent')) { $name = tt("nodetype:type:$nodetype:name", $name); } return $name; } /** * Get the localized language name. */ function noderelationships_get_localized_language_list() { if (module_exists('locale')) { return locale_language_list(); } $language_list = array(); foreach (language_list() as $langcode => $language) { $language_list[$langcode] = $language->name; } return $language_list; } /** * Check if translation is enabled for the given type and the current user * is allowed to create translations. */ function noderelationships_translation_supported_type($nodetype) { if (module_exists('translation') && user_access('translate content') && translation_supported_type($nodetype)) { return TRUE; } return FALSE; } /** * Compose a query string to append to node reference extra page requests. * * @return * A query string that consists of all components of the current page request. */ function noderelationships_querystring() { return drupal_query_string_encode($_GET, array_merge(array('q', 'destination', 'pass'), array_keys($_COOKIE))); }