# PLUGIN PREVIEW BY TEXTPATTERN.INFO
if (txpinterface === 'public') {
register_callback('ext_file_attach', 'comconnect.deliver');
// Register tags if necessary.
if (class_exists('\Textpattern\Tag\Registry')) {
Txp::get('\Textpattern\Tag\Registry')
->register('com_connect_file');
}
}
/**
* Callback hook for com_connect to handle attaching the file.
*
* @param string $evt Textpattern event
* @param string $stp Textpattern step (action)
* @param array $payload Delivery content, passed in from com_connect
*/
function ext_file_attach($evt, $stp, &$payload)
{
global $com_connect_error;
$file_attached = false;
foreach ($payload['fields'] as $key => $value) {
if (strpos($key, 'ext_file_') === 0) {
$file_size = $value['size'];
$file_type = $value['type'];
$file_name = $value['name'];
$file_temp = $value['tmp_name'];
$file_error = $value['error'];
$field = current($value);
$out = '';
// Check for errors.
// This is rarely triggered unfortunately because most browsers validate file sizes and types,
// throwing empty arrays or just silently failing on our behalf. Grrr.
// Not only that, com_connect doesn't know what to do with any error strings at the moment so it'd just
// report a generic 'sorry' message.
if ($file_error > 0) {
switch ($file_error) {
case 1:
case 2:
$max = ext_file_max();
$com_connect_error[] = gTxt('com_connect_maxval_warning', array('{field}' => $hlabel, '{value}' => $max));
$out = 'comconnect.fail';
break;
case 3:
// File only partially uploaded.
$out = 'comconnect.fail';
break;
case 4:
// No file uploaded: no worries, ignore it. Field is probably not required.
break;
case 6:
// Missing temporary folder.
$out = 'comconnect.fail';
break;
}
return $out;
} else {
$handle = fopen($file_temp, 'r');
$content = fread($handle, $file_size);
fclose($handle);
// Only one file can be attached per message.
$encoded_content = chunk_split(base64_encode($content));
$file_attached = true;
}
// TODO: delete temp file or does PHP do it?
break;
}
}
if ($file_attached) {
$fileBoundary = md5('boundary1');
$textBoundary = md5('boundary2');
$sep = PHP_EOL;
$payload['headers']['MIME-Version'] = '1.0';
$payload['headers']['content_type'] = 'multipart/mixed; boundary=' . $fileBoundary;
$payload['body'] = '--' . $fileBoundary . $sep
. 'Content-Type: multipart/alternative; boundary=' . $textBoundary . $sep
. '--' . $textBoundary . $sep
. 'Content-Type: text/plain; charset=utf-8' . $sep
. $payload['body'] . $sep
. '--' . $textBoundary . '--' . $sep
. '--' . $fileBoundary . $sep
. 'Content-Type:' . $file_type . '; '
. 'name="' . $file_name . '"' . $sep
. 'Content-Transfer-Encoding:base64' . $sep
. 'Content-Disposition:attachment; '
. 'filename="' . $file_name . '"' . $sep
. 'X-Attachment-Id:' . rand(1000, 9000) . $sep . $sep
. $encoded_content . $sep
. '--' . $fileBoundary . '--';
}
// Back to com_connect to mail out the modified content
return;
}
/**
* Tag: Render a file input field.
*
* @param array $atts Tag attributes
* @return string HTML
*/
function com_connect_file($atts)
{
global $com_connect_error, $com_connect_submit, $com_connect_flags;
$max_upload_size = ext_file_max();
extract(com_connect_lAtts(array(
'accept' => '',
'break' => br,
'class' => 'comFile',
'html_form' => $com_connect_flags['this_form'],
'isError' => '',
'label' => gTxt('com_connect_file'),
'label_position' => 'before',
'max' => $max_upload_size,
'min' => 0,
'placeholder' => '',
'required' => $com_connect_flags['required'],
'type' => 'file',
), $atts));
$doctype = get_pref('doctype', 'xhtml');
if (empty($name)) {
$name = com_connect_label2name($label);
}
if ($com_connect_submit) {
$hlabel = txpspecialchars($label);
if (array_key_exists($name, $_FILES)) {
$fileInfo = $_FILES[$name];
$acceptableTypes = do_list($accept);
if ($fileInfo['size'] && ($fileInfo['size'] > $max)) {
$com_connect_error[] = gTxt('com_connect_maxval_warning', array('{field}' => $hlabel, '{value}' => $max));
$isError = "errorElement";
} elseif ($accept && $fileInfo['name'] !== '') {
$isOK = false;
foreach ($acceptableTypes as $acceptable) {
if (strpos($acceptable, '.') === 0) {
// It's a file extension check.
if (strpos($fileInfo['name'], $acceptable) !== false) {
$isOK = true;
break;
}
} else {
// It's a MIME type check.
if (in_array($fileInfo['type'], $acceptableTypes)) {
$isOK = true;
break;
}
}
}
if ($isOK) {
com_connect_store('ext_file_' . $name, $label, $fileInfo);
} else {
$com_connect_error[] = gTxt('ext_file_invalid_type', array('{field}' => $hlabel));
$isError = "errorElement";
}
} else {
com_connect_store('ext_file_' . $name, $label, $fileInfo);
}
} elseif ($required && empty($_FILES)) {
$com_connect_error[] = gTxt('com_connect_maxval_warning', array('{field}' => $hlabel, '{value}' => $max));
$isError = "errorElement";
} elseif ($required) {
$com_connect_error[] = gTxt('com_connect_field_missing', array('{field}' => $hlabel));
$isError = "errorElement";
}
}
// Core attributes.
$attr = com_connect_build_atts(array(
'accept' => $accept,
'id' => (isset($id) ? $id : $name),
'name' => $name,
'type' => $type,
));
if ($min) {
$attr['minlength'] = 'minlength="' . intval($min) . '"';
}
if ($max) {
$attr['maxlength'] = 'maxlength="' . intval($max) . '"';
}
// HTML5 attributes.
$required = ($required) ? 'required' : '';
if ($doctype !== 'xhtml') {
$attr += com_connect_build_atts(array(
'form' => $html_form,
'placeholder' => $placeholder,
'required' => $required,
));
}
// Global attributes.
$attr += com_connect_build_atts($com_connect_globals, $atts);
$classes = array();
foreach (array($class, ($required ? 'comRequired' : ''), $isError) as $cls) {
if ($cls) {
$classes[] = $cls;
}
}
$classStr = ($classes ? ' class="' . implode(' ', $classes) . '"' : '');
$labelStr = '<label for="' . $name . '"' . $classStr . '>' . txpspecialchars($label) . '</label>';
return ($label_position === 'before' ? $labelStr . $break : '') .
'<input' . $classStr . ($attr ? ' ' . implode(' ', $attr) : '') . ' />' .
($label_position === 'after' ? $break . $labelStr : '') .
script_js(<<<EOJS
function com_ext_attach_handler() {
var b = document.getElementById('{$html_form}');
b.setAttribute("enctype", "multipart/form-data");
}
if (document.readyState != 'loading') {
com_ext_attach_handler();
} else if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', com_ext_attach_handler);
} else document.attachEvent('onreadystatechange', function() {
if (document.readyState=='interactive') {
com_ext_attach_handler()();
}
});
EOJS
);
}
// Returns a file size limit in bytes.
function ext_file_max()
{
$max_size = -1;
if ($max_size < 0) {
// Start with post_max_size.
$max_size = ext_file_parse_size(ini_get('post_max_size'));
// If upload_max_size is less, then reduce. Except if
// zero, which indicates no limit.
$upload_max = ext_file_parse_size(ini_get('upload_max_filesize'));
if ($upload_max > 0 && $upload_max < $max_size) {
$max_size = $upload_max;
}
// If Textpattern's file_max_upload_size is less, then reduce. Except if
// zero, which indicates no limit.
$upload_max = get_pref('file_max_upload_size');
if ($upload_max > 0 && $upload_max < $max_size) {
$max_size = $upload_max;
}
}
return $max_size;
}
/**
* Convert a size value with suffix (K, M, G, T, etc) to bytes.
*/
function ext_file_parse_size($size)
{
$unit = preg_replace('/[^bkmgtpezy]/i', '', $size); // Remove the non-unit characters from the size.
$size = preg_replace('/[^0-9\.\,]/', '', $size); // Remove the non-numeric characters from the size.
if ($unit) {
// Find the position of the unit in the ordered string which is
// the power of magnitude to multiply a kilobyte by.
return round($size * pow(1024, stripos('bkmgtpezy', $unit[0])));
} else {
return round($size);
}
}