It appears shortcodes are executed (and cached) on the admin/content page. For most of my short codes this isn't an issue, however when dealing with any short codes that leverage theme overrides it spells disaster. If some innocent change (say a tweak to display settings) or a cache clear is performed, and the admin/content page is hit, every node cache's its shortcodes using the admin theme. Any shortcodes that use tpl overrides are rendered on the front end incorrectly.

For example if we were to perform:

function somemodule_SOMECODE_process($attrs, $text){
  // ...handle attrs...
  $node =  node_load(1);
  $node_view = node_view($node, 'teaser');
  return render($node_view);
}

After a structure change, or a cache clear, hitting admin/content would potentially be disastrous. Is there a way to prevent caching of a given short code?

(This may be a function of the text_filter caching, apologies if it is)

Comments

jpschroeder’s picture

Title: Shortcodes are rendered on admin/content page » Shortcodes are rendered & cached on admin/content page
dave reid’s picture

Status: Active » Closed (works as designed)

This is because doing a node_load() causes any filtered text fields to run through the filter system; see the calls to _text_sanitize() in text_field_load(). This is a core issue and not related to shortcodes.

nicodh’s picture

Up !
Do you have any solution around this issue ?
I have a shortcode that uses a theme template and inside a call to noad_view, and at each edit/save of the page in admin/content, the text is cached, and the output is made with admin theme and logged user (in my case user 1), so with admin contextual links (like edit, view...). It's annoying...
Thanks in advance !

nicodh’s picture

Category: Bug report » Support request
Status: Closed (works as designed) » Active
denes.szabo’s picture

Status: Active » Closed (works as designed)

@rkcreation: try this in your Shortcode:

$content = node_view($node, $params->view_mode);
  if (!empty($content['#contextual_links'])) {
    unset($content['#contextual_links']);
  }
  return render($content);

(Code based on the shortcode_embed_content module).

As you see, I remove the contextual_links content from the output. As Dave Reid mentioned you above, it is a core issue, if ever solved it, the solution will be very complicated.

nicodh’s picture

@Denes.Szabo : thanks for the reply.

It's a solution for contextual links, but node.tpl overrides in my front theme is not used.

After search, I don't find any way to force theme used for node_view, if you have a solution (if exists) I'm interested !

Thanks

denes.szabo’s picture

@rkcreation: I would try to add a special node theming into a module (not theme), then use this theming function for display the embedded node.

This theming function should be independent from all enabled themes - just add special css formatting into the theme's css.

jpschroeder’s picture

@Denes.Szabo is right, there is no way around this; you need to manually get the same markup to render no matter what theme is selected (ugly, but possible with a module). The only real solution is to write a separate module to parse your special shortcode way after the text_filter, like in a theme function, say theme_node_preprocess(); I'm going to write a module to this effect and will post a link to it when its done.

EDIT:

The module I've written is sandboxed here: https://www.drupal.org/sandbox/man4mac/2442015

khaled.zaidan’s picture

If anyone is still interested in this issue, I did manage a workaround. I have not looked at the module by jpschroeder (but since it's still in sandbox, I'll guess he didn't finish it).

so here's the snippet of code I used on my project:

function MODULE_node_load($nodes, $types) {

  /**
   * ISSUE:
   * When a node is loaded, any text field with text formats (e.g body) is rendered.
   * If the value of the field has a shortcode to embed other content, then that is rendered as well.
   * Then the result of the rendering is cached in the 'safe_value' column of the field in cache_field.
   * If all this happens in the admin theme, then none of our theme's templates and preprocess functions are applied.
   * This is bad!
   *
   * SOLUTION:
   * When a node is loaded, we will check if its body has an embed_content shortcode.
   * If it does, and we're currently in the admin theme, we'll just clear the cached values in cache_field for this node.
   * Debugging showed that the fields are cached right before hook_entity/node_load() is invoked, which means that the caching has just happened before coming here!
   * And clearing the cache should be fine as it would just be re-created the next time the node is loaded!
   */
  if (path_is_admin(implode('/', arg()))) {
    foreach ($nodes as $node) {
      if (!empty($node->body['und'][0]['value'])) {
        $short_codes = array('[embed_content', '[quote');
        foreach ($short_codes as $short_code) {
          if (strpos($node->body['und'][0]['value'], $short_code) !== FALSE) {
            $cid = "field:node:" . $node->nid;

            /**
             * We need to check how long ago this cache was created.
             * If it's created really recently (within less than half a minute),
             * then that means it was probably created in this page load in the admin theme,
             * and it needs to be cleared.
             */
            $cache = cache_get($cid, 'cache_field');
            if ($cache && $cache->created > time() - 30) {
              cache_clear_all($cid, 'cache_field');
            }

            break;
          }
        }
      }
    }
  }
}

The bit where I'm checking if the field has a shortcode or not may need to be tweaked, as well as which fields are to be checked.

I suppose the nicer solution would be to fetch all shortcodes (from the shortcode module's API), and all the long text fields (from Drupal's API) and go through them all... Or maybe just always clear them if you're on an admin path and you have a different admin theme than your default theme.

I hope this helps.