Answers.Body: <p>You are correct that <code>postProcess</code> runs too late. Consider the <a href="https://wiki.civicrm.org/confluence/display/CRMDOC/QuickForm+Reference#QuickFormReference-RequestLifecycle" rel="nofollow noreferrer">conventional request lifecycle for HTML_QuickForm/CRM_Core_Form</a>:</p>
  
  <p><a href="https://i.sstatic.net/9LMNl.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/9LMNl.png" alt="Overall Request Lifecycle in CRM_Core_Form"></a></p>
  
  <p>At the end of the <code>Validate</code> phase, it makes a major decision about whether to rerender the form with errors. However, the <code>postProcess</code> function is part of the <code>Process</code> phase which should only run on valid forms.</p>
  
  <p>The conventional way to set a validation error in <code>HTML_QuickForm</code> is to register a validation function. For example, in <a href="https://github.com/civicrm/civicrm-core/blob/4.7.10/CRM/Group/Form/Edit.php#L296" rel="nofollow noreferrer"><code>CRM_Group_Form_Edit::buildQuickForm()</code></a>, note how it calls <code>addFormRule(...)</code> (and how the <code>static function formRule()</code> conditionally returns an array of errors). This runs at the correct time.</p>
  
  <p>For mitigations, you might consider:</p>
  
  <ul>
  <li>Add some checks in the validation phase to predict whether the error condition will arise. (Ex: If the issue is a missing dependency or inadequate RAM, then validate the dependency or RAM beforehand.)</li>
  <li>Report the error via <code>CRM_Core_Error::fatal()</code>, <code>throw new Exception()</code>, or <code>CRM_Core_Error::statusBounce()</code>.</li>
  <li>Don't rely on <code>HTML_QuickForm</code> as the main form processor. Implement client-side logic with Javascript and AJAX callbacks.</li>
  <li>(Evil but possibly working) Do the heavy lifting during the validate phase. (This will, e.g., make it hard for third-parties to hook into the form... but it may work...)</li>
  <li>(Evil but possibly working) Dig into the mechanics of the <code>CRM_Core_Form</code> lifecycle and figure out a way to short-circuit/cross-wire the phases for your controller.</li>
  </ul>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-08-22T06:13:51.597Z
Answers.Text: "Do the heavy lifting during the validate phase." - I will pick this approach, I'll generate the PDF at validation phase and serve it via HTTP at submission.
Answers.UserDisplayName: Aron Novak
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-08-19T17:59:26.390Z
Answers.LastActivityDate: 2016-08-19T17:59:26.390Z
Answers.UserDisplayName: Tim Otten
Body: <p>I have a class with a signature:</p>
  
  <p>class CRM_Contact_Form_Task_FooPdf extends CRM_Contact_Form_Task {</p>
  
  <p>What generates a PDF. During the PDF generation, there might be an error. This task also uses the standard mail template:</p>
  
  <pre><code>public function buildQuickForm() {
    // Add message templates.
    CRM_Mailing_BAO_Mailing::commonCompose($this);
    CRM_Core_Resources::singleton()-&gt;addScriptFile('civicrm', 'templates/CRM/Contact/Form/Task/EmailCommon.js', 0, 'html-header');
    $this-&gt;addDefaultButtons(ts('Continue'), 'done', 'cancel');
  }
  </code></pre>
  
  <p>It works totally fine. It shows up among the actions on a search form, when I select it, the template UI shows up, and i have access to the data. The only issue is that during the PDF generation, sometimes I need to raise an error, which is not a fatal error, but something that the user must correct himself.
  At the PDF generation, i am in the \CRM_Core_Form::postProcess() phase. Can I set a form error and re-display the form with the submitted values?</p>
  
  <p>Issuing this (and of course skip to stream the PDF) does not work, as I am redirected simply to the main admin page of CiviCRM, the form is not re-generated:</p>
  
  <pre><code>$this-&gt;setElementError('text_message', 'moo');
  </code></pre>
  
  <p>But I guess it's because i am already at the postprocessing.</p>
  
Tags: <mailing><quickform>
Title: How to raise a form error in \CRM_Core_Form::postProcess()
ThreadText: # How to raise a form error in \CRM_Core_Form::postProcess()
  
  (Asked by: username_0 on 2016-08-19)
  
  I have a class with a signature:
  class CRM_Contact_Form_Task_FooPdf extends CRM_Contact_Form_Task {
  What generates a PDF. During the PDF generation, there might be an error. This task also uses the standard mail template:
  public function buildQuickForm() {
    // Add message templates.
    CRM_Mailing_BAO_Mailing::commonCompose($this);
    CRM_Core_Resources::singleton()->addScriptFile('civicrm', 'templates/CRM/Contact/Form/Task/EmailCommon.js', 0, 'html-header');
    $this->addDefaultButtons(ts('Continue'), 'done', 'cancel');
  }
  
  It works totally fine. It shows up among the actions on a search form, when I select it, the template UI shows up, and i have access to the data. The only issue is that during the PDF generation, sometimes I need to raise an error, which is not a fatal error, but something that the user must correct himself.
  At the PDF generation, i am in the \CRM_Core_Form::postProcess() phase. Can I set a form error and re-display the form with the submitted values?
  Issuing this (and of course skip to stream the PDF) does not work, as I am redirected simply to the main admin page of CiviCRM, the form is not re-generated:
  $this->setElementError('text_message', 'moo');
  
  But I guess it's because i am already at the postprocessing.
  
  **Tags:** mailing, quickform
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2016-08-19. Score: 1 | Accepted answer
  
  You are correct that `postProcess` runs too late. Consider the [conventional request lifecycle for HTML\_QuickForm/CRM\_Core\_Form](https://wiki.civicrm.org/confluence/display/CRMDOC/QuickForm+Reference#QuickFormReference-RequestLifecycle):
  
  [![Overall Request Lifecycle in CRM_Core_Form](https://i.sstatic.net/9LMNl.png)](https://i.sstatic.net/9LMNl.png)
  
  At the end of the `Validate` phase, it makes a major decision about whether to rerender the form with errors. However, the `postProcess` function is part of the `Process` phase which should only run on valid forms.
  
  The conventional way to set a validation error in `HTML_QuickForm` is to register a validation function. For example, in [`CRM_Group_Form_Edit::buildQuickForm()`](https://github.com/civicrm/civicrm-core/blob/4.7.10/CRM/Group/Form/Edit.php#L296), note how it calls `addFormRule(...)` (and how the `static function formRule()` conditionally returns an array of errors). This runs at the correct time.
  
  For mitigations, you might consider:
  
  * Add some checks in the validation phase to predict whether the error condition will arise. (Ex: If the issue is a missing dependency or inadequate RAM, then validate the dependency or RAM beforehand.)
  * Report the error via `CRM_Core_Error::fatal()`, `throw new Exception()`, or `CRM_Core_Error::statusBounce()`.
  * Don't rely on `HTML_QuickForm` as the main form processor. Implement client-side logic with Javascript and AJAX callbacks.
  * (Evil but possibly working) Do the heavy lifting during the validate phase. (This will, e.g., make it hard for third-parties to hook into the form... but it may work...)
  * (Evil but possibly working) Dig into the mechanics of the `CRM_Core_Form` lifecycle and figure out a way to short-circuit/cross-wire the phases for your controller.
  
  > **Comments:**
  > **username_0**: "Do the heavy lifting during the validate phase." - I will pick this approach, I'll generate the PDF at validation phase and serve it via HTTP at submission.  
  
  ---
  
  