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

F4922 / Excel Import: First, untested, version of generic excel export

parent c9fb2a67
Pipeline #670 passed with stage
in 1 minute and 34 seconds
......@@ -22,6 +22,7 @@ maintainer-clean:
archive: clean qfq.zip
qfq.zip:
cd extension/Resources/Private/vendor/phpoffice; rm -rf phpspreadsheet/{C*,bin,c*,d*,m*,p*,sa*,.g*,.p*,.s*,.t*}
cd extension; zip -r ../$@ $(EXTENSION_CONTENT)
clean:
......
......@@ -26,7 +26,7 @@ $error = '';
try {
try {
$download = new \qfq\Download();
$download = new Download();
// If all is fine: process() will output file via print() !!
$error = $download->process(STORE_SIP, OUTPUT_MODE_DIRECT);
......
......@@ -17,6 +17,7 @@ require_once(__DIR__ . '/../qfq/exceptions/ShellException.php');
require_once(__DIR__ . '/../qfq/Evaluate.php');
require_once(__DIR__ . '/../qfq/report/SendMail.php');
require_once(__DIR__ . '/../qfq/helper/Support.php');
require_once(__DIR__ . '/../qfq/helper/DownloadPage.php');
/**
* Class AutoCron
......@@ -154,21 +155,9 @@ class AutoCron {
if (!empty($job[AUTOCRON_CONTENT])) {
// If the URL does not start with 'http...': prefix with the site config.
if (substr($job[AUTOCRON_CONTENT], 0, 4) != 'http') {
// We need to prefix
$baseUrl = $this->store->getVar(SYSTEM_BASE_URL, STORE_SYSTEM);
$job[AUTOCRON_CONTENT] = $baseUrl . $job[AUTOCRON_CONTENT];
}
$baseUrl = $this->store->getVar(SYSTEM_BASE_URL, STORE_SYSTEM);
// Download page
$ctx = array(
"ssl" => array(
"verify_peer" => false,
"verify_peer_name" => false,
),
);
$page = file_get_contents($job[AUTOCRON_CONTENT], false, stream_context_create($ctx));
$page = DownloadPage::getContent($job[AUTOCRON_CONTENT], $baseUrl);
if ($page === false) {
$job[AUTOCRON_LAST_STATUS] = htmlspecialchars(AUTOCRON_STATUS_ERROR . 'failed to fetch "' . $job[AUTOCRON_CONTENT] . '"');
......
......@@ -258,6 +258,9 @@ const ERROR_DOWNLOAD_UNEXPECTED_MIME_TYPE = 1703;
const ERROR_DOWNLOAD_UNEXPECTED_NUMBER_OF_SOURCES = 1704;
const ERROR_DOWNLOAD_FILE_NOT_READABLE = 1705;
// Excel
const ERROR_EXCEL_POSITION_ARGUMENT_EMPTY = 1800;
// KeyValueParser
const ERROR_KVP_VALUE_HAS_NO_KEY = 1900;
......@@ -1114,6 +1117,8 @@ const HTML_INPUT_TYPE_NUMBER = 'number';
const SHEBANG_REPORT = '#!report';
CONST TOKEN_COMMENT = '#';
// SUPPORT
const PARAM_T3_ALL = 't3 all';
const PARAM_T3_NO_ID = "t3 no id";
......@@ -1336,6 +1341,19 @@ const JSON_TEXT = 'text';
const OUTPUT_MODE_DIRECT = 'direct';
const OUTPUT_MODE_FILE = 'file';
const EXCEL_WORKSHEET = 'worksheet';
const EXCEL_MODE = 'mode';
const EXCEL_MODE_INSERT = 'insert';
const EXCEL_MODE_OVERWRITE = 'overwrite';
const EXCEL_POSITION = 'position';
const EXCEL_ROW = 'row';
const EXCEL_STRING = 'string';
const EXCEL_NUMBER = 'number';
const EXCEL_CURRENCY = 'currency';
const EXCEL_DATE = 'date';
const EXCEL_DATETIME = 'datetime';
const EXCEL_TIME = 'time';
const MONITOR_TAIL_DEFAULT = 30;
const MONITOR_APPEND_DEFAULT = 0;
const MONITOR_INTERVAL_DEFAULT = 1000;
......@@ -1520,4 +1538,5 @@ const DND_HOVER_POSITION = 'hoverPosition';
const DND_COLUMN_ID = 'id';
const DND_COLUMN_ORD = 'ord';
const DND_COLUMN_ORD_NEW = 'ordNew';
const DND_DATA_DND_API = 'data-dnd-api';
\ No newline at end of file
const DND_DATA_DND_API = 'data-dnd-api';
<?php
/**
* Created by PhpStorm.
* User: crose
* Date: 7/8/18
* Time: 11:17 PM
*/
namespace qfq;
/**
* Class DownloadPage
* @package qfq
*/
class DownloadPage {
/**
* Retrieve the content of the given page and returns them.
* $page
*
* @param string $page - Full URL like 'https://example.com' or T3 specific like 'export&s=badcaffee1234'
* @param string $baseUrl - baserUrl from Configfile
* @return bool|string
*/
public static function getContent($page, $baseUrl) {
if (empty($page)) {
return false;
}
if (substr($page, 0, 4) != 'http') {
if (empty($baseUrl)) {
return false;
}
// We need to prefix
$page = $baseUrl . $page;
}
// Download page
$ctx = array(
"ssl" => array(
"verify_peer" => false,
"verify_peer_name" => false,
),
);
return file_get_contents($page, false, stream_context_create($ctx));
}
}
\ No newline at end of file
......@@ -35,6 +35,14 @@ class HelperFile {
}
}
/**
* Returns a uniqe (use for temporary) filename, prefixed with QFQ TMP_FILE_PREFIX
*
* @return bool|string
*/
public static function tempnam(){
return tempnam(sys_get_temp_dir(), TMP_FILE_PREFIX);
}
/**
* Check against standard QFQ Temp location. If it is a Qfq Temp location, return true, else false.
......
......@@ -84,4 +84,16 @@ class OnString {
return $str;
}
/**
* Split an Excel position string line 'A1' oder 'ACD4567' to [ 'A', '1'], resp. [ 'ACD', '4567' ]
*
* @param $pos
* @return mixed
*/
public static function splitExcelPos($pos){
preg_match_all('/(\w)|(\d)/', $pos, $matches);
return $matches;
}
}
......@@ -23,6 +23,9 @@ require_once(__DIR__ . '/../report/Html2Pdf.php');
require_once(__DIR__ . '/Thumbnail.php');
require_once(__DIR__ . '/Monitor.php');
require_once(__DIR__ . '/../exceptions/DownloadException.php');
require_once(__DIR__ . '/Excel.php');
require_once(__DIR__ . '/../helper/DownloadPage.php');
//require_once(__DIR__ . '/../Evaluate.php');
//require_once(__DIR__ . '/../helper/KeyValueStringParser.php');
//
......@@ -120,8 +123,7 @@ class Download {
break;
}
$concatFile = tempnam(sys_get_temp_dir(), TMP_FILE_PREFIX);
$concatFile = HelperFile::tempnam();
if (false === $concatFile) {
throw new DownloadException('Error creating output file.', ERROR_DOWNLOAD_CREATE_NEW_FILE);
}
......@@ -202,16 +204,20 @@ class Download {
}
/**
* Interprets $element and fetches corresponding content as file.
* Interprets $element and fetches corresponding content, either as a file or the content in a variable.
*
* @param string $element - U:id=myExport&r=12, u:http://www.nzz.ch/issue?nr=21, f:fileadmin/sample.pdf
*
* @param string $downloadMode - DOWNLOAD_MODE_EXCEL | ....
* @param string $tmpData - With $downloadMod=DOWNLOAD_MODE_EXCEL, this contains the rendered code from the give T3 page.
* @return string filename - already ready or fresh exported. Fresh exported needs to be deleted later.
* @throws CodeException
* @throws DownloadException
* @throws \exception
* @throws UserFormException
*/
private function getElement($element) {
private function getElement($element, $downloadMode, &$tmpData) {
$filename='';
$arr = explode(':', $element, 2);
if (count($arr) != 2) {
throw new DownloadException('Missing parameter for "' . $element . '"', ERROR_MISSING_REQUIRED_PARAMETER);
......@@ -224,7 +230,13 @@ class Download {
case TOKEN_URL:
case TOKEN_URL_PARAM:
case TOKEN_PAGE:
$filename = $this->html2pdf->page2pdf($token, $value);
if ($downloadMode == DOWNLOAD_MODE_EXCEL) {
$baseUrl = $this->store->getVar(SYSTEM_BASE_URL, STORE_SYSTEM);
$tmpData = DownloadPage::getContent($value, $baseUrl);
} else {
$filename = $this->html2pdf->page2pdf($token, $value);
}
break;
case TOKEN_FILE:
......@@ -250,7 +262,7 @@ class Download {
*/
private function zipFiles(array $files) {
$zipFile = tempnam(sys_get_temp_dir(), TMP_FILE_PREFIX);
$zipFile = HelperFile::tempnam();
if (false === $zipFile) {
throw new DownloadException("Error creating output file.", ERROR_DOWNLOAD_CREATE_NEW_FILE);
}
......@@ -264,15 +276,14 @@ class Download {
$len = strlen(TMP_FILE_PREFIX);
$ii = 1;
foreach ($files AS $filename) {
$localname = substr($filename, strrpos($filename, '/') + 1);
$localName = substr($filename, strrpos($filename, '/') + 1);
if (substr($localname, 0, $len) == TMP_FILE_PREFIX) {
$localname = 'file-' . $ii;
if (substr($localName, 0, $len) == TMP_FILE_PREFIX) {
$localName = 'file-' . $ii;
$ii++;
}
// $localname = $this->targetFilenameExtension($filename, $localname, $mimetype);
$zip->addFile($filename, $localname);
$zip->addFile($filename, $localName);
}
$zip->close();
......@@ -280,8 +291,8 @@ class Download {
}
/**
* exportFilename=<new filename>
* mode=file | pdf | excel | thumbnail | monitor - default is 'file' in case of only one or 'pdf' in case of multiple sources.
* $vars[DOWNLOAD_EXPORT_FILENAME] - Optional. '<new filename>'
* $vars[DOWNLOAD_MODE] - Optional. file | pdf | excel | thumbnail | monitor - default is a) 'file' in case of only one or b) 'pdf' in case of multiple sources.
* HTML to PDF | Excel
* <i>_id=<Typo3 pageId>
* <i>_<key>=<value i>
......@@ -293,6 +304,7 @@ class Download {
* @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 DbException
* @throws DownloadException
* @throws UserFormException
* @throws UserReportException
......@@ -323,9 +335,14 @@ class Download {
$elements = explode(PARAM_DELIMITER, $vars[SIP_DOWNLOAD_PARAMETER]);
// Get all files
// Get all files / content
$tmpData=array();
foreach ($elements as $element) {
$tmpFiles[] = $this->getElement($element);
$data='';
$tmpFiles[] = $this->getElement($element, $downloadMode, $data);
if(!empty($data)){
$tmpData[]=$data;
}
}
// Export, Concat File(s)
......@@ -338,7 +355,15 @@ class Download {
break;
case DOWNLOAD_MODE_EXCEL:
throw new DownloadException("Not implemented: $downloadMode", ERROR_NOT_IMPLEMENTED);
$excel = new Excel();
$filename = $excel->process($tmpFiles, $tmpData);
if (empty($vars[DOWNLOAD_EXPORT_FILENAME])) {
if (HelperFile::isQfqTemp($filename)) {
$vars[DOWNLOAD_EXPORT_FILENAME] = DOWNLOAD_OUTPUT_FILENAME . ".xlsx";
} else {
$vars[DOWNLOAD_EXPORT_FILENAME] = basename($filename);
}
}
break;
case DOWNLOAD_MODE_FILE:
......@@ -349,7 +374,9 @@ class Download {
break;
case DOWNLOAD_MODE_PDF:
$filename = $this->concatPdfFiles($tmpFiles);
// try to find a meaningful filename
if (empty($vars[DOWNLOAD_EXPORT_FILENAME])) {
if (count($tmpFiles) > 1) {
......@@ -377,7 +404,7 @@ class Download {
case OUTPUT_MODE_DIRECT:
$this->outputFile($filename, $vars[DOWNLOAD_EXPORT_FILENAME]);
HelperFile::cleanTempFiles([$filename]);
$filename='';
$filename = '';
break;
default:
......@@ -410,6 +437,7 @@ class Download {
*
* @return string
* @throws CodeException
* @throws DbException
* @throws DownloadException
* @throws UserFormException
* @throws UserReportException
......
<?php
/**
* Created by PhpStorm.
* User: crose
* Date: 8/7/18
* Time: 11:32 AM
*
* Check: CODING.md > Download
*/
namespace qfq;
//use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
require_once(__DIR__ . '/../Constants.php');
//require_once(__DIR__ . '/../store/Session.php');
//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__ . '/Thumbnail.php');
//require_once(__DIR__ . '/Monitor.php');
require_once(__DIR__ . '/../exceptions/DownloadException.php');
//require_once(__DIR__ . '/../Evaluate.php');
//require_once(__DIR__ . '/../helper/KeyValueStringParser.php');
//
require __DIR__ . '/../../../Resources/Private/vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
const COLUMN = 0;
const ROW = 1;
/**
* Class Excel
* @package qfq
*/
class Excel {
/**
* @param array $files
* @param array $data
* @return string
* @throws downloadException
*/
public function process(array $files, array $data) {
// Check that there are some sources
if (empty($files) && empty($data)) {
throw new downloadException("No source found to offer to download", ERROR_DOWNLOAD_NOTHING_TO_DO);
}
// Check if there are only File(s) given: return them.
if (empty($data)) {
if (count($files) == 1) {
return $files[0]; // Return the given file.
} else {
throw new downloadException("Only one source file per Excel download supported", ERROR_DOWNLOAD_UNEXPECTED_NUMBER_OF_SOURCES);
}
}
// Use Template or NEW
if (isset($files[0])) {
// Open template
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($files[0]);
} else {
// Create new
$spreadsheet = new Spreadsheet();
}
// Iterate over all sources.
foreach($data as $page) {
$spreadsheet = $this->fillSpreadsheet($spreadsheet, $page);
}
$filename = HelperFile::tempnam();
$writer = new Xlsx($spreadsheet);
$writer->save($filename);
return $filename;
}
/**
* @param Spreadsheet $spreadsheet
* @param string $data
* @return Spreadsheet
* @throws downloadException
*/
private function fillSpreadsheet(Spreadsheet $spreadsheet, $data) {
$worksheet = $spreadsheet->getActiveSheet();
$pos = 'A1';
$tmpArr = OnString::splitExcelPos($pos);
$posColumn = $tmpArr[COLUMN];
$posRow = $tmpArr[ROW];
$arr = explode(PHP_EOL, $data);
foreach ($arr as $line) {
$line = trim($line);
// Skip empty /comment lines
if (empty($line) || $line[0] == TOKEN_COMMENT) {
continue;
}
$token = explode(':', $line, 2);
switch ($token[0]) {
case EXCEL_WORKSHEET:
throw new downloadException("Not implemented: " . $token[0], ERROR_NOT_IMPLEMENTED);
case EXCEL_MODE:
throw new downloadException("Not implemented: " . $token[0], ERROR_NOT_IMPLEMENTED);
case EXCEL_POSITION:
if (empty($token[1])) {
throw new downloadException("Position argument is empty", ERROR_EXCEL_POSITION_ARGUMENT_EMPTY);
}
$pos = $token[1];
$tmpArr = OnString::splitExcelPos($pos);
$posColumn = $tmpArr[COLUMN];
$posRow = $tmpArr[ROW];
break;
case EXCEL_ROW:
$posRow++;
break;
case EXCEL_STRING:
$worksheet->setCellValue($posColumn . $posRow, $token[1]);
$posColumn = $this->nextColumn($posColumn);
break;
case EXCEL_NUMBER:
case EXCEL_CURRENCY:
case EXCEL_DATE:
case EXCEL_DATETIME:
case EXCEL_TIME:
throw new downloadException("Not implemented: " . $token[0], ERROR_NOT_IMPLEMENTED);
}
}
return $spreadsheet;
}
/**
* Increment the alpha string. If 'Z' is reached on the last position, it appends 'A'.
* 'A' > 'B'
* 'Z' > 'ZA'
* 'ZA' > 'ZB'
*
* @param $column
* @return string - incremented column.
*/
private function nextColumn($column) {
$len = strlen($column);
$c = $column[$len - 1];
if ($c == 'Z') {
$column .= 'A';
} else {
$c = ++$c;
$column[$len - 1] = $c;
}
return $column;
}
}
\ No newline at end of file
......@@ -93,7 +93,7 @@ class Html2Pdf {
private function readCleanGetParam(array $get) {
$param = array();
$pattern = '^[\-_\.,;:\/a-zA-Z0-9]*$'; // ':alnum:' does not work here in FF
$pattern = '^[\-_\.,;:\/a-zA-Z0-9]*$';
foreach ($get as $key => $value) {
if (preg_match("/$pattern/", $value) === 1) {
......@@ -178,7 +178,7 @@ class Html2Pdf {
break;
case TOKEN_URL_PARAM:
case TOKEN_PAGE:
$host = $this->config[SYSTEM_BASE_URL];
$host = $this->config[SYSTEM_BASE_URL];
$urlParamString = $url;
break;
default:
......@@ -207,7 +207,7 @@ class Html2Pdf {
$urlPrint = escapeshellarg($url);
$wkhtmlToPdf = $this->config[SYSTEM_CMD_WKHTMLTOPDF];
$filename = tempnam(sys_get_temp_dir(), TMP_FILE_PREFIX);
$filename = HelperFile::tempnam();
$filenameEscape = escapeshellarg($filename);
$cookieOptions = '--cookie-jar ' . escapeshellarg($this->sessionCookie->getFile());
......
Markdown is supported
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