Commit 9ca14668 authored by Elias Villiger's avatar Elias Villiger
Browse files

Merge branch 'master' into 5467-min-max-parameters

parents 1b25afad e5bb6217
Pipeline #868 passed with stage
in 1 minute and 51 seconds
......@@ -37,6 +37,29 @@ Bug Fixes
^^^^^^^^^
Version 18.9.0
--------------
Date: 07.09.2018
Features
^^^^^^^^
* #6357 / Save pdf on server
* #5381 / Stored procedures can be called from QFQ Reports
* F4996 / Log QFQ Update with timestamp.
* #6255 / Inline Report Editing - now with SIP and save.php api
Bug Fixes
^^^^^^^^^
* #6465 / Allow newlines in form action queries (e.g. sqlInsert)
* #4654 / Better FE color highlighting (UX)
* #5689 / Default BS Columns for FormElement match Form setting
* #6484 / Download Links mit css class
* #6576 / download buttons are now rendered disabled with render mode r:3
* Cookie Sitepath: wrong detected in case of API calls.
Version 18.8.2
--------------
......
......@@ -40,15 +40,16 @@ Neue Versionsnummer
make t3sphinx (dadurch fallen Fehler in der RESTdoc Syntax auf)
5) **Update Version & Commit**
* Update the version number in this document (topic 6)
* Commit & Push new version changes to master branch:
New version 18.8.2
New version 18.9.0
6) **New Tag**:
git tag v18.8.2
git push -u origin v18.8.2
git tag v18.9.0
git push -u origin v18.9.0
7) PhpStorm: **Sync** all files to VM qfq.
......
This diff is collapsed.
......@@ -37,6 +37,29 @@ Bug Fixes
^^^^^^^^^
Version 18.9.0
--------------
Date: 07.09.2018
Features
^^^^^^^^
* #6357 / Save pdf on server
* #5381 / Stored procedures can be called from QFQ Reports
* #4996 / Log QFQ Update with timestamp.
* #6255 / Inline Report Editing - now with SIP and save.php api
Bug Fixes
^^^^^^^^^
* #6465 / Allow newlines in form action queries (e.g. sqlInsert)
* #4654 / Better FE color highlighting (UX)
* #5689 / Default BS Columns for FormElement match Form setting
* #6484 / Download Links mit css class
* #6576 / download buttons are now rendered disabled with render mode r:3
* Cookie Sitepath: wrong detected in case of API calls.
Version 18.8.2
--------------
......
......@@ -2,8 +2,8 @@
[general]
project = QFQ - Quick Form Query
version = 18.8
release = 18.8.2
version = 18.9
release = 18.9.0
t3author = Carsten Rose
copyright = since 2017 by the author
......
......@@ -57,9 +57,9 @@ copyright = u'2017, Carsten Rose'
# built documents.lease
#
# The short X.Y version.
version = '18.8'
version = '18.9'
# The full version, including alpha/beta/rc tags.
release = '18.8.2'
release = '18.9.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
......
......@@ -37,6 +37,29 @@ Bug Fixes
^^^^^^^^^
Version 18.9.0
--------------
Date: 07.09.2018
Features
^^^^^^^^
* #6357 / Save pdf on server
* #5381 / Stored procedures can be called from QFQ Reports
* F4996 / Log QFQ Update with timestamp.
* #6255 / Inline Report Editing - now with SIP and save.php api
Bug Fixes
^^^^^^^^^
* #6465 / Allow newlines in form action queries (e.g. sqlInsert)
* #4654 / Better FE color highlighting (UX)
* #5689 / Default BS Columns for FormElement match Form setting
* #6484 / Download Links mit css class
* #6576 / download buttons are now rendered disabled with render mode r:3
* Cookie Sitepath: wrong detected in case of API calls.
Version 18.8.2
--------------
......
......@@ -11,7 +11,7 @@ $EM_CONF[$_EXTKEY] = array(
'dependencies' => 'fluid,extbase',
'clearcacheonload' => true,
'state' => 'stable',
'version' => '18.8.2',
'version' => '18.9.0',
'constraints' => [
'depends' => [
'typo3' => '6.0.0-9.2.99',
......
......@@ -59,6 +59,11 @@ try {
$data = $qfq->saveForm();
if (isset($data[REPORT_SAVE])) {
// Redirect to previous page
header("Location: {$_SERVER['HTTP_REFERER']}");
}
$arr = $qfq->getForwardMode();
$answer[API_REDIRECT] = $arr[API_REDIRECT];
$answer[API_REDIRECT_URL] = $arr[API_REDIRECT_URL];
......
......@@ -35,13 +35,37 @@ require_once(__DIR__ . '/report/Report.php');
* @package qfq
*/
abstract class AbstractBuildForm {
/**
* @var array
*/
protected $formSpec = array(); // copy of the loaded form
/**
* @var array
*/
protected $feSpecAction = array(); // copy of all formElement.class='action' of the loaded form
/**
* @var array
*/
protected $feSpecNative = array(); // copy of all formElement.class='native' of the loaded form
/**
* @var array
*/
protected $buildElementFunctionName = array();
/**
* @var array
*/
protected $pattern = array();
/**
* @var array
*/
protected $wrap = array();
/**
* @var array
*/
protected $symbol = array();
/**
* @var bool
*/
protected $showDebugInfoFlag = false;
// protected $feDivClass = array(); // Wrap FormElements in <div class="$feDivClass[type]">
......@@ -80,7 +104,13 @@ abstract class AbstractBuildForm {
*/
protected $dbArray = array();
/**
* @var bool|mixed
*/
protected $dbIndexData = false;
/**
* @var bool|string
*/
protected $dbIndexQfq = false;
/**
......@@ -183,8 +213,12 @@ abstract class AbstractBuildForm {
* formElement.dynamicUpdate-yes values/states
* @throws CodeException
* @throws DbException
* @throws DownloadException
* @throws UserFormException
* @throws UserReportException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
*/
public function process($mode, $htmlElementNameIdZero = false, $latestFeSpecNative = array()) {
$htmlHead = '';
......@@ -493,9 +527,12 @@ abstract class AbstractBuildForm {
* @return array|string
* @throws CodeException
* @throws DbException
* @throws DownloadException
* @throws UserFormException
* @throws UserReportException
* @throws DownloadException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
*/
private function processReportSyntax($value) {
......@@ -544,8 +581,12 @@ abstract class AbstractBuildForm {
* @return string
* @throws CodeException
* @throws DbException
* @throws DownloadException
* @throws UserFormException
* @throws UserReportException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
*/
public function elements($recordId, $filter = FORM_ELEMENTS_NATIVE, $feIdContainer = 0, array &$json,
$modeCollectFe = FLAG_DYNAMIC_UPDATE, $htmlElementNameIdZero = false,
......@@ -1049,6 +1090,7 @@ abstract class AbstractBuildForm {
* @return string
* @throws CodeException
* @throws UserFormException
* @throws UserReportException
*/
public function deriveNewRecordUrlFromExistingSip(&$toolTipNew) {
......@@ -1114,6 +1156,7 @@ abstract class AbstractBuildForm {
* @return string complete rendered HTML input element.
* @throws CodeException
* @throws UserFormException
* @throws UserReportException
*/
public function buildInput(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
$textarea = '';
......@@ -1446,6 +1489,7 @@ abstract class AbstractBuildForm {
* @return string
* @throws CodeException
* @throws UserFormException
* @throws UserReportException
*/
public function buildCheckbox(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
$itemKey = array();
......@@ -2048,6 +2092,7 @@ abstract class AbstractBuildForm {
* @return string
* @throws CodeException
* @throws UserFormException
* @throws UserReportException
*/
public function buildRadio(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
......@@ -2269,6 +2314,7 @@ abstract class AbstractBuildForm {
* @return mixed
* @throws CodeException
* @throws UserFormException
* @throws UserReportException
*/
public function buildSelect(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
$itemKey = array();
......@@ -2337,7 +2383,8 @@ abstract class AbstractBuildForm {
/**
* @param string $linkNew Complete Button, incl. SIP href
* @param string $deleteColumnTitle - if null, no delete column is rendered
* @param $flagDelete
* @param $deleteTitle
* @param array $firstRow First row of all subrecords to extract columntitles
* @param array $control Array with <th> column names / format.
*
......@@ -2376,6 +2423,7 @@ abstract class AbstractBuildForm {
*
* @return string
* @throws CodeException
* @throws DbException
* @throws UserFormException
* @throws UserReportException
*/
......@@ -3132,6 +3180,7 @@ abstract class AbstractBuildForm {
* @return string
* @throws CodeException
* @throws UserFormException
* @throws UserReportException
*/
public function buildDateTime(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
$attribute = '';
......@@ -3329,6 +3378,7 @@ abstract class AbstractBuildForm {
* @return string
* @throws CodeException
* @throws UserFormException
* @throws UserReportException
*/
public function buildEditor(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
$attribute = '';
......@@ -3517,6 +3567,7 @@ abstract class AbstractBuildForm {
* @return mixed
* @throws CodeException
* @throws DbException
* @throws DownloadException
* @throws UserFormException
* @throws UserReportException
*/
......@@ -3587,6 +3638,7 @@ abstract class AbstractBuildForm {
* @return mixed
* @throws CodeException
* @throws DbException
* @throws DownloadException
* @throws UserFormException
* @throws UserReportException
*/
......@@ -3703,6 +3755,7 @@ EOT;
* @return string
* @throws CodeException
* @throws DbException
* @throws DownloadException
* @throws UserFormException
* @throws UserReportException
*/
......
......@@ -111,26 +111,30 @@ class BodytextParser {
return;
}
$pos = 0;
$tokenList = '{}<>[]()';
// Definition: first line of bodytext, has to be a comment line. If the last char is one of the valid token: set that one.
// Nothing found: set {}.
$nestingOpen = '{';
$nestingClose = '}';
if ($firstLine[0] === '#') {
$token = substr($firstLine, -1);
$pos = strpos($tokenList, $token);
if ($pos === false) {
$pos = 0;
} else {
if ($pos % 2 === 1) {
$pos -= 1;
}
switch($token) {
case '<':
$nestingOpen = '<';
$nestingClose = '>';
break;
case '[':
$nestingOpen = '[';
$nestingClose = ']';
break;
case '(':
$nestingOpen = '(';
$nestingClose = ')';
break;
default:
break;
}
}
$nestingOpen = substr($tokenList, $pos, 1);
$nestingClose = substr($tokenList, $pos + 1, 1);
}
/**
......
......@@ -574,6 +574,7 @@ class BuildFormBootstrap extends AbstractBuildForm {
* @return string
* @throws CodeException
* @throws UserFormException
* @throws UserReportException
*/
public function tail() {
......@@ -654,8 +655,12 @@ EOF;
* @return mixed
* @throws CodeException
* @throws DbException
* @throws DownloadException
* @throws UserFormException
* @throws UserReportException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
*/
public function buildPill(array $formElement, $htmlFormElementName, $value, array &$json) {
$html = '';
......
......@@ -65,7 +65,9 @@ class BuildFormPlain extends AbstractBuildForm {
* @return string
* @throws CodeException
* @throws DbException
* @throws DownloadException
* @throws UserFormException
* @throws UserReportException
*/
public function doSubrecords() {
$json = array();
......
......@@ -68,7 +68,9 @@ class BuildFormTable extends AbstractBuildForm {
* @return string
* @throws CodeException
* @throws DbException
* @throws DownloadException
* @throws UserFormException
* @throws UserReportException
*/
public function doSubrecords() {
//TODO: $json is not returned - which is wrong. In this case, dynamic update won't work for subrecords
......@@ -83,6 +85,7 @@ class BuildFormTable extends AbstractBuildForm {
* @throws CodeException
* @throws DbException
* @throws UserFormException
* @throws UserReportException
*/
public function head($mode = FORM_LOAD) {
$html = '';
......
......@@ -45,6 +45,8 @@ const FORM_BUTTON_DELETE = 'delete';
const FORM_BUTTON_CLOSE = 'close';
const FORM_BUTTON_SAVE = 'save';
const REPORT_SAVE = 'reportSave';
const F_BS_COLUMNS = 'bsColumns';
const F_BS_LABEL_COLUMNS = 'bsLabelColumns';
......@@ -234,6 +236,7 @@ const ERROR_MULTIPLE_URL_PAGE_MAILTO_DEFINITION = 1406;
const ERROR_UNKNOWN_TOKEN = 1407;
const ERROR_TOO_FEW_PARAMETER_FOR_SENDMAIL = 1408;
const ERROR_TOO_MANY_PARAMETER = 1409;
const ERROR_INVALID_SAVE_PDF_FILENAME = 1410;
// Upload
const ERROR_UPLOAD = 1500;
......@@ -408,6 +411,8 @@ const SYSTEM_DB_1_SERVER = 'DB_1_SERVER';
const SYSTEM_DB_1_PASSWORD = 'DB_1_PASSWORD';
const SYSTEM_DB_1_NAME = 'DB_1_NAME';
const SYSTEM_T3_DB_NAME = 'T3_DB_NAME';
const SYSTEM_DB_INIT = 'init';
const SYSTEM_DB_INDEX_DATA = "indexData";
......@@ -1204,7 +1209,10 @@ const INDEX_PHP = 'index.php';
// QuickFormQuery.php
const T3DATA_BODYTEXT = 'bodytext';
const T3DATA_BODYTEXT_RAW = 'bodytext-raw';
const T3DATA_UID = 'uid';
const T3DATA_HEADER = 'header';
const REPORT_INLINE_BODYTEXT = 'bodytext';
// Special Column to check for uploads
const COLUMN_PATH_FILE_NAME = 'pathFileName';
......@@ -1284,11 +1292,16 @@ const TOKEN_FORM = CLIENT_FORM;
const TOKEN_RECORD_ID = CLIENT_RECORD_ID;
const TOKEN_DEBUG_BODYTEXT = TYPO3_DEBUG_SHOW_BODY_TEXT;
const TOKEN_DB_INDEX = F_DB_INDEX;
const TOKEN_CONTENT = 'content';
const TOKEN_VALID_LIST = 'sql|head|althead|altsql|tail|shead|stail|rbeg|rend|renr|rsep|fbeg|fend|fsep|rbgd|debug|form|r|debugShowBodyText|dbIndex|sqlLog|sqlLogMode';
const TOKEN_VALID_LIST = 'sql|head|althead|altsql|tail|shead|stail|rbeg|rend|renr|rsep|fbeg|fend|fsep|rbgd|debug|form|r|debugShowBodyText|dbIndex|sqlLog|sqlLogMode|content';
const TOKEN_COLUMN_CTRL = '_';
const TOKEN_CONTENT_STORE = 'store';
const TOKEN_CONTENT_HIDE = 'hide';
const TOKEN_CONTENT_SHOW = 'show';
//Report: Column Token
const COLUMN_LINK = 'link';
const COLUMN_EXEC = 'exec';
......@@ -1318,6 +1331,7 @@ const COLUMN_PAGES = 'pages';
const COLUMN_YANK = 'yank';
const COLUMN_PDF = 'pdf';
const COLUMN_SAVE_PDF = 'savePdf';
const COLUMN_FILE = 'file';
const COLUMN_ZIP = 'zip';
const COLUMN_MONITOR = 'monitor';
......@@ -1351,6 +1365,7 @@ const FORM_LOG_ACTIVE = 'formLogActive';
const DOWNLOAD_MODE = 'mode';
const DOWNLOAD_MODE_FILE = 'file';
const DOWNLOAD_MODE_PDF = 'pdf';
const DOWNLOAD_MODE_SAVE_PDF = 'save-pdf';
const DOWNLOAD_MODE_EXCEL = 'excel';
const DOWNLOAD_MODE_ZIP = 'zip';
const DOWNLOAD_MODE_THUMBNAIL = 'thumbnail';
......
......@@ -34,6 +34,7 @@ class Delete {
* @throws CodeException
* @throws DbException
* @throws UserFormException
* @throws UserReportException
*/
public function __construct($dbIndexData = false, $phpUnit = false) {
......
......@@ -23,6 +23,7 @@ use qfq;
require_once(__DIR__ . '/store/Store.php');
require_once(__DIR__ . '/store/Sip.php');
require_once(__DIR__ . '/store/FillStoreForm.php');
require_once(__DIR__ . '/store/Session.php');
require_once(__DIR__ . '/Constants.php');
......@@ -151,6 +152,7 @@ class QuickFormQuery {
}
$btp = new BodytextParser();
$t3data[T3DATA_BODYTEXT_RAW] = $t3data[T3DATA_BODYTEXT];
$t3data[T3DATA_BODYTEXT] = $btp->process($t3data[T3DATA_BODYTEXT]);
$this->t3data = $t3data;
......@@ -225,6 +227,9 @@ class QuickFormQuery {
* @throws DownloadException
* @throws UserFormException
* @throws UserReportException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
*/
public function process() {
$html = '';
......@@ -283,7 +288,7 @@ class QuickFormQuery {
*
* @param $formName
* @param $formLogMode
* @return
* @return string
* @throws CodeException
* @throws UserFormException
* @throws UserReportException
......@@ -1342,16 +1347,99 @@ class QuickFormQuery {
* @throws DownloadException
* @throws UserFormException
* @throws UserReportException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
*/
private function doReport() {
$report = new Report($this->t3data, $this->eval, $this->phpUnit);
$html = '';
$html = $report->process($this->t3data['bodytext']);
if ($this->store->getVar(TYPO3_BE_USER, STORE_TYPO3, SANITIZE_ALLOW_ALNUMX)) {
$html .= $this->buildInlineReport();
}
$html .= $report->process($this->t3data[T3DATA_BODYTEXT]);
return $html;
}
/** Constructs a form to directly edit qfq content elements inline.
*
* @return string - the html code
* @throws CodeException
* @throws UserFormException
*/
private function buildInlineReport() {
$uid = $this->t3data[T3DATA_UID];
$bodytext = $this->t3data[T3DATA_BODYTEXT_RAW];
$header = $this->t3data[T3DATA_HEADER];
$icon = Support::renderGlyphIcon(GLYPH_ICON_TASKS);
$showFormJs = '$("#tt-content-edit-' . $uid . '").toggleClass("hidden")';
$toggleBtn = Support::wrapTag("<a href='#' onclick='$showFormJs' style='float:right;'>", $icon);
$saveBtnAttributes = Support::doAttribute('class', 'btn btn-default') .
Support::doAttribute('id', "tt-content-save-$uid") .
Support::doAttribute('type', 'submit') .
Support::doAttribute('style', 'float:right; margin:-5px;') .
Support::doAttribute('title', 'Save & Reload');
$saveBtnIcon = Support::renderGlyphIcon(GLYPH_ICON_CHECK);
$saveBtn = Support::wrapTag("<button $saveBtnAttributes>", $saveBtnIcon);
$header = "QFQ Page Content '$header'";
$headerBar = Support::wrapTag("<div class='col-md-12 qfq-form-title'>", $header . $saveBtn);
$ttContentCode = Support::htmlEntityEncodeDecode(MODE_ENCODE, $bodytext);
$codeBoxAttributes = Support::doAttribute('style', "width:100%;") .
Support::doAttribute('id', "tt-content-code-$uid") .
Support::doAttribute('rows',20) .
Support::doAttribute('name', REPORT_INLINE_BODYTEXT);
$codeBox = Support::wrapTag("<textarea $codeBoxAttributes>", $ttContentCode);
$form = join(' ', [$headerBar, $codeBox]);
$sipObj = new Sip;
$action = $sipObj->queryStringToSip(API_DIR . "/save.php?uid=$uid&" . REPORT_SAVE . "=1");
$formAttributes = Support::doAttribute('id', "tt-content-edit-$uid") .
Support::doAttribute('class', 'hidden') .
Support::doAttribute('method', 'post') .
Support::doAttribute('action', $action);
$form = Support::wrapTag("<form $formAttributes>", $form);
return $toggleBtn . $form;
}
/**
* @throws CodeException
* @throws DbException
* @throws UserFormException
*/
public function saveReport() {
$uid = $this->store->getVar(T3DATA_UID, STORE_SIP . STORE_ZERO, SANITIZE_ALLOW_DIGIT);
if ($uid == 0) {
// Check if it was called with a SIP (containing a uid)
// If not, this might be an attack => cancel.