Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Sun, Feb 2, 10:23 PM

in-portal

Index: branches/5.1.x/system/user_files/submission_log
===================================================================
--- branches/5.1.x/system/user_files/submission_log (nonexistent)
+++ branches/5.1.x/system/user_files/submission_log (revision 13400)
Property changes on: branches/5.1.x/system/user_files/submission_log
___________________________________________________________________
Added: bugtraq:logregex
## -0,0 +1,2 ##
+(?:[Bb]ugs?|[Ii]ssues?|[Rr]eports?|[Ff]ixe?s?|[Rr]esolves?)+\s+(?:#?(?:\d+)[,\.\s]*)+
+(\d+)
\ No newline at end of property
Added: bugtraq:url
## -0,0 +1 ##
+http://tracker.in-portal.org/view.php?id=%BUGID%
\ No newline at end of property
Added: svn:ignore
## -0,0 +1 ##
+*.*
Index: branches/5.1.x/core/units/form_fields
===================================================================
--- branches/5.1.x/core/units/form_fields (revision 13399)
+++ branches/5.1.x/core/units/form_fields (nonexistent)
Property changes on: branches/5.1.x/core/units/form_fields
___________________________________________________________________
Deleted: bugtraq:logregex
## -1,2 +0,0 ##
-(?:[Bb]ugs?|[Ii]ssues?|[Rr]eports?|[Ff]ixe?s?|[Rr]esolves?)+\s+(?:#?(?:\d+)[,\.\s]*)+
-(\d+)
\ No newline at end of property
Deleted: bugtraq:url
## -1 +0,0 ##
-http://tracker.in-portal.org/view.php?id=%BUGID%
\ No newline at end of property
Index: branches/5.1.x/core/units/helpers/mime_decode_helper.php
===================================================================
--- branches/5.1.x/core/units/helpers/mime_decode_helper.php (revision 13399)
+++ branches/5.1.x/core/units/helpers/mime_decode_helper.php (revision 13400)
@@ -1,482 +1,496 @@
<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+ defined('FULL_PATH') or die('restricted access!');
/**
* The MIME decoding class
*
*/
class MimeDecodeHelper extends kHelper {
/**
* Contains headers part of email message
*
* @var string
*/
var $_headerPart;
/**
* Contains body part of email message
*
* @var string
*/
var $_bodyPart;
/**
* Last parsing error message (if any)
*
* @var string
*/
var $_lastErrorMessage = '';
/**
* Decode message headers
*
* @var bool
*/
var $_decodeHeaders = false;
/**
* Include email body in decoded result
*
* @var bool
*/
var $_includeBodies = true;
/**
* Decode email body (only in case, when it will be included in result)
*
* @var bool
*/
var $_decodeBodies = false;
/**
* Displays parsing error
*
* @param string $str
*/
function raiseError($str)
{
trigger_error('Error during email parsing: ' . $str, E_USER_WARNING);
}
/**
* Initializes mime parsing using given email message
*
* @param string $message
*/
function InitHelper($message = null)
{
if (!isset($message)) {
return ;
}
list ($header, $body) = $this->_splitBodyHeader($message);
$this->_headerPart = $header;
$this->_bodyPart = $body;
}
/**
* Decodes email message, that was previously set using InitHelper method
*
* @param bool $decode_headers
* @param bool $include_bodies
* @param bool $decode_bodies
* @return stdClass
*/
function decode($decode_headers = false, $include_bodies = false, $decode_bodies = false)
{
$this->_decodeHeaders = $decode_headers;
$this->_includeBodies = $include_bodies;
$this->_decodeBodies = $decode_bodies;
$ret = $this->decodePart($this->_headerPart, $this->_bodyPart);
if ($ret === false) {
$this->raiseError($this->_lastErrorMessage);
return false;
}
return $ret;
}
function decodePart($headers, $body, $default_ctype = 'text/plain', $only_headers = false)
{
$return = new stdClass;
// process headers
$return->headers = Array ();
$headers = $this->_parseHeaders($headers, $this->_decodeHeaders);
$single_headers = Array ('subject', 'from', 'to', 'cc', 'reply-to', 'date');
foreach ($headers as $value) {
$header_name = strtolower($value['name']);
$header_value = $only_headers ? $this->_decodeHeader($value['value']) : $value['value'];
if (array_key_exists($header_name, $return->headers) && !is_array($return->headers[$header_name]) && !in_array($header_name, $single_headers)) {
// this is not a single header, so convert it to array, when 2nd value is found
$return->headers[$header_name] = Array ( $return->headers[$header_name] );
$return->headers[$header_name][] = $header_value;
}
elseif (array_key_exists($header_name, $return->headers) && !in_array($header_name, $single_headers)) {
$return->headers[$header_name][] = $header_value;
}
else {
$return->headers[$header_name] = $header_value;
}
}
if ($only_headers) {
return $return->headers;
}
foreach ($headers as $value) {
$header_name = strtolower($value['name']);
$header_value = $value['value'];
switch ($header_name) {
case 'content-type':
$content_type = $this->_parseHeaderValue($header_value);
if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {
// "text/plain", "text/html", etc.
$return->ctype_primary = $regs[1];
$return->ctype_secondary = $regs[2];
}
if (array_key_exists('other', $content_type)) {
// "charset", etc.
foreach ($content_type['other'] as $p_name => $p_value) {
$return->ctype_parameters["$p_name"] = $p_value;
}
}
break;
case 'content-disposition';
$content_disposition = $this->_parseHeaderValue($header_value);
$return->disposition = $content_disposition['value'];
if (array_key_exists('other', $content_disposition)) {
// "filename", etc.
foreach ($content_disposition['other'] as $p_name => $p_value) {
$return->d_parameters["$p_name"] = $p_value;
}
}
break;
case 'content-transfer-encoding':
$content_transfer_encoding = $this->_parseHeaderValue($header_value);
break;
}
}
// process message body
if (isset($content_type)) {
switch ( strtolower($content_type['value']) ) {
case 'text/plain':
case 'text/html':
if ($this->_includeBodies) {
$encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
$return->body = $this->_decodeBodies ? $this->_decodeBody($body, $encoding) : $body;
}
break;
case 'multipart/parallel':
case 'multipart/report': // RFC1892
case 'multipart/signed': // PGP
case 'multipart/digest':
case 'multipart/alternative':
case 'multipart/appledouble':
case 'multipart/related':
case 'multipart/mixed':
if (!isset($content_type['other']['boundary'])) {
$this->_lastErrorMessage = 'No boundary found for ' . $content_type['value'] . ' part';
return false;
}
$default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain';
$parts = $this->_boundarySplit($body, $content_type['other']['boundary']);
for ($i = 0; $i < count($parts); $i++) {
list ($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]);
$part = $this->decodePart($part_header, $part_body, $default_ctype);
if ($part === false) {
// part is broken
$this->raiseError($this->_lastErrorMessage);
}
$return->parts[] = $part;
}
break;
case 'message/rfc822':
case 'message/disposition-notification':
// create another instance, not to interfear with main parser
$mime_decode_helper =& $this->Application->makeClass('MimeDecodeHelper');
/* @var $mime_decode_helper MimeDecodeHelper */
$mime_decode_helper->InitHelper($body);
$return->parts[] = $mime_decode_helper->decode(true, $this->_includeBodies, $this->_decodeBodies);
unset($mime_decode_helper);
break;
default:
if ($this->_includeBodies) {
$encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
$return->body = $this->_decodeBodies ? $this->_decodeBody($body, $encoding) : $body;
}
break;
}
} else {
$ctype = explode('/', $default_ctype);
$return->ctype_primary = $ctype[0];
$return->ctype_secondary = $ctype[1];
if ($this->_includeBodies) {
$return->body = $this->_decodeBodies ? $this->_decodeBody($body) : $body;
}
}
return $return;
}
/**
* Divides message into header and body parts
*
* @param string $input
* @return Array
*/
function _splitBodyHeader($input)
{
if (strpos($input, "\r\n\r\n") === false) {
return Array ($input, '');
} elseif (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) {
return Array ($match[1], $match[2]);
} else {
$this->_lastErrorMessage = 'Could not split header and body';
return false;
}
}
/**
* Parses headers string into array and optionally decode them
*
* @param string $input
* @param bool $decode
* @return Array
*/
function _parseHeaders($input, $decode = false)
{
if (!$input) {
return Array ();
}
$ret = Array ();
// Unfold the input
$input = preg_replace("/\r\n/", "\n", $input);
$input = preg_replace("/\n(\t| )+/", ' ', $input);
$headers = explode("\n", trim($input));
foreach ($headers as $value) {
$pos = strpos($value, ':');
$hdr_name = substr($value, 0, $pos);
$hdr_value = substr($value, $pos + 1);
if ($hdr_value[0] == ' ') {
$hdr_value = substr($hdr_value, 1);
}
$ret[] = Array (
'name' => $hdr_name,
'value' => $decode ? $this->_decodeHeader($hdr_value) : $hdr_value
);
}
return $ret;
}
/**
* Parses header value in following format (without quotes): "multipart/alternative; boundary=001636c9274051e332048498d8cc"
*
* @param string $input
* @return Array
*/
function _parseHeaderValue($input)
{
$ret = Array ();
$pos = strpos($input, ';');
if ($pos === false) {
$ret['value'] = trim($input);
return $ret;
}
// get text until first ";"
$ret['value'] = trim(substr($input, 0, $pos));
$input = trim(substr($input, $pos + 1));
if (strlen($input) > 0) {
// This splits on a semi-colon, if there's no preceeding backslash
// Can't handle if it's in double quotes however. (Of course anyone
// sending that needs a good slap).
$parameters = preg_split('/\s*(?<!\\\\);\s*/i', $input);
for ($i = 0; $i < count($parameters); $i++) {
$pos = strpos($parameters[$i], '=');
$param_name = substr($parameters[$i], 0, $pos);
$param_value = substr($parameters[$i], $pos + 1);
if ($param_value[0] == '"') {
$param_value = substr($param_value, 1, -1);
}
$ret['other']["$param_name"] = $param_value;
$ret['other'][ strtolower($param_name) ] = $param_value;
}
}
return $ret;
}
/**
* Splits input body using given boundary
*
* @param string $input
* @param string $boundary
* @return Array
*/
function _boundarySplit($input, $boundary)
{
$tmp = explode('--' . $boundary, $input);
for ($i = 1; $i < count($tmp) - 1; $i++) {
$parts[] = $tmp[$i];
}
return $parts;
}
/**
* Decode message header value
*
* @param string $input
* @return string
*/
function _decodeHeader($input)
{
// Remove white space between encoded-words (http://www.ietf.org/rfc/rfc2047.txt)
$regexp = '/(=\?[^?]+\?(Q|B)\?[^?]*\?=)(\s)+=\?/i';
while (preg_match($regexp, $input)) {
// process each word separately
$input = preg_replace($regexp, '\1=?', $input);
}
// For each encoded-word...
while (preg_match('/(=\?([^?]+)\?(Q|B)\?([^?]*)\?=)/i', $input, $matches)) {
$encoded = $matches[1];
$charset = $matches[2];
$encoding = $matches[3];
$text = $matches[4];
switch (strtoupper($encoding)) {
case 'B':
$text = base64_decode($text);
break;
case 'Q':
// $text = $this->_quotedPrintableDecode($text);
$text = str_replace('_', ' ', $text);
preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
foreach($matches[1] as $value) {
$text = str_replace('=' . $value, chr(hexdec($value)), $text);
}
break;
}
$input = $this->convertEncoding($charset, str_replace($encoded, $text, $input));
}
return $input;
}
/**
* Converts encoding to one, that site uses
*
* @param string $from_engoding
* @param string $text
* @return string
* @author Alex
*/
function convertEncoding($from_engoding, $text)
{
if (!function_exists('mb_convert_encoding')) {
// if mbstring extension not installed
return $text;
}
static $to_encoding = false;
if ($to_encoding === false) {
$language =& $this->Application->recallObject('lang.current');
/* @var $language LanguagesItem */
$to_encoding = $language->GetDBField('Charset');
}
return mb_convert_encoding($text, $to_encoding, $from_engoding);
}
/**
* Decodes message body
*
* @param string $input
* @param string $encoding
* @return string
*/
function _decodeBody($input, $encoding = '7bit')
{
switch (strtolower($encoding)) {
case 'quoted-printable':
return $this->_quotedPrintableDecode($input);
break;
case 'base64':
return base64_decode($input);
break;
}
// for 7bit, 8bit, anything else
return $input;
}
/**
* Decodes "quoted-printable" encoding
*
* @param string $string
* @return string
*/
function _quotedPrintableDecode($string)
{
// Remove soft line breaks
$string = preg_replace("/=\r?\n/", '', $string);
// Replace encoded characters
if (preg_match_all('/=[a-f0-9]{2}/i', $string, $matches)) {
$matches = array_unique($matches[0]);
foreach ($matches as $value) {
$string = str_replace($value, chr(hexdec(substr($value,1))), $string);
}
}
return $string;
}
}
\ No newline at end of file
Property changes on: branches/5.1.x/core/units/helpers/mime_decode_helper.php
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Index: branches/5.1.x/core/units/helpers/mailbox_helper.php
===================================================================
--- branches/5.1.x/core/units/helpers/mailbox_helper.php (revision 13399)
+++ branches/5.1.x/core/units/helpers/mailbox_helper.php (revision 13400)
@@ -1,480 +1,493 @@
<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+ defined('FULL_PATH') or die('restricted access!');
class MailboxHelper extends kHelper {
var $headers = Array ();
var $parsedMessage = Array ();
/**
* Maximal megabytes of data to process
*
* @var int
*/
var $maxMegabytes = 2;
/**
* Maximal message count to process
*
* @var int
*/
var $maxMessages = 50;
/**
* Reads mailbox and gives messages to processing callback
*
* @param Array $connection_info
* @param Array $verify_callback
* @param Array $process_callback
* @param Array $callback_params
* @param bool $include_attachment_contents
* @return string
*/
function process($connection_info, $verify_callback, $process_callback, $callback_params = Array (), $include_attachment_contents = true)
{
$pop3_helper =& $this->Application->makeClass('POP3Helper', $connection_info);
/* @var $pop3_helper POP3Helper */
$connection_status = $pop3_helper->initMailbox();
if (is_string($connection_status)) {
return $connection_status;
}
if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) {
$this->Application->Debugger->appendHTML('Reading MAILBOX: ' . $connection_info['username']);
}
// Figure out if all messages are huge
$only_big_messages = true;
$max_message_size = $this->maxMegabytes * (1024 * 1024);
foreach ($pop3_helper->messageSizes as $message_size) {
if (($message_size <= $max_message_size) && ($max_message_size > 0)) {
$only_big_messages = false;
break;
}
}
$count = $total_size = 0;
foreach ($pop3_helper->messageSizes as $message_number => $message_size) {
// Too many messages?
if (($count++ > $this->maxMessages) && ($this->maxMessages > 0)) {
break;
}
// Message too big?
if (!$only_big_messages && ($message_size > $max_message_size) && ($max_message_size > 0)) {
$this->_displayLogMessage('message <strong>#' . $message_number . '</strong> too big, skipped');
continue;
}
// Processed enough for today?
if (($total_size > $max_message_size) && ($max_message_size > 0)) {
break;
}
$total_size += $message_size;
$pop3_helper->getEmail($message_number, $message_source);
$processed = $this->normalize($message_source, $verify_callback, $process_callback, $callback_params, $include_attachment_contents);
if ($processed) {
// delete message from server immediatly after retrieving & processing
$pop3_helper->deleteEmail($message_number);
$this->_displayLogMessage('message <strong>#' . $message_number . '</strong>: processed');
}
else {
$this->_displayLogMessage('message <strong>#' . $message_number . '</strong>: skipped');
}
}
$pop3_helper->close();
return 'success';
}
/**
* Displays log message
*
* @param string $text
*/
function _displayLogMessage($text)
{
if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) {
$this->Application->Debugger->appendHTML($text);
}
}
/**
* Takes an RFC822 formatted date, returns a unix timestamp (allowing for zone)
*
* @param string $rfcdate
* @return int
*/
function rfcToTime($rfcdate)
{
$date = strtotime($rfcdate);
if ($date == -1) {
return false;
}
return $date;
}
/**
* Gets recipients from all possible headers
*
* @return string
*/
function getRecipients()
{
$ret = '';
// headers that could contain recipients
$recipient_headers = Array (
'to', 'cc', 'envelope-to', 'resent-to', 'delivered-to',
'apparently-to', 'envelope-to', 'x-envelope-to', 'received',
);
foreach ($recipient_headers as $recipient_header) {
if (!array_key_exists($recipient_header, $this->headers)) {
continue;
}
if (!is_array($this->headers["$recipient_header"])) {
$ret .= ' ' . $this->headers["$recipient_header"];
} else {
$ret .= ' ' . implode(' ', $this->headers["$recipient_header"]);
}
}
return $ret;
}
/**
* "Flattens" the multi-demensinal headers array into a single dimension one
*
* @param Array $input
* @param string $add
* @return Array
*/
function flattenHeadersArray($input, $add = '')
{
$output = Array ();
foreach ($input as $key => $value) {
if (!empty($add)) {
$newkey = ucfirst( strtolower($add) );
} elseif (is_numeric($key)) {
$newkey = '';
} else {
$newkey = ucfirst( strtolower($key) );
}
if (is_array($value)) {
$output = array_merge($output, $this->flattenHeadersArray($value, $newkey));
} else {
$output[] = (!empty($newkey) ? $newkey . ': ' : '') . $value;
}
}
return $output;
}
/**
* Processes given message using given callbacks
*
* @param string $message
* @param Array $verify_callback
* @param Array $process_callback
* @param bool $include_attachment_contents
* @return bool
*/
function normalize($message, $verify_callback, $process_callback, $callback_params, $include_attachment_contents = true)
{
// Decode message
$this->decodeMime($message, $include_attachment_contents);
// Init vars; $good will hold all the correct infomation from now on
$good = Array ();
// trim() some stuff now instead of later
$this->headers['from'] = trim($this->headers['from']);
$this->headers['to'] = trim($this->headers['to']);
$this->headers['cc'] = array_key_exists('cc', $this->headers) ? trim($this->headers['cc']) : '';
$this->headers['x-forward-to'] = array_key_exists('x-forward-to', $this->headers) ? $this->headers['x-forward-to'] : '';
$this->headers['subject'] = trim($this->headers['subject']);
$this->headers['received'] = is_array($this->headers['received']) ? $this->headers['received'] : Array ($this->headers['received']);
if (array_key_exists('return-path', $this->headers) && is_array($this->headers['return-path'])) {
$this->headers['return-path'] = implode(' ', $this->flattenHeadersArray($this->headers['return-path']));
}
// Create our own message-ID if it's missing
$message_id = array_key_exists('message-id', $this->headers) ? trim($this->headers['message-id']) : '';
$good['emailid'] = $message_id ? $message_id : md5($message) . "@in-portal";
// Stops us looping in stupid conversations with other mail software
if (isset($this->headers['x-loop-detect']) && $this->headers['x-loop-detect'] > 2) {
return false;
}
$esender =& $this->Application->recallObject('EmailSender');
/* @var $esender kEmailSendingHelper */
// Get the return address
$return_path = '';
if (array_key_exists('return-path', $this->headers)) {
$return_path = $esender->ExtractRecipientEmail($this->headers['return-path']);
}
if (!$return_path) {
if (array_key_exists('reply-to', $this->headers)) {
$return_path = $esender->ExtractRecipientEmail( $this->headers['reply-to'] );
}
else {
$return_path = $esender->ExtractRecipientEmail( $this->headers['from'] );
}
}
// Get the sender's name & email
$good['fromemail'] = $esender->ExtractRecipientEmail($this->headers['from']);
$good['fromname'] = $esender->ExtractRecipientName($this->headers['from'], $good['fromemail']);
// Get the list of recipients
if (!$verify_callback[0]->$verify_callback[1]($callback_params)) {
// error: mail is propably spam
return false;
}
// Handle the subject
$good['subject'] = $this->headers['subject'];
// Priorities rock
$good['priority'] = array_key_exists('x-priority', $this->headers) ? (int)$this->headers['x-priority'] : 0;
switch ($good['priority']) {
case 1: case 5: break;
default:
$good['priority'] = 3;
}
// If we have attachments it's about time we tell the user about it
if (array_key_exists('attachments', $this->parsedMessage) && is_array($this->parsedMessage['attachments'])) {
$good['attach'] = count( $this->parsedMessage['attachments'] );
} else {
$good['attach'] = 0;
}
// prepare message text (for replies, etc)
if (isset($this->parsedMessage['text'][0]) && trim($this->parsedMessage['text'][0]['body']) != '') {
$message_body = trim($this->parsedMessage['text'][0]['body']);
$message_type = 'text';
} elseif (isset($this->parsedMessage['html']) && trim($this->parsedMessage['html'][0]['body']) != '') {
$message_body = trim($this->parsedMessage['html'][0]['body']);
$message_type = 'html';
} else {
$message_body = '[no message]';
$message_type = 'text';
}
// remove scripts
$message_body = preg_replace("/<script[^>]*>[^<]+<\/script[^>]*>/is", '', $message_body);
$message_body = preg_replace("/<iframe[^>]*>[^<]*<\/iframe[^>]*>/is", '', $message_body);
if ($message_type == 'html') {
$message_body = $esender->ConvertToText($message_body);
}
$mime_decode_helper =& $this->Application->recallObject('MimeDecodeHelper');
/* @var $mime_decode_helper MimeDecodeHelper */
// convert to site encoding
$message_charset = $this->parsedMessage[$message_type][0]['charset'];
if ($message_charset) {
$good['message'] = $mime_decode_helper->convertEncoding($message_charset, $message_body);
}
if (array_key_exists('delivery-date', $this->headers)) {
// We found the Delivery-Date header (and it's not too far in the future)
$dateline = $this->rfcToTime($this->headers['delivery-date']);
if ($dateline > TIMENOW + 86400) {
unset($dateline);
}
}
// We found the latest date from the received headers
$received_timestamp = $this->headers['received'][0];
$dateline = $this->rfcToTime(trim( substr($received_timestamp, strrpos($received_timestamp, ';') + 1) ));
if ($dateline == $this->rfcToTime(0)) {
unset($dateline);
}
if (!isset($dateline)) {
$dateline = TIMENOW;
}
// save collected data to database
$fields_hash = Array (
'DeliveryDate' => $dateline, // date, when SMTP server received the message
'ReceivedDate' => TIMENOW, // date, when message was retrieved from POP3 server
'CreatedOn' => $this->rfcToTime($this->headers['date']), // date, when created on sender's computer
'ReturnPath' => $return_path,
'FromEmail' => $good['fromemail'],
'FromName' => $good['fromname'],
'To' => $this->headers['to'],
'Subject' => $good['subject'],
'Message' => $good['message'],
'MessageType' => $message_type,
'AttachmentCount' => $good['attach'],
'MessageId' => $good['emailid'],
'Source' => $message,
'Priority' => $good['priority'],
'Size' => strlen($message),
);
return $process_callback[0]->$process_callback[1]($callback_params, $fields_hash);
}
/**
* Function that decodes the MIME message and creates the $this->headers and $this->parsedMessage data arrays
*
* @param string $message
* @param bool $include_attachments
*
*/
function decodeMime($message, $include_attachments = true)
{
$message = preg_replace("/\r?\n/", "\r\n", trim($message));
$mime_decode_helper =& $this->Application->recallObject('MimeDecodeHelper');
/* @var $mime_decode_helper MimeDecodeHelper */
// 1. separate headers from bodies
$mime_decode_helper->InitHelper($message);
$decoded_message = $mime_decode_helper->decode(true, true, true);
// 2. extract attachments
$this->parsedMessage = Array (); // ! reset value
$this->parseOutput($decoded_message, $this->parsedMessage, $include_attachments);
// 3. add "other" attachments (text part, that is not maked as attachment)
if (array_key_exists('text', $this->parsedMessage) && count($this->parsedMessage['text']) > 1) {
for ($attach = 1; $attach < count($this->parsedMessage['text']); $attach++) {
$this->parsedMessage['attachments'][] = Array (
'data' => $this->parsedMessage['text']["$attach"]['body'],
);
}
}
$this->headers = $this->parsedMessage['headers']; // ! reset value
if (empty($decoded_message->ctype_parameters['boundary'])) {
// when no boundary, then assume all message is it's text
$this->parsedMessage['text'][0]['body'] = $decoded_message->body;
}
}
/**
* Returns content-id's from inline attachments in message
*
* @return Array
*/
function getContentIds()
{
$cids = Array();
if (array_key_exists('attachments', $this->parsedMessage) && is_array($this->parsedMessage['attachments'])) {
foreach ($this->parsedMessage['attachments'] as $attachnum => $attachment) {
if (!isset($attachment['headers']['content-id'])) {
continue;
}
$cid = $attachment['headers']['content-id'];
if (substr($cid, 0, 1) == '<' && substr($cid, -1) == '>') {
$cid = substr($cid, 1, -1);
}
$cids["$attachnum"] = $cid;
}
}
return $cids;
}
/**
* Get more detailed information about attachments
*
* @param stdClass $decoded parsed headers & body as object
* @param Array $parts parsed parts
* @param bool $include_attachments
*/
function parseOutput(&$decoded, &$parts, $include_attachments = true)
{
$ctype = strtolower($decoded->ctype_primary . '/' . $decoded->ctype_secondary);
// don't parse attached messages recursevely
if (!empty($decoded->parts) && $ctype != 'message/rfc822') {
for ($i = 0; $i < count($decoded->parts); $i++) {
$this->parseOutput($decoded->parts["$i"], $parts, $include_attachments);
}
} else/*if (!empty($decoded->disposition) && $decoded->disposition != 'inline' or 1)*/ {
switch ($ctype) {
case 'text/plain':
case 'text/html':
if (!empty($decoded->disposition) && ($decoded->disposition == 'attachment')) {
$parts['attachments'][] = Array (
'data' => $include_attachments ? $decoded->body : '',
'filename' => array_key_exists('filename', $decoded->d_parameters) ? $decoded->d_parameters['filename'] : '', // from content-disposition
'filename2' => $decoded->ctype_parameters['name'], // from content-type
'type' => $decoded->ctype_primary, // "text"
'encoding' => $decoded->headers['content-transfer-encoding']
);
} else {
$body_type = $decoded->ctype_secondary == 'plain' ? 'text' : 'html';
$parts[$body_type][] = Array (
'content-type' => $ctype,
'charset' => array_key_exists('charset', $decoded->ctype_parameters) ? $decoded->ctype_parameters['charset'] : 'ISO-8859-1',
'body' => $decoded->body
);
}
break;
case 'message/rfc822':
// another e-mail as attachment
$parts['attachments'][] = Array (
'data' => $include_attachments ? $decoded->body : '',
'filename' => array_key_exists('filename', $decoded->d_parameters) ? $decoded->d_parameters['filename'] : '',
'filename2' => array_key_exists('name', $decoded->ctype_parameters) ? $decoded->ctype_parameters['name'] : $decoded->parts[0]->headers['subject'],
'type' => $decoded->ctype_primary, // "message"
'headers' => $decoded->headers // individual copy of headers with each attachment
);
break;
default:
if (!stristr($decoded->headers['content-type'], 'signature')) {
$parts['attachments'][] = Array (
'data' => $include_attachments ? $decoded->body : '',
'filename' => array_key_exists('filename', $decoded->d_parameters) ? $decoded->d_parameters['filename'] : '', // from content-disposition
'filename2' => $decoded->ctype_parameters['name'], // from content-type
'type' => $decoded->ctype_primary,
'headers' => $decoded->headers // individual copy of headers with each attachment
);
}
}
}
$parts['headers'] = $decoded->headers; // headers of next parts overwrite previous part headers
}
}
\ No newline at end of file
Property changes on: branches/5.1.x/core/units/helpers/mailbox_helper.php
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Index: branches/5.1.x/core/units/helpers/form_submission_helper.php
===================================================================
--- branches/5.1.x/core/units/helpers/form_submission_helper.php (revision 13399)
+++ branches/5.1.x/core/units/helpers/form_submission_helper.php (revision 13400)
@@ -1,105 +1,119 @@
<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+ defined('FULL_PATH') or die('restricted access!');
class FormSubmissionHelper extends kHelper {
/**
* Role names for easy usage via FormField tag
*
* @var Array
*/
var $roleNames = Array (
'name' => EMAIL_COMMUNICATION_ROLE_NAME,
'email' => EMAIL_COMMUNICATION_ROLE_EMAIL,
'subject' => EMAIL_COMMUNICATION_ROLE_SUBJECT,
'body' => EMAIL_COMMUNICATION_ROLE_BODY,
);
/**
* Returns submission field based on given role
*
* @param kDBItem $form_submission
* @param string $role
* @return string
*/
function getFieldByRole(&$form_submission, $role, $formatted = false, $format = null)
{
static $cache = Array ();
$form_id = $form_submission->GetDBField('FormId');
if (!array_key_exists($form_id, $cache)) {
$id_field = $this->Application->getUnitOption('formflds', 'IDField');
$table_name = $this->Application->getUnitOption('formflds', 'TableName');
$sql = 'SELECT ' . $id_field . ', EmailCommunicationRole
FROM ' . $table_name . '
WHERE FormId = ' . $form_id . ' AND EmailCommunicationRole <> 0';
$cache[$form_id] = $this->Conn->GetCol($sql, 'EmailCommunicationRole');
}
// convert string representation of role to numeric
if (!is_numeric($role)) {
$role = strtolower($role);
$role = array_key_exists($role, $this->roleNames) ? $this->roleNames[$role] : false;
}
// get field by role
$field_id = array_key_exists($role, $cache[$form_id]) ? $cache[$form_id][$role] : false;
if ($field_id) {
return $formatted ? $form_submission->GetField('fld_' . $field_id, $format) : $form_submission->GetDBField('fld_' . $field_id);
}
return false;
}
/**
* Returns submission field based on given name
*
* @param kDBItem $form_submission
* @param string $name
* @return string
*/
function getFieldByName(&$form_submission, $name, $formatted = false, $format = null)
{
static $cache = Array ();
$form_id = $form_submission->GetDBField('FormId');
if (!array_key_exists($form_id, $cache)) {
$id_field = $this->Application->getUnitOption('formflds', 'IDField');
$table_name = $this->Application->getUnitOption('formflds', 'TableName');
$sql = 'SELECT ' . $id_field . ', FieldName
FROM ' . $table_name . '
WHERE FormId = ' . $form_id;
$cache[$form_id] = $this->Conn->GetCol($sql, 'FieldName');
}
if ($field_id) {
return $formatted ? $form_submission->GetField('fld_' . $field_id, $format) : $form_submission->GetDBField('fld_' . $field_id);
}
return false;
}
/**
* Returns form object field based on form submission
*
* @param $form_submission kDBItem
* @return kDBItem
*/
function &getForm(&$form_submission)
{
$form_id = $form_submission->GetDBField('FormId');
$form =& $this->Application->recallObject('form', null, Array ('skip_autoload' => true));
/* @var $form kDBItem */
if (!$form->isLoaded() || ($form->GetID() != $form_id)) {
$form->Load($form_id);
}
return $form;
}
}
\ No newline at end of file
Property changes on: branches/5.1.x/core/units/helpers/form_submission_helper.php
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Index: branches/5.1.x/core/units/helpers/pop3_helper.php
===================================================================
--- branches/5.1.x/core/units/helpers/pop3_helper.php (revision 13399)
+++ branches/5.1.x/core/units/helpers/pop3_helper.php (revision 13400)
@@ -1,339 +1,353 @@
<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+ defined('FULL_PATH') or die('restricted access!');
/**
* POP3 connection class, that uses sockets
*
*/
class POP3Helper extends kHelper {
/**
* Pointer to socket to POP3 server
*
* @var resource
*/
var $fp = null;
/**
* Connection to POP3 server established
*
* @var bool
*/
var $connected = false;
/**
* Connection timeout
*
* @var int
*/
var $connectionTimeout = 60;
/**
* Server connection information
*
* @var Array
*/
var $serverInfo = Array ();
/**
* Size of each message in mailbox
*
* @var Array
*/
var $messageSizes = Array ();
/**
* Sets up the server information array
*
* @param Array $server_info
* @return POP3Helper
*/
function POP3Helper($server_info, $connection_timeout = null)
{
parent::kHelper();
$this->serverInfo = $server_info;
if (isset($connection_timeout)) {
$this->connectionTimeout = $connection_timeout;
}
}
/**
* Check if we can connect and log into the mailbox
*
* @return string
*/
function initMailbox($dry_run = false)
{
if ($this->connect() === false) {
return 'socket';
}
elseif ($this->auth() === false) {
$this->close();
return 'login';
}
elseif ($this->getMessageList() === false) {
$this->close();
return 'list';
}
elseif (empty($this->messageSizes)) {
$this->close();
return 'empty'; // Still 'good'
}
elseif ($dry_run) {
$this->close();
return 'success';
}
return true;
}
/**
* Logs failed logins to our server
*
* @param int $error error code
* @param Array $error_array params of error
*/
function error($error, $error_array)
{
trigger_error('POP3 Error. Code: ' . $error . '; Params: ' . print_r($error_array, true), E_USER_WARNING);
}
/**
* Connect to the server
*
* @return bool
*/
function connect()
{
// Apparently fsockopen() doesn't return false in these cases:
if (empty($this->serverInfo['server'])) {
return false;
}
if ($this->connected == true) {
return true;
}
$this->fp = @fsockopen($this->serverInfo['server'], intval($this->serverInfo['port']), $error_no, $error_str, $this->connectionTimeout);
if (!is_resource($this->fp)) {
$this->error(101, $this->serverInfo);
return false;
}
$this->connected = true;
$buffer = fgets($this->fp, 4096);
if (substr($buffer, 0, 3) != '+OK') {
$this->error(101, $this->serverInfo);
$this->close();
return false;
}
else {
return true;
}
}
/**
* Close connection to the server
*
*/
function close()
{
if ($this->connected == true) { // and is_resource($this->fp)) {
$this->connected = false;
@fputs($this->fp, "QUIT\r\n");
@fclose($this->fp);
}
}
/**
* Login to the server
*
* @param bool $dry_run
* @return bool
*/
function auth($dry_run = false)
{
if (!is_resource($this->fp) && ($this->connect() === false)) {
return false;
}
fputs($this->fp, 'USER ' . $this->serverInfo['username'] . "\r\n");
$buffer = fgets($this->fp, 4096);
if (substr($buffer, 0, 3) != '+OK') {
$this->close();
return false;
}
fputs($this->fp, 'PASS ' . $this->serverInfo['password'] . "\r\n");
$buffer = fgets($this->fp, 4096);
if (substr($buffer, 0, 3) != '+OK') {
$this->error(102, $this->serverInfo);
$this->close();
return false;
}
else {
if ($dry_run) {
$this->close();
}
return true;
}
}
/**
* Get the list of messages and their ID's
*
* @return bool
*/
function getMessageList()
{
fputs($this->fp, "LIST\r\n");
if (substr(fgets($this->fp, 4096), 0, 3) != '+OK') {
$this->close();
return false;
}
// Store the message numbers and sizes
$buffer = fgets($this->fp, 4096);
while ($buffer != ".\r\n") {
$msginfo = explode(' ', $buffer);
$this->messageSizes[ trim($msginfo[0]) ] = trim($msginfo[1]);
$buffer = fgets($this->fp, 4096);
}
return true;
}
/**
* Gets the size of a message
*
* @param int $message_number
* @return int
*/
function getSize($message_number)
{
return $this->messageSizes["$message_number"];
}
/**
* Gets email number $message_number from the server
*
* @param int $message_number
* @param string $source
* @return string
*/
function getEmail($message_number, &$source)
{
return $this->getData("RETR $message_number\r\n", $source);
}
/**
* Gets the top $lines from the message
*
* @param int $message_number
* @param string $source
* @param int $lines
* @param string $stopat
* @param bool $onelineonly
* @return string
*/
function getTop($message_number, &$source, $lines = 0, $stopat = '', $onelineonly = false)
{
return $this->getData("TOP $message_number $lines\r\n", $source, $stopat, $onelineonly);
}
/**
* Issues $command and returns the output
*
* @param string $command
* @param string $source
* @param string $stopat
* @param bool $onelineonly
* @return string
*/
function getData($command, &$source, $stopat = '', $onelineonly = false)
{
fputs($this->fp, $command);
if (substr(fgets($this->fp, 4096), 0, 3) != '+OK') {
return false;
}
$source = '';
$buffer = fgets($this->fp, 4096);
while ($buffer != ".\r\n") {
if (!$onelineonly) {
$source .= $buffer;
}
if (!empty($stopat)) {
if (strtolower(substr(trim($buffer), 0, strlen($stopat))) == strtolower($stopat)) {
if ($onelineonly) {
$source = $buffer;
}
else {
$onelineonly = true;
}
$stopat = '';
}
}
$buffer = fgets($this->fp, 4096);
}
return true;
}
/**
* Sends the given command to the server and returns true or false on success
*
* @param string $command
* @return bool
*/
function sendCommand($command)
{
fputs($this->fp, $command . "\r\n");
$buffer = trim(fgets($this->fp, 4096));
if (substr($buffer, 0, 3) != '+OK') {
$this->error(103, Array ('cmd' => $command, 'resp' => $buffer));
return false;
}
else {
return true;
}
}
/**
* Delete message number $message_number from the server
*
* @param int $message_number
* @return bool
*/
function deleteEmail($message_number)
{
return $this->sendCommand("DELE $message_number");
}
/**
* Create new instance of object
*
* @return kBase
*/
function &makeClass($server_info, $connection_timeout = null)
{
$object = new POP3Helper($server_info, $connection_timeout);
return $object;
}
}
\ No newline at end of file
Property changes on: branches/5.1.x/core/units/helpers/pop3_helper.php
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Index: branches/5.1.x/core/units/forms/drafts/draft_eh.php
===================================================================
--- branches/5.1.x/core/units/forms/drafts/draft_eh.php (revision 13399)
+++ branches/5.1.x/core/units/forms/drafts/draft_eh.php (revision 13400)
@@ -1,43 +1,57 @@
<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+ defined('FULL_PATH') or die('restricted access!');
class DraftEventHandler extends kDBEventHandler
{
/**
* Sets user, who created draft
*
* @param kEvent $event
*/
function OnBeforeItemCreate(&$event)
{
parent::OnBeforeItemCreate($event);
$object =& $event->getObject();
/* @var $object kDBItem */
$user_id = $this->Application->RecallVar('user_id');
$object->SetDBField('CreatedById', $user_id);
}
/**
* Allows to load draft, that best matches given form submission
*
* @param kEvent $event
* @return int
*/
function getPassedID(&$event)
{
if ($event->Special == 'related') {
$form_submission =& $this->Application->recallObject('formsubs');
/* @var $form_submission kDBItem */
return Array (
'FormSubmissionId' => $form_submission->GetID(),
'CreatedById' => $this->Application->RecallVar('user_id'),
);
}
return parent::getPassedID($event);
}
}
\ No newline at end of file
Property changes on: branches/5.1.x/core/units/forms/drafts/draft_eh.php
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Index: branches/5.1.x/core/units/forms/drafts/drafts_config.php
===================================================================
--- branches/5.1.x/core/units/forms/drafts/drafts_config.php (revision 13399)
+++ branches/5.1.x/core/units/forms/drafts/drafts_config.php (revision 13400)
@@ -1,48 +1,62 @@
<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+ defined('FULL_PATH') or die('restricted access!');
$config = Array (
'Prefix' => 'draft',
'ItemClass' => Array ('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'),
'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'),
'EventHandlerClass' => Array ('class' => 'DraftEventHandler', 'file' => 'draft_eh.php', 'build_event' => 'OnBuild'),
'TagProcessorClass' => Array ('class' => 'kDBTagProcessor', 'file' => '', 'build_event' => 'OnBuild'),
'AutoLoad' => true,
'QueryString' => Array (
1 => 'id',
2 => 'Page',
3 => 'event',
4 => 'mode',
),
'IDField' => 'DraftId',
'ParentPrefix' => 'formsubs',
'ForeignKey' => 'FormSubmissionId',
'ParentTableKey' => 'FormSubmissionId',
'AutoDelete' => true,
'AutoClone' => true,
'TableName' => TABLE_PREFIX . 'Drafts',
'TitleField' => 'DraftId',
'ListSQLs' => Array (
'' => ' SELECT %1$s.* %2$s FROM %1$s',
),
'ListSortings' => Array (
'' => Array (
'Sorting' => Array ('DraftId' => 'desc'),
)
),
'Fields' => Array (
'DraftId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'FormSubmissionId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'CreatedOn' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => '#NOW#'),
'CreatedById' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'Message' => Array ('type' => 'string', 'default' => NULL),
),
);
\ No newline at end of file
Property changes on: branches/5.1.x/core/units/forms/drafts/drafts_config.php
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Index: branches/5.1.x/core/units/forms/submission_log/submission_log_tp.php
===================================================================
--- branches/5.1.x/core/units/forms/submission_log/submission_log_tp.php (revision 13399)
+++ branches/5.1.x/core/units/forms/submission_log/submission_log_tp.php (revision 13400)
@@ -1,99 +1,113 @@
<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+ defined('FULL_PATH') or die('restricted access!');
class SubmissionLogTagProcessor extends kDBTagProcessor {
/**
* Checks, that current log record is mail from client to admin and it's not replied
*
* @param Array $params
* @return bool
*/
function IsNewUserReply($params)
{
$object =& $this->getObject($params);
/* @var $object kDBItem */
$user_reply = $this->IsUserReply($params);
return $user_reply && ($object->GetDBField('ReplyStatus') != SUBMISSION_LOG_REPLIED);
}
/**
* Checks, that current log record is mail from client to admin
*
* @param Array $params
* @return bool
*/
function IsUserReply($params)
{
$object =& $this->getObject($params);
/* @var $object kDBItem */
$reply_email = $this->Application->ConfigValue('SubmissionReplyFromEmail');
return $object->GetDBField('ToEmail') == $reply_email;
}
/**
* Checks if there is draft for given article
*
* @param Array $params
* @return bool
*/
function HasDraft($params)
{
if (!$this->IsNewItem($params)) {
// use drafts only for unsent (new) messages
return false;
}
$object =& $this->getObject($params);
/* @var $object kDBItem */
$draft =& $this->Application->recallObject('draft', null, Array('skip_autoload' => true));
/* @var $draft kDBItem */
$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);
return $draft->isLoaded();
}
/**
* Lists all files, uploadeded to given field
*
* @param Array $params
* @return string
*/
function IterateFiles($params)
{
$object =& $this->getObject($params);
/* @var $object kDBItem */
$field = $this->SelectParam($params, 'name,field');
$value = $object->GetDBField($field);
if (!$value) {
return '';
}
$ret = '';
$files = explode('|', $value);
$block_params = $this->prepareTagParams($params);
$block_params['name'] = $params['render_as'];
foreach ($files as $file) {
$object->SetDBField($field, $file);
$ret .= $this->Application->ParseBlock($block_params);
}
$object->SetDBField($field, $value);
return $ret;
}
}
\ No newline at end of file
Property changes on: branches/5.1.x/core/units/forms/submission_log/submission_log_tp.php
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Index: branches/5.1.x/core/units/forms/submission_log/submission_log_config.php
===================================================================
--- branches/5.1.x/core/units/forms/submission_log/submission_log_config.php (revision 13399)
+++ branches/5.1.x/core/units/forms/submission_log/submission_log_config.php (revision 13400)
@@ -1,116 +1,130 @@
<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+ defined('FULL_PATH') or die('restricted access!');
$config = Array (
'Prefix' => 'submission-log',
'ItemClass' => Array ('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'),
'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'),
'EventHandlerClass' => Array ('class' => 'SubmissionLogEventHandler', 'file' => 'submission_log_eh.php', 'build_event' => 'OnBuild'),
'TagProcessorClass' => Array ('class' => 'SubmissionLogTagProcessor', 'file' => 'submission_log_tp.php', 'build_event' => 'OnBuild'),
'AutoLoad' => true,
'QueryString' => Array (
1 => 'id',
2 => 'Page',
3 => 'event',
4 => 'form_id'
),
'IDField' => 'SubmissionLogId',
'TableName' => TABLE_PREFIX . 'SubmissionLog',
'StatusField' => Array ('ReplyStatus'),
'ParentPrefix' => 'formsubs',
'ForeignKey' => 'FormSubmissionId',
'ParentTableKey' => 'FormSubmissionId',
'AutoDelete' => true,
'AutoClone' => true,
'TitleField' => 'Subject',
'ListSQLs' => Array (
'' => ' SELECT %1$s.* %2$s FROM %1$s',
),
'ListSortings' => Array (
'' => Array (
'Sorting' => Array ('SubmissionLogId' => 'desc'),
)
),
'Fields' => Array (
'SubmissionLogId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'FormSubmissionId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'FromEmail' => Array (
'type' => 'string', 'max_len' => 255,
'regexp' => '/' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . '/',
'not_null' => 1, 'required' => 1, 'default' => ''
),
'ToEmail' => Array (
'type' => 'string', 'max_len' => 255,
'regexp' => '/' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . '/',
'not_null' => 1, 'required' => 1, 'default' => ''
),
'Cc' => Array ('type' => 'string', 'default' => NULL),
'Bcc' => Array ('type' => 'string', 'default' => NULL),
'Subject' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'required' => 1, 'default' => ''),
'Message' => Array ('type' => 'string', 'required' => 1, 'using_fck' => 1, 'default' => NULL),
'Attachment' => Array (
'type' => 'string',
'formatter' => 'kUploadFormatter', 'upload_dir' => SUBMISSION_LOG_ATTACHMENT_PATH,
'file_types' => '*.*', 'files_description' => '!la_hint_AnyFiles!', 'multiple' => 100,
'default' => NULL
),
'ReplyStatus' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0 // create as not replied
),
'SentStatus' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No', 2 => 'la_opt_Bounce'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0 // create as not sent
),
'SentOn' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => NULL),
'RepliedOn' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => NULL),
'VerifyCode' => Array ('type' => 'string', 'max_len' => 32, 'not_null' => 1, 'default' => ''),
'DraftId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'MessageId' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''),
'BounceInfo' => Array ('type' => 'string', 'default' => NULL),
'BounceDate' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => NULL),
),
'VirtualFields' => Array (
'ReplyTo' => Array ('type' => 'int', 'default' => null),
),
'Grids' => Array (
'Default' => Array (
'Icons' => Array (0 => 'icon16_not_replied.gif', 1 => 'icon16_replied.gif'),
'Fields' => Array (
'SubmissionLogId' => Array ('title' => 'la_col_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', ),
'Subject' => Array ('title' => 'la_col_Subject', 'data_block' => 'grid_subject_td', 'filter_block' => 'grid_like_filter',),
'Message' => Array ('title' => 'la_col_Message', 'filter_block' => 'grid_like_filter', 'first_chars' => 100),
'ReplyStatus' => Array ('title' => 'la_col_ReplyStatus', 'filter_block' => 'grid_options_filter',),
'SentStatus' => Array ('title' => 'la_col_SentStatus', 'filter_block' => 'grid_options_filter',),
'FromEmail' => Array ('title' => 'la_col_FromEmail', 'filter_block' => 'grid_like_filter',),
'ToEmail' => Array ('title' => 'la_col_ToEmail', 'filter_block' => 'grid_like_filter',),
'SentOn' => Array ('title' => 'la_col_SentOn', 'filter_block' => 'grid_date_range_filter',),
'RepliedOn' => Array ('title' => 'la_col_RepliedOn', 'filter_block' => 'grid_date_range_filter',),
'Cc' => Array ('title' => 'la_col_Cc', 'filter_block' => 'grid_like_filter', 'hidden' => 1),
'Bcc' => Array ('title' => 'la_col_Bcc', 'filter_block' => 'grid_like_filter', 'hidden' => 1),
'BounceInfo' => Array ('title' => 'la_col_BounceInfo', 'filter_block' => 'grid_like_filter', 'hidden' => 1),
'BounceDate' => Array ('title' => 'la_col_BounceDate', 'filter_block' => 'grid_date_range_filter', 'hidden' => 1),
),
),
),
);
\ No newline at end of file
Property changes on: branches/5.1.x/core/units/forms/submission_log/submission_log_config.php
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Index: branches/5.1.x/core/units/forms/submission_log/submission_log_eh.php
===================================================================
--- branches/5.1.x/core/units/forms/submission_log/submission_log_eh.php (revision 13399)
+++ branches/5.1.x/core/units/forms/submission_log/submission_log_eh.php (revision 13400)
@@ -1,677 +1,691 @@
<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+ defined('FULL_PATH') or die('restricted access!');
class SubmissionLogEventHandler extends kDBEventHandler {
function mapPermissions()
{
parent::mapPermissions();
$permissions = Array (
'OnResendReply' => 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 event permissions
*
* @param kEvent $event
* @return bool
*/
function CheckPermission(&$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
* @access protected
*/
function OnNew(&$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);
$from_email = $form->GetDBField('ReplyFromEmail');
$to_email = $form_submission_helper->getFieldByRole($form_submission, EMAIL_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, EMAIL_COMMUNICATION_ROLE_SUBJECT);
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, EMAIL_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
*/
function OnBeforeItemCreate(&$event)
{
parent::OnBeforeItemCreate($event);
$this->_validateRecipients($event);
$this->_updateStatusDates($event);
}
/**
* Updates last operation dates for log record
*
* @param kEvent $event
*/
function OnBeforeItemUpdate(&$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(),
getmicrotime()
);
$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, EMAIL_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
*/
function OnAfterItemCreate(&$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
*/
function OnAfterItemUpdate(&$event)
{
parent::OnAfterItemUpdate($event);
$this->_updateSubmission($event);
$object =& $event->getObject();
/* @var $object kDBItem */
// send out email event to admin for bouncing
if ( $object->GetOriginalField('SentStatus') != $object->GetDBField('SentStatus')
&& $object->GetDBField('SentStatus') == 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 (($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);
$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);
$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);
$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;
}
}
\ No newline at end of file
Property changes on: branches/5.1.x/core/units/forms/submission_log/submission_log_eh.php
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Index: branches/5.1.x/core/install.php
===================================================================
--- branches/5.1.x/core/install.php (revision 13399)
+++ branches/5.1.x/core/install.php (revision 13400)
@@ -1,1473 +1,1474 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
ini_set('display_errors', 1);
error_reporting(E_ALL);
define('IS_INSTALL', 1);
define('ADMIN', 1);
define('FULL_PATH', realpath(dirname(__FILE__).'/..') );
define('REL_PATH', '/core');
// run installator
$install_engine = new kInstallator();
$install_engine->Init();
$install_engine->Run();
$install_engine->Done();
class kInstallator {
/**
* Reference to kApplication class object
*
* @var kApplication
*/
var $Application = null;
/**
* Connection to database
*
* @var kDBConnection
*/
var $Conn = null;
/**
* XML file containing steps information
*
* @var string
*/
var $StepDBFile = '';
/**
* Step name, that currently being processed
*
* @var string
*/
var $currentStep = '';
/**
* Steps list (preset) to use for current installation
*
* @var string
*/
var $stepsPreset = '';
/**
* Installtion steps to be done
*
* @var Array
*/
var $steps = Array (
'fresh_install' => Array ('check_paths', 'db_config', 'select_license', /*'download_license',*/ 'select_domain', 'root_password', 'choose_modules', 'post_config', 'select_theme', 'security', 'finish'),
'clean_reinstall' => Array ('check_paths', 'clean_db', 'db_config', 'select_license', /*'download_license',*/ 'select_domain', 'root_password', 'choose_modules', 'post_config', 'select_theme', 'security', 'finish'),
'already_installed' => Array ('check_paths', 'install_setup'),
'upgrade' => Array ('check_paths', 'install_setup', 'upgrade_modules', 'security', 'finish'),
'update_license' => Array ('check_paths', 'install_setup', 'select_license', /*'download_license',*/ 'select_domain', 'security', 'finish'),
'db_reconfig' => Array ('check_paths', 'install_setup', 'db_reconfig', 'security', 'finish'),
'fix_paths' => Array ('check_paths', 'install_setup', 'fix_paths', 'security', 'finish'),
);
/**
* Steps, that doesn't required admin to be logged-in to proceed
*
* @var Array
*/
var $skipLoginSteps = Array ('check_paths', 'select_license', /*'download_license',*/ 'select_domain', 'root_password', 'choose_modules', 'post_config', 'select_theme', 'security', 'finish', -1);
/**
* Steps, on which kApplication should not be initialized, because of missing correct db table structure
*
* @var Array
*/
var $skipApplicationSteps = Array ('check_paths', 'clean_db', 'db_config', 'db_reconfig' /*, 'install_setup'*/); // remove install_setup when application will work separately from install
/**
* Folders that should be writeable to continue installation. $1 - main writeable folder from config.php ("/system" by default)
*
* @var Array
*/
var $writeableFolders = Array (
'$1',
'$1/images',
'$1/images/resized',
'$1/images/pending',
'$1/images/pending/resized',
'$1/images/emoticons', // for "In-Bulletin"
'$1/images/manufacturers', // for "In-Commerce"
'$1/images/manufacturers/resized', // for "In-Commerce"
'$1/images/polls', // for "In-Bulletin"
'$1/images/polls/resized', // for "In-Bulletin"
'$1/backupdata',
'$1/export',
'$1/stylesheets',
'$1/user_files',
+ '$1/user_files/submission_log',
'$1/cache',
);
/**
* Contains last error message text
*
* @var string
*/
var $errorMessage = '';
/**
* Base path for includes in templates
*
* @var string
*/
var $baseURL = '';
/**
* Holds number of last executed query in the SQL
*
* @var int
*/
var $LastQueryNum = 0;
/**
* Common tools required for installation process
*
* @var kInstallToolkit
*/
var $toolkit = null;
function Init()
{
include_once(FULL_PATH . REL_PATH . '/kernel/utility/multibyte.php'); // emulating multi-byte php extension
require_once(FULL_PATH . REL_PATH . '/install/install_toolkit.php'); // toolkit required for module installations to installator
$this->toolkit = new kInstallToolkit();
$this->toolkit->setInstallator($this);
$this->StepDBFile = FULL_PATH.'/'.REL_PATH.'/install/steps_db.xml';
$base_path = rtrim(preg_replace('/'.preg_quote(rtrim(REL_PATH, '/'), '/').'$/', '', str_replace('\\', '/', dirname($_SERVER['PHP_SELF']))), '/');
$this->baseURL = 'http://'.$_SERVER['HTTP_HOST'].$base_path.'/core/install/';
set_error_handler( Array(&$this, 'ErrorHandler') );
if (file_exists($this->toolkit->INIFile)) {
// if config.php found, then check his write permission too
$this->writeableFolders[] = $this->toolkit->defaultWritablePath . '/config.php';
}
if (!$this->toolkit->getSystemConfig('Misc', 'WriteablePath')) {
// set global writable folder when such setting is missing
$this->toolkit->setSystemConfig('Misc', 'WriteablePath', $this->toolkit->defaultWritablePath);
$this->toolkit->SaveConfig(true); // immediately save, because this path will be used in Application later
}
$this->currentStep = $this->GetVar('step');
// can't check login on steps where no application present anyways :)
$this->skipLoginSteps = array_unique(array_merge($this->skipLoginSteps, $this->skipApplicationSteps));
$this->SelectPreset();
if (!$this->currentStep) {
$this->SetFirstStep(); // sets first step of current preset
}
$this->InitStep();
}
function SetFirstStep()
{
reset($this->steps[$this->stepsPreset]);
$this->currentStep = current($this->steps[$this->stepsPreset]);
}
/**
* Selects preset to proceed based on various criteria
*
*/
function SelectPreset()
{
$preset = $this->GetVar('preset');
if ($this->toolkit->systemConfigFound()) {
// only at installation first step
$status = $this->CheckDatabase(false);
if ($status && $this->AlreadyInstalled()) {
// if already installed, then all future actions need login to work
$this->skipLoginSteps = Array ('check_paths', -1);
if (!$preset) {
$preset = 'already_installed';
$this->currentStep = '';
}
}
}
if ($preset === false) {
$preset = 'fresh_install'; // default preset
}
$this->stepsPreset = $preset;
}
function GetVar($name)
{
return array_key_exists($name, $_REQUEST) ? $_REQUEST[$name] : false;
}
function SetVar($name, $value)
{
$_REQUEST[$name] = $value;
}
/**
* Performs needed intialization of data, that step requires
*
*/
function InitStep()
{
$require_login = !in_array($this->currentStep, $this->skipLoginSteps);
$this->InitApplication($require_login);
if ($require_login) {
// step require login to proceed
if (!$this->Application->LoggedIn()) {
$this->stepsPreset = 'already_installed';
$this->currentStep = 'install_setup'; // manually set 2nd step, because 'check_paths' step doesn't contain login form
// $this->SetFirstStep();
}
}
switch ($this->currentStep) {
case 'check_paths':
$writeable_base = $this->toolkit->getSystemConfig('Misc', 'WriteablePath');
foreach ($this->writeableFolders as $folder_path) {
$file_path = FULL_PATH . str_replace('$1', $writeable_base, $folder_path);
if (file_exists($file_path) && !is_writable($file_path)) {
$this->errorMessage = '<br/>Installation can not continue until all required permissions are set correctly';
break;
}
}
break;
case 'clean_db':
// don't use Application, because all tables will be erased and it will crash
$sql = 'SELECT Path
FROM ' . TABLE_PREFIX . 'Modules';
$modules = $this->Conn->GetCol($sql);
foreach ($modules as $module_folder) {
$remove_file = '/' . $module_folder . 'install/remove_schema.sql';
if (file_exists(FULL_PATH . $remove_file)) {
$this->toolkit->RunSQL($remove_file);
}
}
$this->currentStep = $this->GetNextStep();
break;
case 'db_config':
case 'db_reconfig':
$fields = Array (
'DBType', 'DBHost', 'DBName', 'DBUser',
'DBUserPassword', 'DBCollation', 'TablePrefix'
);
// set fields
foreach ($fields as $field_name) {
$submit_value = $this->GetVar($field_name);
if ($submit_value !== false) {
$this->toolkit->setSystemConfig('Database', $field_name, $submit_value);
}
/*else {
$this->toolkit->setSystemConfig('Database', $field_name, '');
}*/
}
break;
case 'download_license':
$license_source = $this->GetVar('license_source');
if ($license_source !== false && $license_source != 1) {
// previous step was "Select License" and not "Download from Intechnic" option was selected
$this->currentStep = $this->GetNextStep();
}
break;
case 'choose_modules':
// if no modules found, then proceed to next step
$modules = $this->ScanModules();
if (!$modules) {
$this->currentStep = $this->GetNextStep();
}
break;
case 'select_theme':
// put available theme list in database
$this->toolkit->rebuildThemes();
break;
case 'upgrade_modules':
// get installed modules from db and compare their versions to upgrade script
$modules = $this->GetUpgradableModules();
if (!$modules) {
$this->currentStep = $this->GetNextStep();
}
break;
case 'install_setup':
$next_preset = $this->Application->GetVar('next_preset');
if ($next_preset !== false) {
if ($this->Application->GetVar('login') == 'root') {
// verify "root" user using configuration settings
$login_event = new kEvent('u.current:OnLogin');
$this->Application->HandleEvent($login_event);
if ($login_event->status != erSUCCESS) {
$user =& $this->Application->recallObject('u.current');
/* @var $user UsersItem */
$this->errorMessage = $user->GetErrorMsg('ValidateLogin') . '. If you don\'t know your username or password, contact Intechnic Support';
}
}
else {
// non "root" user -> verify using licensing server
$url_params = Array (
'login=' . md5( $this->GetVar('login') ),
'password=' . md5( $this->GetVar('password') ),
'action=check',
'license_code=' . base64_encode( $this->toolkit->getSystemConfig('Intechnic', 'LicenseCode') ),
'version=' . '4.3.0',//$this->toolkit->GetMaxModuleVersion('core/'),
'domain=' . base64_encode($_SERVER['HTTP_HOST']),
);
$license_url = GET_LICENSE_URL . '?' . implode('&', $url_params);
$file_data = curl_post($license_url, '', null, 'GET');
if (substr($file_data, 0, 5) == 'Error') {
$this->errorMessage = substr($file_data, 6) . ' If you don\'t know your username or password, contact Intechnic Support';
}
if ($this->errorMessage == '') {
$user_id = -1;
$session =& $this->Application->recallObject('Session');
$session->SetField('PortalUserId', $user_id);
$this->Application->SetVar('u.current_id', $user_id);
$this->Application->StoreVar('user_id', $user_id);
}
}
if ($this->errorMessage == '') {
// processed with redirect to selected step preset
if (!isset($this->steps[$next_preset])) {
$this->errorMessage = 'Preset "'.$next_preset.'" not yet implemented';
}
else {
$this->stepsPreset = $next_preset;
}
}
}
else {
// if preset was not choosen, then raise error
$this->errorMessage = 'Please select action to perform';
}
break;
case 'security':
// perform write check
if ($this->Application->GetVar('skip_security_check')) {
// administrator intensionally skips security checks
break;
}
$write_check = true;
$check_paths = Array ('/', '/index.php', $this->toolkit->defaultWritablePath . '/config.php', ADMIN_DIRECTORY . '/index.php');
foreach ($check_paths as $check_path) {
$path_check_status = $this->toolkit->checkWritePermissions(FULL_PATH . $check_path);
if (is_bool($path_check_status) && $path_check_status) {
$write_check = false;
break;
}
}
// script execute check
if (file_exists(WRITEABLE . '/install_check.php')) {
unlink(WRITEABLE . '/install_check.php');
}
$fp = fopen(WRITEABLE . '/install_check.php', 'w');
fwrite($fp, "<?php\n\techo 'OK';\n");
fclose($fp);
$curl_helper =& $this->Application->recallObject('CurlHelper');
/* @var $curl_helper kCurlHelper */
$output = $curl_helper->Send($this->Application->BaseURL(WRITEBALE_BASE) . 'install_check.php');
unlink(WRITEABLE . '/install_check.php');
$execute_check = ($output !== 'OK');
$directive_check = true;
$ini_vars = Array ('register_globals' => false, 'open_basedir' => true, 'allow_url_fopen' => false);
foreach ($ini_vars as $var_name => $var_value) {
$current_value = ini_get($var_name);
if (($var_value && !$current_value) || (!$var_value && $current_value)) {
$directive_check = false;
break;
}
}
if (!$write_check || !$execute_check || !$directive_check) {
$this->errorMessage = true;
}
/*else {
$this->currentStep = $this->GetNextStep();
}*/
break;
}
$this->PerformValidation(); // returns validation status (just in case)
}
/**
* Validates data entered by user
*
* @return bool
*/
function PerformValidation()
{
if ($this->GetVar('step') != $this->currentStep) {
// just redirect from previous step, don't validate
return true;
}
$status = true;
switch ($this->currentStep) {
case 'db_config':
case 'db_reconfig':
// 1. check if required fields are filled
$section_name = 'Database';
$required_fields = Array ('DBType', 'DBHost', 'DBName', 'DBUser', 'DBCollation');
foreach ($required_fields as $required_field) {
if (!$this->toolkit->getSystemConfig($section_name, $required_field)) {
$status = false;
$this->errorMessage = 'Please fill all required fields';
break;
}
}
if (!$status) break;
// 2. check permissions, that use have in this database
$status = $this->CheckDatabase(($this->currentStep == 'db_config') && !$this->GetVar('UseExistingSetup'));
break;
case 'select_license':
$license_source = $this->GetVar('license_source');
if ($license_source == 2) {
// license from file -> file must be uploaded
$upload_error = $_FILES['license_file']['error'];
if ($upload_error != UPLOAD_ERR_OK) {
$this->errorMessage = 'Missing License File';
}
}
elseif (!is_numeric($license_source)) {
$this->errorMessage = 'Please select license';
}
$status = $this->errorMessage == '';
break;
case 'root_password':
// check, that password & verify password match
$password = $this->Application->GetVar('root_password');
$password_verify = $this->Application->GetVar('root_password_verify');
if ($password != $password_verify) {
$this->errorMessage = 'Passwords does not match';
}
elseif (mb_strlen($password) < 4) {
$this->errorMessage = 'Root Password must be at least 4 characters';
}
$status = $this->errorMessage == '';
break;
case 'choose_modules':
break;
case 'upgrade_modules':
$modules = $this->Application->GetVar('modules');
if (!$modules) {
$modules = Array ();
$this->errorMessage = 'Please select module(-s) to ' . ($this->currentStep == 'choose_modules' ? 'install' : 'upgrade');
}
// check interface module
$upgrade_data = $this->GetUpgradableModules();
if (array_key_exists('core', $upgrade_data) && !in_array('core', $modules)) {
// core can be upgraded, but isn't selected
$this->errorMessage = 'Please select "Core" as interface module';
}
$status = $this->errorMessage == '';
break;
}
return $status;
}
/**
* Perform installation step actions
*
*/
function Run()
{
if ($this->errorMessage) {
// was error during data validation stage
return ;
}
switch ($this->currentStep) {
case 'db_config':
case 'db_reconfig':
// store db configuration
$sql = 'SHOW COLLATION
LIKE \''.$this->toolkit->getSystemConfig('Database', 'DBCollation').'\'';
$collation_info = $this->Conn->Query($sql);
if ($collation_info) {
$this->toolkit->setSystemConfig('Database', 'DBCharset', $collation_info[0]['Charset']);
// database is already connected, that's why set collation on the fly
$this->Conn->Query('SET NAMES \''.$this->toolkit->getSystemConfig('Database', 'DBCharset').'\' COLLATE \''.$this->toolkit->getSystemConfig('Database', 'DBCollation').'\'');
}
$this->toolkit->SaveConfig();
if ($this->currentStep == 'db_config') {
if ($this->GetVar('UseExistingSetup')) {
// abort clean install and redirect to already_installed
$this->stepsPreset = 'already_installed';
break;
}
// import base data into new database, not for db_reconfig
$this->toolkit->RunSQL('/core/install/install_schema.sql');
$this->toolkit->RunSQL('/core/install/install_data.sql');
// create category using sql, because Application is not available here
$table_name = $this->toolkit->getSystemConfig('Database', 'TablePrefix') . 'IdGenerator';
$this->Conn->Query('UPDATE ' . $table_name . ' SET lastid = lastid + 1');
$resource_id = $this->Conn->GetOne('SELECT lastid FROM ' . $table_name);
if ($resource_id === false) {
$this->Conn->Query('INSERT INTO '.$table_name.' (lastid) VALUES (2)');
$resource_id = 2;
}
$fields_hash = Array (
'l1_Name' => 'Content', 'Filename' => 'Content', 'AutomaticFilename' => 0,
'CreatedById' => -1, 'CreatedOn' => time(), 'ResourceId' => $resource_id - 1,
'l1_Description' => 'Content', 'Status' => 4,
);
$this->Conn->doInsert($fields_hash, $this->toolkit->getSystemConfig('Database', 'TablePrefix') . 'Category');
$this->toolkit->SetModuleRootCategory('Core', $this->Conn->getInsertID());
// set module "Core" version after install (based on upgrade scripts)
$this->toolkit->SetModuleVersion('Core', 'core/');
// for now we set "In-Portal" module version to "Core" module version (during clean install)
$this->toolkit->SetModuleVersion('In-Portal', 'core/');
}
break;
case 'select_license':
// reset memory cache, when application is first available (on fresh install and clean reinstall steps)
$this->Application->HandleEvent($event, 'adm:OnResetMemcache');
$license_source = $this->GetVar('license_source');
switch ($license_source) {
case 1: // Download from Intechnic
break;
case 2: // Upload License File
$file_data = array_map('trim', file($_FILES['license_file']['tmp_name']));
if ((count($file_data) == 3) && $file_data[1]) {
$modules_helper =& $this->Application->recallObject('ModulesHelper');
/* @var $modules_helper kModulesHelper */
if ($modules_helper->verifyLicense($file_data[1])) {
$this->toolkit->setSystemConfig('Intechnic', 'License', $file_data[1]);
$this->toolkit->setSystemConfig('Intechnic', 'LicenseCode', $file_data[2]);
$this->toolkit->SaveConfig();
}
else {
$this->errorMessage = 'Invalid License File';
}
}
else {
$this->errorMessage = 'Invalid License File';
}
break;
case 3: // Use Existing License
$license_hash = $this->toolkit->getSystemConfig('Intechnic', 'License');
if ($license_hash) {
$modules_helper =& $this->Application->recallObject('ModulesHelper');
/* @var $modules_helper kModulesHelper */
if (!$modules_helper->verifyLicense($license_hash)) {
$this->errorMessage = 'Invalid or corrupt license detected';
}
}
else {
// happens, when browser's "Back" button is used
$this->errorMessage = 'Missing License File';
}
break;
case 4: // Skip License (Local Domain Installation)
if ($this->toolkit->sectionFound('Intechnic')) {
// remove any previous license information
$this->toolkit->setSystemConfig('Intechnic', 'License');
$this->toolkit->setSystemConfig('Intechnic', 'LicenseCode');
$this->toolkit->SaveConfig();
}
break;
}
break;
case 'download_license':
$license_login = $this->GetVar('login');
$license_password = $this->GetVar('password');
$license_id = $this->GetVar('licenses');
if (strlen($license_login) && strlen($license_password) && !$license_id) {
// Here we determine weather login is ok & check available licenses
$url_params = Array (
'login=' . md5($license_login),
'password=' . md5($license_password),
'version=' . $this->toolkit->GetMaxModuleVersion('core/'),
'domain=' . base64_encode($_SERVER['HTTP_HOST']),
);
$license_url = GET_LICENSE_URL . '?' . implode('&', $url_params);
$file_data = curl_post($license_url, '', null, 'GET');
if (!$file_data) {
// error connecting to licensing server
$this->errorMessage = 'Unable to connect to the Intechnic server! Please try again later!';
}
else {
if (substr($file_data, 0, 5) == 'Error') {
// after processing data server returned error
$this->errorMessage = substr($file_data, 6);
}
else {
// license received
if (substr($file_data, 0, 3) == 'SEL') {
// we have more, then one license -> let user choose
$this->SetVar('license_selection', base64_encode( substr($file_data, 4) )); // we received html with radio buttons with names "licenses"
$this->errorMessage = 'Please select which license to use';
}
else {
// we have one license
$this->toolkit->processLicense($file_data);
}
}
}
}
else if (!$license_id) {
// licenses were not queried AND user/password missing
$this->errorMessage = 'Incorrect Username or Password. If you don\'t know your username or password, contact Intechnic Support';
}
else {
// Here we download license
$url_params = Array (
'license_id=' . md5($license_id),
'dlog=' . md5($license_login),
'dpass=' . md5($license_password),
'version=' . $this->toolkit->GetMaxModuleVersion('core/'),
'domain=' . base64_encode($_SERVER['HTTP_HOST']),
);
$license_url = GET_LICENSE_URL . '?' . implode('&', $url_params);
$file_data = curl_post($license_url, '', null, 'GET');
if (!$file_data) {
// error connecting to licensing server
$this->errorMessage = 'Unable to connect to the Intechnic server! Please try again later!';
}
else {
if (substr($file_data, 0, 5) == 'Error') {
// after processing data server returned error
$this->errorMessage = substr($file_data, 6);
}
else {
$this->toolkit->processLicense($file_data);
}
}
}
break;
case 'select_domain':
$modules_helper =& $this->Application->recallObject('ModulesHelper');
/* @var $modules_helper kModulesHelper */
$license_hash = $this->toolkit->getSystemConfig('Intechnic', 'License');
if ($license_hash) {
// when license present, then extract domain from it
$license_hash = base64_decode($license_hash);
list ( , , $license_keys) = $modules_helper->_ParseLicense($license_hash);
$license_domain = $license_keys[0]['domain'];
}
else {
// when license missing, then use current domain
$license_domain = $_SERVER['HTTP_HOST'];
}
$domain = $this->GetVar('domain') == 1 ? $_SERVER['HTTP_HOST'] : str_replace(' ', '', $this->GetVar('other'));
if ($domain != '') {
if (strstr($domain, $license_domain) || $modules_helper->_IsLocalSite($domain)) {
$this->toolkit->setSystemConfig('Misc', 'Domain', $domain);
$this->toolkit->SaveConfig();
}
else {
$this->errorMessage = 'Domain name entered does not match domain name in the license!';
}
}
else {
$this->errorMessage = 'Please enter valid domain!';
}
break;
case 'root_password':
// update root password in database
$password = md5( md5($this->Application->GetVar('root_password')) . 'b38');
$config_values = Array (
'RootPass' => $password,
'Site_Path' => BASE_PATH.'/', // set Site_Path (for SSL & old in-portal code)
'Backup_Path' => FULL_PATH . $this->toolkit->getSystemConfig('Misc', 'WriteablePath') . DIRECTORY_SEPARATOR . 'backupdata',
'Smtp_AdminMailFrom' => 'portal@' . $this->toolkit->getSystemConfig('Misc', 'Domain')
);
$this->toolkit->saveConfigValues($config_values);
// login as "root", when no errors on password screen
$this->Application->SetVar('login', 'root');
$this->Application->SetVar('password', $this->Application->GetVar('root_password'));
$login_event = new kEvent('u.current:OnLogin');
$this->Application->HandleEvent($login_event);
// import base language for core (english)
$this->toolkit->ImportLanguage('/core/install/english');
// make sure imported language is set as active in session, created during installation
$this->Application->Session->SetField('Language', 1);
// set imported language as primary
$lang =& $this->Application->recallObject('lang.-item', null, Array('skip_autoload' => true));
/* @var $lang LanguagesItem */
$lang->Load(1); // fresh install => ID=1
$lang->setPrimary(true); // for Front-End
break;
case 'choose_modules':
// run module install scripts
$modules = $this->Application->GetVar('modules');
if ($modules) {
foreach ($modules as $module) {
$install_file = MODULES_PATH.'/'.$module.'/install.php';
if (file_exists($install_file)) {
include_once($install_file);
}
}
}
// update category cache
$updater =& $this->Application->recallObject('kPermCacheUpdater');
/* @var $updater kPermCacheUpdater */
$updater->OneStepRun();
break;
case 'post_config':
$this->toolkit->saveConfigValues( $this->GetVar('config') );
break;
case 'select_theme':
// 1. mark theme, that user is selected
$theme_id = $this->GetVar('theme');
$theme_table = $this->Application->getUnitOption('theme', 'TableName');
$theme_idfield = $this->Application->getUnitOption('theme', 'IDField');
$sql = 'UPDATE ' . $theme_table . '
SET Enabled = 1, PrimaryTheme = 1
WHERE ' . $theme_idfield . ' = ' . $theme_id;
$this->Conn->Query($sql);
$this->toolkit->rebuildThemes(); // rescan theme to create structure after theme is enabled !!!
if ($this->Application->isModuleEnabled('In-Portal')) {
// 2. compile theme stylesheets (only In-Portal uses them)
$css_table = $this->Application->getUnitOption('css', 'TableName');
$css_idfield = $this->Application->getUnitOption('css', 'IDField');
$sql = 'SELECT LOWER(Name) AS Name, ' . $css_idfield . '
FROM ' . $css_table;
$css_hash = $this->Conn->GetCol($sql, $css_idfield);
$css_item =& $this->Application->recallObject('css', null, Array('skip_autoload' => true));
/* @var $css_item StyleshetsItem */
foreach ($css_hash as $stylesheet_id => $theme_name) {
$css_item->Load($stylesheet_id);
$css_item->Compile();
$sql = 'UPDATE ' . $theme_table . '
SET StylesheetId = ' . $stylesheet_id . '
WHERE LOWER(Name) = ' . $this->Conn->qstr($theme_name);
$this->Conn->Query($sql);
}
}
// install theme dependent demo data
if ($this->Application->GetVar('install_demo_data')) {
$sql = 'SELECT Name
FROM ' . $theme_table . '
WHERE ' . $theme_idfield . ' = ' . $theme_id;
$theme_name = $this->Conn->GetOne($sql);
foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
if ($module_name == 'In-Portal') {
continue;
}
$this->toolkit->RunSQL('/themes' . '/' . $theme_name . '/' . $module_info['TemplatePath'] . '_install/install_data.sql', '{ThemeId}', $theme_id);
}
}
break;
case 'upgrade_modules':
// get installed modules from db and compare their versions to upgrade script
$modules = $this->Application->GetVar('modules');
if ($modules) {
$upgrade_data = $this->GetUpgradableModules();
$start_from_module = $this->GetVar('continue_from_module');
$start_from_query = $this->GetVar('continue_from_query');
if (!$start_from_query) $start_from_query = 0;
foreach ($modules as $module_name) {
if ($start_from_module && $module_name != $start_from_module) {
continue;
}
else {
$start_from_module = false; //otherwise it will skip all modules after the one we start with!
}
$module_info = $upgrade_data[$module_name];
$upgrades_file = sprintf(UPGRADES_FILE, $module_info['Path'], 'sql');
$sqls = file_get_contents($upgrades_file);
$version_mark = preg_replace('/(\(.*?\))/', $module_info['FromVersion'], VERSION_MARK);
// get only sqls from next (relative to current) version to end of file
$start_pos = strpos($sqls, $version_mark);
$sqls = substr($sqls, $start_pos);
preg_match_all('/'.VERSION_MARK.'/s', $sqls, $regs);
if (!$start_from_module) {
$this->RunUpgrades($module_info['Path'], $regs[1], 'before');
}
if (!$this->toolkit->RunSQLText($sqls, null, null, $start_from_query)) {
$this->errorMessage .= '<input type="hidden" name="continue_from_module" value="'.$module_name.'">';
$this->errorMessage .= '<input type="hidden" name="continue_from_query" value="'.$this->LastQueryNum.'">';
$this->errorMessage .= '<br/>Click Continue button below to skip this query and go further<br/>';
$this->Done();
}
$start_from_query = 0; // so that next module start from the beggining
$this->toolkit->ImportLanguage('/' . $module_info['Path'] . 'install/english', true);
$this->RunUpgrades($module_info['Path'], $regs[1], 'after'); // upgrade script could operate resulting language pack
// after upgrade sqls are executed update version and upgrade language pack
$this->toolkit->SetModuleVersion($module_name, false, $module_info['ToVersion']);
}
// for now we set "In-Portal" module version to "Core" module version (during upgrade)
if (in_array('core', $modules)) {
$this->toolkit->SetModuleVersion('In-Portal', false, $upgrade_data['core']['ToVersion']);
}
}
break;
case 'fix_paths':
$this->toolkit->saveConfigValues( $this->Application->GetVar('config') );
break;
case 'finish':
// delete cache
$this->toolkit->deleteCache();
// set installation finished mark
if ($this->Application->ConfigValue('InstallFinished') === false) {
$fields_hash = Array (
'VariableName' => 'InstallFinished',
'VariableValue' => 1,
);
$this->Conn->doInsert($fields_hash, TABLE_PREFIX.'ConfigurationValues');
}
break;
}
if ($this->errorMessage) {
// was error during run stage
return ;
}
$this->currentStep = $this->GetNextStep();
$this->InitStep(); // init next step (that will be shown now)
$this->InitApplication();
if ($this->currentStep == -1) {
// step after last step -> redirect to admin
$this->Application->Redirect('index', null, '', 'index.php');
}
}
/**
* Run upgrade PHP scripts for module with specified path
*
* @param string $module_path
* @param Array $versions
* @param string $mode upgrade mode = {before,after}
*/
function RunUpgrades($module_path, $versions, $mode)
{
static $upgrade_classes = Array ();
$upgrades_file = sprintf(UPGRADES_FILE, $module_path, 'php');
if (!file_exists($upgrades_file) || !$versions) {
return ;
}
if (!isset($upgrade_classes[$module_path])) {
// save class name, because 2nd time
// (in after call $upgrade_class variable will not be present)
include_once $upgrades_file;
$upgrade_classes[$module_path] = $upgrade_class;
}
$upgrade_object = new $upgrade_classes[$module_path]();
if (method_exists($upgrade_object, 'setToolkit')) {
$upgrade_object->setToolkit($this->toolkit);
}
foreach ($versions as $version) {
$upgrade_method = 'Upgrade_'.str_replace(Array ('.', '-'), '_', $version);
if (method_exists($upgrade_object, $upgrade_method)) {
$upgrade_object->$upgrade_method($mode);
}
}
}
/**
* Initialize kApplication
*
* @param bool $force initialize in any case
*/
function InitApplication($force = false)
{
if (($force || !in_array($this->currentStep, $this->skipApplicationSteps)) && !isset($this->Application)) {
// step is allowed for application usage & it was not initialized in previous step
global $start, $debugger, $dbg_options, $vars;
include_once(FULL_PATH.'/core/kernel/startup.php');
$this->Application =& kApplication::Instance();
$this->toolkit->Application =& kApplication::Instance();
$this->Application->Init();
$this->Conn =& $this->Application->GetADODBConnection();
$this->toolkit->Conn =& $this->Application->GetADODBConnection();
}
}
/**
* Show next step screen
*
*/
function Done($error_message = null)
{
if (isset($error_message)) {
$this->errorMessage = $error_message;
}
include_once (FULL_PATH.'/'.REL_PATH.'/install/incs/install.tpl');
if (isset($this->Application)) {
$this->Application->Done();
}
exit;
}
function ConnectToDatabase()
{
include_once FULL_PATH . '/core/kernel/db/db_connection.php';
$required_keys = Array ('DBType', 'DBUser', 'DBName');
foreach ($required_keys as $required_key) {
if (!$this->toolkit->getSystemConfig('Database', $required_key)) {
// one of required db connection settings missing -> abort connection
return false;
}
}
$this->Conn = new kDBConnection($this->toolkit->getSystemConfig('Database', 'DBType'), Array(&$this, 'DBErrorHandler'));
$this->Conn->Connect(
$this->toolkit->getSystemConfig('Database', 'DBHost'),
$this->toolkit->getSystemConfig('Database', 'DBUser'),
$this->toolkit->getSystemConfig('Database', 'DBUserPassword'),
$this->toolkit->getSystemConfig('Database', 'DBName')
);
// setup toolkit too
$this->toolkit->Conn =& $this->Conn;
return $this->Conn->errorCode == 0;
}
/**
* Checks if core is already installed
*
* @return bool
*/
function AlreadyInstalled()
{
$table_prefix = $this->toolkit->getSystemConfig('Database', 'TablePrefix');
$sql = 'SELECT VariableValue
FROM ' . $table_prefix . 'ConfigurationValues
WHERE VariableName = "InstallFinished"';
return $this->TableExists('ConfigurationValues') && $this->Conn->GetOne($sql);
}
function CheckDatabase($check_installed = true)
{
// perform various check type to database specified
// 1. user is allowed to connect to database
// 2. user has all types of permissions in database
if (mb_strlen($this->toolkit->getSystemConfig('Database', 'TablePrefix')) > 7) {
$this->errorMessage = 'Table prefix should not be longer than 7 characters';
return false;
}
// connect to database
$status = $this->ConnectToDatabase();
if ($status) {
// if connected, then check if all sql statements work
$sql_tests[] = 'DROP TABLE IF EXISTS test_table';
$sql_tests[] = 'CREATE TABLE test_table(test_col mediumint(6))';
$sql_tests[] = 'LOCK TABLES test_table WRITE';
$sql_tests[] = 'INSERT INTO test_table(test_col) VALUES (5)';
$sql_tests[] = 'UPDATE test_table SET test_col = 12';
$sql_tests[] = 'UNLOCK TABLES';
$sql_tests[] = 'ALTER TABLE test_table ADD COLUMN new_col varchar(10)';
$sql_tests[] = 'SELECT * FROM test_table';
$sql_tests[] = 'DELETE FROM test_table';
$sql_tests[] = 'DROP TABLE IF EXISTS test_table';
foreach ($sql_tests as $sql_test) {
$this->Conn->Query($sql_test);
if ($this->Conn->getErrorCode() != 0) {
$status = false;
break;
}
}
if ($status) {
// if statements work & connection made, then check table existance
if ($check_installed && $this->AlreadyInstalled()) {
$this->errorMessage = 'An In-Portal Database already exists at this location';
return false;
}
}
else {
// user has insufficient permissions in database specified
$this->errorMessage = 'Permission Error: ('.$this->Conn->getErrorCode().') '.$this->Conn->getErrorMsg();
return false;
}
}
else {
// was error while connecting
if (!$this->Conn) return false;
$this->errorMessage = 'Connection Error: ('.$this->Conn->getErrorCode().') '.$this->Conn->getErrorMsg();
return false;
}
return true;
}
/**
* Checks if all passed tables exists
*
* @param string $tables comma separated tables list
* @return bool
*/
function TableExists($tables)
{
$prefix = $this->toolkit->getSystemConfig('Database', 'TablePrefix');
$all_found = true;
$tables = explode(',', $tables);
foreach ($tables as $table_name) {
$sql = 'SHOW TABLES LIKE "'.$prefix.$table_name.'"';
if (count($this->Conn->Query($sql)) == 0) {
$all_found = false;
break;
}
}
return $all_found;
}
/**
* Returns modules list found in modules folder
*
* @return Array
*/
function ScanModules()
{
static $modules = null;
if (!isset($modules)) {
$modules = Array();
$fh = opendir(MODULES_PATH);
while ( ($sub_folder = readdir($fh)) ) {
$folder_path = MODULES_PATH . '/'.$sub_folder;
if ($sub_folder != '.' && $sub_folder != '..' && is_dir($folder_path)) {
// this is folder in MODULES_PATH directory
if (file_exists($folder_path.'/install.php') && file_exists($folder_path.'/install/install_schema.sql')) {
$install_order = trim( file_get_contents($folder_path . '/install/install_order.txt') );
$modules[$install_order] = $sub_folder;
}
}
}
}
// allows to control module install order
ksort($modules, SORT_NUMERIC);
return $modules;
}
/**
* Virtually place module under "modules" folder or it won't be recognized during upgrade to 5.1.0 version
*
* @param string $name
* @param string $path
* @param string $version
* @return string
*/
function getModulePath($name, $path, $version)
{
if ($name == 'Core') {
// don't transform path for Core module
return $path;
}
$version = $this->toolkit->ConvertModuleVersion($version);
if ($version < $this->toolkit->ConvertModuleVersion('5.1.0')) {
return 'modules/' . $path;
}
return $path;
}
/**
* Returns list of modules, that can be upgraded
*
*/
function GetUpgradableModules()
{
$ret = Array ();
foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
if ($module_name == 'In-Portal') {
// don't show In-Portal, because it shares upgrade scripts with Core module
continue;
}
$module_info['Path'] = $this->getModulePath($module_name, $module_info['Path'], $module_info['Version']);
$upgrades_file = sprintf(UPGRADES_FILE, $module_info['Path'], 'sql');
if (!file_exists($upgrades_file)) {
// no upgrade file
continue;
}
$sqls = file_get_contents($upgrades_file);
$versions_found = preg_match_all('/'.VERSION_MARK.'/s', $sqls, $regs);
if (!$versions_found) {
// upgrades file doesn't contain version definitions
continue;
}
$to_version = end($regs[1]);
$this_version = $this->toolkit->ConvertModuleVersion($module_info['Version']);
if ($this->toolkit->ConvertModuleVersion($to_version) > $this_version) {
// destination version is greather then current
foreach ($regs[1] as $version) {
if ($this->toolkit->ConvertModuleVersion($version) > $this_version) {
$from_version = $version;
break;
}
}
$version_info = Array (
'FromVersion' => $from_version,
'ToVersion' => $to_version,
);
$ret[ strtolower($module_name) ] = array_merge_recursive2($module_info, $version_info);
}
}
return $ret;
}
/**
* Returns content to show for current step
*
* @return string
*/
function GetStepBody()
{
$step_template = FULL_PATH.'/core/install/step_templates/'.$this->currentStep.'.tpl';
if (file_exists($step_template)) {
ob_start();
include_once ($step_template);
return ob_get_clean();
}
return '{step template "'.$this->currentStep.'" missing}';
}
/**
* Parses step information file, cache result for current step ONLY & return it
*
* @return Array
*/
function &_getStepInfo()
{
static $info = Array('help_title' => null, 'step_title' => null, 'help_body' => null, 'queried' => false);
if (!$info['queried']) {
$fdata = file_get_contents($this->StepDBFile);
$parser = xml_parser_create();
xml_parse_into_struct($parser, $fdata, $values, $index);
xml_parser_free($parser);
foreach ($index['STEP'] as $section_index) {
$step_data =& $values[$section_index];
if ($step_data['attributes']['NAME'] == $this->currentStep) {
$info['step_title'] = $step_data['attributes']['TITLE'];
if (isset($step_data['attributes']['HELP_TITLE'])) {
$info['help_title'] = $step_data['attributes']['HELP_TITLE'];
}
else {
// if help title not set, then use step title
$info['help_title'] = $step_data['attributes']['TITLE'];
}
$info['help_body'] = trim($step_data['value']);
break;
}
}
$info['queried'] = true;
}
return $info;
}
/**
* Returns particular information abou current step
*
* @param string $info_type
* @return string
*/
function GetStepInfo($info_type)
{
$step_info =& $this->_getStepInfo();
if (isset($step_info[$info_type])) {
return $step_info[$info_type];
}
return '{step "'.$this->currentStep.'"; param "'.$info_type.'" missing}';
}
/**
* Returns passed steps titles
*
* @param Array $steps
* @return Array
* @see kInstaller:PrintSteps
*/
function _getStepTitles($steps)
{
$fdata = file_get_contents($this->StepDBFile);
$parser = xml_parser_create();
xml_parse_into_struct($parser, $fdata, $values, $index);
xml_parser_free($parser);
$ret = Array ();
foreach ($index['STEP'] as $section_index) {
$step_data =& $values[$section_index];
if (in_array($step_data['attributes']['NAME'], $steps)) {
$ret[ $step_data['attributes']['NAME'] ] = $step_data['attributes']['TITLE'];
}
}
return $ret;
}
/**
* Returns current step number in active steps_preset.
* Value can't be cached, because same step can have different number in different presets
*
* @return int
*/
function GetStepNumber()
{
return array_search($this->currentStep, $this->steps[$this->stepsPreset]) + 1;
}
/**
* Returns step name to process next
*
* @return string
*/
function GetNextStep()
{
$next_index = $this->GetStepNumber();
if ($next_index > count($this->steps[$this->stepsPreset]) - 1) {
return -1;
}
return $this->steps[$this->stepsPreset][$next_index];
}
/**
* Returns step name, that was processed before this step
*
* @return string
*/
function GetPreviousStep()
{
$next_index = $this->GetStepNumber() - 1;
if ($next_index < 0) {
$next_index = 0;
}
return $this->steps[$this->stepsPreset][$next_index];
}
/**
* Prints all steps from active steps preset and highlights current step
*
* @param string $active_tpl
* @param string $passive_tpl
* @return string
*/
function PrintSteps($active_tpl, $passive_tpl)
{
$ret = '';
$step_titles = $this->_getStepTitles($this->steps[$this->stepsPreset]);
foreach ($this->steps[$this->stepsPreset] as $step_name) {
$template = $step_name == $this->currentStep ? $active_tpl : $passive_tpl;
$ret .= sprintf($template, $step_titles[$step_name]);
}
return $ret;
}
/**
* Installation error handler for sql errors
*
* @param int $code
* @param string $msg
* @param string $sql
* @return bool
* @access private
*/
function DBErrorHandler($code, $msg, $sql)
{
$this->errorMessage = 'Query: <br />'.htmlspecialchars($sql).'<br />execution result is error:<br />['.$code.'] '.$msg;
return true;
}
/**
* Installation error handler
*
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
* @param Array $errcontext
*/
function ErrorHandler($errno, $errstr, $errfile = '', $errline = '', $errcontext = '')
{
if ($errno == E_USER_ERROR) {
// only react on user fatal errors
$this->Done($errstr);
}
}
/**
* Checks, that given button should be visible on current installation step
*
* @param string $name
* @return bool
*/
function buttonVisible($name)
{
$button_visibility = Array (
'continue' => $this->GetNextStep() != -1 || ($this->stepsPreset == 'already_installed'),
'refresh' => in_array($this->currentStep, Array ('check_paths', 'security')),
'back' => in_array($this->currentStep, Array (/*'select_license',*/ 'download_license', 'select_domain')),
);
if ($name == 'any') {
foreach ($button_visibility as $button_name => $button_visible) {
if ($button_visible) {
return true;
}
}
return false;
}
return array_key_exists($name, $button_visibility) ? $button_visibility[$name] : true;
}
}

Event Timeline