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; } // Allow external modules deny access to the Relationships tab for this node. $access = (array) module_invoke_all('noderelationships_backref_access', $referred_node); foreach ($access as $value) { if ($value === FALSE) { 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] = noderelationships_get_localized_content_type_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') { if (!$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]); } } } elseif ($op == 'prepare translation') { // Here we can assume translation module is enabled, translation for this // content type is enabled and the current user is allowed to translate. // 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'); // Proceed only if the "Translate and reference" feature is enabled for // any field in this content type. if (!empty($noderef_settings['translate_and_reference'])) { module_load_include('inc', 'noderelationships', 'noderelationships.translation'); _noderelationships_nodeapi_prepare_translation($node, $noderef_settings); } } } /** * 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) { $type_name = $form['#node']->type; // Include common module functions. module_load_include('inc', 'noderelationships'); // Obtain settings for nodereference extras for this type. $noderef_settings = noderelationships_settings_load($type_name, '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']) || (!empty($noderef_settings['translate_and_reference']) && noderelationships_translation_supported_type($type_name))) { module_load_include('inc', 'noderelationships', 'noderelationships.pages'); _noderelationships_parent_node_form_alter($form, $form_state, $type_name, $noderef_settings); } // Child node processing when the page request has been flagged to do so // from noderelationships_noderef_page(). if (!empty($_GET['noderelationships_referrer_type']) && !empty($_GET['noderelationships_field_name'])) { module_load_include('inc', 'noderelationships', 'noderelationships.pages'); _noderelationships_child_node_form_alter($form, $_GET['noderelationships_referrer_type'], $_GET['noderelationships_field_name']); } return; } } /** * Proxy function to invoke node form after_build and pre_render callbacks, * because the file where the real handler is implemented might not be included * yet when the form is processed and invokes the callback. */ function _noderelationships_parent_node_form_scanner_proxy($form) { module_load_include('inc', 'noderelationships'); module_load_include('inc', 'noderelationships', 'noderelationships.pages'); return _noderelationships_parent_node_form_scanner($form); } /** * Proxy function to invoke node form after_build and pre_render callbacks, * because the file where the real handler is implemented might not be included * yet when the form is processed and invokes the callback. */ function _noderelationships_child_node_form_pre_render_proxy($form) { module_load_include('inc', 'noderelationships'); module_load_include('inc', 'noderelationships', 'noderelationships.pages'); return _noderelationships_child_node_form_pre_render($form); }