type, 'backref');
$backref_regions = $backref_settings['regions'];
if (empty($backref_regions[NODERELATIONSHIPS_BACKREF_REGION_TAB])) {
drupal_not_found();
return;
}
$region_settings = $backref_regions[NODERELATIONSHIPS_BACKREF_REGION_TAB];
$content = noderelationships_backref_build_content($referred_node, $region_settings);
$output = (!empty($content) ? drupal_render($content) : '');
if (empty($output)) {
drupal_set_message(t('This %type-name does not have relations.', array('%type-name' => node_get_types('name', $referred_node->type))), 'warning');
}
return $output;
}
/**
* Implementation of hook_nodeapi('view').
*
* @param $referred_node
* @param $region_settings
*/
function _noderelationships_nodeapi_view($referred_node, &$region_settings) {
$content = noderelationships_backref_build_content($referred_node, $region_settings);
if (!empty($content)) {
$referred_node->content['noderelationships'] = array(
'#type' => 'fieldset',
'#title' => t('Related content'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#weight' => noderelationships_get_element_weight($referred_node->type),
'content' => $content,
);
}
}
/**
* Build back reference content for the given node and region settings.
*
* @param $referred_node
* @param $region_settings
*/
function noderelationships_backref_build_content($referred_node, &$region_settings) {
$content = array();
$referred_type = node_get_types('type', $referred_node->type);
foreach ($region_settings as $relation_key => $relation_info) {
list($type_name, $field_name) = explode(':', $relation_key);
$referrer_type = node_get_types('type', $type_name);
$context = array(
'region_settings' => $region_settings,
'referred_node' => $referred_node,
'referred_type' => $referred_type,
'referrer_type' => $referrer_type,
'field_name' => $field_name,
);
$output = noderelationships_backref_render_view($relation_info['back_reference_view'], $context);
if (!empty($output)) {
$content['backref_'. $type_name .'_'. $field_name] = array(
'#value' => $output,
'#weight' => $relation_info['weight'],
);
}
}
// Allow external modules alter back references content.
$context = array(
'region_settings' => $region_settings,
'referred_node' => $referred_node,
'referred_type' => $referred_type,
);
drupal_alter('noderelationships_backref_content', $content, $context);
return $content;
}
/**
* Display a back reference view.
*
* @param $back_reference_view
* @param $context
* @param $render_view_title
*
* @see noderelationships_views_pre_view()
* @see noderelationships_customize_backref_view()
*/
function noderelationships_backref_render_view($back_reference_view, $context, $render_view_title = TRUE) {
static $pager_element = 0;
$referred_node = &$context['referred_node'];
$referred_type = &$context['referred_type'];
$referrer_type = &$context['referrer_type'];
$field_name = $context['field_name'];
// Load settings of the referring field.
$referrer_field = content_fields($field_name, $referrer_type->type);
if (empty($referrer_field)) {
drupal_set_message(t('Could not locate %field in content type %type.', array('%field' => $field_name, '%type' => $referrer_type->type)), 'error');
watchdog('noderelationships', 'Could not locate %field in content type %type.', array('%field' => $field_name, '%type' => $referrer_type->type), WATCHDOG_ERROR);
return '';
}
// Coder note: Widget labels are user interface literals.
$referrer_label = t($referrer_field['widget']['label']);
// Prepare the name of the view and display.
if (empty($back_reference_view)) {
$view_name = NODERELATIONSHIPS_BACKREF_VIEW_NAME;
$display_id = 'default';
}
else {
list($view_name, $display_id) = explode(':', $back_reference_view);
}
// Load the view and check access to the given display.
if (!($view = views_get_view($view_name)) || !$view->access($display_id)) {
drupal_set_message(t('Could not load the view %view:%display.', array('%view' => $view_name, '%display' => $display_id)), 'error');
watchdog('noderelationships', 'Could not load the view %view:%display.', array('%view' => $view_name, '%display' => $display_id), WATCHDOG_ERROR);
return '';
}
// Prepare values for the view arguments as follows:
// 0 - nid of the referred node.
// 1 - type of the referring node.
// 2 - name of the nodereference field in the referring type.
$view_args = array($referred_node->nid, $referrer_type->type, $field_name);
// Assign a different pager element for each view in the same page request.
$view->display_handler->override_option('pager_element', $pager_element);
// Make sure the view is completely valid.
$errors = $view->validate();
if (is_array($errors)) {
foreach ($errors as $error) {
drupal_set_message($error, 'error');
}
return '';
}
// Execute and render the view in preview mode. Note that we only generate
// output if the executed view query returns any result.
$view->preview = TRUE;
$view->pre_execute($view_args);
$view->execute($display_id);
if (!empty($view->result)) {
$content = $view->display_handler->preview();
}
$view->post_execute();
if (empty($view->result)) {
return '';
}
// Now that we have output for this view, we need to increase the pager
// element counter.
$pager_element++;
// Find out a title for this view.
if ($render_view_title) {
// If the view title is empty (default), then we'll generate one.
$view_title = $view->get_title();
if (empty($view_title)) {
$arguments = array(
'%node-title' => $referred_node->title,
'%referrer-type-name' => $referrer_type->name,
'%referred-type-name' => $referred_type->name,
'%referrer-label' => $referrer_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_view_title', $context, $arguments);
}
}
else {
$view_title = '';
}
return theme('noderelationships_backref_view', $view_title, $content);
}
/**
* Render a block for a back reference view.
*/
function theme_noderelationships_backref_view($title, $content) {
$output = '
';
if (!empty($title)) {
$output .= '
'. $title .'
';
}
$output .= '
'. $content .'
';
$output .= '
';
return $output;
}
/**
* Back reference field formatter.
*/
function theme_noderelationships_formatter_default($element) {
// Check if we have a node here. We should.
if (empty($element['#node']) || empty($element['#node']->nid)) {
return '';
}
// Load back reference field data.
$field = content_fields($element['#field_name'], $element['#type_name']);
if (empty($field['referrer_field']) || empty($field['referrer_type'])) {
return '';
}
// Obtain back reference settings for this type.
$backref_settings = noderelationships_settings_load($element['#type_name'], 'backref');
$backref_regions = $backref_settings['regions'];
if (empty($backref_regions[NODERELATIONSHIPS_BACKREF_REGION_FIELD])) {
return '';
}
// Check we really have relation information.
$region_settings = $backref_regions[NODERELATIONSHIPS_BACKREF_REGION_FIELD];
if (empty($region_settings[$field['referrer_type'] .':'. $field['referrer_field']])) {
return '';
}
$relation_info = $region_settings[$field['referrer_type'] .':'. $field['referrer_field']];
// Build the context structure and render the back reference view.
$context = array(
'region_settings' => $region_settings,
'referred_node' => $element['#node'],
'referred_type' => node_get_types('type', $element['#type_name']),
'referrer_type' => node_get_types('type', $field['referrer_type']),
'field_name' => $field['referrer_field'],
);
return noderelationships_backref_render_view($relation_info['back_reference_view'], $context, FALSE);
}
/**
* Menu callback; Search or Create and reference.
*/
function noderelationships_noderef_page($mode, $referrer_type = NULL, $field_name = NULL, $arg = NULL) {
// Enable the Modal Frame API for the child window.
modalframe_child_js();
// Try to load information about the requested nodereference field.
$referrer_field = (isset($field_name) && isset($referrer_type) ? content_fields($field_name, $referrer_type) : NULL);
if (empty($referrer_field)) {
drupal_not_found();
return;
}
$module_path = drupal_get_path('module', 'noderelationships');
drupal_add_css($module_path .'/css/noderef_dialog.css');
drupal_add_js($module_path .'/js/noderef_dialog.js');
// Include common module functions.
module_load_include('inc', 'noderelationships');
// Obtain settings for nodereference extras for this type.
$noderef_settings = noderelationships_settings_load($referrer_type, 'noderef');
if ($mode == 'create' && isset($noderef_settings['create_and_reference'][$field_name])) {
$content = noderelationships_noderef_page_create($referrer_type, $field_name, $arg);
if (!empty($content)) {
$output = '';
if (isset($noderef_settings['search_and_reference_view'][$field_name])) {
$output .= theme('noderelationships_noderef_page_tabs', $mode, $referrer_type, $field_name);
}
$output .= theme('noderelationships_noderef_page_content', $content);
}
}
elseif ($mode == 'search' && isset($noderef_settings['search_and_reference_view'][$field_name])) {
list($view_name, $display_id) = explode(':', $noderef_settings['search_and_reference_view'][$field_name]);
$field_multiple = (isset($arg) && $arg == 'multiselect' ? (int)$referrer_field['multiple'] : 0);
$content = noderelationships_noderef_page_search($referrer_type, $field_name, $referrer_field, $view_name, $display_id, $field_multiple);
if (!empty($content)) {
$output = '';
if (isset($noderef_settings['create_and_reference'][$field_name]) && !$field_multiple) {
$output .= theme('noderelationships_noderef_page_tabs', $mode, $referrer_type, $field_name);
}
$output .= theme('noderelationships_noderef_page_content', $content);
}
}
if (isset($output)) {
return $output;
}
// Determine if an HTTP error has already been sent.
if (preg_match('`^HTTP/1.1\s*[45][0-9][0-9]\s*`m', drupal_get_headers())) {
return;
}
// Default to a 404 error page.
drupal_not_found();
}
/**
* Build the create and reference page.
*
* @see node_add_page()
* @see node_add()
* @see _noderelationships_child_node_form_alter()
* @see _noderelationships_child_node_form_submit()
*/
function noderelationships_noderef_page_create($referrer_type, $field_name, $new_type) {
$reference_fields = noderelationships_get_reference_fields($referrer_type);
if (isset($reference_fields[$field_name])) {
// Build the list of content types related to the given parent node type,
// and nodereference field, that can be created by the current user.
$creatable_types = noderelationships_get_creatable_types($referrer_type);
// Do we know the content type the user wants to create?
if (empty($new_type)) {
// Display the list of content types that can be referred from the
// given field in the current parent content type.
$node_add_list = array();
foreach ($reference_fields[$field_name] as $new_type) {
if (isset($creatable_types[$new_type])) {
$translated_menuitem = menu_get_item('node/add/'. str_replace('_', '-', $new_type));
if (!empty($translated_menuitem)) {
$translated_menuitem['href'] = 'noderelationships/create/'. $referrer_type .'/'. $field_name .'/'. $new_type;
$translated_menuitem['localized_options']['attributes'] = array('class' => 'modalframe-exclude');
$node_add_list[] = $translated_menuitem;
}
}
}
// Only display the content type selection screen if more than one
// type is available.
if (count($node_add_list) > 1) {
return theme('node_add_list', $node_add_list);
}
}
// And now? Do we know the content type the user wants to create?
if (!empty($new_type)) {
// Let's try to render the node create form for the specified type.
if (isset($creatable_types[$new_type])) {
module_load_include('inc', 'node', 'node.pages');
global $user;
$type_name = node_get_types('name', $new_type);
if ($type_name) {
$new_node = (object)array(
'uid' => $user->uid,
'name' => (isset($user->name) ? $user->name : ''),
'type' => $new_type,
'language' => '',
'_noderelationships_child' => TRUE,
);
drupal_set_title(t('Create @name', array('@name' => $type_name)));
return drupal_get_form($new_node->type .'_node_form', $new_node);
}
}
}
}
}
/**
* Build the search and reference page.
*/
function noderelationships_noderef_page_search($referrer_type, $field_name, $referrer_field, $view_name, $display_id, $field_multiple) {
// Load the view and check access to the given display.
if (!($view = views_get_view($view_name)) || !$view->access($display_id)) {
drupal_set_message(t('Could not load the view %view:%display.', array('%view' => $view_name, '%display' => $display_id)), 'error');
watchdog('noderelationships', 'Could not load the view %view:%display.', array('%view' => $view_name, '%display' => $display_id), WATCHDOG_ERROR);
return;
}
// Add javascript to the search and reference page.
$js_settings = array(
'maxAllowedValues' => ($field_multiple == 1 ? 0 : ($field_multiple == 0 ? 1 : $field_multiple)),
);
drupal_add_js(array('nodeRelationships' => $js_settings), 'setting');
jquery_ui_add(array('ui.sortable', 'effects.transfer'));
// Prepare values for the view arguments as follows:
// 0 - type of the referrer node.
// 1 - name of the nodereference field in the referrer type.
$view_args = array($referrer_type, $field_name);
// Make sure the view is completely valid.
$errors = $view->validate();
if (is_array($errors)) {
foreach ($errors as $error) {
drupal_set_message($error, 'error');
}
return '';
}
// Execute the view display.
$output = $view->execute_display($display_id, $view_args);
if (!empty($output)) {
if ($referrer_field['multiple'] && $js_settings['maxAllowedValues'] != 1) {
$output = theme('noderelationships_noderef_multiselect', $referrer_field) ."\n". $output;
}
else {
$output = theme('noderelationships_noderef_singleselect', $referrer_field) ."\n". $output;
}
}
return $output;
}
/**
* Render the single selection panel for search and reference views.
*/
function theme_noderelationships_noderef_singleselect($referrer_field) {
// Coder note: Widget labels are user interface literals.
$widget_label = t($referrer_field['widget']['label']);
$output = '
';
$output .= '';
$output .= ' ';
$output .= '
';
return $output;
}
/**
* Render the multi selection panel for search and reference views.
*/
function theme_noderelationships_noderef_multiselect($referrer_field) {
// Coder note: Widget labels are user interface literals.
$widget_label = t($referrer_field['widget']['label']);
$multiselect = '
'."\n";
}
/**
* Alter parent node form.
*
* Attach extra buttons to node reference fields.
*/
function _noderelationships_parent_node_form_alter(&$form, $form_state, $node, $noderef_settings) {
$reference_fields = noderelationships_get_reference_fields($node->type);
$creatable_types = noderelationships_get_creatable_types($node->type);
$content_fields = array();
// Prepare nodereference settings.
$field_settings = array();
foreach (array_keys($reference_fields) as $field_name) {
if (!isset($content_fields[$field_name])) {
$content_fields[$field_name] = content_fields($field_name, $node->type);
}
$field_settings[$field_name] = array(
'maxAllowedValues' => ($content_fields[$field_name]['multiple'] == 1 ? 0 : (empty($content_fields[$field_name]['multiple']) ? 1 : (int)$content_fields[$field_name]['multiple'])),
'ahahSearchUrl' => url('noderelationships/ahah/search/'. $node->type .'/'. $field_name),
);
if (isset($noderef_settings['search_and_reference_view'][$field_name])) {
$field_settings[$field_name]['searchUrl'] = url('noderelationships/search/'. $node->type .'/'. $field_name);
}
if (isset($noderef_settings['create_and_reference'][$field_name]) && isset($reference_fields[$field_name])) {
// Compute the list of content types that can be referred from this
// nodereference field and the current user is allowed to create.
$field_creatable_types = array_intersect($creatable_types, $reference_fields[$field_name]);
if (!empty($field_creatable_types)) {
$field_settings[$field_name]['createUrl'] = url('noderelationships/create/'. $node->type .'/'. $field_name);
}
}
}
$js_settings = array(
'fieldSettings' => $field_settings,
'viewUrl' => url('node/nid'),
);
$form['#noderelationships'] = $field_settings;
// Install and after_build callback to the form.
if (!isset($form['#after_build'])) {
$form['#after_build'] = array();
}
$form['#after_build'][] = '_noderelationships_parent_node_form_after_build_proxy';
// Enable the Modal Frame API for the parent window.
modalframe_parent_js();
// Send javascript and stylesheets to the page.
$module_path = drupal_get_path('module', 'noderelationships');
drupal_add_css($module_path .'/css/node_form.css');
drupal_add_js($module_path .'/js/node_form.js');
drupal_add_js(array('nodeRelationships' => $js_settings), 'setting');
}
/**
* After build callback for multiple value fields.
*/
function _noderelationships_parent_node_form_after_build($form, &$form_state) {
_noderelationships_parent_node_form_after_build_recursive($form, $form['#noderelationships']);
return $form;
}
/**
* Search for node reference fields recursively.
*/
function _noderelationships_parent_node_form_after_build_recursive(&$elements, $field_settings) {
// Proceed only if user has access to this element and children.
if (isset($elements['#access']) && !$elements['#access']) {
return;
}
if (isset($elements['#field_name']) && isset($field_settings[$elements['#field_name']]) && isset($elements['#type'])) {
$field_name = $elements['#field_name'];
if ($elements['#type'] == 'nodereference_autocomplete' && isset($elements['nid']) && isset($elements['nid']['nid'])) {
_noderelationships_element_append_class($elements['nid']['nid'], 'noderelationships-nodereference-autocomplete noderelationships['. $field_name .']');
}
}
else {
// Recurse through all children elements.
foreach (element_children($elements) as $key) {
if (isset($elements[$key]) && $elements[$key]) {
_noderelationships_parent_node_form_after_build_recursive($elements[$key], $field_settings);
}
}
}
}
/**
* Append a class to a form element.
*/
function _noderelationships_element_append_class(&$element, $class_name) {
if (!isset($element['#attributes'])) {
$element['#attributes'] = array();
}
if (!isset($element['#attributes']['class'])) {
$element['#attributes']['class'] = $class_name;
}
else {
$element['#attributes']['class'] .= ' '. $class_name;
}
}
/**
* Alter child node form.
*/
function _noderelationships_child_node_form_alter(&$form) {
// Enable the Modal Frame API for the child window.
modalframe_child_js();
// Append our submit handler so we can tell the parent window to close
// the modal frame and update the node reference field.
$form['buttons']['submit']['#submit'][] = '_noderelationships_child_node_form_submit';
}
/**
* Submit handler for child nodes.
*/
function _noderelationships_child_node_form_submit($form, &$form_state) {
modalframe_close_dialog(array(
'operation' => 'updateSingleValue',
'value' => $form_state['values']['title'] .' [nid:'. $form_state['nid'] .']',
));
}
/**
* Menu callback for AHAH management of multiple value fields.
*
* @see content_add_more_js()
*/
function noderelationships_noderef_ahah_search($type_name, $field_name, $item_count) {
$type = content_types($type_name);
$field = content_fields($field_name, $type['type']);
if (($field['multiple'] != 1) || empty($_POST['form_build_id'])) {
// Invalid request.
drupal_json(array('data' => ''));
exit;
}
// Retrieve the cached form.
$form_state = array('submitted' => FALSE);
$form_build_id = $_POST['form_build_id'];
$form = form_get_cache($form_build_id, $form_state);
if (!$form) {
// Invalid form_build_id.
drupal_json(array('data' => ''));
exit;
}
// Define the requested number of "empty" multiple values.
if (!is_numeric($item_count) || $item_count <= 0) {
$item_count = 1;
}
$_POST[$field_name] = array();
for ($i = 0; $i < $item_count; $i++) {
$_POST[$field_name][] = array('_weight' => $i);
}
// We don't simply return a new empty widget to append to existing ones, because
// - ahah.js won't simply let us add a new row to a table
// - attaching the 'draggable' behavior won't be easy
// So we resort to rebuilding the whole table of widgets including the existing ones,
// which makes us jump through a few hoops.
// The form that we get from the cache is unbuilt. We need to build it so that
// _value callbacks can be executed and $form_state['values'] populated.
// We only want to affect $form_state['values'], not the $form itself
// (built forms aren't supposed to enter the cache) nor the rest of $form_data,
// so we use copies of $form and $form_data.
$form_copy = $form;
$form_state_copy = $form_state;
$form_copy['#post'] = array();
form_builder($_POST['form_id'], $form_copy, $form_state_copy);
// Reset cached ids, so that they don't affect the actual form we output.
form_clean_id(NULL, TRUE);
// Sort the $form_state['values'] we just built *and* the incoming $_POST data
// according to d-n-d reordering.
unset($form_state['values'][$field_name][$field['field_name'] .'_add_more']);
$form_state['values'][$field_name] = array();
foreach ($_POST[$field_name] as $delta => $item) {
$form_state['values'][$field_name][$delta]['_weight'] = $item['_weight'];
$form_state['values'][$field_name][$delta]['_remove'] = isset($item['_remove']) ? $item['_remove'] : 0;
}
$form_state['values'][$field_name] = _content_sort_items($field, $form_state['values'][$field_name]);
$_POST[$field_name] = _content_sort_items($field, $_POST[$field_name]);
// Build our new form element for the whole field, asking for one more element.
$form_state['item_count'] = array($field_name => count($_POST[$field_name]));
$form_element = content_field_form($form, $form_state, $field);
// Let other modules alter it.
drupal_alter('form', $form_element, array(), 'content_add_more_js');
// Add the new element at the right place in the (original, unbuilt) form.
if (module_exists('fieldgroup') && ($group_name = _fieldgroup_field_get_group($type['type'], $field_name))) {
$form[$group_name][$field_name] = $form_element[$field_name];
}
else {
$form[$field_name] = $form_element[$field_name];
}
// Save the new definition of the form.
$form_state['values'] = array();
form_set_cache($form_build_id, $form, $form_state);
// Build the new form against the incoming $_POST values so that we can
// render the new element.
$form_state = array('submitted' => FALSE);
$form += array(
'#post' => $_POST,
'#programmed' => FALSE,
);
$form = form_builder($_POST['form_id'], $form, $form_state);
// Render the new output.
$field_form = (!empty($group_name)) ? $form[$group_name][$field_name] : $form[$field_name];
// We add a div around the new content to receive the ahah effect.
$field_form['#prefix'] = '
';
// If a newly inserted widget contains AHAH behaviors, they normally won't
// work because AHAH doesn't know about those - it just attaches to the exact
// form elements that were initially specified in the Drupal.settings object.
// The new ones didn't exist then, so we need to update Drupal.settings
// by ourselves in order to let AHAH know about those new form elements.
$javascript = drupal_add_js(NULL, NULL);
$output_js = isset($javascript['setting']) ? '' : '';
$output = theme('status_messages') . drupal_render($field_form) . $output_js;
// Using drupal_json() breaks filefield's file upload, because the jQuery
// Form plugin handles file uploads in a way that is not compatible with
// 'text/javascript' response type.
$GLOBALS['devel_shutdown'] = FALSE;
print drupal_to_js(array('status' => TRUE, 'data' => $output));
exit;
}