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

Merge branch 'develop' into 'master'

Develop

See merge request !349
parents 739a7f7d c2cc7ffe
Pipeline #5246 passed with stages
in 3 minutes and 25 seconds
......@@ -657,14 +657,14 @@ class BuildFormBootstrap extends AbstractBuildForm {
$actionUpload = FILE_ACTION . '=' . FILE_ACTION_UPLOAD;
$actionDelete = FILE_ACTION . '=' . FILE_ACTION_DELETE;
$apiDeletePhp = Path::appToApi(API_DELETE_PHP);
$apiDeletePhp = Path::urlApi(API_DELETE_PHP);
$dirtyAction = ($this->formSpec[F_DIRTY_MODE] == DIRTY_MODE_NONE) ? '' : "dirtyUrl: '" . Path::appToApi(API_DIRTY_PHP) . "',";
$dirtyAction = ($this->formSpec[F_DIRTY_MODE] == DIRTY_MODE_NONE) ? '' : "dirtyUrl: '" . Path::urlApi(API_DIRTY_PHP) . "',";
$submitTo = Path::appToApi(API_SAVE_PHP);
$refreshUrl = Path::appToApi(API_LOAD_PHP);
$fileUploadTo = Path::appToApi(API_FILE_PHP) . '?' . $actionUpload;
$fileDeleteUrl = Path::appToApi(API_FILE_PHP) . '?' . $actionDelete;
$submitTo = Path::urlApi(API_SAVE_PHP);
$refreshUrl = Path::urlApi(API_LOAD_PHP);
$fileUploadTo = Path::urlApi(API_FILE_PHP) . '?' . $actionUpload;
$fileDeleteUrl = Path::urlApi(API_FILE_PHP) . '?' . $actionDelete;
$html .= '</form>'; // <form class="form-horizontal" ...
$html .= <<<EOF
......
......@@ -552,6 +552,8 @@ const SYSTEM_FORM_BS_NOTE_COLUMNS = 'formBsNoteColumns';
const SYSTEM_BASE_URL = 'baseUrl';
const SYSTEM_PASWORD_HASH_TYPE = 'PASSWORD_HASH_TYPE';
const SYSTEM_SEND_E_MAIL_OPTIONS = 'sendEMailOptions';
const SYSTEM_EDIT_FORM_PAGE = 'editFormPage';
......@@ -1119,6 +1121,7 @@ const FE_BS_INPUT_COLUMNS = F_BS_INPUT_COLUMNS;
const FE_BS_NOTE_COLUMNS = F_BS_NOTE_COLUMNS;
const FE_WRAP_ROW_LABEL_INPUT_NOTE = 'rowLabelInputNote';
const FE_MAX_LENGTH = 'maxLength';
const FE_MAX_LENGTH_COLUMN = 'maxLengthColumn';
const FE_PARAMETER = 'parameter';
const FE_ENCODE = 'encode';
const FE_CHECK_TYPE = 'checkType';
......@@ -2060,3 +2063,6 @@ const I_UNCHECKED = 'unchecked';
// Rest Client
const HTTP_STATUS = 'http-status';
const ERROR_MESSAGE = 'error-message';
// Misc
const BASE_DIR_FAKE = 'http://xxxxxxxxxxxxxxxxxxxxxxxxxxxx/qfq/';
......@@ -318,7 +318,7 @@ class Evaluate {
$this->store::setVar(SYSTEM_DRAG_AND_DROP_JS, 'true', STORE_SYSTEM);
// data-dnd-api="typo3conf/ext/qfq/qfq/Api/dragAndDrop.php?s={{'U:form=<form name>[&paramX=<any value>]|s|r:8' AS _link}}"
return DND_DATA_DND_API . '="' . Path::appToApi(API_DRAG_AND_DROP_PHP) . '?s=' . $s . '"';
return DND_DATA_DND_API . '="' . Path::urlApi(API_DRAG_AND_DROP_PHP) . '?s=' . $s . '"';
}
/**
......
......@@ -457,7 +457,7 @@ class HelperFile {
}
if (isset($extToFileType[$ext])) {
return Path::appToExt(Path::EXT_TO_HIGHLIGHT_JSON) . '/' . $extToFileType[$ext];
return Path::urlExt(Path::EXT_TO_HIGHLIGHT_JSON) . '/' . $extToFileType[$ext];
}
return '';
......
......@@ -3,14 +3,17 @@
namespace IMATHUZH\Qfq\Core\Helper;
/*
* Glossar:
/**
* Glossary:
* - App: directory in which the index.php file is located. All urls should be relative to this.
* - Ext: directory in which the QFQ extension is loacted. e.g. the folder Classes is in there.
* - Ext: directory in which the QFQ extension is located. e.g. the folder Classes is in there.
* - API: api folder of qfq extension
*
* Naming convention of path constants/functions/variables:
* 1) name a path by its origin and its destination separated by 'to'. E.g. APP_TO_SYSTEM_LOG, $appToProject.
* Conventions of Path class:
* 1) naming conventions of of path constants/functions/variables:
* a) name a path by its origin and its destination separated by 'to'. E.g. APP_TO_SYSTEM_LOG, $appToProject.
* b) Or only by its destination and prefix "absolute" if the path is absolute e.g. absoluteApp().
* c) Or by its destination and prefix "url" if the path returns a fully qualified url using the base path e.g. urlExt().
* 2) if the destination is a file, append "File". E.g. APP_TO_SYSTEM_QFQ_LOG_FILE.
* 3) if a path has to be variable, create a setter and getter. E.g. self::setAbsoluteApp(), self::absoluteApp(), private static $absoluteApp.
* 4) a path getter appends the given arguments to the requested path using self::join(..., func_get_args()). E.g. see absoluteApp().
......@@ -22,11 +25,10 @@ namespace IMATHUZH\Qfq\Core\Helper;
use IMATHUZH\Qfq\Core\Exception\Thrower;
use IMATHUZH\Qfq\Core\Store\Config;
class Path
{
class Path {
// App
// This is manually set in self::setMainPaths()
private static $absoluteApp = null;
private static $urlApp = null;
// Extension
const APP_TO_EXT = 'typo3conf/ext/qfq';
......@@ -89,34 +91,18 @@ class Path
// Send Email
const EXT_TO_SEND_EMAIL_FILE = 'Classes/External/sendEmail';
/**
* Manually set the paths which are not constant nor can be inferred by other paths.
* This function must be called at the beginning of every entry point, to tell the Path class, where things are.
*
* @param string|null $absoluteApp
* @throws \CodeException
* @throws \UserFormException
*/
public static function setMainPaths(string $absoluteApp = null)
{
if (is_null($absoluteApp)) {
$absoluteApp = self::findAbsoluteApp();
}
self::setAbsoluteApp($absoluteApp);
// Only executed on first call:
self::findAppToProject();
self::findAbsoluteLog();
}
/**
* @param array $pathPartsToAppend
* @return string
* @throws \UserFormException
*/
public static function absoluteApp(...$pathPartsToAppend): string
{
self::enforcePathIsSet(self::$absoluteApp);
public static function absoluteApp(...$pathPartsToAppend): string {
if (is_null(self::$absoluteApp)) {
self::findAbsoluteApp();
}
if (is_null(self::$absoluteApp)) {
Thrower::userFormException('Path not set.', 'Absolute app path is null. This should not happen at this point.');
}
return self::join(self::$absoluteApp, $pathPartsToAppend);
}
......@@ -125,8 +111,7 @@ class Path
* @return string
* @throws \UserFormException
*/
public static function absoluteExt(...$pathPartsToAppend): string
{
public static function absoluteExt(...$pathPartsToAppend): string {
return self::absoluteApp(self::appToExt(), $pathPartsToAppend);
}
......@@ -135,12 +120,13 @@ class Path
* @return string
* @throws \UserFormException
*/
public static function absoluteLog(...$pathPartsToAppend): string
{
public static function absoluteLog(...$pathPartsToAppend): string {
if (is_null(self::$absoluteLog)) {
self::findAbsoluteLog();
}
self::enforcePathIsSet(self::$absoluteLog);
if (is_null(self::$absoluteLog)) {
Thrower::userFormException('Path not set.', 'Absolute log path is null. This should not happen at this point.');
}
return self::join(self::$absoluteLog, $pathPartsToAppend);
}
......@@ -148,8 +134,7 @@ class Path
* @return string
* @throws \UserFormException
*/
public static function absoluteSqlLogFile(): string
{
public static function absoluteSqlLogFile(): string {
if (is_null(self::$overloadAbsoluteSqlLogFile)) {
return self::absoluteLog(self::LOG_TO_SQL_LOG_FILE_DEFAULT);
}
......@@ -160,8 +145,7 @@ class Path
* @return string
* @throws \UserFormException
*/
public static function absoluteQfqLogFile(): string
{
public static function absoluteQfqLogFile(): string {
if (is_null(self::$overloadAbsoluteQfqLogFile)) {
return self::absoluteLog(self::LOG_TO_QFQ_LOG_FILE_DEFAULT);
}
......@@ -172,8 +156,7 @@ class Path
* @return string
* @throws \UserFormException
*/
public static function absoluteMailLogFile(): string
{
public static function absoluteMailLogFile(): string {
if (is_null(self::$overloadAbsoluteMailLogFile)) {
return self::absoluteLog(self::LOG_TO_MAIL_LOG_FILE_DEFAULT);
}
......@@ -183,11 +166,16 @@ class Path
/**
* @param array $pathPartsToAppend
* @return string
* @throws \CodeException
* @throws \UserFormException
*/
public static function appToProject(...$pathPartsToAppend): string
{
self::enforcePathIsSet(self::$appToProject);
public static function appToProject(...$pathPartsToAppend): string {
if (is_null(self::$appToProject)) {
self::findAppToProject();
}
if (is_null(self::$appToProject)) {
Thrower::userFormException('Path not set.', 'Project path is null. This should not happen at this point.');
}
return self::join(self::$appToProject, $pathPartsToAppend);
}
......@@ -196,8 +184,7 @@ class Path
* @return string
* @throws \UserFormException
*/
public static function absoluteProject(...$pathPartsToAppend): string
{
public static function absoluteProject(...$pathPartsToAppend): string {
return self::absoluteApp(self::appToProject($pathPartsToAppend));
}
......@@ -206,8 +193,7 @@ class Path
* @return string
* @throws \UserFormException
*/
public static function absoluteConf(...$pathPartsToAppend): string
{
public static function absoluteConf(...$pathPartsToAppend): string {
return self::absoluteProject(self::PROJECT_TO_CONF, $pathPartsToAppend);
}
......@@ -216,19 +202,36 @@ class Path
* @return string
* @throws \UserFormException
*/
public static function appToExt(...$pathPartsToAppend): string
{
public static function appToExt(...$pathPartsToAppend): string {
return self::join(self::APP_TO_EXT, $pathPartsToAppend);
}
/**
* @param mixed ...$pathPartsToAppend
* @return string
* @throws \CodeException
* @throws \UserFormException
* @throws \UserReportException
*/
public static function urlApp(...$pathPartsToAppend): string {
if (is_null(self::$urlApp)) {
self::setUrlApp(Config::get(SYSTEM_BASE_URL));
}
// ensure base url is configured
if (is_null(self::$urlApp) || self::$urlApp === '') {
Thrower::userFormException('Base url not configured.', 'Go to QFQ extension configuration in the Typo3 backend and fill in a value for config.baseUrl');
}
return self::join(self::$urlApp, $pathPartsToAppend);
}
/**
* @param array $pathPartsToAppend
* @return string
* @throws \UserFormException
*/
public static function extToApi(...$pathPartsToAppend): string
{
return self::join(self::EXT_TO_API, $pathPartsToAppend);
public static function urlExt(...$pathPartsToAppend): string {
return self::urlApp(self::appToExt($pathPartsToAppend));
}
/**
......@@ -236,17 +239,24 @@ class Path
* @return string
* @throws \UserFormException
*/
public static function appToApi(...$pathPartsToAppend): string
{
public static function appToApi(...$pathPartsToAppend): string {
return self::join(self::APP_TO_EXT, self::EXT_TO_API, $pathPartsToAppend);
}
/**
* @param array $pathPartsToAppend
* @return string
* @throws \UserFormException
*/
public static function urlApi(...$pathPartsToAppend): string {
return self::urlApp(self::appToApi($pathPartsToAppend));
}
/**
* @param mixed ...$pathPartsToAppend
* @return string
*/
public static function projectToForm(...$pathPartsToAppend) : string
{
public static function projectToForm(...$pathPartsToAppend): string {
$projectToForm = is_null(self::$projectToForm) ? self::PROJECT_TO_FORM_DEFAULT : self::$projectToForm;
return self::join($projectToForm, $pathPartsToAppend);
}
......@@ -255,8 +265,7 @@ class Path
* @param mixed ...$pathPartsToAppend
* @return string
*/
public static function projectToReport(...$pathPartsToAppend) : string
{
public static function projectToReport(...$pathPartsToAppend): string {
$projectToReport = is_null(self::$projectToReport) ? self::PROJECT_TO_REPORT_DEFAULT : self::$projectToReport;
return self::join($projectToReport, $pathPartsToAppend);
}
......@@ -264,16 +273,14 @@ class Path
/**
* @param string $newPath
*/
public static function setProjectToForm(string $newPath)
{
public static function setProjectToForm(string $newPath) {
self::$projectToForm = $newPath;
}
/**
* @param string $newPath
*/
public static function setProjectToReport(string $newPath)
{
public static function setProjectToReport(string $newPath) {
self::$projectToReport = $newPath;
}
......@@ -281,8 +288,7 @@ class Path
* @param string $newPath
* @throws \UserFormException
*/
public static function setAbsoluteSqlLogFile(string $newPath)
{
public static function setAbsoluteSqlLogFile(string $newPath) {
self::enforcePathIsAbsolute($newPath);
self::$overloadAbsoluteSqlLogFile = $newPath;
}
......@@ -291,8 +297,7 @@ class Path
* @param string $newPath
* @throws \UserFormException
*/
public static function setAbsoluteQfqLogFile(string $newPath)
{
public static function setAbsoluteQfqLogFile(string $newPath) {
self::enforcePathIsAbsolute($newPath);
self::$overloadAbsoluteQfqLogFile = $newPath;
}
......@@ -301,12 +306,18 @@ class Path
* @param string $newPath
* @throws \UserFormException
*/
public static function setAbsoluteMailLogFile(string $newPath)
{
public static function setAbsoluteMailLogFile(string $newPath) {
self::enforcePathIsAbsolute($newPath);
self::$overloadAbsoluteMailLogFile = $newPath;
}
/**
* @param $urlApp
*/
public static function setUrlApp($urlApp) {
self::$urlApp = $urlApp;
}
/**
* Override the paths of sql.log, qfq.log, mail.log using the values from the config file or Typo3.
*
......@@ -314,8 +325,7 @@ class Path
* @throws \UserFormException
* @throws \UserReportException
*/
public static function overrideLogPathsFromConfig()
{
public static function overrideLogPathsFromConfig() {
// QFQ log
$absoluteQfqLogFile = Config::get(SYSTEM_QFQ_LOG_PATHFILENAME);
if (!empty($absoluteQfqLogFile)) {
......@@ -343,8 +353,7 @@ class Path
* @return string
* @throws \UserFormException
*/
public static function joinIfNotAbsolute (string $path1, string $path2): string
{
public static function joinIfNotAbsolute(string $path1, string $path2): string {
if ($path2 !== '' && $path2[0] === '/') {
return $path2;
}
......@@ -360,8 +369,7 @@ class Path
* @param array $pathPartsToAppend
* @return string
*/
public static function join(...$pathPartsToAppend): string
{
public static function join(...$pathPartsToAppend): string {
$parts = $pathPartsToAppend;
// concatenate all parts (arrays are flattened)
......@@ -390,34 +398,36 @@ class Path
}
});
// remove multiple occurrences of '/'
return preg_replace('/\/{2,}/','/', $path);
// remove multiple occurrences of '/' (but keep http://)
if (preg_match('/^\w*:\/\//', $path, $match)) {
$protocol = $match[0];
$path = substr($path, strlen($protocol));
} else {
$protocol = '';
}
$path = preg_replace('/\/{2,}/', '/', $path);
/////////////////////////////////////////////////// Private //////////////////////////////////////////////////////
return $protocol . $path;
}
/**
* @param string $newPath
*/
private static function setAbsoluteApp(string $newPath)
{
public static function setAbsoluteApp(string $newPath) {
self::$absoluteApp = $newPath;
}
/**
* @param string $newPath
*/
private static function setAppToProject(string $newPath)
{
private static function setAppToProject(string $newPath) {
self::$appToProject = $newPath;
}
/**
* @param string $newPath
* @return string
*/
private static function setAbsoluteLog(string $newPath)
{
private static function setAbsoluteLog(string $newPath) {
self::$absoluteLog = $newPath;
}
......@@ -429,12 +439,7 @@ class Path
*
* @throws \UserFormException
*/
private static function findAbsoluteLog()
{
if (!is_null(self::$absoluteLog)) {
// only execute once
return;
}
private static function findAbsoluteLog() {
// search log dir qfqProject/log
$absoluteLog = self::absoluteApp(self::appToProject(self::PROJECT_TO_LOG_DEFAULT));
......@@ -458,12 +463,7 @@ class Path
* @throws \CodeException
* @throws \UserFormException
*/
private static function findAppToProject()
{
if (!is_null(self::$appToProject)) {
// only execute once
return;
}
private static function findAppToProject() {
// does qfq.project.path.php exist? => read path
$absoluteProjectPathFile = self::absoluteApp(PROJECT_PATH_PHP_FILE);
......@@ -496,17 +496,15 @@ class Path
*
* @throws \UserFormException
*/
private static function findAbsoluteApp()
{
public static function findAbsoluteApp() {
// look for typo3conf directory
$absoluteApp = self::realpath(self::join(__DIR__, '../../../../../../'));
if (!file_exists(self::join($absoluteApp, self::APP_TO_TYPO3_CONF)))
{
Thrower::userFormException('App path seems to be wrong: Directory "typo3conf" not found in app path.'
, "Current app path: $absoluteApp");
if (!file_exists(self::join($absoluteApp, self::APP_TO_TYPO3_CONF))) {
Thrower::userFormException('App path seems to be wrong: Directory "typo3conf" not found in app path.',
" Current app path: $absoluteApp .",
" In unit tests this can be manually set using Path::setAbsoluteApp() before the path is accessed.");
}
return $absoluteApp;
self::setAbsoluteApp($absoluteApp);
}
/**
......@@ -514,8 +512,7 @@ class Path
*
* @throws \UserFormException
*/
private static function writeProjectPathPhp()
{
private static function writeProjectPathPhp() {
$appToProject = self::appToProject();
$fileContent = <<<EOF
<?php
......@@ -531,18 +528,6 @@ EOF;
HelperFile::file_put_contents(self::absoluteApp(PROJECT_PATH_PHP_FILE), $fileContent);
}
/**
* Throw an exception if the given path is not set (i.e. === null).
*
* @param $path
* @throws \UserFormException
*/
private static function enforcePathIsSet($path) {
if(is_null($path)) {
Thrower::userFormException('Path accessed before set.', 'Make sure Path::setMainPaths(...) is called before this code is executed..');
}
}
/**
* Throw exception if path does not start with '/'
*
......@@ -550,7 +535,7 @@ EOF;
* @throws \UserFormException
*/
private static function enforcePathIsAbsolute(string $path) {
if($path !== '' && $path[0] === '/') {
if ($path !== '' && $path[0] === '/') {
return;
}
Thrower::userFormException('Path is not absolute', "Path does not start with '/' : $path");
......@@ -565,8 +550,7 @@ EOF;
* @return string
* @throws \UserFormException
*/
private static function realpath(string $pathToSomethingThatExists, ...$pathPartsToAppend): string
{
private static function realpath(string $pathToSomethingThatExists, ...$pathPartsToAppend): string {
$absolutePath = realpath($pathToSomethingThatExists);
if ($absolutePath === false) {
Thrower::userFormException('Path not found.', "Either path does not exist or access not permitted. Make sure the whole path is executable by the current user. Path: $pathToSomethingThatExists.");
......@@ -581,8 +565,7 @@ EOF;
* @param string $path
* @return bool
*/
private static function containsDoubleDot(string $path)
{
return $path === '..' || OnString::strStartsWith($path, '../') || OnString::strEndsWith($path, '/..' ) || OnString::strContains($path, '/../');
private static function containsDoubleDot(string $path) {
return $path === '..' || OnString::strStartsWith($path, '../') || OnString::strEndsWith($path, '/..') || OnString::strContains($path, '/../');
}
}
\ No newline at end of file
......@@ -233,10 +233,11 @@ class Support {
*
* @return string
* @throws \CodeException
* @throws \UserFormException
*/
public static function doTooltip($htmlId, $tooltipText) {
return "<img " . self::doAttribute('id', $htmlId) . " src='" . Path::appToExt(Path::EXT_TO_GFX_INFO_FILE) . "' title=\"" . htmlentities($tooltipText) . "\">";
return "<img " . self::doAttribute('id', $htmlId) . " src='" . Path::urlExt(Path::EXT_TO_GFX_INFO_FILE) . "' title=\"" . htmlentities($tooltipText) . "\">";
}
/**
......@@ -1170,6 +1171,7 @@ class Support {
* @return int
*/
private static function maxLengthSetEnum($typeSpec) {
$isSet = substr($typeSpec, 0, 4) === 'set(';
$startPos = $isSet ? 4 : 5;
$max = 0;
......
......@@ -1772,7 +1772,7 @@ class QuickFormQuery {
$form = join(' ', [$headerBar, $codeBox]);
$sipObj = new Sip;
$action = $sipObj->queryStringToSip(Path::appToApi(API_SAVE_PHP) . "?uid=$uid&" . REPORT_SAVE . "=1&" . REPORT_SAVE_FILE . "=" . (is_null($reportPathFileNameFull) ? 0 : 1));
$action = $sipObj->queryStringToSip(Path::urlApi(API_SAVE_PHP) . "?uid=$uid&" . REPORT_SAVE . "=1&" . REPORT_SAVE_FILE . "=" . (is_null($reportPathFileNameFull) ? 0 : 1));
$formAttributes = Support::doAttribute('id', "tt-content-edit-$uid") .
Support::doAttribute('class', 'hidden') .
Support::doAttribute('method', 'post') .
......@@ -2193,7 +2193,7 @@ class QuickFormQuery {
*/
private function getModalCode() {
$iconGearRelToApp = Path::appToExt('Resources/Public/icons/gear.svg');
$iconGearRelToApp = Path::urlExt('Resources/Public/icons/gear.svg');
$code = <<<EOF
<!-- Modal -->
......
......@@ -1675,7 +1675,7 @@ EOF;
// Check for SIP encoded download link or persistent download link
if ($vars[NAME_SIP] == '1') {
// SIP encoded.
$vars[NAME_URL] = Path::appToApi(API_DOWNLOAD_PHP);
$vars[NAME_URL] = Path::urlApi(API_DOWNLOAD_PHP);
} else {
// Persistent Download Link
if ($vars[NAME_DOWNLOAD] == '') {
......@@ -1684,7 +1684,8 @@ EOF;
// Check if there is a shortcut version defined like d:dl.php/123.
if (strstr($vars[NAME_DOWNLOAD], '.php/') === false) {
// Set the default: API_DOWNLOAD_PHP
$vars[NAME_URL] = Path::appToApi(API_DOWNLOAD_PHP) . '/' . $vars[NAME_DOWNLOAD];
// example.com/typo3conf/ext/qfq/api/download.php/file1
$vars[NAME_URL] = Path::urlApi(API_DOWNLOAD_PHP, $vars[NAME_DOWNLOAD]);
} else {