if tinymce_elements define $type['textarea']['#process'] it will override system_elements textarea. That's bad, bad, bad. Makes hook_elements almost unusable -- I think it's much more common to extend an existing element than defining a new one.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

m3avrck’s picture

Just thoroughly tested patch and it works. Been talking with chx on IRC for almost 2 hours, if this patch was in there I don't think I would have had any issues and chx would not have had a headache ;-)

However, one problem does exist. If the same value is to be overriden twice, there is a race condition. For example, if one module has hook_elements() defined and defines #suffix = foo for all textareas, and another defines #suffix = bar for all textareas, whichever module's hook is called last (alphabetically) will take precedence. This happens for *all* form values, #prefix, #process, etc...

chx’s picture

FileSize
1.94 KB

Well.. let's try this.

chx’s picture

FileSize
1.94 KB
m3avrck’s picture

New patch works great.

One caveat, if you use hook_elements() and set #process to a callback, if this callback overrides say #suffix or #prefix for a textarea that has been defined elsewhere, you will have to do something of this nature: if (isset($element['#prefix'])) if (is_array($element['#prefix'])) ... else $elemen...=array($ele... $mystring)

m3avrck’s picture

An example of how to test this (see TinyMCE.module for full code) if you have defined a #process callback:

    // Because we are calling this function from #process we need to make sure we don't override any other values
    if (isset($element['#suffix'])) {
      if (is_array($element['#suffix'])) {
        element['#suffix'])[] = $wysiwyg_link;
      }
      else {
        $element['#suffix'] = array($element['#suffix'], $wysiwyg_link);
      }
    }
    else {
      $element['#suffix'] = $wysiwyg_link;
    }
m3avrck’s picture

Status: Needs review » Reviewed & tested by the community
Dries’s picture

Wouldn't it be nicer if modules would not have to worry about overwriting other module's values?

chx’s picture

If you override $node->body in nodeapi view (or load) then you have, well, overridden that. So it's not unheard of that you should be careful when operating on objects that others may operate on, too. We are far more ahead with this patch than without it.

m3avrck’s picture

Dries this patch fixes the *biggest* override problem that I encountered when defining hook_elements() that was simply not even the slightest intuitive and chx can attest to this :-)

While I agree about my last comment and #process, I'm not aware of a better practice for this isolate case. This patch doesn't introduce this problem (at least to my knowledge it is already there) but at least fixes the other more major override problem. Perhaps someone with a better understanding of forms.inc can create a patch to fix this caveat in that special circumstance (only override left after this patch).

chx’s picture

Once again, if you are operatin in place on an object, you should be careful. But if we want, we can add this

      foreach (array('#prefix', '#suffix') as $key) {
        if (isset($form['$key'])) {
          if (!is_array($form['$key'])) {
            $form['$key'] = array($form['$key']);
          }
        }
        else {
          $form['$key'] = array();
        }
      }

after if (is_array($form['#process'])) {. I am not too fond this one, that's why I have not rolled a patch. But if Dries thinks so, well, the code is there, even code style is ok, so it's peanuts to roll into the patch.

chx’s picture

FileSize
1.98 KB

After much consideration I arrived to this. #prefix and #suffix is always a string and the exception is the #process. Why? It's very awkward to type #prefix => array('

chx’s picture

Status: Reviewed & tested by the community » Needs work

something eat my followup:

array('<div....'); when that array will be practically always one element. And while theming it's also strange to have #prefix as an array. So, in the beginning and at the end we need strings. However, we need to 'escape' the string during #process so that if two textarea processors are adding prefixes, it's OK.

As I thought more, here is the simplified version: just take care to append/prepend to #prefix instead of overwriting it. No need to muck with arrays. Commiting new patch soon.

chx’s picture

FileSize
1.97 KB

There we go -- now I collapse #prefix and #suffix just after merging the default element info in. That's where an array may arise. So, #prefix and #suffix now are always strings, treat them as such.

chx’s picture

Status: Needs work » Needs review
m3avrck’s picture

Status: Needs review » Reviewed & tested by the community

Patch fully tested and working 100%. Tested various #suffix defined in different places, including hook_elements() and in #process callback functions. Nothing was overwritten so long as ['#suffix'] .= 'foobar' was use, e.g., appending the string to the #suffix or #prefix string.

I think this solution is very elegant and *much* nicer than the above proposed array patch. Modules can make use of this extremely easily, all they have to do is append the string and it works, no long, overdrawn array tests and what not. Please commit!

chx’s picture

FileSize
1.56 KB

On further thought, it's unlikely to set static #prefix for an element. Use #process to do that. Now please, commit this, even the performance penalty is almost zero, now.

m3avrck’s picture

Modules still work with this patch, however after recent commits, the patch is off by 19 lines, but still applies cleanly.

chx’s picture

What holds this patch?

Dries’s picture

- I don't understand where you explain that people need to append the string. Please elaborate.

- In general, I don't like code which allow both scalar and array values to be used. Simpler is better.

chx’s picture

By now, array is gone. Elaborate: if you define a #process callback and you change #prefix or #suffix then just append to these. This is the same as in form_alter now, there would have been a difference with former versions of the patch. The current version is very simple.

m3avrck’s picture

Right Dries, simple example:

hook_elements() {
  $type['textarea'] = array('#process' => 'foobar');
  return $type;
}

foobar($element) {
  $element['prefix'] .= 'append this text to prefix of textareas';
}

Simple enough! We'll make sure the Forms API docs make this clear since it can be confusing, but in reality, this is much of Drupal works, just append strings :-)

Dries’s picture

Status: Reviewed & tested by the community » Fixed

Committed to HEAD. Thanks.

Wesley Tanaka’s picture

I guess an equivalent patch which isn't attached to this issue got committed?

I see the $cache = array_merge_recursive changes in CVS, but the changes around line 268 are different from any of the patches here.

chx’s picture

because there were other patches since then :)

Anonymous’s picture

Status: Fixed » Closed (fixed)