array( 'arguments' => array('diagram' => NULL), 'file' => 'noderelationships.admin.inc', ), 'noderelationships_erd_group' => array( 'arguments' => array('group_type' => NULL, 'entities' => NULL), 'file' => 'noderelationships.admin.inc', ), 'noderelationships_erd_current' => array( 'arguments' => array('label' => NULL, 'items' => NULL), 'file' => 'noderelationships.admin.inc', ), 'noderelationships_erd_entity' => array( 'arguments' => array('label' => NULL, 'items' => NULL), 'file' => 'noderelationships.admin.inc', ), 'noderelationships_erd_relation' => array( 'arguments' => array('label' => NULL), 'file' => 'noderelationships.admin.inc', ), // Node relationship settings. 'noderelationships_admin_settings_noderef' => array( 'arguments' => array('form' => NULL), 'file' => 'noderelationships.admin.inc', ), 'noderelationships_admin_settings_backref' => array( 'arguments' => array('form' => NULL), 'file' => 'noderelationships.admin.inc', ), 'noderelationships_admin_help' => array( 'arguments' => array('message' => NULL), 'file' => 'noderelationships.admin.inc', ), // Back reference views. 'noderelationships_backref_view' => array( 'arguments' => array('title' => NULL, 'content' => NULL), 'file' => 'noderelationships.pages.inc', ), 'noderelationships_formatter_default' => array( 'arguments' => array('element' => NULL), 'file' => 'noderelationships.pages.inc', ), 'noderelationships_formatter_count' => array( 'arguments' => array('element' => NULL), 'file' => 'noderelationships.pages.inc', ), // Node reference extras. 'noderelationships_noderef_page_tabs' => array( 'arguments' => array('mode' => NULL, 'referrer_type' => NULL, 'field_name' => NULL), 'file' => 'noderelationships.pages.inc', ), 'noderelationships_noderef_page_content' => array( 'arguments' => array('content' => NULL), 'file' => 'noderelationships.pages.inc', ), 'noderelationships_noderef_singleselect' => array( 'arguments' => array('referrer_field' => NULL), 'file' => 'noderelationships.pages.inc', ), 'noderelationships_noderef_multiselect' => array( 'arguments' => array('referrer_field' => NULL), 'file' => 'noderelationships.pages.inc', ), ); } /** * Implementation of hook_menu(). */ function noderelationships_menu() { $items = array(); // Include common module functions. module_load_include('inc', 'noderelationships'); // Any event which causes a menu_rebuild could potentially mean that the // relationships data is updated -- content type changes, etc. noderelationships_cache_clear(); // Build new menu items for each content type (relationships management). foreach (node_get_types() as $nodetype) { $type_url_str = str_replace('_', '-', $nodetype->type); $items['admin/content/node-type/'. $type_url_str .'/relationships'] = array( 'title' => 'Relationships', 'page callback' => 'noderelationships_admin_page', 'page arguments' => array(5, $nodetype), 'access arguments' => array('administer content types'), 'type' => MENU_LOCAL_TASK, 'weight' => 10, 'file' => 'noderelationships.admin.inc', ); $admin_tabs = array( 'erd' => 'Entity relations diagram', 'noderef' => 'Node reference extras', 'backref' => 'Back reference settings', ); $item_weight = 0; foreach ($admin_tabs as $op => $title) { $items['admin/content/node-type/'. $type_url_str .'/relationships/'. $op] = array( // Coder note: the content of $title is a literal string, which is fine. 'title' => $title, 'page callback' => 'noderelationships_admin_page', 'page arguments' => array(5, $nodetype), 'access arguments' => array('administer content types'), 'type' => ($item_weight == 0 ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK), 'weight' => $item_weight, 'file' => 'noderelationships.admin.inc', ); $item_weight++; } } // Add back reference tab to nodes. Note this tab is added for all nodes, // but visibility is controlled by the access callback function. $items['node/%node/relationships'] = array( 'title' => 'Relationships', 'page callback' => 'noderelationships_backref_page', 'page arguments' => array(1), 'access callback' => 'noderelationships_backref_access', 'access arguments' => array(1), 'type' => MENU_LOCAL_TASK, 'weight' => 10, 'file' => 'noderelationships.pages.inc', ); // Search or Create and reference. $items['noderelationships/search'] = array( 'title' => 'Search and reference', 'page callback' => 'noderelationships_noderef_page', 'page arguments' => array(1), 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, 'file' => 'noderelationships.pages.inc', ); $items['noderelationships/create'] = array( 'title' => 'Create and reference', 'page callback' => 'noderelationships_noderef_page', 'page arguments' => array(1), 'access callback' => '_node_add_access', 'type' => MENU_CALLBACK, 'file' => 'noderelationships.pages.inc', ); $items['noderelationships/ahah/search'] = array( 'title' => 'Search and reference', 'page callback' => 'noderelationships_noderef_ahah_search', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, 'file' => 'noderelationships.pages.inc', ); return $items; } /** * Access callback for the node relationships tab. * * @see noderelationships_menu() */ function noderelationships_backref_access($referred_node) { // Deny access if the user does not have access to view the referred node. if (!node_access('view', $referred_node)) { return FALSE; } // Include common module functions. module_load_include('inc', 'noderelationships'); // Obtain back reference settings for this type. $backref_settings = noderelationships_settings_load($referred_node->type, 'backref'); // Deny access (meaning the tab is not visible) when the node type // does not have back references enabled for this region. if (empty($backref_settings['regions'][NODERELATIONSHIPS_BACKREF_REGION_TAB])) { return FALSE; } // Ok, the user is allowed to view the node and this type has potential // back references, so we can grant access to the relationships tab. // @todo: Find out a way to hide the relationships tab if THIS node does // not have real back references. The problem here is that a series of // SELECT COUNT(*) queries would affect performance, so we would have to // use a summary field or something similar. But this information would // have to be updated when a nodereference is added, changed or removed // from the database. return TRUE; } /** * Implementation of hook_views_api(). */ function noderelationships_views_api() { return array( 'api' => 2, 'path' => drupal_get_path('module', 'noderelationships') .'/views', ); } /** * Implementation of hook_views_pre_view(). * * @see noderelationships_backref_render_view() */ function noderelationships_views_pre_view(&$view, $display_id, &$view_args) { // Apply custom configuration to the given view. if ($view->tag == NODERELATIONSHIPS_BACKREF_VIEW_TAG) { module_load_include('inc', 'noderelationships'); noderelationships_customize_backref_view($view, $display_id, $view_args); } elseif ($view->tag == NODERELATIONSHIPS_NODEREF_VIEW_TAG) { module_load_include('inc', 'noderelationships'); noderelationships_customize_noderef_view($view, $display_id, $view_args); } } /** * Implementation of hook_views_pre_execute(). * * @see noderelationships_customize_backref_view() */ function noderelationships_views_pre_execute(&$view) { if (!empty($view->noderelationships_breadcrumb)) { drupal_set_breadcrumb($view->noderelationships_breadcrumb); } } /** * Proxy function to preprocess the list views theme. */ function noderelationships_preprocess_views_ui_list_views(&$vars) { if (!isset($vars['views']) || !is_array($vars['views'])) { return; } foreach ($vars['views'] as $view) { if (!empty($view->tag) && in_array($view->tag, array(NODERELATIONSHIPS_BACKREF_VIEW_TAG, NODERELATIONSHIPS_NODEREF_VIEW_TAG))) { if (!empty($view->path)) { $view->path = t('Dynamically generated by the Node Relationships module.'); } $view->description = t('This view is dynamically customized by the Node Relationships module.') .'
'. $view->description; } } } /** * Implementation of hook_field_info(). * * Back reference fields: * - Are single-valued fields and cannot be shared (per type storage is a must). * This is why these fields are created automatically. Note that these fields * are hidden from the "Add new field" and "Add existing field" selectors in * the "Manage fields" screen. See _noderelationships_cck_admin_forms_alter(). * - Do not implement database columns and storage method is always per type. * Hence, adding and removing back reference fields do not affect user data. * - Do not provide CCK to Views integration for back reference fields. * - Do not provide any widget in the node edit form. * - The widget is defined to manage its own multiple values, therefore these * fields cannot be added to multigroups. * - Can be dragged from the "Manage fields" screen to any position in the node. * - The widget label can be changed at will from the "Manage fields" screen. * - The formatter simply renders a dynamically customized back reference view * or the back references count. */ function noderelationships_field_info() { return array( 'noderelationships_backref' => array( 'label' => t('Back reference'), 'description' => t('Display node back references.'), ), ); } /** * Implementation of hook_field_settings(). * * @see noderelationships_cck_backref_create() * @see _noderelationships_cck_admin_forms_alter() */ function noderelationships_field_settings($op, $field) { if ($op == 'save') { return array('referrer_type', 'referrer_field'); } elseif ($op == 'views data') { return array(); } } /** * Implementation of hook_field(). */ function noderelationships_field($op, $node, $field, &$items, $teaser, $page) { if ($op == 'load') { // This is necessary so that we can use features such as // content_view_field() invoked by Panels integration, etc. return array($field['field_name'] => array(NULL)); } } /** * Implementation of hook_content_is_empty(). */ function noderelationships_content_is_empty($item, $field) { // Back reference fields are not stored in database, so tell CCK they are // always empty. return TRUE; } /** * Implementation of hook_widget_info(). */ function noderelationships_widget_info() { return array( 'noderelationships_backref' => array( 'label' => t('Back reference'), 'field types' => array('noderelationships_backref'), 'multiple values' => CONTENT_HANDLE_MODULE, ), ); } /** * Implementation of hook_widget(). */ function noderelationships_widget(&$form, &$form_state, $field, $items, $delta = NULL) { // Back reference fields do not provide any widget in the node edit form. return array(); } /** * Implementation of hook_field_formatter_info(). * * @see theme_noderelationships_formatter_default() */ function noderelationships_field_formatter_info() { return array( 'default' => array( 'label' => t('Back references view'), 'field types' => array('noderelationships_backref'), 'multiple values' => CONTENT_HANDLE_MODULE, ), 'count' => array( 'label' => t('Back references count'), 'field types' => array('noderelationships_backref'), 'multiple values' => CONTENT_HANDLE_MODULE, ), ); } /** * Alter variables used for content-admin-field-overview-form.tpl.php. */ function noderelationships_preprocess_content_field_overview_form(&$vars) { module_load_include('inc', 'noderelationships'); module_load_include('inc', 'noderelationships', 'noderelationships.admin'); _noderelationships_preprocess_content_field_overview_form($vars); } /** * Implementation of hook_content_extra_fields(). */ function noderelationships_content_extra_fields($nodetype) { $extra = array(); // Include common module functions. module_load_include('inc', 'noderelationships'); // Obtain back reference settings for this type. $backref_settings = noderelationships_settings_load($nodetype, 'backref'); $backref_regions = $backref_settings['regions']; if (!empty($backref_regions[NODERELATIONSHIPS_BACKREF_REGION_PAGE])) { $type_names = array(); foreach (array_keys($backref_regions[NODERELATIONSHIPS_BACKREF_REGION_PAGE]) as $relation_key) { list($type_name, $field_name) = explode(':', $relation_key); if (!isset($type_names[$type_name])) { $type_names[$type_name] = node_get_types('name', $type_name); } } // Sort types alphabetically. uksort($type_names, '_noderelationships_sort_strncmp'); $extra['noderelationships'] = array( 'label' => t('Node relationships'), 'description' => t('Back reference views for the following types: @types.', array('@types' => implode(', ', $type_names))), 'weight' => noderelationships_get_element_weight($nodetype), ); } return $extra; } /** * Implementation of hook_content_fieldapi(). */ function noderelationships_content_fieldapi($op, $field) { // Watch for changes in nodereference fields. if ($op != 'read instance' && $field['type'] == 'nodereference') { module_load_include('inc', 'noderelationships'); if ($op == 'delete instance') { // Delete relations related to this field instance. $args = array($field['type_name'], $field['field_name']); noderelationships_settings_delete("relation_type = 'noderef' AND type_name = '%s' AND field_name = '%s'", $args); noderelationships_settings_delete("relation_type = 'backref' AND related_type = '%s' AND field_name = '%s'", $args); } // Clear cached information about relationships (all operations). noderelationships_cache_clear(); } } /** * Implementation of hook_node_type(). */ function noderelationships_node_type($op, $info) { module_load_include('inc', 'noderelationships'); if ($op == 'update') { // Deal with content type name changes. if (!empty($info->old_type) && $info->old_type != $info->type) { noderelationships_settings_rename_type($info->old_type, $info->type); } } elseif ($op == 'delete') { // Delete relationship settings related to the content type being deleted. noderelationships_settings_delete("type_name = '%s' OR related_type = '%s'", array($info->type, $info->type)); } // Clear cached information about relationships (all operations). noderelationships_cache_clear(); } /** * Implementation of hook_nodeapi(). */ function noderelationships_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { if ($op == 'view' && !$teaser && $page && $node->build_mode == NODE_BUILD_NORMAL) { // Include common module functions. module_load_include('inc', 'noderelationships'); // Obtain back reference settings for this type. $backref_settings = noderelationships_settings_load($node->type, 'backref'); $backref_regions = $backref_settings['regions']; // Process the node only if we have back reference settings for this region. if (!empty($backref_regions[NODERELATIONSHIPS_BACKREF_REGION_PAGE])) { module_load_include('inc', 'noderelationships', 'noderelationships.pages'); _noderelationships_nodeapi_view($node, $backref_regions[NODERELATIONSHIPS_BACKREF_REGION_PAGE]); } } } /** * Implementation of hook_form_alter(). */ function noderelationships_form_alter(&$form, $form_state, $form_id) { // Alter CCK administration forms. if (in_array($form_id, array('content_field_edit_form', 'content_field_overview_form'))) { // Delegate form manipulation to external script. module_load_include('inc', 'noderelationships'); module_load_include('inc', 'noderelationships', 'noderelationships.admin'); _noderelationships_cck_admin_forms_alter($form, $form_state, $form_id); return; } // Alter views ui / settings forms. if (strpos($form_id, 'views_ui_edit_') === 0 && isset($form_state['view']) && isset($form_state['view']->tag)) { if ($form_state['view']->tag == NODERELATIONSHIPS_BACKREF_VIEW_TAG || $form_state['view']->tag == NODERELATIONSHIPS_NODEREF_VIEW_TAG) { // Delegate form manipulation to external script. module_load_include('inc', 'noderelationships'); module_load_include('inc', 'noderelationships', 'noderelationships.admin'); _noderelationships_views_ui_form_alter($form, $form_state, $form_id, $form_state['view']->tag); } return; } // Alter views ui / clone view form. if ($form_id == 'views_ui_add_form' && isset($form['tag'])) { if ($form['tag']['#default_value'] == NODERELATIONSHIPS_BACKREF_VIEW_TAG || $form['tag']['#default_value'] == NODERELATIONSHIPS_NODEREF_VIEW_TAG) { // Delegate form manipulation to external script. module_load_include('inc', 'noderelationships'); module_load_include('inc', 'noderelationships', 'noderelationships.admin'); _noderelationships_views_ui_form_alter($form, $form_state, $form_id, $form['tag']['#default_value']); } return; } // Alter node form. if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) { $node = &$form['#node']; // Include common module functions. module_load_include('inc', 'noderelationships'); // Obtain settings for nodereference extras for this type. $noderef_settings = noderelationships_settings_load($node->type, 'noderef'); // Parent node processing only when we have settings for nodereference extras. if (!empty($noderef_settings['search_and_reference_view']) || !empty($noderef_settings['create_and_reference'])) { module_load_include('inc', 'noderelationships', 'noderelationships.pages'); _noderelationships_parent_node_form_alter($form, $form_state, $node, $noderef_settings); } // Child node processing only when the node structure has been flagged // from noderelationships_noderef_page(). if (!empty($node->_noderelationships_child)) { module_load_include('inc', 'noderelationships', 'noderelationships.pages'); _noderelationships_child_node_form_alter($form); } return; } } /** * Proxy function to invoke node form after_build callback, because it might * not be included yet when the form is processed and invokes the callback. */ function _noderelationships_parent_node_form_after_build_proxy($elements, &$form_state) { module_load_include('inc', 'noderelationships'); module_load_include('inc', 'noderelationships', 'noderelationships.pages'); return _noderelationships_parent_node_form_after_build($elements, $form_state); }