During development of a module for DRUPAL I met some problems
with generation of 'Preview' of a node. The following text is not
call for support cause I solved my problem (I'll discuss the
solution later) but it is rather discussion of the 'Preview'
problem generally.

The problem with preview seem to be deeper than you think at first sight. See for example:
http://drupal.org/node/104047
http://drupal.org/node/109204

Personally I think that the problem is that the Preview generation is slightly inconsistent with the generation of node's view. I will try to explain this on the real problem I met during last days.

The task is:

The format of the data the user enter/see is different from the format in which the data are stored in database.

  • I have a field with number of players (nop).
  • The user should enter something like this '1, 2, 6'
  • Then the data should be converted into the number, where every number of player is represented by the value of corresponding bit, thus for the previous example the conversion should produce b'00100011'=35
  • The data are stored in database as integer
  • For node-view purposes the data needs to be converted back into the human readable form

If you don't use 'Preview' everything is - I think - perfectly designed:

  1. When you submit the node then the hook_submit is called first. There you plug the conversion of the data.
  2. Afterwards either hook_insert or hook_update provide the real save of the data into the database. During this step nop is already number.
  3. If you go to edit/add page of the node then at first step hook_load is called and then hook_prepare should make conversion as is recommended by documentation (see e.g. http://drupal.org/node/71962).
  4. hook_prepare is provided only for purposes of editing not viewing. Thus if you 'View' existing node, then after load of node by hook_load the hook_view has to do its own conversion suitable to view the node.

And now we come to the problematic point! From the first point of view this model could seem strange. Why the hook_prepare is not called before hook_view?

But this design is really great. It allows separate the data representation for the purposes of value entering and how they are presented to a reader.

Moreover, the hook_prepare is much more powerful than you think. It does not have to only manipulate loaded data but (see for example image.module) it can be used, e.g., for files upload.

The inconsistent part of this model is - as I think - the preview mode. What happens when you 'click' button 'Preview'?

  1. the form is sent
  2. the hook_submit and hook_insert (hook_update) are skipped
  3. then hook_load is invoked and followed by hook_prepare. But! But 'prepare operations' are performed for "old" data (from database) and these are overwritten with new data.
  4. These data are sent to hook_view.

What happens in our real example:

  1. Enter new value, let say '2,4' and press 'Preview button. The form is sent.
  2. The hook_load loads data from database, i.e., 35 (see example above).
  3. The hook_prepare then converts number to '1, 2, 6'
  4. But finally the data are updated with the values from the form (sorry, I don't know which function performs this exactly), thus the value of nop is '2, 4'.
  5. Finally the hook_view invokes rendering. But the view function normally suppose that the provided data are from database and the hook_prepare does not make conversion. Thus hook_view make conversion for it selves which is not made for valid data because nop contains string '2, 4' instead of number!

Solution:

This could be solved, sure. For example you can test whether you are in preview mode by testing $node->in_preview (function node_preview set this variable). E.g. if you are in preview node don't make conversion in hook_view:

  if ((!isset($node->in_preview)) || !($node->in_preview)) {
    $node->nop=_number_to_nop($node->nop);
  }

Discussion:

  • I don't know right know how to solve this problem according to DRUPAL's module philosophy. I think that for 'Preview' must be simulated complete process made during submit. But its really waste of resources to do fake writings to database.
  • At the first, it seems hook_prepare and hook_submit could solve the problem. But as I said already. They can perform other tasks then then data conversion from/to database.
  • Finaly, I'm not really sure that the solution I have proposed is optimal. From one point of view it is simple but on the other hand I feel that it is somehow fragile ...

By the way, thanks to this problem I understand the DRUPAL's core much more better ;] But that it prise lot of hours of code reading ;]]

Alladjex }I{

Comments

alladjex’s picture

I'm sorry. I forgot to mention that I'm running DRUPAL 4.7.6 (and testing v. 5). But the text is mainly based on the study of v. 4.7.6