?) 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(); } /** * @inheritDoc * * Unlike the default implementation, the size is expressed in **bytes**: MySQL TEXT columns are limited * in bytes (65535), not in characters, and {@see static::GetMaxSize()} for this class returns a number of bytes. */ public function GetSize(?string $sValue) { // If the value is null, we return 0 if ($sValue === null) { return 0; } return strlen($sValue); } /** * @inheritDoc * * Truncation is performed on a **byte** budget (MySQL TEXT limit) without ever cutting through a multibyte * UTF-8 sequence: the returned value is always valid UTF-8 and never exceeds GetMaxSize() bytes, * truncation suffix included. */ public function TrimValue(?string $sValue) { // If the value is null, we return an empty string if ($sValue === null) { return ''; } $iMaxSize = $this->GetMaxSize(); $iLength = strlen($sValue); $iLengthChar = mb_strlen($sValue); if ($iMaxSize && ($iLength > $iMaxSize)) { $sMessage = " -truncated ($iLengthChar chars)"; $iTruncatedValueMaxSize = $iMaxSize - strlen($sMessage); // mb_strcut cuts on a byte budget but moves the cut point back to a character boundary, // so it never returns a broken multibyte sequence at the end of the value $sTruncatedValue = mb_strcut($sValue, 0, $iTruncatedValueMaxSize, 'UTF-8'); return $sTruncatedValue.$sMessage; } return $sValue; } 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; } /** * @param string|null $sText * @param bool $bWikiOnly * * @return string * @throws \ArchivedObjectException * @throws \ConfigException * @throws \CoreException * @throws \DictExceptionMissingString * * @since 3.3.0 N°8681 Add type hint for parameters and return value */ public static function RenderWikiHtml(string|null $sText, bool $bWikiOnly = false): string { // N°8681 - Ensure to have a string value $sText = $sText ?? ''; 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 "