1

I have a particular base path recorded as a settings var, and I have a number of Views (v3) that unfortunately have this same value hard-coded as part of its links. These links are rendered using the Rewrite results and Output this field as a link options, and are built from a number of field values.

Problem is I have to configure this on every dev/test/prod site. It would be far better if these links could pull the information from the persistent variable.

A couple ideas:

  • I have tried defining this value as a bespoke token, but as I expected these View options do not support checking for and rendering Tokens. I was hoping that perhaps using the global fields of the Views Field View module might work. So that I might render a field as Exclude from display and reference it with normal Views replacement patterns. However this failed to render Tokens as well.

  • I could step into each specific field in question and override with a particular template, wrapping its content in the link and sourcing the variable directly with variable_get. But this would actually be quite involved, and would also remove the primary reason for using Views in the first place — the possibility of live modification.

Is there a better way of using a persistent variable from within a View?

I already have a bespoke module set up, so have looked to see if it is possible to modify the replacement patterns of a view with a particular hook or something; however that hasn't turned anything up so far. My last remaining idea is to hook into the final render of the view and perform a token pass, but I don't know if this is such a good idea or not.

  • Since you're already rewriting results I suspect a custom token would be the simplest solution - failing that you could always right a custom field handler and apply it to the relevant fields – Clive Sep 23 '14 at 15:34
  • You might consider computed field? drupal.org/project/computed_field Though it would obviously change the way you're approaching this now. – Dave Bruns Sep 23 '14 at 15:47
  • @Clive Thanks, so you would expect a custom token to render as part of a view field automatically, or you would expect to add extra code to make it happen? Currently I can get the token to render in the header and footer because those areas use Text Filters, and I can implement my tokens via a filter; but other areas like Rewrite results do not seem to apply Tokens by default, and have no text filter options. – Pebbl Sep 23 '14 at 16:19
  • @DaveBruns Thanks for the module idea, so it seems that this module is like enabling PHP Filter (which I can't and wouldn't do), however with this you define far safer callbacks in your code. Interesting. I'll definitely keep this in mind although perhaps for another future attempt. – Pebbl Sep 23 '14 at 16:24
1

Your instincts are correct (and you're right about the lack of examples too).

In order to add a custom replacement pattern / token, you'll need to extend a Views field handler class and override the document_self_tokens() and add_self_tokens() methods. You can then instruct a field to use your custom field handler via hook_views_data_alter() (thanks to Daniel Wehner for this info, and very likely the hook itself).

You can see example code from my need for adding a Views field replacement pattern / token.

If you need help adapting it for your need above provide the field or fields you'd like the replacement token to appear for and maybe more on how you'd like to construct it, but if you look at my example and remember to list in your .info the file with your field handler extending class so that Drupal registers it, you'll probably be all set.

(In giving the same answer over here, i also commiserated about the confusing naming of Views rewrite field replacement pattern "tokens" versus Drupal's Token API tokens.)

  • Apologies I missed this answer, and I have indeed used this solution for a different issue, it works well. For this actual question however, at the time, I fell back to triggering a token pass in a hook_views_post_render -- the reason being is that with your solution you have to target specific fields (which is perfectly fine if you know what they should be), but I wanted the tokens to be more generally available. Totally with you on the naming conventions too ;) – Pebbl Aug 4 '15 at 11:53
0

Just in case it is useful, I'll add another solution to this question — with a different perspective. The following needs to be placed in a bespoke module — with HOOK replaced by your module's name.

/**
 * Implements hook_views_post_render().
 */
function HOOK_views_post_render(&$view, &$output, &$cache){
  // enable token rendering only if this view has the right class
  if ( _HOOK_view_has_class($view, 'token-render') ) {
    // annoying little hack because a colon placed in a link 
    // href seems to be filtered out by Drupal. so we aren't 
    // able to formulate bespoke tokens properly. Instead 
    // we write them slightly differently
    // i.e. replacing the colon with an underscore; and then 
    // we rewrite them to be understood as tokens. Remove this
    // next line as you see fit.
    $output = preg_replace('/\[(HOOK)_([^\]]+)\]/', '[$1:$2]', $output);
    $output = token_replace($output, ($data = array()), ($options = array(
      'callback' => '_HOOK_filter_token_list',
      'clear' => TRUE,
    )));
  }
}

Also, just in case it is useful, here is the code to test that a view has a particular class. This isn't required, but I decided that I didn't want the extra processing all the time. So the above only functions if the view is given a class of token-render.

/**
 * Tests to see if a particular view has a particular class.
 */
function _HOOK_view_has_class(&$view, $class_name){
  if ( $view->display_handler ) {
    $classes = $view->display_handler->get_option('css_class');
    if ( !empty($classes) ) {
      $class_name = preg_quote($class_name);
      return preg_match('/(^|\s+)' . $class_name . '(\s+|$)/i', $classes);
    }
  }
  return FALSE;
}

Just for added security I wanted to make sure that only the tokens I wanted were being rendered. The following callback made that possible:

/**
 * Callback that controls exactly what tokens are allowed to be inserted
 */
function _HOOK_filter_token_list( &$replacements, $data, $options ){
  // blank anything that isn't allowed
  foreach ( $replacements as $key => $value ) {
    switch ( $key ){
      // this is just an example token key, add those you want to allow
      case '[current-page:title]':
        // the token was allowed
      break;
      default:
        $replacements[$key] = ''; // this token, wasn't
      break;
    }
  }
}
0

There is another rather clean way to do it - create handler just to return the variable. There's a blog post about this here.

It's pretty simple. In MYMODULE_field_handler_VARIABLENAME.inc file:

<?php

/**
 * Provide the VARIABLENAME variable to views.
 */
class MYMODULE_field_handler_VARIABLENAME extends views_handler_field {

  // No query to run here.
  function query() { }

  public function render() {
    return variable_get('VARIABLENAME');
  }

}

Then in hook_views_data_alter():

$data['views']['VARIABLENAME'] = array(
  'title' => t('VARIABLENAME title'),
  'help' => t('Description of variable.'),
  'field' => array(
    'handler' => 'MYMODULE_field_handler_VARIABLENAME',
  ),
);

Now in your view you can add the field Global: VARIABLENAME title.

Don't forget to implement hook_views_api() in MYMODULE.module and add files[] = MYMODULE_field_handler_VARIABLENAME.inc to the MYMODULE.info file.

There are some shortcomings to this method though. You don't get everything that a normal field has because there is no table to join on. For example, the filter Global: Fields comparison doesn't work with it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for?Browse other questions tagged or ask your own question.