Commit ee3eb8ac authored by Carsten  Rose's avatar Carsten Rose
Browse files

Feature 4255 / AttachmentsInEmail: Code ready for first test.

parent bc8b6a04
......@@ -28,7 +28,7 @@ try {
$download = new \qfq\Download();
// If all is fine - 'process()' never returns! The output file is delivered and PHP is stopped after that.
$data = $download->process();
$data = $download->process(STORE_SIP, OUTPUT_MODE_DIRECT);
} catch (qfq\CodeException $e) {
$data = $e->formatMessage();
......
......@@ -1102,8 +1102,6 @@ const SENDMAIL_TOKEN_DOWNLOAD_FILENAME = 'd';
const SENDMAIL_TOKEN_ATTACHMENT_URL = 'u';
const SENDMAIL_TOKEN_ATTACHMENT_URL_LOCAL = 'U';
const SENDMAIL_PREFIX_TMP_ATTACHMENT = '/tmp/qfq.attachment/';
// Report, BodyText
const TOKEN_SQL = 'sql';
const TOKEN_HEAD = 'head';
......@@ -1170,10 +1168,13 @@ const DOWNLOAD_MODE_PDF = 'pdf';
const DOWNLOAD_MODE_EXCEL = 'excel';
const DOWNLOAD_MODE_ZIP = 'zip';
const DOWNLOAD_EXPORT_FILENAME = '_exportFilename';
const DOWNLOAD_FILE_PREFIX = 'qfq.temp'; // temporary filename on server of single export file
const TMP_FILE_PREFIX = 'qfq.temp'; // temporary filename on server of single export file
const DOWNLOAD_OUTPUT_PDF = 'output';
const DOWNLOAD_SIP_ENCODE_PARAMETER = '_sip';
const OUTPUT_MODE_DIRECT = 'direct';
const OUTPUT_MODE_FILE = 'file';
// HTML2PDF
const HTML2PDF_PAGEID = 'id';
const HTML2PDF_PARAM_GET = 'paramGet';
......@@ -1183,6 +1184,7 @@ const SESSION_COOKIE_PREFEIX = 'qfq.cookie.'; // temporary 'cookie file' to forw
// Class: LINK
const PARAM_DELIMITER = '|';
const PARAM_TOKEN_DELIMITER = ':';
const TOKEN_URL = 'u';
const TOKEN_MAIL = 'm';
......@@ -1202,7 +1204,6 @@ const TOKEN_HELP = 'H';
const TOKEN_INFO = 'I';
const TOKEN_NEW = 'N';
const TOKEN_SHOW = 'S';
const TOKEN_SHOW = 'S';
const TOKEN_GLYPH = 'G';
const TOKEN_RENDER = 'r';
const TOKEN_TARGET = 'g';
......
<?php
/**
* Created by PhpStorm.
* User: crose
* Date: 1/28/16
* Time: 8:05 AM
*/
namespace qfq;
require_once(__DIR__ . '/../Constants.php');
class HelperFile {
/**
* Iterate over array $files. Delete only named files which are stored in '/tmp/' . DOWNLOAD_FILE_PREFIX.
*
* @param array $files
*/
public static function cleanTempFiles(array $files) {
foreach ($files as $file) {
if (self::isQfqTemp($file)) {
unlink($file);
}
$dir = dirname($file);
if (self::isQfqTemp($dir)) {
rmdir($dir);
}
}
}
/**
* Check against standard QFQ Temp location. If it is a Qfq Temp location, return true, else false.
*
* @param string $name Absolute filename.
*
* @return bool
*/
public static function isQfqTemp($name) {
$prefix = sys_get_temp_dir() . '/' . TMP_FILE_PREFIX;
$len = strlen($prefix);
return (substr($name, 0, $len) == $prefix);
}
/**
* Creates a temporary directory.
*/
public static function mktempdir() {
$name = tempnam(sys_get_temp_dir(), TMP_FILE_PREFIX);
unlink($name);
mkdir($name);
return $name;
}
}
\ No newline at end of file
......@@ -158,7 +158,7 @@ class Sanitize {
*
* @return mixed
*/
public static function safeFilename($filename) {
public static function safeFilename($filename, $flagBaseName = false) {
$search = array(
// Definition of German Umlauts START
'/ß/',
......@@ -177,6 +177,10 @@ class Sanitize {
'_',
);
if ($flagBaseName) {
$filename = basename($filename);
}
return preg_replace($search, $replace, $filename);
} // safeFilename()
......
......@@ -18,6 +18,7 @@ require_once(__DIR__ . '/../store/Store.php');
require_once(__DIR__ . '/../helper/OnArray.php');
require_once(__DIR__ . '/../helper/Logger.php');
require_once(__DIR__ . '/../helper/Sanitize.php');
require_once(__DIR__ . '/../helper/HelperFile.php');
require_once(__DIR__ . '/../report/Html2Pdf.php');
//require_once(__DIR__ . '/Link.php');
//require_once(__DIR__ . '/Sendmail.php');
......@@ -97,7 +98,8 @@ class Download {
break;
}
$concatFile = tempnam(sys_get_temp_dir(), DOWNLOAD_FILE_PREFIX);
$concatFile = tempnam(sys_get_temp_dir(), TMP_FILE_PREFIX);
if (false === $concatFile) {
throw new DownloadException('Error creating output file.', ERROR_DOWNLOAD_CREATE_NEW_FILE);
}
......@@ -179,27 +181,6 @@ class Download {
print file_get_contents($file);
}
/**
* Iterate over array $files. Delete all named files which exist in DOWNLOAD_TMP_DIR.
*
* @param array $files
*/
private function cleanTempFiles(array $files) {
if ($this->downloadDebugLog != '') {
return;
}
$prefix = sys_get_temp_dir() . '/' . DOWNLOAD_FILE_PREFIX;
$len = strlen($prefix);
foreach ($files as $file) {
if (substr($file, 0, $len) == $prefix) {
unlink($file);
}
}
}
/**
* Interprets $element and fetches corresponding content as file.
*
......@@ -248,7 +229,7 @@ class Download {
*/
private function zipFiles(array $files) {
$zipFile = tempnam(sys_get_temp_dir(), DOWNLOAD_FILE_PREFIX);
$zipFile = tempnam(sys_get_temp_dir(), TMP_FILE_PREFIX);
if (false === $zipFile) {
throw new DownloadException("Error creating output file.", ERROR_DOWNLOAD_CREATE_NEW_FILE);
}
......@@ -259,12 +240,12 @@ class Download {
throw new DownloadException("Error creating/opening new empty zip file: $zipFile", ERROR_IO_OPEN);
}
$len = strlen(DOWNLOAD_FILE_PREFIX);
$len = strlen(TMP_FILE_PREFIX);
$ii = 1;
foreach ($files AS $filename) {
$localname = substr($filename, strrpos($filename, '/') + 1);
if (substr($localname, 0, $len) == DOWNLOAD_FILE_PREFIX) {
if (substr($localname, 0, $len) == TMP_FILE_PREFIX) {
$localname = 'file-' . $ii;
$ii++;
}
......@@ -288,9 +269,12 @@ class Download {
*
* @param array $vars [ DOWNLOAD_EXPORT_FILENAME, DOWNLOAD_MODE, SIP_DOWNLOAD_PARAMETER ]
*
* @param string $outputMode OUTPUT_MODE_DIRECT | OUTPUT_MODE_FILE
* @return string Filename of the generated file. The filename only points to a real existing filename with $outputMode=OUTPUT_MODE_FILE
* @throws CodeException
* @throws DownloadException
*/
private function doElements(array $vars) {
private function doElements(array $vars, $outputMode) {
$tmpFiles = array();
......@@ -333,24 +317,49 @@ class Download {
break;
}
// Temporary files can be deleted.
if ($this->downloadDebugLog == '') {
HelperFile::cleanTempFiles($tmpFiles);
}
$exportFilename = empty($vars[DOWNLOAD_EXPORT_FILENAME]) ? DOWNLOAD_OUTPUT_PDF : $vars[DOWNLOAD_EXPORT_FILENAME];
switch ($outputMode) {
case OUTPUT_MODE_FILE:
break;
case OUTPUT_MODE_DIRECT:
$this->outputFile($filename, $exportFilename);
HelperFile::cleanTempFiles([$filename]);
break;
$tmpFiles[] = $filename;
$this->cleanTempFiles($tmpFiles);
default:
throw new CodeException('Unkown mode: ' . $outputMode, ERROR_UNKNOWN_MODE);
}
return $filename;
}
/**
* Process download as requested in $vars. Output is either directly send to the browser, or a file which has to be deleted later.
*
* @param string|array $vars If $config is not an array, get values from STORE_SIP. If $config is an array, take it.
* @param string $outputMode OUTPUT_MODE_DIRECT | OUTPUT_MODE_FILE
*
* @return string
* @throws CodeException
* @throws DownloadException
* @throws UserFormException
*/
public function process() {
public function process($vars, $outputMode = OUTPUT_MODE_DIRECT) {
if (!is_array($vars)) {
$vars = $this->store->getStore(STORE_SIP);
}
$this->doElements($vars);
return $this->doElements($vars, $outputMode);
}
}
......
......@@ -206,7 +206,7 @@ class Html2Pdf {
$urlPrint = escapeshellarg($url);
$wkhtmlToPdf = $this->config[SYSTEM_WKHTMLTOPDF];
$filename = tempnam(sys_get_temp_dir(), DOWNLOAD_FILE_PREFIX);
$filename = tempnam(sys_get_temp_dir(), TMP_FILE_PREFIX);
$filenameEscape = escapeshellarg($filename);
$cookieOptions = '--cookie-jar ' . escapeshellarg($this->sessionCookie->getFile());
......
......@@ -1301,16 +1301,16 @@ class Report {
];
// Token based string? No: just explode and all is fine. This is the deprecated fixed position setup
if (!isset($data[1]) || $data[1] != ':') {
return explode('|', $data);
if (!isset($data[1]) || $data[1] != PARAM_TOKEN_DELIMITER) {
return explode(PARAM_DELIMITER, $data);
}
$segments = explode('|', $data);
$segments = explode(PARAM_DELIMITER, $data);
$arr = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''];
$attachmentCombineOpen = false;
$attachment = array();
foreach ($segments AS $line) {
$piece = explode(':', $line, 2);
$piece = explode(PARAM_TOKEN_DELIMITER, $line, 2);
if (empty($piece[0])) {
throw new UserReportException ("Missing token in sendmail", ERROR_UNKNOWN_TOKEN);
......@@ -1334,7 +1334,9 @@ class Report {
case SENDMAIL_TOKEN_ATTACHMENT_URL_LOCAL:
case SENDMAIL_TOKEN_DOWNLOAD_FILENAME:
$val = empty($piece[1]) ? '' : $piece[1];
$attachment[] = [$piece[0] => $val];
// $attachment[] = [$piece[0] => $val];
$attachment[] = $piece[0] . PARAM_TOKEN_DELIMITER . $val;
break;
default:
......
......@@ -7,6 +7,7 @@ namespace qfq;
require_once(__DIR__ . '/../Constants.php');
require_once(__DIR__ . '/../database/Database.php');
require_once(__DIR__ . '/../store/Store.php');
require_once(__DIR__ . '/../helper/Sanitize.php');
class Sendmail {
......@@ -95,6 +96,7 @@ class Sendmail {
*/
private function sendEmail(array $mailConfig) {
$args = array();
$attachments = array();
foreach ($mailConfig as $key => $value) {
$mailConfig[$key] = Support::escapeDoubleTick($value);
......@@ -180,7 +182,7 @@ class Sendmail {
}
}
$this->attachmentsDelete($attachments);
HelperFile::cleanTempFiles($attachments);
}
/**
......@@ -251,29 +253,44 @@ class Sendmail {
}
/**
* Removes all temporary created files.
*
* @param array $attachments
* @return array
*/
private function attachmentsDelete(array $attachments) {
private function attachmentsBuild(array $attachments) {
$files = array();
$download = new Download();
$tmpDir = sys_get_temp_dir();
// Several attachments are possible. Process one by one.
foreach ($attachments as $attach) {
$vars = array();
$len = strlen(SENDMAIL_PREFIX_TMP_ATTACHMENT);
foreach ($attach as $element) {
foreach ($attachments as $file) {
if (SENDMAIL_PREFIX_TMP_ATTACHMENT == substr($file, 0, $len)) {
unlink($file);
$dir = dirname($file);
rmdir($dir);
}
$exportFilename = '';
foreach ($element as $key => $param) {
if (substr($param, 0, 2) == SENDMAIL_TOKEN_DOWNLOAD_FILENAME . PARAM_TOKEN_DELIMITER) {
$exportFilename = Sanitize::safeFilename(substr($param, 2), true);
unset($element[$key]);
}
}
$vars[SIP_DOWNLOAD_PARAMETER] = implode(PARAM_DELIMITER, $element);
$vars[DOWNLOAD_MODE] = DOWNLOAD_MODE_PDF;
private function attachmentsBuild(array $attachments) {
$files = array();
foreach ($attachments as $attach) {
if (isset($attach[0] && count()) )
$files[] =
$file = $download->process($vars, OUTPUT_MODE_FILE);
if (!empty($exportFilename)) {
$dir = HelperFile::mktempdir();
if (HelperFile::isQfqTemp($file)) {
rename($file, $exportFilename);
$file = $exportFilename;
}
}
$files[] = $file;
}
}
return $files;
}
}
......@@ -1096,32 +1096,32 @@ EOF;
$this->assertEquals($expect, $result);
// Single attachment 'token based'
$attach = [[['F' => 'fileadmin/test1.pdf']]];
$attach = [['F:fileadmin/test1.pdf']];
$result = $this->report->sendmailConvertToken('t:john@doe.com|f:jane@miller.com|s:Latest|b:Dear John|F:fileadmin/test1.pdf');
$expect = ['john@doe.com', 'jane@miller.com', 'Latest', 'Dear John', '', '', '', '', '', '', $attach, '', '', '', '', '', ''];
$this->assertEquals($expect, $result);
// Three individual attachment 'token based'
// $attach = [ [ [ 'F' => 'fileadmin/test1.pdf' ], [ 'F' => 'fileadmin/test2.pdf' ], [ 'F' => 'fileadmin/test3.pdf' ] ] ] ;
$attach = [[['F' => 'fileadmin/test1.pdf']], [['F' => 'fileadmin/test2.pdf']], [['F' => 'fileadmin/test3.pdf']]];
$attach = [['F:fileadmin/test1.pdf'], ['F:fileadmin/test2.pdf'], ['F:fileadmin/test3.pdf']];
$result = $this->report->sendmailConvertToken('t:john@doe.com|f:jane@miller.com|s:Latest|b:Dear John|F:fileadmin/test1.pdf|F:fileadmin/test2.pdf|F:fileadmin/test3.pdf');
$expect = ['john@doe.com', 'jane@miller.com', 'Latest', 'Dear John', '', '', '', '', '', '', $attach, '', '', '', '', '', ''];
$this->assertEquals($expect, $result);
// One individual attachment, one dual combined attachment
$attach = [[['F' => 'fileadmin/test1.pdf']], [['F' => 'fileadmin/test2.pdf'], ['F' => 'fileadmin/test3.pdf']]];
$attach = [['F:fileadmin/test1.pdf'], ['F:fileadmin/test2.pdf', 'F:fileadmin/test3.pdf']];
$result = $this->report->sendmailConvertToken('t:john@doe.com|f:jane@miller.com|s:Latest|b:Dear John|F:fileadmin/test1.pdf|C|F:fileadmin/test2.pdf|F:fileadmin/test3.pdf');
$expect = ['john@doe.com', 'jane@miller.com', 'Latest', 'Dear John', '', '', '', '', '', '', $attach, '', '', '', '', '', ''];
$this->assertEquals($expect, $result);
// One individual attachment, one quad combined attachment
$attach = [[['F' => 'fileadmin/test1.pdf']], [['F' => 'fileadmin/test2.pdf'], ['F' => 'fileadmin/test3.pdf'], ['u' => 'http://nzz.ch'], ['U' => 'export&a=100']]];
$attach = [['F:fileadmin/test1.pdf'], ['F:fileadmin/test2.pdf', 'F:fileadmin/test3.pdf', 'u:http://nzz.ch', 'U:export&a=100']];
$result = $this->report->sendmailConvertToken('t:john@doe.com|f:jane@miller.com|s:Latest|b:Dear John|F:fileadmin/test1.pdf|C|F:fileadmin/test2.pdf|F:fileadmin/test3.pdf|u:http://nzz.ch|U:export&a=100');
$expect = ['john@doe.com', 'jane@miller.com', 'Latest', 'Dear John', '', '', '', '', '', '', $attach, '', '', '', '', '', ''];
$this->assertEquals($expect, $result);
// Two quad combined attachmente
$attach = [[['d-' => 'output1.pdf'], ['F' => 'fileadmin/test11.pdf'], ['F' => 'fileadmin/test12.pdf'], ['u' => 'http://nzz.ch.1'], ['U' => 'export1&a=100']], [['d' => 'output2.pdf'], ['F' => 'fileadmin/test21.pdf'], ['F' => 'fileadmin/test22.pdf'], ['u' => 'http://nzz.ch.2'], ['U' => 'export2&a=100']]];
// Two quad combined attachments
$attach = [['d:output1.pdf', 'F:fileadmin/test11.pdf', 'F:fileadmin/test12.pdf', 'u:http://nzz.ch.1', 'U:export1&a=100'], ['d:output2.pdf', 'F:fileadmin/test21.pdf', 'F:fileadmin/test22.pdf', 'u:http://nzz.ch.2', 'U:export2&a=100']];
$result = $this->report->sendmailConvertToken('t:john@doe.com|f:jane@miller.com|s:Latest|b:Dear John|C|d:output1.pdf|F:fileadmin/test11.pdf|F:fileadmin/test12.pdf|u:http://nzz.ch.1|U:export1&a=100|C|d:output2.pdf|F:fileadmin/test21.pdf|F:fileadmin/test22.pdf|u:http://nzz.ch.2|U:export2&a=100');
$expect = ['john@doe.com', 'jane@miller.com', 'Latest', 'Dear John', '', '', '', '', '', '', $attach, '', '', '', '', '', ''];
$this->assertEquals($expect, $result);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment