Patch/mod: insert form values into mail

Ask your feature Requests about CKforms component

Patch/mod: insert form values into mail

Postby Cyberwizzard » Fri Sep 11, 2009 6:35 pm

I needed to be able to insert the form values into the mail sent to the user ("Hello Mister X, thank you for visiting my site....") and I rewrote the model class to be able to do that.

Also, because the extraction of parameters from the string in the database was done using unnamed and reused variables, I lifted the logic into a seperate function (getAttr) which uses regular expressions to grab everything in one go.

Usage: adding the form data works as it used to, enable to append to the mail body. Insert values into the text by placing {fieldname} tags in the mail body. For example:
Hello {name}, You selected the following packets: {packet}. Regards....

Inserting the IP address, date and form name can be done with the {ckforms_ip}, {ckforms_date} and {ckforms_title} tags.

components/com_ckforms/models/ckforms.php:
Code: Select all
<?php
/**
* Ckforms for CK Forms Component
*
* @package    CK.Joomla
* @subpackage Components
* @link http://cookex.eu
* @license      GNU/GPL
*/

// Check to ensure this file is included in Joomla!
defined('_JEXEC') or die();

jimport( 'joomla.application.component.model' );

/**
* Ckforms Model
*
* @package    CK.Joomla
* @subpackage Components
*/
class CkformsModelCkforms extends JModel {
   var $_data;
   
   /**
    * Retrieves the form data
    * @return array Array of objects containing the data from the database
    */
   function getData() {
      $array = JRequest::getVar('id',  0, '', 'array');
      $id=(int)$array[0];
         
      $query = 'SELECT * FROM #__ckforms where id=' . $id;
                  
      $this->_db->setQuery( $query );
      $this->_data = $this->_db->loadObject();

      $query = 'SELECT * FROM #__ckfields where fid='.$id." and published=1 order by ordering asc";
      $fields = $this->_getList( $query );
      
      $n=count($fields );
      for ($i=0; $i < $n; $i++) {
         // Fetch all values
         $attribs = self::getAttr($fields[$i]->defaultvalue);
         
         switch ($fields[$i]->typefield)
         {
            case 'text':
               $fields[$i]->t_initvalueT = ( isset($attribs['t_initvalueT']) ? $attribs['t_initvalueT'] : '');
               $fields[$i]->t_maxchar = ( isset($attribs['t_maxchar']) ? $attribs['t_maxchar'] : '');
               $fields[$i]->t_texttype = ( isset($attribs['t_texttype']) ? $attribs['t_texttype'] : '');
               $fields[$i]->t_minchar = ( isset($attribs['t_minchar']) ? $attribs['t_minchar'] : '');
               break;
   
            case 'hidden':
               $fields[$i]->t_initvalueH = ( isset($attribs['t_initvalueH']) ? $attribs['t_initvalueH'] : '');
               $fields[$i]->t_filluid = ( isset($attribs['t_filluid']) ? $attribs['t_filluid'] : '');
               break;
               
            case 'textarea':
               $fields[$i]->t_initvalueTA = ( isset($attribs['t_initvalueTA']) ? $attribs['t_initvalueTA'] : '');
               $fields[$i]->t_HTMLEditor = ( isset($attribs['t_HTMLEditor']) ? $attribs['t_HTMLEditor'] : '');
               $fields[$i]->t_columns = ( isset($attribs['t_columns']) ? $attribs['t_columns'] : '');
               $fields[$i]->t_rows = ( isset($attribs['t_rows']) ? $attribs['t_rows'] : '');
               $fields[$i]->t_wrap = ( isset($attribs['t_wrap']) ? $attribs['t_wrap'] : '');
               $fields[$i]->t_maxchar = ( isset($attribs['t_maxchar']) ? $attribs['t_maxchar'] : '');
               $fields[$i]->t_minchar = ( isset($attribs['t_minchar']) ? $attribs['t_minchar'] : '');
               break;
   
            case 'checkbox':
               $fields[$i]->t_initvalueCB = ( isset($attribs['t_initvalueCB']) ? $attribs['t_initvalueCB'] : '');
               $fields[$i]->t_checkedCB = ( isset($attribs['t_checkedCB']) ? $attribs['t_checkedCB'] : '');
               break;
               
            case 'radiobutton':
               $fields[$i]->t_listHRB = ( isset($attribs['t_listHRB']) ? $attribs['t_listHRB'] : '');
               $fields[$i]->t_displayRB = ( isset($attribs['t_displayRB']) ? $attribs['t_displayRB'] : '');
               break;

            case 'select':
               $fields[$i]->t_multipleS = ( isset($attribs['t_multipleS']) ? $attribs['t_multipleS'] : '');
               $fields[$i]->t_heightS = ( isset($attribs['t_heightS']) ? $attribs['t_heightS'] : '');
               $fields[$i]->t_listHS = ( isset($attribs['t_listHS']) ? $attribs['t_listHS'] : '');
               break;

            case 'button':
               $fields[$i]->t_typeBT = ( isset($attribs['t_typeBT']) ? $attribs['t_typeBT'] : '');
               break;
         }               
      
      }            

      $this->_data->fields = $fields;
      
      return $this->_data;
   }
   
   /**
    * Save Hits
    * @return void
    */
   function addHits() {
      $dba   =& JFactory::getDBO();
      $ckform = $this->getData();
      $query = " update #__ckforms set hits = ".($ckform->hits + 1). " where id = ".$ckform->id;
      $dba->Execute($query);      
   }
   
   /**
    * Save data
    * @return void
    */
   function saveData($post) {      
      $ckform = $this->getData();
      
      // Handle uploaded files
      if (file_exists ($ckform->uploadpath) == true) {
         $nb_uploaded_file = 0;
         $n=count($ckform->fields );
         // Loop over all fields to find the uploads
         for ($i=0; $i < $n; $i++) {   
            $field = $ckform->fields[$i];
            if ($field->typefield == 'fileupload') {
               // Found an uploaded file
               $target_fu_path = $ckform->uploadpath . basename($_FILES[$field->name]['name']);
               
               if(move_uploaded_file($_FILES[$field->name]['tmp_name'], $target_fu_path)) {
                  $uploaded_file[$nb_uploaded_file] = $target_fu_path;
                  $nb_uploaded_file++;                  
               }            
            }            
         }
      }

      // Insert record into database if enabled
      if ($ckform->saveresult == 1) {         
         $dba   =& JFactory::getDBO();
         $query = 'insert into #__ckforms_'.$ckform->id."(" ;
         
         $n=count($ckform->fields);
         for ($i=0; $i < $n; $i++)
            $query .= $ckform->fields[$i]->name.",";

         $query = $query."created,ipaddress) values (";

         for ($i=0; $i < $n; $i++) {   
            $field = $ckform->fields[$i];
            
            if ($field->typefield == 'fileupload') {
               $fieldValue = $ckform->uploadpath . basename($_FILES[$field->name]['name']);
            } else if (isset($post[$field->name])){
               $fieldValue = $post[$field->name];
            } else {
               $fieldValue = '';
            }
            
            if (is_array ($fieldValue))   $fieldValue = implode(',',$fieldValue);
            $query .= "'".addslashes($fieldValue)."',";
         }
         
         $query .= "'".date("Y-m-d H:i:s")."','".$_SERVER['REMOTE_ADDR']."')";

         $dba->Execute($query);
      }      
      
      /* ************************* */
      /*     Send Email Result     */
      /* ************************* */
      if ($ckform->emailresult == 1) {
         $mail =& JFactory::getMailer();
         $mail->CharSet = "utf-8";
      
         $mailBody = "Form : ".$ckform->title." [".$ckform->name."]<br/>\n";
         $mailBody = $mailBody."registered at ".date("Y-m-d H:i:s")."<br/><br/>\n\n";

         $n=count($ckform->fields);
         // Loop over all fields to include them in the mail
         for ($i=0; $i < $n; $i++) {   
            // Grab the field by index
            $field = $ckform->fields[$i];
            // Load the value if it exists
            $fieldValue = (isset($post[$field->name]) ? $post[$field->name] : '');
            // Convert arrays to CSV
            if (is_array($fieldValue)) $fieldValue = implode(',', $fieldValue);

            // If this is a mail value, convert it into a link
            if ($field->typefield == 'text')
               if (self::getAttr($field->defaultvalue, 't_texttype') == 'email')
                  $fieldValue = '<a href="mailto:'.$fieldValue.'">'.$fieldValue.'</a>';
   
            $mailBody .= $field->name . " : " . $fieldValue . "<br/>\n";
         }
         $mailBody .= JText::_( 'IP Address' ) . " : " . $_SERVER['REMOTE_ADDR'] . "<br/>\n";
         
         // Add the mail targets, explode CSV into arrays
         if ($ckform->emailto != "")
            $mail->addRecipient( explode(",", $ckform->emailto) );
         if ($ckform->emailcc != "")
            $mail->addCC( explode(",", $ckform->emailcc) );
         if ($ckform->emailbcc != 0)
            $mail->addBCC( explode(",", $ckform->emailbcc) );
         
         // Set other info
         $mail->setSender( array( $ckform->emailfrom, "" ) );
         $mail->setSubject( $ckform->subject );
         $mail->setBody( $mailBody );

         $mail->IsHTML (true);
         
         // Add attachments if desired
         if (Isset($nb_uploaded_file) && $ckform->emailresultincfile == "1")
            for ($i=0; $i < $nb_uploaded_file; $i++)
               $mail->addAttachment($uploaded_file[$i]);
         
         $sent = $mail->Send();
      }      
      
      /* ************************** */
      /*     Send Email Receipt     */
      /* ************************** */
      if ($ckform->emailreceipt == 1) {
         $IsSendMail = false;
         $emailReceiptTo = '';
         
         $mail =& JFactory::getMailer();
         $mail->CharSet = "utf-8";
      
         $mailBody = $ckform->emailreceipttext;
         
         $formData = 'Form: {ckforms_title}<br/>\n';
         $formData .= 'Date: {ckforms_date}<br/>\n';
         $formData .= JText::_( 'IP Address' ) . " : {ckforms_ip}<br/><br/>\n";

         $n = count($ckform->fields);
         // Iterate of all fields to find the first email field and add each field to the form dump
         for ($i=0; $i < $n; $i++) {   
            $field = $ckform->fields[$i];
            
            if ($field->typefield == 'text') {
               $type = self::getAttr($field->defaultvalue, 't_texttype');
               if($type == 'email') {
                     $IsSendMail = true;
                     $emailReceiptTo = $post[$field->name];
               }               
            }
            
            // Now add it to the form dump, note that we do not yet insert the value here
            $formData .= $field->name . ": {" . $field->name . "}<br/>\n";
         }
         
         // Start substitution by checking if we want the form dump in the first place, if so, append to message
         if ($ckform->emailreceiptincfield == 1)
            $mailBody .= $formData;
         
         // Loop over all fields to insert all values
         for ($i=0; $i < $n; $i++) {   
            $field = $ckform->fields[$i];
            
            // See if we have a value for this field
            $fieldValue = (isset($post[$field->name]) ? $post[$field->name] : '');
            
            // If it is an array, make it a comma seperated list
            if (is_array ($fieldValue)) $fieldValue = implode(',', $fieldValue);
            
            // Now replace the value markers by their value
            $mailBody = str_replace('{'.$field->name.'}', $fieldValue, $mailBody);
         }
         
         // Replace the generated fields: ckforms_title, ckforms_ip and ckforms_date
         $mailBody = str_replace('{ckforms_title}', $ckform->title, $mailBody);
         $mailBody = str_replace('{ckforms_date}', date("Y-m-d H:i:s"), $mailBody);
         $mailBody = str_replace('{ckforms_ip}', $_SERVER['REMOTE_ADDR'], $mailBody);

         // Add attachements if desired            
         if (isset($nb_uploaded_file) && $ckform->emailreceiptincfile == "1")
            for ($i=0; $i < $nb_uploaded_file; $i++)
               $mail->addAttachment($uploaded_file[$i]);
         
         // Check if we have a recipient and we still want to send the mail
         if ($emailReceiptTo != "" && $IsSendMail == true) {
            $mail->addRecipient($emailReceiptTo);
                  
            $mail->setSender( array( $ckform->emailfrom, "" ) );
            $mail->setSubject( $ckform->emailreceiptsubject );
            $mail->setBody( $mailBody );
      
            $mail->IsHTML (true);
         
            $sent = $mail->Send();
         }
      }      
      
   }
   
   /**
    * Extract a named attribute from the given raw string. The format is:
    * t_initvalueT===[--]t_maxchar===128[--]t_texttype===email[--]t_minchar===6
    *
    * Returns null when a value is not found or an empty string when its empty.
    *
    * If no name is given (name == null) the complete array of name and value pairs will be returned as an array
    */
   static function getAttr($raw, $attributeName = null) {
                if(!is_string($raw) || trim($raw) == '') return '';
      // The following regular expression cuts out all attribute name and value pairs
      $reg = "/([A-Za-z_]+)===([^\[]*)/";
      // Regex the values out, note that the m array will hold the following sub arrays:
      // 0: Full pattern match
      // 1: First subpattern match aka the names
      // 2: Second subpattern match aka the values
      if (preg_match_all($reg, $raw, $m) !== false) {
         // Test if we have a name
         if (is_null($attributeName)) {
            // Return the full result
            return array_combine($m[1], $m[2]);
         }
         
         // Fetch the index of the given name in the results
         $id = array_search($attributeName, $m[1]);
         if($id === false) return null;
         // Found it, return the value
         return $m[2][$id];
      } else {
         // Something went wrong parsing the string
         return null;
      }
   }

}


Edit: the source above has the fix for the problem mentioned below.
Last edited by Cyberwizzard on Thu Sep 17, 2009 9:12 pm, edited 1 time in total.
Cyberwizzard
 
Posts: 7
Joined: Fri Sep 11, 2009 6:26 pm
Top

Re: Patch/mod: insert form values into mail

Postby Brandy » Sat Sep 12, 2009 9:03 am

First of all thank you for that patch. Works great - except that I encounter an error when sending the mail (even the mails are sent as expected):

Warning: array_combine() [function.array-combine]: Both parameters should have at least 1 element in .../components/com_ckforms/models/ckforms.php on line 314

This message appears 12x on a form with 19 fields (running PHP5).
Brandy
 
Posts: 13
Joined: Fri Sep 04, 2009 7:39 am
Top

Re: Patch/mod: insert form values into mail

Postby Cyberwizzard » Sat Sep 12, 2009 12:12 pm

In fact thats quite possible: the regex was tested on one of my sites which worked like a charm, it might be insufficient given other environments :)

Lets try this to create debug output, replace this with (line 314):
Code: Select all
return array_combine($m[1], $m[2]);


With:

Code: Select all
if(!is_array($m[1]) || !is_array($m[2]) || count($m[1]) != count($m[2])) {
                   echo "<pre>";
                   echo "Raw: " . $raw . "\n";
                   print_r($m);
                   echo "</pre>";
                   exit;
                }
                return array_combine($m[1], $m[2]);


In the code above I look for errors in possibly not ending up with an array or with a count mismatch. Both shouldn't happen but 'should not' and 'can not' are 2 different things :)

Please rerun the form post with that and post back what the result is.

If for some reason you do not get the debug output, remove the if statement (don't forget the closing bracket) and run it again to force a dump.
Cyberwizzard
 
Posts: 7
Joined: Fri Sep 11, 2009 6:26 pm
Top

Re: Patch/mod: insert form values into mail

Postby Brandy » Sat Sep 12, 2009 10:32 pm

ok, I added the debug script and with the IF I encountered the same error, but without I got this here

Code: Select all
Raw: t_multipleS===[--]t_heightS===[--]t_listHS===optrb0==Anfrage||Anfrage [default][-]optrb1==Reservierung||Reservierung[-]optrb2==Mitteilung||Mitteilung[-]optrb3==Sonstiges||Sonstiges
Array
(
    [0] => Array
        (
            [0] => t_multipleS===
            [1] => t_heightS===
            [2] => t_listHS===optrb0==Anfrage||Anfrage
        )

    [1] => Array
        (
            [0] => t_multipleS
            [1] => t_heightS
            [2] => t_listHS
        )

    [2] => Array
        (
            [0] =>
            [1] =>
            [2] => optrb0==Anfrage||Anfrage
        )

)



But there are still lots of other things which I didn't like.
One of them is that the original mail (with all fields) is sent to me and only your well formatted mail is sent to the form filler. I would like that all mails are the same, because my client would like readable well formatted mails.

One other thing is that form fillers mail address is not the sender address for my mail so a response is not possible. There is a solution in the newest CKForms version that the mail contains a MAILTO link, but my client would like to click on ANSWER in his Outlook to send the response with the original mail content.
Brandy
 
Posts: 13
Joined: Fri Sep 04, 2009 7:39 am
Top

Re: Patch/mod: insert form values into mail

Postby Cyberwizzard » Sun Sep 13, 2009 12:26 pm

I'll have a look later on the data dump as it looks fine to me and without the 'if' it is probably a valid field that you got there.

You could try to remove the exit statement so it will continue to see which field throws the error.

On a side note: there is no way to format the email to the admin as there is no field for that. Also, most users don't want to receive the client version of the mail as it most likely will not have all information.

In your case, you could hardcode a CC address into the last bit of the mail sending function so your admin will receive a copy.

Also, the message sent to the admin is not meant to be replied. In case sensitive information was part of the form, replying to it means sending it to the client. That is why there is a link to click. Making the mail reply-able is fairly easy though: add a reply-to address to the admin mail. Hitting reply will then address the new mail to the user.
Cyberwizzard
 
Posts: 7
Joined: Fri Sep 11, 2009 6:26 pm
Top

Re: Patch/mod: insert form values into mail

Postby Brandy » Thu Sep 17, 2009 7:28 am

Sorrry for the delay, but I had some hardware problems.

I tried it without the EXIT and here you'll find the result:
http://transfer.brandenburg-online.biz/debug_output.pdf

I put it to an PDF after it is quite a long output.

I will test your other hints (reply to sender and formatted mail to form owner). But for me it is normal that if a homepage owner will receive also well looking mails and will reply them. E.g. in my case, it is a hotel where guests can make a reservation or only a simple request. The guy in the hotel will also a well looking mail and he wants to reply directly sending the guest his data as well so that they know what they are talking about (normally a guest sends mails to other hotels as well).

And there are lots of other cases where it is handled like that. Another example: You send a mail to a shop asking for something. Will you not get an answer with your mail at the bottom?? I will.
Brandy
 
Posts: 13
Joined: Fri Sep 04, 2009 7:39 am
Top

Re: Patch/mod: insert form values into mail

Postby Cyberwizzard » Thu Sep 17, 2009 3:22 pm

I get your point but since the current CKForms plugin has no field to format the mail, it is impossible to do that. The only other solution is to hardcode the mail format into the plugin and process it like the one sent to the customer.

I suggest you post a formal feature request for that :)

On the other hand, he might like to reply to the email but the customer never sent an email. So technically you are responding to an automated message which also includes stuff like IP address and such.

I'll look to the PDF dump later to figure out what is going on, thanks for posting that :)
Cyberwizzard
 
Posts: 7
Joined: Fri Sep 11, 2009 6:26 pm
Top

Re: Patch/mod: insert form values into mail

Postby Cyberwizzard » Thu Sep 17, 2009 3:27 pm

Scratch that: sometimes the imput is empty, hence the errors (weird btw but whatever).

Insert the following 'if' in the getAttr function:
Code: Select all
if(!is_string($raw) || trim($raw) == '') return '';


So it becomes:
Code: Select all
   static function getAttr($raw, $attributeName = null) {
      if(!is_string($raw) || trim($raw) == '') return '';
      // The following regular expression cuts out all attribute name and value pairs
      $reg = "/([A-Za-z_]+)===([^\[]*)/";
      // Regex the values out, note that the m array will hold the following sub arrays:
      // 0: Full pattern match
      // 1: First subpattern match aka the names
      // 2: Second subpattern match aka the values
      if (preg_match_all($reg, $raw, $m) !== false) {


The errors should be gone now :)
Cyberwizzard
 
Posts: 7
Joined: Fri Sep 11, 2009 6:26 pm
Top

Re: Patch/mod: insert form values into mail

Postby Brandy » Thu Sep 17, 2009 5:38 pm

ok, now it works.

Thank you.
Brandy
 
Posts: 13
Joined: Fri Sep 04, 2009 7:39 am
Top

Re: Patch/mod: insert form values into mail

Postby Brandy » Thu Sep 17, 2009 9:06 pm

BTW I managed it to send mails with sender address as recpient and vice versa as I mentioned it in one of my entries. It is not hardcoded and was absolutely easy to do.

I unchecked the Email result option in CKForms component but entered the mail addresses in their fields because they are still used for my solution.
Now it works like this:

- Customer "C" fills out the form: he gets a mail with form owner address "O" as sender and his address as recipient.
- "O" gets a mail with "C" as sender and his address as recipient. "O" can directly reply to "C".

And: Both get the formatted mail described in this thread.

For me it is the best solution after I need no IP, etc.
Brandy
 
Posts: 13
Joined: Fri Sep 04, 2009 7:39 am
Top

Next

Return to Feature Requests

Who is online

Users browsing this forum: No registered users and 3 guests