?) to an attribute * * @package iTopORM */ class AttributeText extends AttributeString { /** * Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329) * * @see https://www.php.net/manual/fr/language.oop5.decon.php states that child constructor can be ommited * @see https://bugs.php.net/bug.php?id=79010 bug solved in PHP 7.4.9 * * @param string $sCode * @param array $aParams * * @throws Exception * @noinspection SenselessProxyMethodInspection */ public function __construct($sCode, $aParams) { parent::__construct($sCode, $aParams); } public function GetEditClass() { return ($this->GetFormat() == 'text') ? 'Text' : "HTML"; } protected function GetSQLCol($bFullSpec = false) { return "TEXT".CMDBSource::GetSqlStringColumnDefinition(); } public function GetSQLColumns($bFullSpec = false) { $aColumns = []; $aColumns[$this->Get('sql')] = $this->GetSQLCol($bFullSpec); if ($this->GetOptional('format', null) != null) { // Add the extra column only if the property 'format' is specified for the attribute $aColumns[$this->Get('sql').'_format'] = "ENUM('text','html')".CMDBSource::GetSqlStringColumnDefinition(); if ($bFullSpec) { $aColumns[$this->Get('sql').'_format'] .= " DEFAULT 'text'"; // default 'text' is for migrating old records } } return $aColumns; } public function GetSQLExpressions($sPrefix = '') { if ($sPrefix == '') { $sPrefix = $this->Get('sql'); } $aColumns = []; // Note: to optimize things, the existence of the attribute is determined by the existence of one column with an empty suffix $aColumns[''] = $sPrefix; if ($this->GetOptional('format', null) != null) { // Add the extra column only if the property 'format' is specified for the attribute $aColumns['_format'] = $sPrefix.'_format'; } return $aColumns; } public function GetMaxSize() { // Is there a way to know the current limitation for mysql? // See mysql_field_len() return 65535; } public static function RenderWikiHtml($sText, $bWikiOnly = false) { if (!$bWikiOnly) { $sPattern = '/'.str_replace('/', '\/', utils::GetConfig()->Get('url_validation_pattern')).'/i'; if (preg_match_all( $sPattern, $sText, $aAllMatches, PREG_SET_ORDER /* important !*/ | PREG_OFFSET_CAPTURE /* important ! */ )) { $i = count($aAllMatches); // Replace the URLs by an actual hyperlink ... // Let's do it backwards so that the initial positions are not modified by the replacement // This works if the matches are captured: in the order they occur in the string AND // with their offset (i.e. position) inside the string while ($i > 0) { $i--; $sUrl = $aAllMatches[$i][0][0]; // String corresponding to the main pattern $iPos = $aAllMatches[$i][0][1]; // Position of the main pattern $sText = substr_replace($sText, "$sUrl", $iPos, strlen($sUrl)); } } } if (preg_match_all(WIKI_OBJECT_REGEXP, $sText, $aAllMatches, PREG_SET_ORDER)) { foreach ($aAllMatches as $iPos => $aMatches) { $sClass = trim($aMatches[1]); $sName = trim($aMatches[2]); $sLabel = (!empty($aMatches[4])) ? trim($aMatches[4]) : null; if (MetaModel::IsValidClass($sClass)) { $bFound = false; // Try to find by name, then by id if (is_object($oObj = MetaModel::GetObjectByName($sClass, $sName, false /* MustBeFound */))) { $bFound = true; } elseif (is_object($oObj = MetaModel::GetObject($sClass, (int)$sName, false /* MustBeFound */, true))) { $bFound = true; } if ($bFound === true) { // Propose a std link to the object $sHyperlinkLabel = (empty($sLabel)) ? $oObj->GetName() : $sLabel; $sText = str_replace($aMatches[0], $oObj->GetHyperlink(null, true, $sHyperlinkLabel), $sText); } else { // Propose a std link to the object $sClassLabel = MetaModel::GetName($sClass); $sToolTipForHtml = utils::EscapeHtml(Dict::Format('Core:UnknownObjectLabel', $sClass, $sName)); $sReplacement = "$sClassLabel:$sName".(!empty($sLabel) ? " ($sLabel)" : "").""; $sText = str_replace($aMatches[0], $sReplacement, $sText); // Later: propose a link to create a new object // Anyhow... there is no easy way to suggest default values based on the given FRIENDLY name //$sText = preg_replace('/\[\[(.+):(.+)\]\]/', ''.$sName.'', $sText); } } } } return $sText; } public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true) { $aStyles = []; if ($this->GetWidth() != '') { $aStyles[] = 'width:'.$this->GetWidth(); } if ($this->GetHeight() != '') { $aStyles[] = 'height:'.$this->GetHeight(); } $sStyle = ''; if (count($aStyles) > 0) { $sStyle = 'style="'.implode(';', $aStyles).'"'; } if ($this->GetFormat() == 'text') { $sValue = parent::GetAsHTML($sValue, $oHostObject, $bLocalize); $sValue = self::RenderWikiHtml($sValue); $sValue = nl2br($sValue); return "
$sValue
"; } else { $sValue = self::RenderWikiHtml($sValue, true /* wiki only */); return "
".InlineImage::FixUrls($sValue).'
'; } } public function GetEditValue($sValue, $oHostObj = null) { // N°4517 - PHP 8.1 compatibility: str_replace call with null cause deprecated message if ($sValue == null) { return ''; } if ($this->GetFormat() == 'text') { if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER)) { foreach ($aAllMatches as $iPos => $aMatches) { $sClass = trim($aMatches[1]); $sName = trim($aMatches[2]); $sLabel = (!empty($aMatches[4])) ? trim($aMatches[4]) : null; if (MetaModel::IsValidClass($sClass)) { $sClassLabel = MetaModel::GetName($sClass); $sReplacement = "[[$sClassLabel:$sName".(!empty($sLabel) ? " | $sLabel" : "")."]]"; $sValue = str_replace($aMatches[0], $sReplacement, $sValue); } } } } return $sValue; } /** * For fields containing a potential markup, return the value without this markup * * @param string $sValue * @param DBObject $oHostObj * * @return string */ public function GetAsPlainText($sValue, $oHostObj = null) { if ($this->GetFormat() == 'html') { return (string)utils::HtmlToText($this->GetEditValue($sValue, $oHostObj)); } else { return parent::GetAsPlainText($sValue, $oHostObj); } } public function MakeRealValue($proposedValue, $oHostObj) { $sValue = $proposedValue; // N°4517 - PHP 8.1 compatibility: str_replace call with null cause deprecated message if ($sValue == null) { return ''; } switch ($this->GetFormat()) { case 'html': if (($sValue !== null) && ($sValue !== '')) { $sValue = HTMLSanitizer::Sanitize($sValue); } break; case 'text': default: if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER)) { foreach ($aAllMatches as $iPos => $aMatches) { $sClassLabel = trim($aMatches[1]); $sName = trim($aMatches[2]); $sLabel = (!empty($aMatches[4])) ? trim($aMatches[4]) : null; if (!MetaModel::IsValidClass($sClassLabel)) { $sClass = MetaModel::GetClassFromLabel($sClassLabel); if ($sClass) { $sReplacement = "[[$sClassLabel:$sName".(!empty($sLabel) ? " | $sLabel" : "")."]]"; $sValue = str_replace($aMatches[0], $sReplacement, $sValue); } } } } } return $sValue; } public function GetAsXML($value, $oHostObject = null, $bLocalize = true) { return Str::pure2xml($value); } public function GetWidth() { return $this->GetOptional('width', ''); } public function GetHeight() { return $this->GetOptional('height', ''); } public static function GetFormFieldClass() { return '\\Combodo\\iTop\\Form\\Field\\TextAreaField'; } /** * @param DBObject $oObject * @param TextAreaField $oFormField * * @return TextAreaField * @throws CoreException */ public function MakeFormField(DBObject $oObject, $oFormField = null) { if ($oFormField === null) { $sFormFieldClass = static::GetFormFieldClass(); /** @var TextAreaField $oFormField */ $oFormField = new $sFormFieldClass($this->GetCode(), null, $oObject); $oFormField->SetFormat($this->GetFormat()); } parent::MakeFormField($oObject, $oFormField); return $oFormField; } /** * The actual formatting of the field: either text (=plain text) or html (= text with HTML markup) * * @return string */ public function GetFormat() { return $this->GetOptional('format', 'text'); } /** * Read the value from the row returned by the SQL query and transorms it to the appropriate * internal format (either text or html) * * @see AttributeDBFieldVoid::FromSQLToValue() * * @param string $sPrefix * * @param array $aCols * * @return string */ public function FromSQLToValue($aCols, $sPrefix = '') { $value = $aCols[$sPrefix.'']; if ($this->GetOptional('format', null) != null) { // Read from the extra column only if the property 'format' is specified for the attribute $sFormat = $aCols[$sPrefix.'_format']; } else { $sFormat = $this->GetFormat(); } switch ($sFormat) { case 'text': if ($this->GetFormat() == 'html') { $value = utils::TextToHtml($value); } break; case 'html': if ($this->GetFormat() == 'text') { $value = utils::HtmlToText($value); } else { $value = InlineImage::FixUrls((string)$value); } break; default: // unknown format ?? } return $value; } public function GetSQLValues($value) { $aValues = []; $aValues[$this->Get("sql")] = $this->ScalarToSQL($value); if ($this->GetOptional('format', null) != null) { // Add the extra column only if the property 'format' is specified for the attribute $aValues[$this->Get("sql").'_format'] = $this->GetFormat(); } return $aValues; } public function GetAsCSV( $sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false ) { switch ($this->GetFormat()) { case 'html': if ($bConvertToPlainText) { $sValue = utils::HtmlToText((string)$sValue); } $sFrom = ["\r\n", $sTextQualifier]; $sTo = ["\n", $sTextQualifier.$sTextQualifier]; $sEscaped = str_replace($sFrom, $sTo, (string)$sValue); return $sTextQualifier.$sEscaped.$sTextQualifier; break; case 'text': default: return parent::GetAsCSV( $sValue, $sSeparator, $sTextQualifier, $oHostObject, $bLocalize, $bConvertToPlainText ); } } protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void { /** @noinspection PhpConditionCheckedByNextConditionInspection */ if (false === is_null($original) && ($original instanceof ormCaseLog)) { $original = $original->GetText(); } $oMyChangeOp->Set("prevdata", $original); } protected function GetChangeRecordClassName(): string { return ($this->GetFormat() === 'html') ? CMDBChangeOpSetAttributeHTML::class : CMDBChangeOpSetAttributeText::class; } }