385 lines
12 KiB
Text
385 lines
12 KiB
Text
<?php
|
|
|
|
/**
|
|
* @file
|
|
* Core functions.
|
|
*
|
|
* @todo Double-check potential performance issues mentioned in code comments.
|
|
*/
|
|
|
|
/**
|
|
* Implementation of hook_field_info().
|
|
*/
|
|
function viewfield_field_info() {
|
|
return array(
|
|
'viewfield' => 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'] .=
|
|
'<br />' . $token_description . '; ' . t('or any token from the placeholder token list.')
|
|
. '<br />' . 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'] .= '<br />' . $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 '<div class="viewfield-select">'. $element['#children'] .'</div>';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|