#  $Id:$
'''Emulate Redirect.

Common problems should have simple answers.

Currently, the following common problem has not really a simple answer:

  * user fills in a form, submits it

  * form actions needs to check that everything is correct

  * if not, user must be informed about all problems
    *AND* have the already filled info preserved.

This last step is currently not optimally supported.
There are two partial solutions:

  1. A redirect is used to return to the form with an error indication.

     The problems:

       *  HTTP 1.1 is very weak with respect to redirects.
          It does not specify, whether the original parameters
          must be resubmitted for the redirect request.

          Browsers differ, how this is handled.
          Netscape 4.x discards the original request parameters
          and uses just the new parameters specified in the
          redirection URL.
          The list archive tells about at least one browser,
          that resends the original parameters in addition
          to the new ones.

       *  It is quite difficult to put all request parameters
          into the redirection URL. The URL may get really
          large and unwieldy. File objects can not be
          submitted in this way.

       *  Encoding the parameters in the URL may make them visible
          in the log files. Not that good for sensitive information
          like passwords.

  2. After the check, the Zope form generating option is
     called directly.

     The problems:

       *  Potential side effects can not be discarded.

          This forces a strong separation between checking
          code and operation, causing code duplication
          and breaking encapsulation.

       *  It is only feasible, if the form generating option
          is in the same folder as the form action object
          as otherwise all URL variables are wrong in the
          new context and relative URL references may break
          because the URI base is not longer correct.

'emulateRedirect' emulates a redirect without leaving the Zope
context. This makes approach 2 feasible without the nasty 
side effects.

The method is typically used in the following way::

  &lt;dtml-let obj=--whatever--&gt;
    &lt;dtml-call "emulateRedirect(obj)"&gt;
    &lt;dtml-return obj&gt;
  &lt;/dtml-let&gt;

'emulateRedirect' uses 'absolute_url'. This usually loses context
information. To have better control, 'emulateRedirectURL' can be
used. Instead of an object, it gets an URL as parameter and
redirect to that URL. The URL needs to be a host relative URL (as
e.g. returned by the URLPATH request variables.
It needs a first parameter *self* in order to access the request object.

**ATTENTION: ** The code uses undocumented internal knowledge of
Zopes publishing process. It may no longer work for some Zope versions.

**ATTENTION: ** Both form of 'emulateRedirect' may not work in a
virtual hosting environment with external URL rewriting magic.
The reason is, of course, the missing external magic.
You can use 'emulateRedirectURL' in this case, provided
you apply the magic to the URL before calling the method.
'''


from Zope import zpublisher_transactions_manager, zpublisher_validated_hook


def emulateRedirectURL(self,url,transaction='abort'):
  '''emulate a redirect for *url* (a host relative URL).

  The request variables are reset as they would be for a traversal
  to *url*. The 'base' tag is set accordingly.

  *transaction* controls what to do with the transaction:

   'abort'  -  the transaction is aborted, a new one begins

   'commit' -  the transaction is commited, a new one begins

   'keep' - the transaction is left alone.

  The object addressed by *url* is returned.
  '''

  # sanity check
  if transaction not in ('abort', 'commit', 'keep',):
    raise ValueError, 'emulateRedirect: invalid transaction parameter: %s' % transaction

  REQUEST= self.REQUEST; RESPONSE= REQUEST.RESPONSE

  # the request magic, that resets the REQUEST variables
  REQUEST.steps= []; REQUEST._steps= []; REQUEST._resetURLS()
  obj= REQUEST.traverse(url, validated_hook= zpublisher_validated_hook)
  RESPONSE.setBase(REQUEST.URL1) # URL1, because 'setBase' adds a '/'

  # now transaction handling
  if transaction == 'keep': return obj

  TM= zpublisher_transactions_manager
  getattr(TM,transaction)()
  TM.begin()
  TM.recordMetaData(obj,REQUEST)
  return obj

def emulateRedirect(obj,transaction='abort'):
  '''redirect to *obj*.'''
  emulateRedirectURL(obj,obj.absolute_url(1),transaction)
