Array ('subitem' => 'add|edit'), 'OnSaveDraft' => Array ('subitem' => 'add|edit'), 'OnUseDraft' => Array ('subitem' => 'add|edit'), 'OnDeleteDraft' => Array ('subitem' => 'add|edit'), 'OnProcessBounceMail' => Array ('subitem' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Checks user permission to execute given $event * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent &$event) { $section = $event->getSection(); $form_id = $this->Application->GetVar('form_id'); if ( $form_id ) { // copy form_id to env to be passed info upload links $this->Application->SetVar($event->getPrefixSpecial() . '_form_id', $form_id); } else { $form_id = $this->Application->GetVar($event->getPrefixSpecial() . '_form_id'); } $event->setEventParam('PermSection', $section . ':' . $form_id); return parent::CheckPermission($event); } /** * Prepares new kDBItem object * * @param kEvent $event * @return void * @access protected */ protected function OnNew(kEvent &$event) { parent::OnNew($event); $object =& $event->getObject(); /* @var $object kDBItem */ $form_submission =& $this->Application->recallObject('formsubs'); /* @var $form_submission kDBItem */ $form_submission_helper =& $this->Application->recallObject('FormSubmissionHelper'); /* @var $form_submission_helper FormSubmissionHelper */ $form =& $form_submission_helper->getForm($form_submission); /* @var $form kDBItem */ $from_email = $form->GetDBField('ReplyFromEmail'); $to_email = $form_submission_helper->getFieldByRole($form_submission, SubmissionFormField::COMMUNICATION_ROLE_EMAIL); if ( $this->Application->GetVar('client_mode') ) { // debug code for sending email from client $object->SetDBField('FromEmail', $to_email); $object->SetDBField('ToEmail', $from_email); } else { $object->SetDBField('FromEmail', $from_email); $object->SetDBField('ToEmail', $to_email); } $object->SetDBField('Cc', $form->GetDBField('ReplyCc')); $object->SetDBField('Bcc', $form->GetDBField('ReplyBcc')); $ids = $this->StoreSelectedIDs($event); if ( $ids ) { $org_message =& $this->Application->recallObject($event->Prefix . '.-item', null, Array ('skip_autoload' => true)); /* @var $org_message kDBItem */ $org_message->Load(array_shift($ids)); // client could reply from different email, so compare to admin email! if ( $org_message->GetDBField('ToEmail') == $from_email ) { // can reply only to client email, not own :) // transform subject $message_subject = $org_message->GetDBField('Subject'); if ( $message_subject ) { $object->SetDBField('Subject', $this->_transformSubject($message_subject, 'Re')); } // add signature $message_body = $form->GetDBField('ReplyMessageSignature'); if ( $org_message->GetDBField('Message') ) { // add replied marks $message_body .= '> ' . preg_replace('/([\r]*\n)/', '\\1> ', $org_message->GetDBField('Message')); } $object->SetDBField('ToEmail', $org_message->GetDBField('FromEmail')); // user client's email from reply $object->SetDBField('Message', $message_body); $object->SetDBField('ReplyTo', $org_message->GetID()); } } else { $sql = 'SELECT COUNT(*) FROM ' . $object->TableName . ' WHERE FormSubmissionId = ' . $form_submission->GetID(); $replies_found = $this->Conn->GetOne($sql); if ( !$replies_found ) { // 1st message from admin -> quote subject & text from feedback $message_subject = $form_submission_helper->getFieldByRole($form_submission, SubmissionFormField::COMMUNICATION_ROLE_SUBJECT, true); if ( $message_subject ) { $object->SetDBField('Subject', $this->_transformSubject($message_subject, 'Re')); } // add signature $message_body = $form->GetDBField('ReplyMessageSignature'); // add replied marks $original_message_body = $form_submission_helper->getFieldByRole($form_submission, SubmissionFormField::COMMUNICATION_ROLE_BODY); if ( $original_message_body ) { $message_body .= '> ' . preg_replace('/([\r]*\n)/', '\\1> ', $original_message_body); } $object->SetDBField('Message', $message_body); } } $this->clearSelectedIDs($event); } /** * Parses $search string in subject and reformats it * Used for replying and forwarding * * @param string $subject * @param string $search * @return string */ function _transformSubject($subject, $search = 'Re') { $regex = '/'.$search.'(\[([\d]+)\]){0,1}:/i'; preg_match_all($regex, $subject, $regs); if ($regs[2]) { $reply_count = 0; // reply count without numbers (equals to "re[1]") $max_reply_number = 0; // maximal reply number sort($regs[2], SORT_NUMERIC); // sort ascending (non-numeric replies first) foreach ($regs[2] as $match) { if (!$match) { // found "re:" $reply_count++; } elseif ($match > $max_reply) { // found "re:[number]" $max_reply_number = $match; } } return $search.'['.($reply_count + $max_reply_number + 1).']: '.trim(preg_replace($regex, '', $subject)); } return $search.': '.$subject; } /** * Resends reply, that was not sent last time * * @param kEvent $event */ function OnResendReply(&$event) { $ids = $this->StoreSelectedIDs($event); if (!$ids) { return ; } $object =& $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ $sql = 'SELECT f.ReplyFromEmail, sl.' . $object->IDField . ' FROM ' . $object->TableName . ' sl JOIN ' . $this->Application->getUnitOption('formsubs', 'TableName') . ' fs ON fs.FormSubmissionId = sl.FormSubmissionId JOIN ' . $this->Application->getUnitOption('form', 'TableName') . ' f ON f.FormId = fs.FormId WHERE sl.' . $object->IDField . ' IN (' . implode(',', $ids) . ')'; $reply_emails = $this->Conn->GetCol($sql, $object->IDField); foreach ($ids as $id) { $object->Load($id); // allow to send messages, that were successfully sended before :( if (($object->GetDBField('ToEmail') != $reply_emails[$id]) && ($object->GetDBField('SentStatus') != SUBMISSION_LOG_SENT)) { $object->SetOriginalField('SentStatus', 0); // reset sent status to update sent date automatically $this->_sendEmail($object); // resend email here } } $this->clearSelectedIDs($event); if (!$this->Application->GetVar('from_list')) { $event->SetRedirectParam('opener', 'u'); } } /** * Updates last operation dates for log record * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent &$event) { parent::OnBeforeItemCreate($event); $this->_validateRecipients($event); $this->_updateStatusDates($event); } /** * Updates last operation dates for log record * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent &$event) { parent::OnBeforeItemUpdate($event); $this->_validateRecipients($event); $this->_updateStatusDates($event); } /** * Validates email recipients * * @param kEvent $event */ function _validateRecipients(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ $esender =& $this->Application->recallObject('EmailSender'); /* @var $esender kEmailSendingHelper */ $cc = $object->GetDBField('Cc'); if ($cc && ($esender->GetRecipients($cc) === false)) { $object->SetError('Cc', 'invalid_format'); } $bcc = $object->GetDBField('Bcc'); if ($bcc && ($esender->GetRecipients($bcc) === false)) { $object->SetError('Bcc', 'invalid_format'); } } /** * Generates verification code and sets it inside sent message * * @param kDBItem $object * @return string */ function _generateVerificationCode(&$object) { $code = Array ( $object->GetDBField('FromEmail'), $object->GetDBField('ToEmail'), $object->GetID(), microtime(true) ); $object->SetDBField('VerifyCode', md5( implode('-', $code) )); } /** * Sends email based on fields from given submission-log record * * @param kDBItem $object */ function _sendEmail(&$object) { if ($this->Application->GetVar('client_mode')) { return ; } if (!$object->GetDBField('VerifyCode')) { $this->_generateVerificationCode($object); } $form_submission =& $this->_getFormSubmission($object); $form_submission_helper =& $this->Application->recallObject('FormSubmissionHelper'); /* @var $form_submission_helper FormSubmissionHelper */ $form =& $form_submission_helper->getForm($form_submission); $send_params = Array ( 'from_name' => $form->GetDBField('ReplyFromName'), 'from_email' => $object->GetDBField('FromEmail'), 'to_email' => $object->GetDBField('ToEmail'), 'subject' => $object->GetDBField('Subject'), 'message' => $object->GetDBField('Message'), ); $to_name = $form_submission_helper->getFieldByRole($form_submission, SubmissionFormField::COMMUNICATION_ROLE_NAME); if ($to_name) { $send_params['to_name'] = $to_name; } $esender =& $this->Application->recallObject('EmailSender'); /* @var $esender kEmailSendingHelper */ $esender->SetReturnPath( $form->GetDBField('BounceEmail') ); if ($object->GetDBField('Cc')) { $recipients = $esender->GetRecipients( $object->GetDBField('Cc') ); foreach ($recipients as $recipient_info) { $esender->AddCc($recipient_info['Email'], $recipient_info['Name']); } } if ($object->GetDBField('Bcc')) { $recipients = $esender->GetRecipients( $object->GetDBField('Bcc') ); foreach ($recipients as $recipient_info) { $esender->AddBcc($recipient_info['Email'], $recipient_info['Name']); } } if ($object->GetDBField('Attachment')) { $attachments = explode('|', $object->GetField('Attachment', 'file_paths')); foreach ($attachments as $attachment) { $esender->AddAttachment($attachment); } } $this->Application->EmailEventAdmin('FORM.SUBMISSION.REPLY.TO.USER', null, $send_params); // mark as sent after sending is finished $object->SetDBField('SentStatus', SUBMISSION_LOG_SENT); // reset bounce status before (re-)sending $object->SetDBField('BounceInfo', NULL); $object->SetDBField('BounceDate_date', NULL); $object->SetDBField('BounceDate_time', NULL); if ($object->GetDBField('DraftId')) { $temp_handler =& $this->Application->recallObject('draft_TempHandler', 'kTempTablesHandler'); /* @var $temp_handler kTempTablesHandler */ $temp_handler->DeleteItems('draft', '', Array ($object->GetDBField('DraftId'))); $object->SetDBField('DraftId', 0); } $object->Update(); } /** * Sends new email after log record was created * Updates last update time for submission * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemCreate(kEvent &$event) { parent::OnAfterItemCreate($event); $object =& $event->getObject(); /* @var $object kDBItem */ $this->_sendEmail($object); // send email $this->_updateSubmission($event); $reply_to = $object->GetDBField('ReplyTo'); if ( !$reply_to ) { $reply_to = $this->_getLastMessageId($event, !$this->Application->GetVar('client_mode')); } if ( $reply_to ) { // this is reply to other message -> mark it as replied $org_message =& $this->Application->recallObject($event->Prefix . '.-item', null, Array ('skip_autoload' => true)); /* @var $org_message kDBItem */ $org_message->Load($reply_to); $org_message->SetDBField('ReplyStatus', SUBMISSION_LOG_REPLIED); $org_message->Update(); } if ( $this->Application->GetVar('client_mode') ) { // new reply from client received -> send notification about it $this->Application->EmailEventAdmin('FORM.SUBMISSION.REPLY.FROM.USER'); } } /** * Returns last message id (client OR admin) * * @param kEvent $event * @param bool $from_client * @return int */ function _getLastMessageId(&$event, $from_client = false) { $object =& $event->getObject(); /* @var $object kDBItem */ $form_submission =& $this->_getFormSubmission($object); $form_submission_helper =& $this->Application->recallObject('FormSubmissionHelper'); /* @var $form_submission_helper FormSubmissionHelper */ $form =& $form_submission_helper->getForm($form_submission); $reply_email = $form->GetDBField('ReplyFromEmail'); $sql = 'SELECT MAX(' . $object->IDField . ') FROM ' . $object->TableName . ' WHERE (FormSubmissionId = ' . $form_submission->GetID() . ') AND (ToEmail' . ($from_client ? ' = ' : ' <> ') . $this->Conn->qstr($reply_email) . ')'; return $this->Conn->GetOne($sql); } /** * Updates last update time for submission * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemUpdate(kEvent &$event) { parent::OnAfterItemUpdate($event); $this->_updateSubmission($event); $object =& $event->getObject(); /* @var $object kDBItem */ // send out email event to admin for bouncing $sent_status = $object->GetDBField('SentStatus'); if ( $object->GetOriginalField('SentStatus') != $sent_status && $sent_status == SUBMISSION_LOG_BOUNCE ) { $this->Application->EmailEventAdmin('FORM.SUBMISSION.REPLY.FROM.USER.BOUNCED'); } } /** * Sets last sent/reply dates based on field changes in log record * * @param kEvent $event */ function _updateStatusDates(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ $now = adodb_mktime(); $sent_status = $object->GetDBField('SentStatus'); if (($event->Special != 'merge') && ($sent_status == SUBMISSION_LOG_SENT) && ($sent_status != $object->GetOriginalField('SentStatus'))) { // sent status was set $object->SetDBField('SentOn_date', $now); $object->SetDBField('SentOn_time', $now); } $reply_status = $object->GetDBField('ReplyStatus'); if (($reply_status == SUBMISSION_LOG_REPLIED) && ($reply_status != $object->GetOriginalField('ReplyStatus'))) { // sent status was set $object->SetDBField('RepliedOn_date', $now); $object->SetDBField('RepliedOn_time', $now); } } /** * Returns form submission by given event of submission log * * @param kDBItem $object * @return kDBItem */ function &_getFormSubmission(&$object) { $submission_id = $object->GetDBField('FormSubmissionId'); $form_submission =& $this->Application->recallObject('formsubs.-item', null, Array ('skip_autoload' => true)); /* @var $form_submission kDBItem */ if ($form_submission->isLoaded() && ($form_submission->GetID() == $submission_id)) { // already loaded AND has needed id return $form_submission; } $form_submission->Load($submission_id); return $form_submission; } /** * Sets last updated field for form submission * * @param kEvent $event */ function _updateSubmission(&$event) { $object =& $event->getObject(); /* @var $object kDBItem */ $form_submission =& $this->_getFormSubmission($object); // 1. set last updated $last_updated = max ($object->GetDBField('SentOn'), $object->GetDBField('RepliedOn')); if ($form_submission->GetDBField('LastUpdatedOn') < $last_updated) { // don't set smaller last update, that currenly set $form_submission->SetDBField('LastUpdatedOn_date', $last_updated); $form_submission->SetDBField('LastUpdatedOn_time', $last_updated); } // 2. update submission status $form_submission_helper =& $this->Application->recallObject('FormSubmissionHelper'); /* @var $form_submission_helper FormSubmissionHelper */ $form =& $form_submission_helper->getForm($form_submission); $client_responce = $form->GetDBField('ReplyFromEmail') == $object->GetDBField('ToEmail'); $replied = $object->GetDBField('ReplyStatus') == SUBMISSION_LOG_REPLIED; if (!$client_responce && !$replied) { // admin sends new email to client $form_submission->SetDBField('LogStatus', SUBMISSION_REPLIED); } elseif ($client_responce) { // client email becomes replied OR receiving new unreplied email from client $form_submission->SetDBField('LogStatus', $replied ? SUBMISSION_REPLIED : SUBMISSION_NEW_EMAIL); } if ($object->GetDBField('SentStatus') == SUBMISSION_LOG_BOUNCE) { // propagate bounce status from reply $form_submission->SetDBField('LogStatus', SUBMISSION_BOUNCE); } $form_submission->Update(); } /** * Saves current unsent message as draft * * @param kEvent $event */ function OnSaveDraft(&$event) { $object =& $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ $draft =& $this->Application->recallObject('draft', null, Array('skip_autoload' => true)); /* @var $draft kDBItem */ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); if ($items_info) { foreach ($items_info as $id => $field_values) { $object->setID($id); $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); $load_keys = Array ( 'FormSubmissionId' => $object->GetDBField('FormSubmissionId'), 'CreatedById' => $this->Application->RecallVar('user_id'), ); // get existing draft for given submission and user $draft->Load($load_keys); $draft->SetDBField('Message', $object->GetDBField('Message')); if ($draft->isLoaded()) { $draft->Update(); } else { $draft->SetDBFieldsFromHash($load_keys); $draft->Create(); } } } $this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', 'OnCreate'); $event->SetRedirectParam('opener', 'u'); } /** * Uses found draft instead of submission reply body * * @param kEvent $event */ function OnUseDraft(&$event) { $object =& $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ $draft =& $this->Application->recallObject('draft', null, Array('skip_autoload' => true)); /* @var $draft kDBItem */ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); if ($items_info) { foreach ($items_info as $id => $field_values) { $object->setID($id); $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); $load_keys = Array ( 'FormSubmissionId' => $object->GetDBField('FormSubmissionId'), 'CreatedById' => $this->Application->RecallVar('user_id'), ); // get existing draft for given submission and user $draft->Load($load_keys); if ($draft->isLoaded()) { $object->SetDBField('Message', $draft->GetDBField('Message')); $object->SetDBField('DraftId', $draft->GetID()); } } } $this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', 'OnCreate'); $event->redirect = false; } /** * Deletes draft, that matches given user and form submission * * @param kEvent $event */ function OnDeleteDraft(&$event) { $object =& $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ $draft =& $this->Application->recallObject('draft', null, Array('skip_autoload' => true)); /* @var $draft kDBItem */ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); if ($items_info) { foreach ($items_info as $id => $field_values) { $object->setID($id); $object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values)); $object->SetDBField('DraftId', 0); $load_keys = Array ( 'FormSubmissionId' => $object->GetDBField('FormSubmissionId'), 'CreatedById' => $this->Application->RecallVar('user_id'), ); // get existing draft for given submission and user $draft->Load($load_keys); if ($draft->isLoaded()) { $temp_handler =& $this->Application->recallObject('draft_TempHandler', 'kTempTablesHandler'); /* @var $temp_handler kTempTablesHandler */ $temp_handler->DeleteItems('draft', '', Array ($draft->GetID())); } } } $this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', 'OnCreate'); $event->redirect = false; } }