*/ class BsSimpleFieldRenderer extends BsFieldRenderer { /** * @inheritDoc */ public function Render() { $oOutput = parent::Render(); $sFieldClass = get_class($this->oField); $sFieldMandatoryClass = ($this->oField->GetMandatory()) ? 'form_mandatory' : ''; $sFieldDescriptionForHTMLTag = ($this->oField->HasDescription()) ? 'data-tooltip-content="'.utils::HtmlEntities($this->oField->GetDescription()).'"' : ''; // Prepare input validations tags $sInputTags = $this->ComputeInputValidationTags($this->oField); // Rendering field in edition mode if (!$this->oField->GetReadOnly() && !$this->oField->GetHidden()) { // HTML content switch ($sFieldClass) { case 'Combodo\\iTop\\Form\\Field\\DateTimeField': case 'Combodo\\iTop\\Form\\Field\\PasswordField': case 'Combodo\\iTop\\Form\\Field\\StringField': case 'Combodo\\iTop\\Form\\Field\\UrlField': case 'Combodo\\iTop\\Form\\Field\\EmailField': case 'Combodo\\iTop\\Form\\Field\\PhoneField': case 'Combodo\\iTop\\Form\\Field\\SelectField': case 'Combodo\\iTop\\Form\\Field\\MultipleSelectField': // Opening container $oOutput->AddHtml('
'); // Label $oOutput->AddHtml('
'); if ($this->oField->GetLabel() !== '') { $oOutput->AddHtml(''); } $oOutput->AddHtml('
'); // Value $oOutput->AddHtml('
'); // - Help block $oOutput->AddHtml('
'); // - Value regarding the field type switch ($sFieldClass) { case 'Combodo\\iTop\\Form\\Field\\DateTimeField': /* @see N°803 - Allow display & edition of attributes on n:n relations on Portal * LinkedSetFieldRenderer allow modification of link attributes, the default widget positioning truncates the popup. */ $sParent = ''; if ($this->oField->GetDateTimePickerWidgetParent() != null) { $sParent = ", widgetParent: '{$this->oField->GetDateTimePickerWidgetParent()}'"; } $oOutput->AddHtml('
'); $oOutput->AddHtml(''); $oOutput->AddHtml(''); $oOutput->AddHtml('
'); $sJSFormat = json_encode($this->oField->GetJSDateTimeFormat()); $sLocale = Dict::S('Portal:Calendar-FirstDayOfWeek'); $oOutput->AddJs( <<oField->GetGlobalId()}').datetimepicker({format: $sJSFormat, locale: '$sLocale' $sParent}); EOF ); break; case 'Combodo\\iTop\\Form\\Field\\PasswordField': $oOutput->AddHtml(''); break; case 'Combodo\\iTop\\Form\\Field\\StringField': case 'Combodo\\iTop\\Form\\Field\\UrlField': case 'Combodo\\iTop\\Form\\Field\\EmailField': case 'Combodo\\iTop\\Form\\Field\\PhoneField': $oOutput->AddHtml(''); break; case 'Combodo\\iTop\\Form\\Field\\SelectField': case 'Combodo\\iTop\\Form\\Field\\MultipleSelectField': $oOutput->AddHtml(''); break; } $oOutput->AddHtml('
'); // Closing container $oOutput->AddHtml('
'); break; case 'Combodo\\iTop\\Form\\Field\\TextAreaField': case 'Combodo\\iTop\\Form\\Field\\CaseLogField': $bRichEditor = ($this->oField->GetFormat() === TextAreaField::ENUM_FORMAT_HTML); // Opening container $oOutput->AddHtml('
'); // Label $oOutput->AddHtml('
'); if ($this->oField->GetLabel() !== '') { $oOutput->AddHtml(''); } $oOutput->AddHtml('
'); // Value $oOutput->AddHtml('
'); // - Help block $oOutput->AddHtml('
'); // First the edition area $oOutput->AddHtml('
'); $sEditorClasses = $bRichEditor ? 'htmlEditor' : ''; $oOutput->AddHtml(''); $oOutput->AddHtml('
'); // Then the previous entries if necessary if ($sFieldClass === 'Combodo\\iTop\\Form\\Field\\CaseLogField') { $this->PreparingCaseLogEntries($oOutput); } $oOutput->AddHtml('
'); // Closing container $oOutput->AddHtml('
'); // Some additional stuff if we are displaying it with a rich editor if ($bRichEditor) { // Enable CKEditor CKEditorHelper::ConfigureCKEditorElementForRenderingOutput($oOutput, $this->oField->GetGlobalId(), $this->oField->GetCurrentValue(), false, false, ['maximize' => []]); if (($this->oField->GetObject() !== null) && ($this->oField->GetTransactionId() !== null)) { $oOutput->AddJs(InlineImage::EnableCKEditorImageUpload($this->oField->GetObject(), utils::GetUploadTempId($this->oField->GetTransactionId()))); } } break; case 'Combodo\\iTop\\Form\\Field\\RadioField': case 'Combodo\\iTop\\Form\\Field\\CheckboxField': $sFieldType = ($sFieldClass === 'Combodo\\iTop\\Form\\Field\\RadioField') ? 'radio' : 'checkbox'; // Opening container $oOutput->AddHtml('
'); // Label $oOutput->AddHtml('
'); if ($this->oField->GetLabel() !== '') { $oOutput->AddHtml('
'); } $oOutput->AddHtml('
'); // Value $oOutput->AddHtml('
'); // - Help block $oOutput->AddHtml('
'); $oOutput->AddHtml('
'); $i = 0; foreach ($this->oField->GetChoices() as $sChoice => $sLabel) { // Note : The test is a double equal on purpose as the type of the value received from the XHR is not always the same as the type of the allowed values. (eg : string vs int) $sCheckedAtt = ($this->oField->IsAmongValues($sChoice)) ? 'checked' : ''; $sCheckedClass = ($this->oField->IsAmongValues($sChoice)) ? 'active' : ''; $oOutput->AddHtml(''); $i++; } $oOutput->AddHtml('
'); $oOutput->AddHtml('
'); // Closing container $oOutput->AddHtml('
'); break; case 'Combodo\\iTop\\Form\\Field\\HiddenField': $oOutput->AddHtml(''); break; } // JS FieldChange trigger (:input are not always at the same depth) switch ($sFieldClass) { case 'Combodo\\iTop\\Form\\Field\\PasswordField': case 'Combodo\\iTop\\Form\\Field\\StringField': case 'Combodo\\iTop\\Form\\Field\\UrlField': case 'Combodo\\iTop\\Form\\Field\\EmailField': case 'Combodo\\iTop\\Form\\Field\\PhoneField': case 'Combodo\\iTop\\Form\\Field\\SelectField': case 'Combodo\\iTop\\Form\\Field\\MultipleSelectField': case 'Combodo\\iTop\\Form\\Field\\HiddenField': $oOutput->AddJs(<<oField->GetGlobalId()}").off("change keyup").on("change keyup", function(){ var me = this; $(this).closest(".field_set").trigger("field_change", { id: $(me).attr("id"), name: $(me).closest(".form_field").attr("data-field-id"), value: $(me).val() }); }).on("mouseup", function(){this.focus();}); JS ); break; case 'Combodo\\iTop\\Form\\Field\\TextAreaField': case 'Combodo\\iTop\\Form\\Field\\CaseLogField': if ($this->oField->GetFormat() === TextAreaField::ENUM_FORMAT_HTML) { $oOutput->AddJs(<<oField->GetGlobalId()}") .then((oCKEditor) => { oCKEditor.model.document.on("change:data", () => { const oFieldElem = $("#{$this->oField->GetGlobalId()}"); oFieldElem.val(oCKEditor.getData()); oFieldElem.closest(".field_set").trigger("field_change", { id: oFieldElem.attr("id"), name: oFieldElem.closest(".form_field").attr("data-field-id"), value: oCKEditor.getData() }); }); }); JS ); } else { $oOutput->AddJs(<<oField->GetGlobalId()}").off("change keyup").on("change keyup", function(){ var me = this; $(this).closest(".field_set").trigger("field_change", { id: $(me).attr("id"), name: $(me).closest(".form_field").attr("data-field-id"), value: $(me).val() }); }).on("mouseup", function(){this.focus();}); JS ); } break; case 'Combodo\\iTop\\Form\\Field\\DateTimeField': // We need the focusout event has the datepicker widget seems to override the change event $oOutput->AddJs( <<oField->GetGlobalId()}").off("change keyup focusout").on("change keyup focusout", function(){ var me = this; $(this).closest(".field_set").trigger("field_change", { id: $(me).attr("id"), name: $(me).closest(".form_field").attr("data-field-id"), value: $(me).val() }); }); EOF ); break; case 'Combodo\\iTop\\Form\\Field\\RadioField': case 'Combodo\\iTop\\Form\\Field\\CheckboxField': $oOutput->AddJs( <<oField->GetGlobalId()} input").off("change").on("change", function(){ var me = this; $(this).closest(".field_set").trigger("field_change", { id: $(me).closest("#{$this->oField->GetGlobalId()}").attr("id"), name: $(me).attr("name"), value: $(me).val() }); }); EOF ); break; } } // ... and in read-only mode (or hidden) else { // ... specific rendering for fields with multiple values if (($this->oField instanceof MultipleChoicesField) && ($this->oField->GetMultipleValuesEnabled())) { // TODO } // ... classic rendering for fields with only one value else { switch ($sFieldClass) { case 'Combodo\\iTop\\Form\\Field\\LabelField': case 'Combodo\\iTop\\Form\\Field\\StringField': case 'Combodo\\iTop\\Form\\Field\\UrlField': case 'Combodo\\iTop\\Form\\Field\\EmailField': case 'Combodo\\iTop\\Form\\Field\\PhoneField': case 'Combodo\\iTop\\Form\\Field\\DateTimeField': case 'Combodo\\iTop\\Form\\Field\\DurationField': // Opening container $oOutput->AddHtml('
'); // Showing label / value only if read-only but not hidden if (!$this->oField->GetHidden()) { // Label $oOutput->AddHtml('
'); if ($this->oField->GetLabel() !== '') { $oOutput->AddHtml(''); } $oOutput->AddHtml('
'); // Value $bEncodeHtmlEntities = ( in_array($sFieldClass, array('Combodo\\iTop\\Form\\Field\\UrlField', 'Combodo\\iTop\\Form\\Field\\EmailField', 'Combodo\\iTop\\Form\\Field\\PhoneField')) ) ? false : true; $oOutput->AddHtml('
'); $oOutput->AddHtml('
')->AddHtml($this->oField->GetDisplayValue(), $bEncodeHtmlEntities)->AddHtml('
'); $oOutput->AddHtml('
'); } // Adding hidden input if not a label if($sFieldClass !== 'Combodo\\iTop\\Form\\Field\\LabelField') { $sValueForInput = ($sFieldClass === 'Combodo\\iTop\\Form\\Field\\DateTimeField') ? $this->oField->GetDisplayValue() : $this->oField->GetCurrentValue(); $oOutput->AddHtml(''); } // Closing container $oOutput->AddHtml('
'); break; case 'Combodo\\iTop\\Form\\Field\\TextAreaField': // Opening container $oOutput->AddHtml('
'); // Showing label / value only if read-only but not hidden if (!$this->oField->GetHidden()) { // Label $oOutput->AddHtml('
'); if ($this->oField->GetLabel() !== '') { $oOutput->AddHtml(''); } $oOutput->AddHtml('
'); // Value $oOutput->AddHtml('
'); $oOutput->AddHtml('
')->AddHtml($this->oField->GetDisplayValue(), false)->AddHtml('
'); $oOutput->AddHtml('
'); } // Adding hidden input $oOutput->AddHtml(''); // Closing container $oOutput->AddHtml('
'); break; case 'Combodo\\iTop\\Form\\Field\\CaseLogField': // Opening container $oOutput->AddHtml('
'); // Label $oOutput->AddHtml('
'); if ($this->oField->GetLabel() !== '') { $oOutput->AddHtml(''); } $oOutput->AddHtml('
'); // Value $oOutput->AddHtml('
'); // - Entries if necessary $this->PreparingCaseLogEntries($oOutput); $oOutput->AddHtml('
'); // Closing container $oOutput->AddHtml('
'); break; case 'Combodo\\iTop\\Form\\Field\\BlobField': case 'Combodo\\iTop\\Form\\Field\\ImageField': // Opening container $oOutput->AddHtml('
'); // Showing label / value only if read-only but not hidden if (!$this->oField->GetHidden()) { // Label $oOutput->AddHtml('
'); if ($this->oField->GetLabel() !== '') { $oOutput->AddHtml(''); } $oOutput->AddHtml('
'); // Value $oOutput->AddHtml('
'); $oOutput->AddHtml('
'); if($sFieldClass === 'Combodo\\iTop\\Form\\Field\\ImageField') { $oOutput->AddHtml('', false); } else { $oOutput->AddHtml($this->oField->GetDisplayValue(), false); } $oOutput->AddHtml('
'); $oOutput->AddHtml('
'); } $oOutput->AddHtml(''); // Closing container $oOutput->AddHtml('
'); break; case 'Combodo\\iTop\\Form\\Field\\RadioField': case 'Combodo\\iTop\\Form\\Field\\SelectField': case 'Combodo\\iTop\\Form\\Field\\MultipleSelectField': $aFieldChoices = $this->oField->GetChoices(); $sFieldValue = (isset($aFieldChoices[$this->oField->GetCurrentValue()])) ? $aFieldChoices[$this->oField->GetCurrentValue()] : Dict::S('UI:UndefinedObject'); // Opening container $oOutput->AddHtml('
'); // Showing label / value only if read-only but not hidden if (!$this->oField->GetHidden()) { // Label $oOutput->AddHtml('
'); if ($this->oField->GetLabel() !== '') { $oOutput->AddHtml(''); } $oOutput->AddHtml('
'); // Value $oOutput->AddHtml('
'); $oOutput->AddHtml('
' . $sFieldValue . '
'); $oOutput->AddHtml('
'); } // Adding hidden value $oOutput->AddHtml(''); // Closing container $oOutput->AddHtml('
'); break; case 'Combodo\\iTop\\Form\\Field\\HiddenField': $oOutput->AddHtml(''); break; } } } // Attaching JS widget only if field is hidden or NOT read only if($this->oField->GetHidden() || !$this->oField->GetReadOnly()) { // JS Form field widget construct $aValidators = array(); foreach ($this->oField->GetValidators() as $oValidator) { if (false === ($oValidator instanceof AbstractRegexpValidator)) { // no JS counterpart, so skipping ! continue; } $aValidators[$oValidator::GetName()] = array( 'reg_exp' => $oValidator->GetRegExp(), 'message' => Dict::S($oValidator->GetErrorMessage()), ); } $sFormFieldOptions = json_encode(array( 'validators' => $aValidators )); switch ($sFieldClass) { case 'Combodo\\iTop\\Form\\Field\\PasswordField': case 'Combodo\\iTop\\Form\\Field\\StringField': case 'Combodo\\iTop\\Form\\Field\\UrlField': case 'Combodo\\iTop\\Form\\Field\\EmailField': case 'Combodo\\iTop\\Form\\Field\\PhoneField': case 'Combodo\\iTop\\Form\\Field\\SelectField': case 'Combodo\\iTop\\Form\\Field\\MultipleSelectField': case 'Combodo\\iTop\\Form\\Field\\HiddenField': case 'Combodo\\iTop\\Form\\Field\\RadioField': case 'Combodo\\iTop\\Form\\Field\\CheckboxField': case 'Combodo\\iTop\\Form\\Field\\DateTimeField': $oOutput->AddJs( <<oField->GetFormat() === TextAreaField::ENUM_FORMAT_HTML); if($bRichEditor) { // Overloading $sFormFieldOptions to include the set_current_value_callback. It would have been nicer to refactor the variable for all field types, but as this is a fix for a maintenance release, we rather be safe. $sValidators = json_encode($aValidators); $oOutput->AddJs( <<AddJs( <<oField->GetFormat() === TextAreaField::ENUM_FORMAT_HTML); if($bRichEditor) { // MagnificPopup on images $oOutput->AddJs(InlineImage::FixImagesWidth()); // Trigger highlighter for all code blocks in this caselog $oOutput->AddJs(<<oField->GetEntries(); $iNbEntries = count($aEntries); if ($iNbEntries > 0) { // Dict entries $sOpenAllEntriesTooltip = utils::HtmlEntities(Dict::S('UI:Layout:ActivityPanel:Tab:Toolbar:Action:OpenAll:Tooltip')); $sCloseAllEntriesTooltip = utils::HtmlEntities(Dict::S('UI:Layout:ActivityPanel:Tab:Toolbar:Action:CloseAll:Tooltip')); $sUsersCountTooltip = utils::HtmlEntities(Dict::S('UI:Layout:ActivityPanel:Tab:Toolbar:Info:AuthorsCount:Tooltip')); $sEntriesCountTooltip = utils::HtmlEntities(Dict::S('UI:Layout:ActivityPanel:Tab:Toolbar:Info:MessagesCount:Tooltip')); $sCloseEntryTooltip = utils::HtmlEntities(Dict::S('Portal:Form:Caselog:Entry:Close:Tooltip')); // First pass to retrieve number of users $aUserIds = array(); for ($i = 0; $i < $iNbEntries; $i++) { $iEntryUserId = $aEntries[$i]['user_id']; if (!in_array($iEntryUserId, $aUserIds)) { $aUserIds[] = $iEntryUserId; } } $iNbUsers = count($aUserIds); // Opening thread $oOutput->AddHtml(<< HTML ); // - Header $oOutput->AddHtml(<< {$iNbUsers} {$iNbEntries} HTML ); // - Content $oOutput->AddHtml(<< HTML ); $sThreadUniqueId = uniqid(); $sLastDate = null; $sLastUserId = null; $iLastLoopIndex = $iNbEntries - 1; // Caching profile picture url as it is resource consuming $aContactPicturesCache = array(); $aPeerColorClassCache = array(); // Note: Yes, the config. param. is named after the backoffice element but we hope that we will "soon" have some kind of "light" activity panel in the portal too, so we keep this name. $bHideContactPicture = false; if (defined('PORTAL_ID')) { $bHideContactPicture= in_array(PORTAL_ID, utils::GetConfig()->Get('activity_panel.hide_avatars')); } // Current user $iCurrentUserId = UserRights::GetUserId(); for ($i = 0; $i < $iNbEntries; $i++) { $sEntryDatetime = AttributeDateTime::GetFormat()->Format($aEntries[$i]['date']); $sEntryDate = AttributeDate::GetFormat()->Format($aEntries[$i]['date']); $sEntryUserLogin = $aEntries[$i]['user_login']; $iEntryUserId = $aEntries[$i]['user_id']; // - Friendlyname if (false === empty($iEntryUserId)) { $oEntryUser = MetaModel::GetObject('User', $iEntryUserId, false /* Necessary in case user has been deleted */, true); if(!is_null($oEntryUser)) { $sEntryUserLogin = UserRights::GetUserFriendlyName($oEntryUser->Get('login')); } // Retrieve (and cache) profile picture if available (standard datamodel) // Note: Here the cache is more about nor retrieving the User object several times rather than computing the picture URL if (!array_key_exists($iEntryUserId, $aContactPicturesCache)) { // First, check if we should display the picture if ($bHideContactPicture === true) { $sEntryContactPictureAbsoluteUrl = null; } // Otherwise try to retrieve one for the current contact else { if(is_null($oEntryUser)) { $sEntryContactPictureAbsoluteUrl = null; } else { $sEntryContactPictureAbsoluteUrl = UserRights::GetUserPictureAbsUrl($oEntryUser->Get('login'), false); } } $aContactPicturesCache[$iEntryUserId] = $sEntryContactPictureAbsoluteUrl; } } // Open user block if previous user was different or if previous date was different if (($iEntryUserId !== $sLastUserId) || ($sEntryDate !== $sLastDate)) { if ($sEntryDate !== $sLastDate) { $oOutput->AddHtml(<<{$sEntryDate} HTML ); } // Open block if ($iEntryUserId === $iCurrentUserId) { $sEntryBlockClass = 'ipb-caselog-thread--block-me'; } else { if (!array_key_exists($iEntryUserId, $aPeerColorClassCache)) { $iPeerClassNumber = (count($aPeerColorClassCache) % 5) + 1; $aPeerColorClassCache[$iEntryUserId] = 'ipb-caselog-thread--block-color-'.$iPeerClassNumber; } $sEntryBlockClass = $aPeerColorClassCache[$iEntryUserId]; } $oOutput->AddHtml(<< HTML ); // Open medallion from profile picture or first name letter $bEntryHasMedallionPicture = (empty($aContactPicturesCache[$iEntryUserId]) === false); $sEntryMedallionStyle = $bEntryHasMedallionPicture ? ' background-image: url(\''.$aContactPicturesCache[$iEntryUserId].'\');' : ''; $sEntryMedallionContent = $bEntryHasMedallionPicture ? '' : utils::FormatInitialsForMedallion(UserRights::GetUserInitials($sEntryUserLogin)); // - Entry tooltip $sEntryMedallionTooltip = utils::HtmlEntities($sEntryUserLogin); $sEntryMedallionTooltipPlacement = ($iEntryUserId === $iCurrentUserId) ? 'left' : 'right'; $oOutput->AddHtml(<< $sEntryMedallionContent
{$sEntryMedallionTooltip}
HTML ); // Open entries $oOutput->AddHtml(<< HTML ); } // Prepare entry content $sEntryId = 'ipb-caselog-thread--block-entry-'.$sThreadUniqueId.'-'.$i; $sEntryHtml = AttributeText::RenderWikiHtml($aEntries[$i]['message_html'], true /* wiki only */); $sEntryHtml = InlineImage::FixUrls($sEntryHtml); // Add entry $oOutput->AddHtml(<<
{$sEntryHtml}
HTML ); // Close user block if next user is different or if last entry or if next entry is for another date if (($i === $iLastLoopIndex) || ($i < $iLastLoopIndex && $iEntryUserId !== $aEntries[$i + 1]['user_id']) || ($i < $iLastLoopIndex && $sEntryDate !== AttributeDate::GetFormat()->Format($aEntries[$i + 1]['date']))) { // Close entries and block $oOutput->AddHtml(<< HTML ); } // Update current loop informations $sLastDate = $sEntryDate; $sLastUserId = $iEntryUserId; } // Close thread content and thread $oOutput->AddHtml(<< HTML ); // Add JS handlers $oOutput->AddJs(<<GetValidators() as $oValidator) { // Validator if ($oValidator instanceof AbstractRegexpValidator) { if (!($oField instanceof DateField || $oField instanceof DateTimeField)) { // unrecognized regular expression $sTags .= ' pattern="'.$oValidator->GetRegExp().'" '; } } // Mandatory validator if ($oValidator instanceof MandatoryValidator) { $sTags .= ' required '; } } return $sTags; } }