I've written a filter module to do some custome shortcode parsing like [mytag]whatever[/mytag]. I just wan't to apply those to the full node view only. In teaser view I wan't to strip those custome tags of, because if the teaser gets generated drupal may cut the text on a position that will break the pattern recognition.

Body: "This is the long text with a lot of words and some of my custome tags insert like [mytag]this tag here[/mytag]."

If drupal breaks the text somewhere inside the [mytag] pattern the resulting markup may look like the following:

Teaser: "This is the long text with a lot of words and some of my custome tags insert like [mytag]this tag " <- break

This damaged tag can not be parse by the filter and stays the way it looks above.

I tried to implement the following code to solve the problem:

function MYMODULE_nodeapi(&$node, $op, $a3 = null, $a4 = null) {
  switch($op) {
    case 'presave':
      $node->teaser = node_teaser(preg_replace('@\[[\/\!]*?[^\[\]]*?\]@si', '\\2', $node->body));
      break;
  }
}

First everything looks ok but if you edit the node again, you see a the teaser doubled in the node edit form.

Any idea how to hook into node_teaser or node_save to solve this problem without hacking the core?

Comments

Raf’s picture

I wrote some very basic BBCode functions a few years ago and had a similar problem. Wasn't using Drupal back then, so wasn't with teasers or such. The problem I had, will be the same as you'll have, though. The solution'll also fix your teaser problem.

The problem with the BBCode was that a user could open a tag, but not close it. That'd mess the HTML that comes after that text up.

My solution was as follows (for each tag type):
1) q_open = Count of tags opened
2) q_closed = Count of tags closed
3) result = q_open - q_closed
4) if (result > 0) { (loop result times) { add closing tag at end of message } }

It doesn't result in nicely nested tags, but it gets the job done. I think making it nest properly won't be too difficult to do. That'll be a matter of, if a tag's opened but not closed, finding the next tag (any tag, and both opening and closing) and placing the closing tag before that. If you want to make it really nice, you can also handle result < 0 (meaning some idiot added more closing tags than opening tags :P )

naden’s picture

Dear Raf, thank you for your reply. For me the problem ist not markup validation on user input (that is another problem :)

I found no way to hook into the proccess how drupal generates the teaser. There is nothing like hook_node_teaser.

I need to apply a filter before drupal generates the teaser, because the teaser is generated from the raw, unfiltered body text and I like to clean them up like I pointed out in my initial post.

Raf’s picture

You mean something like this?
node_teaser($body, $format = NULL, $size = NULL)
An example of its use can be found here

naden’s picture

I know the function node_teaser() the problem is, you can't hook into it. node_teaser() receives the raw, unfiltered $node->body text in $body.

Raf’s picture

How're you getting the data and show it in the right theme right now?

naden’s picture

just the default way, no special tricks

naden’s picture

The "solution" is, to reset $node->teaser on edit:

function MYMODULE_nodeapi(&$node, $op, $a3 = null, $a4 = null) {
  switch($op) {
    case 'prepare':
      if(in_array($node->type, $node_types)) {
        if($node->teaser == substr($node->body, 0, strlen($node->teaser))) {
          // reset node teaser on edit
          $node->teaser = '';
        }
      }
      break;
    case 'presave':
      if(in_array($node->type, $node_types)) {
        // strip markup from teaser
        $node->teaser = node_teaser(preg_replace('@\[[\/\!]*?[^\[\]]*?\]@si', '\\2', $node->body));
      }
      
      break;
	}
}

Not very nice, a hook for node_teaser would be very handy.

Raf’s picture

Just learned of another way of doing this. You first need to make an input filter with all the tags out (so no HTML allowed). Then when checking the teaser (like in hook_nodeapi), you use check_markup, like this:

$node = node_load($nid);
$node->teaser = check_markup($node->teaser, 3, FALSE);

Where 3 is your filter's ID. It will automatically apply that filter to $node->teaser. If the filter says: "no tags allowed", it'll filter them all out.

z33k3r’s picture

Just in the knick of time! Exactly what I needed, thanks so much :)