array( // Should be "View", but that would translate into "view" (show) for many // languages due to missing string translation contexts. 'label' => t('Views'), 'description' => t('Displays a selected view in a node.'), 'callbacks' => array( 'tables' => CONTENT_CALLBACK_NONE, 'arguments' => CONTENT_CALLBACK_NONE, ), ), ); } /** * Implementation of hook_field_settings(). */ function viewfield_field_settings($op, $field) { switch ($op) { case 'form': $form['allowed_views'] = array( '#type' => 'checkboxes', '#title' => t('Allowed values'), '#default_value' => is_array($field['allowed_views']) ? $field['allowed_views'] : array(), '#options' => drupal_map_assoc(array_keys(views_get_all_views())), '#description' => t('Only selected views will be available for content authors. Leave empty to allow all.'), ); return $form; case 'validate': if ($field['force_default'] && $field['multiple']) { form_set_error('multiple', t('Multiple views are not supported if force default is enabled.')); } break; case 'save': return array( 'allowed_views', ); case 'database columns': return array( // Views requires at least 96 chars for the view name and display, plus // we need 1 for our delimiter. Round up to a common value of 128. 'vname' => array('type' => 'varchar', 'not null' => FALSE, 'length' => 128), 'vargs' => array('type' => 'varchar', 'not null' => FALSE, 'length' => 255), ); } } /** * Implementation of hook_field(). */ function viewfield_field($op, &$node, $field, &$items, $teaser, $page) { switch ($op) { case 'presave': foreach ($items as $delta => $item) { if (empty($item['vname'])) { unset($items[$delta]); } } break; case 'sanitize': // Replace field values with widget defaults when force_default is set. if ($field['widget']['force_default']) { $items = $field['widget']['default_value']; } break; } } /** * Implementation of hook_content_is_empty(). */ function viewfield_content_is_empty($item, $field) { return empty($item['vname']); } /** * Implementation of hook_field_formatter_info(). */ function viewfield_field_formatter_info() { return array( 'default' => array( 'label' => t('Default'), 'field types' => array('viewfield') ), ); } /** * Return a themed view avoiding viewfield recursion. */ function theme_viewfield_formatter_default($element) { // $_viewfield_stack keeps a record of the current node to prevent infinite // recursion during the view rendering process. global $_viewfield_stack; $node = $element['#node']; if (!empty($element['#item']['vname']) && !isset($_viewfield_stack[$node->nid])) { // Push id of current node unless it's a new node being previewed. if ($node->nid) { $_viewfield_stack[$node->nid] = $node->nid; } list($view_name, $display) = explode('|', $element['#item']['vname'], 2); $view_args = _viewfield_get_view_args($element['#item']['vargs'], $element['#node']); // Render the view like Views would do. // @see views_embed_view() $view = views_get_view($view_name); if ($view && $view->access($display)) { // Override the view's path to the current path. Otherwise, exposed views // filters would submit to the front page. $view->override_path = $_GET['q']; $output = $view->preview($display, $view_args); } // This node is "safe" again. if ($node->nid) { unset($_viewfield_stack[$node->nid]); } // Only return an actual view result to not break empty value behavior. if (isset($output)) { return $output; } } } /** * Implementation of hook_widget_info(). */ function viewfield_widget_info() { return array( 'viewfield_select' => array( 'label' => 'Select List', 'field types' => array('viewfield'), 'multiple_values' => CONTENT_HANDLE_CORE, 'callbacks' => array( 'default value' => CONTENT_CALLBACK_DEFAULT, ), ), ); } /** * Implementation of hook_widget_settings(). */ function viewfield_widget_settings($op, $widget) { switch ($op) { case 'form': $form['force_default'] = array( '#type' => 'checkbox', '#title' => t('Always use default value'), '#default_value' => $widget['force_default'], '#description' => t('Hides this field in forms and forces the default value defined below.'), ); return $form; case 'save': return array( 'force_default', ); } } /** * Implementation of hook_widget(). */ function viewfield_widget(&$form, &$form_state, $field, $items, $delta = 0) { $element = array( '#type' => $field['widget']['type'], '#default_value' => isset($items[$delta]) ? $items[$delta] : NULL, ); return $element; } /** * Implementation of FAPI hook_elements(). */ function viewfield_elements() { return array( 'viewfield_select' => array( '#input' => TRUE, '#columns' => array('vname', 'vargs'), '#delta' => 0, '#process' => array('viewfield_select_process'), ), ); } function viewfield_select_process($element, $edit, $form_state, $form) { // This form is used for both the default value field in the admin as well as // the node edit form, so we have to make sure we show the default value field // always. $is_field_settings_form = !isset($form['#node']); $field = isset($form['#field_info'][$element['#field_name']]) ? $form['#field_info'][$element['#field_name']] : NULL; $element['#field'] = $field; // Display the form to let the user pick a view. $options = _viewfield_potential_references($field, $element['#delta']); $element['vname'] = array( '#type' => 'select', '#title' => $element['#title'], '#options' => $options, '#default_value' => isset($element['#default_value']['vname']) ? $element['#default_value']['vname'] : NULL, '#required' => $element['#required'], '#access' => $is_field_settings_form || !$field['widget']['force_default'], '#description' => $element['#description'], ); // If there is only one option, only show arguments. if (count($options) == 1 && !$is_field_settings_form) { list($key, $label) = each($options); $element['vname']['#access'] = FALSE; $element['vname']['#default_value'] = $key; } $element['vargs'] = array( '#type' => 'textfield', '#title' => !isset($label) ? t('Arguments') : t('%field (@value) arguments', array( '%field' => $field['widget']['label'], '@value' => $label, )), '#default_value' => isset($element['#default_value']['vargs']) ? $element['#default_value']['vargs'] : NULL, '#access' => $is_field_settings_form || !$field['widget']['force_default'], '#description' => t('A comma separated list of arguments to pass to the selected view. Wrap arguments containing commas in double quotes. Replace double quotes in arguments with two double quotes.'), ); $token_description = t('Available tokens: %nid for the id of the current node; %author for the node author; %viewer for the viewing user'); if (module_exists('token')) { $element['vargs']['#description'] .= '
' . $token_description . '; ' . t('or any token from the placeholder token list.') . '
' . t('Note: Using placeholder tokens in combination with the %fields row style may negatively affect site performance.', array('%fields' => t('Fields'))); // Only show token help for first value or in field settings form. if ($element['#delta'] == 0 && ($is_field_settings_form || !$field['widget']['force_default'])) { $element['token_help'] = array( '#type' => 'fieldset', '#title' => t('Placeholder tokens'), '#collapsible' => TRUE, '#collapsed' => TRUE, ); $element['token_help']['tokens'] = array( '#value' => theme('token_help', 'node'), ); } } else { $element['vargs']['#description'] .= '
' . $token_description . '.'; } return $element; } /** * Prepare a list of views for selection. */ function _viewfield_potential_references($field, $delta = 0) { $options = array(); if (isset($field['allowed_views']) && is_array($field['allowed_views'])) { $field['allowed_views'] = array_filter($field['allowed_views']); } if (empty($field['allowed_views'])) { $field['allowed_views'] = array_keys(views_get_all_views()); } foreach ($field['allowed_views'] as $view_name) { // Check for deleted views saved in allowed_views. if ($view = views_get_view($view_name)) { foreach ($view->display as $display) { $options[$view->name .'|'. $display->id] = $view->name .' - '. $display->display_title; } } } // Add a '0' option for non-required or subsequent values of multiple fields. if (empty($field['required']) || ($field['multiple'] && $delta > 0)) { array_unshift($options, '<'. t('none') .'>'); } return $options; } /** * Implementation of hook_theme(). */ function viewfield_theme() { return array( 'viewfield_select' => array( 'arguments' => array('element' => NULL), ), 'viewfield_formatter_default' => array( 'arguments' => array('element' => NULL), ), ); } /** * Add CSS to force help text to wrap correctly on node edit form. */ function theme_viewfield_select($element) { if (!empty($element['#children'])) { $field = $element['#field']; if ($field['multiple'] && $element['#delta'] == 0) { // This is needed only for multiple viewfields. drupal_add_css(drupal_get_path('module', 'viewfield') .'/viewfield.css'); } return '
'. $element['#children'] .'
'; } } /** * Perform argument replacement */ function _viewfield_get_view_args($vargs, $node) { $args = array(); // Prevent token_replace() from running this function a second time // before it completes the first time. static $tokens = TRUE; if ($tokens && !empty($vargs)) { $pos = 0; while ($pos < strlen($vargs)) { $found = FALSE; // If string starts with a quote, start after quote and get everything // before next quote. if (strpos($vargs, '"', $pos) === $pos) { if (($quote = strpos($vargs, '"', ++$pos)) !== FALSE) { // Skip pairs of quotes. while (!(($ql = strspn($vargs, '"', $quote)) & 1)) { $quote = strpos($vargs, '"', $quote + $ql); } $args[] = str_replace('""', '"', substr($vargs, $pos, $quote + $ql - $pos - 1)); $pos = $quote + $ql + 1; $found = TRUE; } } elseif (($comma = strpos($vargs, ',', $pos)) !== FALSE) { // Otherwise, get everything before next comma. $args[] = substr($vargs, $pos, $comma - $pos); // Skip to after comma and repeat $pos = $comma + 1; $found = TRUE; } if (!$found) { $args[] = substr($vargs, $pos); $pos = strlen($vargs); } } // Try to replace tokens if $args might contain one. if (strpos($vargs, '[') !== FALSE && module_exists('token')) { $tokens = FALSE; // If the view field is being loaded as a "view field" of "view row", // instead of a simple "node field", the node object is not fully populated: // we need a full node to perform a correct replacement. $node = node_load($node->nid); foreach ($args as $key => $text) { $args[$key] = token_replace($text, 'node', $node); } $tokens = TRUE; } // For backwards compatibility, we scan for %nid, etc. foreach ($args as $key => $value) { $args[$key] = strtr($value, array( '%nid' => $node->nid, '%author' => isset($node->uid) ? $node->uid : (isset($node_values->uid) ? $node_values->uid : NULL), '%viewer' => $GLOBALS['user']->uid, )); } } return $args; }