Merge branch 'support/3.2' into develop

This commit is contained in:
Stephen Abello
2024-04-16 09:22:58 +02:00
1084 changed files with 82136 additions and 23375 deletions

View File

@@ -0,0 +1,127 @@
<?php
namespace Combodo\iTop\Application\Helper;
use UserRights;
use MetaModel;
use DBSearch;
use utils;
use appUserPreferences;
/***
*
* (34) [
* 'blockQuote',
* 'bold',
* 'link',
* 'ckfinder',
* 'codeBlock',
* 'selectAll',
* 'undo',
* 'redo',
* 'heading',
* 'horizontalLine',
* 'imageTextAlternative',
* 'toggleImageCaption',
* 'imageStyle:inline',
* 'imageStyle:alignLeft',
* 'imageStyle:alignRight',
* 'imageStyle:alignCenter',
* 'imageStyle:alignBlockLeft',
* 'imageStyle:alignBlockRight',
* 'imageStyle:block',
* 'imageStyle:side',
* 'imageStyle:wrapText',
* 'imageStyle:breakText',
* 'uploadImage',
* 'imageUpload',
* 'indent',
* 'outdent',
* 'italic',
* 'numberedList',
* 'bulletedList',
* 'mediaEmbed',
* 'insertTable',
* 'tableColumn',
* 'tableRow',
* 'mergeTableCells']
*
*/
class CKEditorHelper
{
/**
* Return the CKEditor config as an array
*
* @return array
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
* @since 3.0.0
*/
static public function GetCkeditorPref()
{
// extract language from user preferences
$sLanguageCountry = trim(UserRights::GetUserLanguage());
$sLanguage = strtolower(explode(' ', $sLanguageCountry)[0]);
$aDefaultConf = array(
'language'=> $sLanguage,
);
// mentions
$aDefaultConf['mention'] = self::GetMentionConfiguration();
// rich text config
$aRichTextConfig = json_decode(appUserPreferences::GetPref('richtext_config', '{}'), true);
return array_merge($aDefaultConf, $aRichTextConfig);
}
/**
* @return array|array[]
* @throws \CoreException
* @throws \OQLException
*/
static private function GetMentionConfiguration() : array
{
// initialize feeds
$aMentionConfiguration = ['feeds' => []];
// retrieve mentions allowed classes
$aMentionsAllowedClasses = MetaModel::GetConfig()->Get('mentions.allowed_classes');
// iterate throw classes...
foreach($aMentionsAllowedClasses as $sMentionMarker => $sMentionScope) {
// Retrieve mention class
// - First test if the conf is a simple data model class
if (MetaModel::IsValidClass($sMentionScope)) {
$sMentionClass = $sMentionScope;
}
// - Otherwise it must be a valid OQL
else {
$oTmpSearch = DBSearch::FromOQL($sMentionScope);
$sMentionClass = $oTmpSearch->GetClass();
unset($oTmpSearch);
}
// append mention configuration
$aMentionConfiguration['feeds'][] = [
'marker' => $sMentionMarker,
'feed' => null,
'minimumCharacters' => MetaModel::GetConfig()->Get('min_autocomplete_chars'),
'feed_type' => 'ajax',
'feed_ajax_options' => [
'url' => utils::GetAbsoluteUrlAppRoot(). "pages/ajax.render.php?route=object.search&object_class=$sMentionClass&oql=SELECT $sMentionClass&search=",
'throttle' => 500,
'marker' => $sMentionMarker,
],
];
}
return $aMentionConfiguration;
}
}

View File

@@ -72,10 +72,8 @@ class WebResourcesHelper
public static function GetJSFilesRelPathsForCKEditor(): array
{
return [
'js/ckeditor/ckeditor.js',
'js/ckeditor/adapters/jquery.js',
'js/ckeditor/plugins/codesnippet/lib/highlight/highlight.pack.js',
'js/ckeditor.on-init.js',
'js/ckeditor/build/ckeditor.js',
'js/highlight/highlight.js',
];
}

View File

@@ -4,7 +4,9 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Base\Component\Input\RichText;
use Combodo\iTop\Application\Helper\CKEditorHelper;
use Combodo\iTop\Application\UI\Base\UIBlock;
use Dict;
use utils;
/**
@@ -19,13 +21,12 @@ class RichText extends UIBlock
public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/components/input/richtext/layout';
public const DEFAULT_JS_TEMPLATE_REL_PATH = 'base/components/input/richtext/layout';
public const DEFAULT_JS_FILES_REL_PATH = [
'js/ckeditor/ckeditor.js',
'js/ckeditor/adapters/jquery.js',
'js/ckeditor/plugins/codesnippet/lib/highlight/highlight.pack.js',
'js/ckeditor.on-init.js',
'js/ckeditor/build/ckeditor.js',
'js/highlight/highlight.js',
];
public const DEFAULT_CSS_FILES_REL_PATH = [
'js/ckeditor/plugins/codesnippet/lib/highlight/styles/obsidian.css',
'js/highlight/styles/obsidian.css',
'css/ckeditor/contents.css',
];
/** @var string|null */
@@ -42,7 +43,11 @@ class RichText extends UIBlock
{
parent::__construct($sId);
$this->sValue = null;
$this->aConfig = utils::GetCkeditorPref();
$this->aConfig = CKEditorHelper::GetCkeditorPref();
// add CKEditor translations resource
$sLanguage = strtolower(explode(' ', Dict::GetUserLanguage())[0]);
$this->AddJsFileRelPath('js/ckeditor/build/translations/' . $sLanguage . '.js');
}
/**

View File

@@ -24,10 +24,11 @@ class CaseLogEntryForm extends UIContentBlock
// Overloaded constants
public const BLOCK_CODE = 'ibo-caselog-entry-form';
public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/layouts/activity-panel/caselog-entry-form/layout';
public const DEFAULT_JS_TEMPLATE_REL_PATH = 'base/layouts/activity-panel/caselog-entry-form/layout';
public const DEFAULT_JS_ON_READY_TEMPLATE_REL_PATH = 'base/layouts/activity-panel/caselog-entry-form/layout';
public const DEFAULT_JS_FILES_REL_PATH = [
'js/layouts/activity-panel/caselog-entry-form.js',
];
public const REQUIRES_ANCESTORS_DEFAULT_JS_FILES = true;
/** @var string Form is autonomous and can send data on its own */
public const ENUM_SUBMIT_MODE_AUTONOMOUS = 'autonomous';

View File

@@ -209,6 +209,8 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
// Used throughout the app.
$this->LinkScriptFromAppRoot('js/pages/backoffice/toolbox.js');
$this->LinkScriptFromAppRoot('js/ckeditor.handler.js');
$this->LinkScriptFromAppRoot('js/ckeditor.feeds.js');
$this->LinkScriptFromAppRoot('js/pages/backoffice/on-ready.js');
// Used by dashboard editor
@@ -270,9 +272,6 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
$this->LinkStylesheetFromAppRoot('css/font-awesome/css/all.min.css');
$this->LinkStylesheetFromAppRoot('css/font-combodo/font-combodo.css');
// Note: CKEditor files can't be moved easily as we need to find a way to init the "disabler" plugin, {@see js/toolbox.js}
$this->LinkStylesheetFromAppRoot('js/ckeditor/plugins/codesnippet/lib/highlight/styles/obsidian.css');
// Used by external keys and other drop down lists
$this->LinkStylesheetFromAppRoot('css/selectize.default.css');
}

View File

@@ -803,12 +803,15 @@ JS;
// Retrieve query params
$sObjectClass = utils::ReadParam('object_class', '', false, utils::ENUM_SANITIZATION_FILTER_STRING);
$sOql = utils::ReadParam('oql', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
$aFieldsToLoad = json_decode(utils::ReadParam('fields_to_load', '', false, utils::ENUM_SANITIZATION_FILTER_STRING));
$aFieldsToLoad = json_decode(utils::ReadParam('fields_to_load', '[]', false, utils::ENUM_SANITIZATION_FILTER_STRING));
$sSearch = utils::ReadParam('search', '', false, utils::ENUM_SANITIZATION_FILTER_STRING);
// Retrieve this reference object (for OQL)
$sThisObjectData = utils::ReadPostedParam('this_object_data', null, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
$oThisObj = ObjectRepository::GetObjectFromWizardHelperData($sThisObjectData);
$oThisObj = null;
if($sThisObjectData !== null) {
$oThisObj = ObjectRepository::GetObjectFromWizardHelperData($sThisObjectData);
}
// Retrieve data post processor
$aDataProcessor = utils::ReadParam('data_post_processor', null, false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);

View File

@@ -23,6 +23,7 @@ namespace Combodo\iTop\Renderer\Bootstrap\FieldRenderer;
use AttributeDate;
use AttributeDateTime;
use AttributeText;
use Combodo\iTop\Application\Helper\CKEditorHelper;
use Combodo\iTop\Form\Field\DateField;
use Combodo\iTop\Form\Field\DateTimeField;
use Combodo\iTop\Form\Field\Field;
@@ -172,17 +173,20 @@ EOF
// Some additional stuff if we are displaying it with a rich editor
if ($bRichEditor) {
$aConfig = utils::GetCkeditorPref();
$aConfig = CKEditorHelper::GetCkeditorPref();
$aConfig['extraPlugins'] = 'codesnippet';
$sJsConfig = json_encode($aConfig);
$oOutput->AddJs(
<<<EOF
<<<JS
$('#{$this->oField->GetGlobalId()}').addClass('htmlEditor');
$('#{$this->oField->GetGlobalId()}').ckeditor(function(){}, $sJsConfig).editor.on("change", function(){
$('#{$this->oField->GetGlobalId()}').trigger("change");
});
EOF
CombodoCKEditorHandler.CreateInstance('#{$this->oField->GetGlobalId()}').then(( oEditor) => {
oEditor.model.document.on('change:data', (event) => {
$('#{$this->oField->GetGlobalId()}').val(oEditor.getData()).trigger("change");
});
});
JS
);
if (($this->oField->GetObject() !== null) && ($this->oField->GetTransactionId() !== null)) {
$oOutput->AddJs(InlineImage::EnableCKEditorImageUpload($this->oField->GetObject(), utils::GetUploadTempId($this->oField->GetTransactionId())));

View File

@@ -22,6 +22,7 @@ namespace Combodo\iTop\Renderer\Console\FieldRenderer;
use AttributeDate;
use AttributeDateTime;
use AttributeDuration;
use Combodo\iTop\Application\Helper\CKEditorHelper;
use Combodo\iTop\Application\Helper\WebResourcesHelper;
use Combodo\iTop\Application\UI\Base\Component\Field\FieldUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
@@ -162,7 +163,7 @@ class ConsoleSimpleFieldRenderer extends FieldRenderer
if ($bRichEditor)
{
$oText->AddCSSClass('ibo-input-richtext-placeholder');
$aConfig = utils::GetCkeditorPref();
$aConfig = CKEditorHelper::GetCkeditorPref();
$aConfig['extraPlugins'] = 'codesnippet';
$sJsConfig = json_encode($aConfig);

View File

@@ -189,14 +189,17 @@ class ObjectRepository
{
try {
// object class
// Object key
$aData['id'] = $oDbObject->GetKey();
// Object class
$aData['class_name'] = get_class($oDbObject);
// Obsolescence flag
$aData['obsolescence_flag'] = $oDbObject->IsObsolete();
// Additional fields
$sFriendlynameForHtml = utils::EscapeHtml($aData['friendlyname']);
$sFriendlyNameForHtml = utils::EscapeHtml($aData['friendlyname']);
if (count($aComplementAttributeSpec[1]) > 0) {
$aData['has_additional_field'] = true;
$aArguments = [];
@@ -205,9 +208,9 @@ class ObjectRepository
}
$aData['additional_field'] = vsprintf($aComplementAttributeSpec[0], $aArguments);
$sAdditionalFieldForHtml = utils::EscapeHtml($aData['additional_field']);
$aData['full_description'] = "{$sFriendlynameForHtml}<br><i><small>{$sAdditionalFieldForHtml}</small></i>";
$aData['full_description'] = "{$sFriendlyNameForHtml}<br><i><small>{$sAdditionalFieldForHtml}</small></i>";
} else {
$aData['full_description'] = $sFriendlynameForHtml;
$aData['full_description'] = $sFriendlyNameForHtml;
}
// Image
@@ -223,6 +226,9 @@ class ObjectRepository
}
}
// Link
$aData['link'] = utils::GetAbsoluteUrlAppRoot() . "pages/UI.php?operation=details&class=$sClass&id={$oDbObject->GetKey()}";
return $aData;
}
catch (Exception $e) {