/** * Get the HTML fragment corresponding to the HTML editor widget * @param WebPage $oP The web page used for all the output * @param Hash $aArgs Extra context arguments * @return string The HTML fragment to be inserted into the page */ public function Display(WebPage $oPage, $aArgs = array()) { $iId = $this->m_iId; $sCode = $this->m_sAttCode . $this->m_sNameSuffix; $sValue = $this->m_sValue; $sHelpText = $this->m_sHelpText; $sValidationField = $this->m_sValidationField; $sHtmlValue = "<table><tr><td><textarea class=\"htmlEditor\" title=\"{$sHelpText}\" name=\"attr_{$this->m_sFieldPrefix}{$sCode}\" rows=\"10\" cols=\"10\" id=\"{$iId}\">{$sValue}</textarea></td><td>{$sValidationField}</td></tr></table>"; // Replace the text area with CKEditor // To change the default settings of the editor, // a) edit the file /js/ckeditor/config.js // b) or override some of the configuration settings, using the second parameter of ckeditor() $sLanguage = strtolower(trim(UserRights::GetUserLanguage())); $oPage->add_ready_script("\$('#{$iId}').ckeditor(function() { /* callback code */ }, { language : '{$sLanguage}' , contentsLanguage : '{$sLanguage}', extraPlugins: 'disabler' });"); // Transform $iId into a CKEdit // Please read... // ValidateCKEditField triggers a timer... calling itself indefinitely // This design was the quickest way to achieve the field validation (only checking if the field is blank) // because the ckeditor does not fire events like "change" or "keyup", etc. // See http://dev.ckeditor.com/ticket/900 => won't fix // The most relevant solution would be to implement a plugin to CKEdit, and handle the internal events like: setData, insertHtml, insertElement, loadSnapshot, key, afterUndo, afterRedo // Could also be bound to 'instanceReady.ckeditor' $oPage->add_ready_script("\$('#{$iId}').bind('validate', function(evt, sFormId) { return ValidateCKEditField('{$iId}', '', {$this->m_sMandatory}, sFormId, '') } );\n"); $oPage->add_ready_script("\$('#{$iId}').bind('update', function() { BlockField('cke_{$iId}', \$('#{$iId}').attr('disabled')); } );\n"); return $sHtmlValue; }
/** * Get the HTML fragment corresponding to the linkset editing widget * @param WebPage $oP The web page used for all the output * @param Hash $aArgs Extra context arguments * @return string The HTML fragment to be inserted into the page */ public function Display(WebPage $oPage, $aArgs = array()) { $sCode = $this->sAttCode . $this->sNameSuffix; $iWidgetIndex = self::$iWidgetIndex; $aPasswordValues = utils::ReadPostedParam("attr_{$sCode}", null, 'raw_data'); $sPasswordValue = $aPasswordValues ? $aPasswordValues['value'] : '*****'; $sConfirmPasswordValue = $aPasswordValues ? $aPasswordValues['confirm'] : '*****'; $sChangedValue = $sPasswordValue != '*****' || $sConfirmPasswordValue != '*****' ? 1 : 0; $sHtmlValue = ''; $sHtmlValue = '<input type="password" maxlength="255" name="attr_' . $sCode . '[value]" id="' . $this->iId . '" value="' . htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8') . '"/> <span class="form_validation" id="v_' . $this->iId . '"></span><br/>'; $sHtmlValue .= '<input type="password" maxlength="255" id="' . $this->iId . '_confirm" value="' . htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8') . '" name="attr_' . $sCode . '[confirm]"/> ' . Dict::S('UI:PasswordConfirm') . ' <input id="' . $this->iId . '_reset" type="button" value="' . Dict::S('UI:Button:ResetPassword') . '" onClick="ResetPwd(\'' . $this->iId . '\');">'; $sHtmlValue .= '<input type="hidden" id="' . $this->iId . '_changed" name="attr_' . $sCode . '[changed]" value="' . $sChangedValue . '"/>'; $oPage->add_ready_script("\$('#{$this->iId}').bind('keyup change', function(evt) { return PasswordFieldChanged('{$this->iId}') } );"); // Bind to a custom event: validate $oPage->add_ready_script("\$('#{$this->iId}').bind('keyup change validate', function(evt, sFormId) { return ValidatePasswordField('{$this->iId}', sFormId) } );"); // Bind to a custom event: validate $oPage->add_ready_script("\$('#{$this->iId}_confirm').bind('keyup change', function(evt, sFormId) { return ValidatePasswordField('{$this->iId}', sFormId) } );"); // Bind to a custom event: validate $oPage->add_ready_script("\$('#{$this->iId}').bind('update', function(evt, sFormId)\n\t\t\t{\n\t\t\t\tif (\$(this).attr('disabled'))\n\t\t\t\t{\n\t\t\t\t\t\$('#{$this->iId}_confirm').attr('disabled', 'disabled');\n\t\t\t\t\t\$('#{$this->iId}_changed').attr('disabled', 'disabled');\n\t\t\t\t\t\$('#{$this->iId}_reset').attr('disabled', 'disabled');\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t\$('#{$this->iId}_confirm').removeAttr('disabled');\n\t\t\t\t\t\$('#{$this->iId}_changed').removeAttr('disabled');\n\t\t\t\t\t\$('#{$this->iId}_reset').removeAttr('disabled');\n\t\t\t\t}\n\t\t\t}\n\t\t);"); // Bind to a custom event: update to handle enabling/disabling return $sHtmlValue; }
public function DisplayAttachments($oObject, WebPage $oPage, $bEditMode = false) { // Exit here if the class is not allowed if (!$this->IsTargetObject($oObject)) { return; } $oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_class = :class AND item_id = :item_id"); $oSet = new DBObjectSet($oSearch, array(), array('class' => get_class($oObject), 'item_id' => $oObject->GetKey())); if ($this->GetAttachmentsPosition() == 'relations') { $sTitle = $oSet->Count() > 0 ? Dict::Format('Attachments:TabTitle_Count', $oSet->Count()) : Dict::S('Attachments:EmptyTabTitle'); $oPage->SetCurrentTab($sTitle); } $oPage->add_style(<<<EOF .attachment { \tdisplay: inline-block; \ttext-align:center; \tfloat:left; \tpadding:5px;\t } .attachment:hover { \tbackground-color: #e0e0e0; } .attachment img { \tborder: 0; } .attachment a { \ttext-decoration: none; \tcolor: #1C94C4; } .btn_hidden { \tdisplay: none; } .drag_in { \t-webkit-box-shadow:inset 0 0 10px 2px #1C94C4; \tbox-shadow:inset 0 0 10px 2px #1C94C4; } #history .attachment-history-added { \tpadding: 0; \tfloat: none; } EOF ); $oPage->add('<fieldset>'); $oPage->add('<legend>' . Dict::S('Attachments:FieldsetTitle') . '</legend>'); if ($bEditMode) { $sIsDeleteEnabled = $this->m_bDeleteEnabled ? 'true' : 'false'; $iTransactionId = $oPage->GetTransactionId(); $sClass = get_class($oObject); $sTempId = session_id() . '_' . $iTransactionId; $sDeleteBtn = Dict::S('Attachments:DeleteBtn'); $oPage->add_script(<<<EOF \tfunction RemoveAttachment(att_id) \t{ \t\t\$('#attachment_'+att_id).attr('name', 'removed_attachments[]'); \t\t\$('#display_attachment_'+att_id).hide(); \t\t\$('#attachment_plugin').trigger('remove_attachment', [att_id]); \t\treturn false; // Do not submit the form ! \t} EOF ); $oPage->add('<span id="attachments">'); while ($oAttachment = $oSet->Fetch()) { $iAttId = $oAttachment->GetKey(); $oDoc = $oAttachment->Get('contents'); $sFileName = $oDoc->GetFileName(); $sIcon = utils::GetAbsoluteUrlAppRoot() . AttachmentPlugIn::GetFileIcon($sFileName); $sPreview = $oDoc->IsPreviewAvailable() ? 'true' : 'false'; $sDownloadLink = utils::GetAbsoluteUrlAppRoot() . 'pages/ajax.render.php?operation=download_document&class=Attachment&id=' . $iAttId . '&field=contents'; $oPage->add('<div class="attachment" id="display_attachment_' . $iAttId . '"><a data-preview="' . $sPreview . '" href="' . $sDownloadLink . '"><img src="' . $sIcon . '"><br/>' . $sFileName . '<input id="attachment_' . $iAttId . '" type="hidden" name="attachments[]" value="' . $iAttId . '"/></a><br/> <input id="btn_remove_' . $iAttId . '" type="button" class="btn_hidden" value="Delete" onClick="RemoveAttachment(' . $iAttId . ');"/> </div>'); } // Suggested attachments are listed here but treated as temporary $aDefault = utils::ReadParam('default', array(), false, 'raw_data'); if (array_key_exists('suggested_attachments', $aDefault)) { $sSuggestedAttachements = $aDefault['suggested_attachments']; if (is_array($sSuggestedAttachements)) { $sSuggestedAttachements = implode(',', $sSuggestedAttachements); } $oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE id IN({$sSuggestedAttachements})"); $oSet = new DBObjectSet($oSearch, array()); if ($oSet->Count() > 0) { while ($oAttachment = $oSet->Fetch()) { // Mark the attachments as temporary attachments for the current object/form $oAttachment->Set('temp_id', $sTempId); $oAttachment->DBUpdate(); // Display them $iAttId = $oAttachment->GetKey(); $oDoc = $oAttachment->Get('contents'); $sFileName = $oDoc->GetFileName(); $sIcon = utils::GetAbsoluteUrlAppRoot() . AttachmentPlugIn::GetFileIcon($sFileName); $sDownloadLink = utils::GetAbsoluteUrlAppRoot() . 'pages/ajax.render.php?operation=download_document&class=Attachment&id=' . $iAttId . '&field=contents'; $sPreview = $oDoc->IsPreviewAvailable() ? 'true' : 'false'; $oPage->add('<div class="attachment" id="display_attachment_' . $iAttId . '"><a data-preview="' . $sPreview . '" href="' . $sDownloadLink . '"><img src="' . $sIcon . '"><br/>' . $sFileName . '<input id="attachment_' . $iAttId . '" type="hidden" name="attachments[]" value="' . $iAttId . '"/></a><br/> <input id="btn_remove_' . $iAttId . '" type="button" class="btn_hidden" value="Delete" onClick="RemoveAttachment(' . $iAttId . ');"/> </div>'); $oPage->add_ready_script("\$('#attachment_plugin').trigger('add_attachment', [{$iAttId}, '" . addslashes($sFileName) . "']);"); } } } $oPage->add('</span>'); $oPage->add('<div style="clear:both"></div>'); $sMaxUpload = $this->GetMaxUpload(); $oPage->p(Dict::S('Attachments:AddAttachment') . '<input type="file" name="file" id="file"><span style="display:none;" id="attachment_loading"> <img src="../images/indicator.gif"></span> ' . $sMaxUpload); $oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot() . 'js/jquery.iframe-transport.js'); $oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot() . 'js/jquery.fileupload.js'); $oPage->add_ready_script(<<<EOF \$('#file').fileupload({ \t\turl: GetAbsoluteUrlModulesRoot()+'itop-attachments/ajax.attachment.php', \t\tformData: { operation: 'add', temp_id: '{$sTempId}', obj_class: '{$sClass}' }, dataType: 'json', \t\tpasteZone: null, // Don't accept files via Chrome's copy/paste done: function (e, data) { \t\t\tif(typeof(data.result.error) != 'undefined') \t\t\t{ \t\t\t\tif(data.result.error != '') \t\t\t\t{ \t\t\t\t\talert(data.result.error); \t\t\t\t} \t\t\t\telse \t\t\t\t{ \t\t\t\t\tvar sDownloadLink = GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?operation=download_document&class=Attachment&id='+data.result.att_id+'&field=contents'; \t\t\t\t\t\$('#attachments').append('<div class="attachment" id="display_attachment_'+data.result.att_id+'"><a data-preview="'+data.result.preview+'" href="'+sDownloadLink+'"><img src="'+data.result.icon+'"><br/>'+data.result.msg+'<input id="attachment_'+data.result.att_id+'" type="hidden" name="attachments[]" value="'+data.result.att_id+'"/></a><br/><input type="button" class="btn_hidden" value="{$sDeleteBtn}" onClick="RemoveAttachment('+data.result.att_id+');"/></div>'); \t\t\t\t\tif({$sIsDeleteEnabled}) \t\t\t\t\t{ \t\t\t\t\t\t\$('#display_attachment_'+data.result.att_id).hover( function() { \$(this).children(':button').toggleClass('btn_hidden'); } ); \t\t\t\t\t} \t\t\t\t\t\$('#attachment_plugin').trigger('add_attachment', [data.result.att_id, data.result.msg]); \t\t\t\t} \t\t\t} }, start: function() { \t\$('#attachment_loading').show(); \t\t}, stop: function() { \t\$('#attachment_loading').hide(); \t\t} }); \t\$(document).bind('dragover', function (e) { \t\tvar bFiles = false; \t\tif (e.dataTransfer && e.dataTransfer.types) \t\t{ \t\t\tfor (var i = 0; i < e.dataTransfer.types.length; i++) \t\t\t{ \t\t\t\tif (e.dataTransfer.types[i] == "application/x-moz-nativeimage") \t\t\t\t{ \t\t\t\t\tbFiles = false; // mozilla contains "Files" in the types list when dragging images inside the page, but it also contains "application/x-moz-nativeimage" before \t\t\t\t\tbreak; \t\t\t\t} \t\t\t\t \t\t\t\tif (e.dataTransfer.types[i] == "Files") \t\t\t\t{ \t\t\t\t\tbFiles = true; \t\t\t\t\tbreak; \t\t\t\t} \t\t\t} \t\t} \t \t\tif (!bFiles) return; // Not dragging files \t\t \t\tvar dropZone = \$('#file').closest('fieldset'); \t\tif (!dropZone.is(':visible')) \t\t{ \t\t\t// Hidden, but inside an inactive tab? Higlight the tab \t\t\tvar sTabId = dropZone.closest('.ui-tabs-panel').attr('aria-labelledby'); \t\t\tdropZone = \$('#'+sTabId).closest('li'); \t\t} \t timeout = window.dropZoneTimeout; \t if (!timeout) { \t dropZone.addClass('drag_in'); \t } else { \t clearTimeout(timeout); \t } \t window.dropZoneTimeout = setTimeout(function () { \t window.dropZoneTimeout = null; \t dropZone.removeClass('drag_in'); \t }, 300); \t}); EOF ); $oPage->p('<span style="display:none;" id="attachment_loading">Loading, please wait...</span>'); $oPage->p('<input type="hidden" id="attachment_plugin" name="attachment_plugin"/>'); if ($this->m_bDeleteEnabled) { $oPage->add_ready_script('$(".attachment").hover( function() {$(this).children(":button").toggleClass("btn_hidden"); } );'); } } else { $oPage->add('<span id="attachments">'); if ($oSet->Count() == 0) { $oPage->add(Dict::S('Attachments:NoAttachment')); } else { while ($oAttachment = $oSet->Fetch()) { $iAttId = $oAttachment->GetKey(); $oDoc = $oAttachment->Get('contents'); $sFileName = $oDoc->GetFileName(); $sIcon = utils::GetAbsoluteUrlAppRoot() . AttachmentPlugIn::GetFileIcon($sFileName); $sPreview = $oDoc->IsPreviewAvailable() ? 'true' : 'false'; $sDownloadLink = utils::GetAbsoluteUrlAppRoot() . 'pages/ajax.render.php?operation=download_document&class=Attachment&id=' . $iAttId . '&field=contents'; $oPage->add('<div class="attachment" id="attachment_' . $iAttId . '"><a data-preview="' . $sPreview . '" href="' . $sDownloadLink . '"><img src="' . $sIcon . '"><br/>' . $sFileName . '</a><input type="hidden" name="attachments[]" value="' . $iAttId . '"/><br/> </div>'); } } $oPage->add('</span>'); } $oPage->add('</fieldset>'); $sPreviewNotAvailable = addslashes(Dict::S('Attachments:PreviewNotAvailable')); $iMaxWidth = MetaModel::GetModuleSetting('itop-attachments', 'preview_max_width', 290); $oPage->add_ready_script("\$(document).tooltip({ items: '.attachment a', position: { my: 'left top', at: 'right top', using: function( position, feedback ) { \$( this ).css( position ); }}, content: function() { if (\$(this).attr('data-preview') == 'true') { return('<img style=\"max-width:{$iMaxWidth}px\" src=\"'+\$(this).attr('href')+'\"></img>');} else { return '{$sPreviewNotAvailable}'; }}});"); }
public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams) { $iNbPages = $iPageSize < 1 ? 1 : ceil($this->iNbObjects / $iPageSize); if ($iPageSize < 1) { $iPageSize = -1; // convention: no pagination } $aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink); $aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams); $sHtml = '<table class="listContainer">'; foreach ($this->oSet->GetFilter()->GetInternalParams() as $sName => $sValue) { $aExtraParams['query_params'][$sName] = $sValue; } $sHtml .= "<tr><td>"; $sHtml .= $oPage->GetTable($aAttribs, $aValues); $sHtml .= '</td></tr>'; $sHtml .= '</table>'; $iCount = $this->iNbObjects; $aArgs = $this->oSet->GetArgs(); $sExtraParams = addslashes(str_replace('"', "'", json_encode(array_merge($aExtraParams, $aArgs)))); // JSON encode, change the style of the quotes and escape them $sSelectModeJS = ''; $sHeaders = ''; if ($sSelectMode == 'single' || $sSelectMode == 'multiple') { $sSelectModeJS = $sSelectMode; $sHeaders = 'headers: { 0: {sorter: false}},'; } $sDisplayKey = $bViewLink ? 'true' : 'false'; // Protect against duplicate elements in the Zlist $aUniqueOrderedList = array(); foreach ($this->aClassAliases as $sAlias => $sClassName) { foreach ($aColumns[$sAlias] as $sAttCode => $aData) { if ($aData['checked']) { $aUniqueOrderedList[$sAttCode] = true; } } } $aUniqueOrderedList = array_keys($aUniqueOrderedList); $sJSColumns = json_encode($aColumns); $sJSClassAliases = json_encode($this->aClassAliases); $sCssCount = isset($aExtraParams['cssCount']) ? ", cssCount: '{$aExtraParams['cssCount']}'" : ''; $this->oSet->ApplyParameters(); // Display the actual sort order of the table $aRealSortOrder = $this->oSet->GetRealSortOrder(); $aDefaultSort = array(); $iColOffset = 0; if ($sSelectMode == 'single' || $sSelectMode == 'multiple') { $iColOffset += 1; } if ($bViewLink) { // $iColOffset += 1; } foreach ($aRealSortOrder as $sColCode => $bAscending) { $iPos = array_search($sColCode, $aUniqueOrderedList); if ($iPos !== false) { $aDefaultSort[] = "[" . ($iColOffset + $iPos) . "," . ($bAscending ? '0' : '1') . "]"; } else { if (($iPos = array_search(preg_replace('/_friendlyname$/', '', $sColCode), $aUniqueOrderedList)) !== false) { // if sorted on the friendly name of an external key, then consider it sorted on the column that shows the links $aDefaultSort[] = "[" . ($iColOffset + $iPos) . "," . ($bAscending ? '0' : '1') . "]"; } else { if ($sColCode == 'friendlyname' && $bViewLink) { $aDefaultSort[] = "[" . $iColOffset . "," . ($bAscending ? '0' : '1') . "]"; } } } } $sFakeSortList = ''; if (count($aDefaultSort) > 0) { $sFakeSortList = '[' . implode(',', $aDefaultSort) . ']'; } $sOQL = addslashes($this->oSet->GetFilter()->serialize()); $oPage->add_ready_script(<<<EOF var oTable = \$('#{$this->iListId} table.listResults'); oTable.tableHover(); oTable.tablesorter( { {$sHeaders} widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: \$('#pager{$this->iListId}'), totalRows:{$iCount}, size: {$iPageSize}, filter: '{$sOQL}', extra_params: '{$sExtraParams}', select_mode: '{$sSelectModeJS}', displayKey: {$sDisplayKey}, columns: {$sJSColumns}, class_aliases: {$sJSClassAliases} {$sCssCount}}); EOF ); if ($sFakeSortList != '') { $oPage->add_ready_script("oTable.trigger(\"fakesorton\", [{$sFakeSortList}]);"); } //if ($iNbPages == 1) if (false) { if (isset($aExtraParams['cssCount'])) { $sCssCount = $aExtraParams['cssCount']; if ($sSelectMode == 'single') { $sSelectSelector = ":radio[name^=selectObj]"; } else { if ($sSelectMode == 'multiple') { $sSelectSelector = ":checkbox[name^=selectObj]"; } } $oPage->add_ready_script(<<<EOF \t\$('#{$this->iListId} table.listResults {$sSelectSelector}').change(function() { \t\tvar c = \$('{$sCssCount}');\t\t\t\t\t\t\t \t\tvar v = \$('#{$this->iListId} table.listResults {$sSelectSelector}:checked').length; \t\tc.val(v); \t\t\$('#{$this->iListId} .selectedCount').text(v); \t\tc.trigger('change');\t \t}); EOF ); } } return $sHtml; }
/** * Get the HTML fragment corresponding to the ext key editing widget * @param WebPage $oP The web page used for all the output * @param Hash $aArgs Extra context arguments * @return string The HTML fragment to be inserted into the page */ public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select', $bSearchMultiple = true) { if (!is_null($bSearchMode)) { $this->bSearchMode = $bSearchMode; } $sTitle = addslashes($sTitle); $oPage->add_linked_script('../js/extkeywidget.js'); $oPage->add_linked_script('../js/forms-json-utils.js'); $bCreate = !$this->bSearchMode && !MetaModel::IsAbstract($this->sTargetClass) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation); $bExtensions = true; $sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm'); $sAttrFieldPrefix = $this->bSearchMode ? '' : 'attr_'; $sHTMLValue = "<span style=\"white-space:nowrap\">"; // no wrap $sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL()); if ($this->bSearchMode) { $sWizHelper = 'null'; $sWizHelperJSON = "''"; $sJSSearchMode = 'true'; } else { if (isset($aArgs['wizHelper'])) { $sWizHelper = $aArgs['wizHelper']; } else { $sWizHelper = 'oWizardHelper' . $sFormPrefix; } $sWizHelperJSON = $sWizHelper . '.UpdateWizardToJSON()'; $sJSSearchMode = 'false'; } if (is_null($oAllowedValues)) { throw new Exception('Implementation: null value for allowed values definition'); } elseif ($oAllowedValues->Count() < $iMaxComboLength) { // Discrete list of values, use a SELECT or RADIO buttons depending on the config switch ($sDisplayStyle) { case 'radio': case 'radio_horizontal': case 'radio_vertical': $sValidationField = "<span id=\"v_{$this->iId}\"></span>"; $sHTMLValue = ''; $bVertical = $sDisplayStyle != 'radio_horizontal'; $bExtensions = false; $oAllowedValues->Rewind(); $aAllowedValues = array(); while ($oObj = $oAllowedValues->Fetch()) { $aAllowedValues[$oObj->GetKey()] = $oObj->GetName(); } $sHTMLValue = $oPage->GetRadioButtons($aAllowedValues, $value, $this->iId, "{$sAttrFieldPrefix}{$sFieldName}", $bMandatory, $bVertical, $sValidationField); $aEventsList[] = 'change'; break; case 'select': case 'list': default: $sSelectMode = 'true'; $sHelpText = ''; //$this->oAttDef->GetHelpOnEdition(); if ($this->bSearchMode) { if ($bSearchMultiple) { $sHTMLValue = "<select class=\"multiselect\" multiple title=\"{$sHelpText}\" name=\"{$sAttrFieldPrefix}{$sFieldName}[]\" id=\"{$this->iId}\">\n"; } else { $sHTMLValue = "<select title=\"{$sHelpText}\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"{$this->iId}\">\n"; $sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : Dict::S('UI:SearchValue:Any'); $sHTMLValue .= "<option value=\"\">{$sDisplayValue}</option>\n"; } } else { $sHTMLValue = "<select title=\"{$sHelpText}\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"{$this->iId}\">\n"; $sHTMLValue .= "<option value=\"\">" . Dict::S('UI:SelectOne') . "</option>\n"; } $oAllowedValues->Rewind(); while ($oObj = $oAllowedValues->Fetch()) { $key = $oObj->GetKey(); $display_value = $oObj->GetName(); if ($oAllowedValues->Count() == 1 && $bMandatory == 'true') { // When there is only once choice, select it by default $sSelected = ' selected'; } else { $sSelected = is_array($value) && in_array($key, $value) || $value == $key ? ' selected' : ''; } $sHTMLValue .= "<option value=\"{$key}\"{$sSelected}>{$display_value}</option>\n"; } $sHTMLValue .= "</select>\n"; if ($this->bSearchMode && $bSearchMultiple) { $aOptions = array('header' => true, 'checkAllText' => Dict::S('UI:SearchValue:CheckAll'), 'uncheckAllText' => Dict::S('UI:SearchValue:UncheckAll'), 'noneSelectedText' => Dict::S('UI:SearchValue:Any'), 'selectedText' => Dict::S('UI:SearchValue:NbSelected'), 'selectedList' => 1); $sJSOptions = json_encode($aOptions); $oPage->add_ready_script("\$('.multiselect').multiselect({$sJSOptions});"); } $oPage->add_ready_script(<<<EOF \t\toACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '{$sFilter}', '{$sTitle}', true, {$sWizHelper}, '{$this->sAttCode}', {$sJSSearchMode}); \t\toACWidget_{$this->iId}.emptyHtml = "<div style=\\"background: #fff; border:0; text-align:center; vertical-align:middle;\\"><p>{$sMessage}</p></div>"; \t\t\$('#{$this->iId}').bind('update', function() { oACWidget_{$this->iId}.Update(); } ); \t\t\$('#{$this->iId}').bind('change', function() { \$(this).trigger('extkeychange') } ); EOF ); } // Switch } else { // Too many choices, use an autocomplete $sSelectMode = 'false'; // Check that the given value is allowed $oSearch = $oAllowedValues->GetFilter(); $oSearch->AddCondition('id', $value); $oSet = new DBObjectSet($oSearch); if ($oSet->Count() == 0) { $value = null; } if (is_null($value) || $value == 0) { $sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : ''; } else { $sDisplayValue = $this->GetObjectName($value); } $iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 3; //@@@ $this->oAttDef->GetMinAutoCompleteChars(); $iFieldSize = isset($aArgs['iFieldSize']) ? $aArgs['iFieldSize'] : 30; //@@@ $this->oAttDef->GetMaxSize(); // the input for the auto-complete $sHTMLValue = "<input count=\"" . $oAllowedValues->Count() . "\" type=\"text\" id=\"label_{$this->iId}\" size=\"{$iFieldSize}\" value=\"{$sDisplayValue}\"/> "; $sHTMLValue .= "<img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif\" onClick=\"oACWidget_{$this->iId}.Search();\"/> "; // another hidden input to store & pass the object's Id $sHTMLValue .= "<input type=\"hidden\" id=\"{$this->iId}\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"" . htmlentities($value, ENT_QUOTES, 'UTF-8') . "\" />\n"; $JSSearchMode = $this->bSearchMode ? 'true' : 'false'; // Scripts to start the autocomplete and bind some events to it $oPage->add_ready_script(<<<EOF \t\toACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '{$sFilter}', '{$sTitle}', false, {$sWizHelper}, '{$this->sAttCode}', {$sJSSearchMode}); \t\toACWidget_{$this->iId}.emptyHtml = "<div style=\\"background: #fff; border:0; text-align:center; vertical-align:middle;\\"><p>{$sMessage}</p></div>"; \t\t\$('#label_{$this->iId}').autocomplete(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, mustMatch: true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'{$sFilter}',bSearchMode:{$JSSearchMode}, json: function() { return {$sWizHelperJSON}; } }}); \t\t\$('#label_{$this->iId}').keyup(function() { if (\$(this).val() == '') { \$('#{$this->iId}').val(''); } } ); // Useful for search forms: empty value in the "label", means no value, immediatly ! \t\t\$('#label_{$this->iId}').result( function(event, data, formatted) { OnAutoComplete('{$this->iId}', event, data, formatted); } ); \t\t\$('#{$this->iId}').bind('update', function() { oACWidget_{$this->iId}.Update(); } ); \t\tif (\$('#ac_dlg_{$this->iId}').length == 0) \t\t{ \t\t\t\$('body').append('<div id="ac_dlg_{$this->iId}"></div>'); \t\t} EOF ); } if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false) { $sHTMLValue .= "<img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/> "; $oPage->add_ready_script(<<<EOF \t\t\tif (\$('#ac_tree_{$this->iId}').length == 0) \t\t\t{ \t\t\t\t\$('body').append('<div id="ac_tree_{$this->iId}"></div>'); \t\t\t}\t\t EOF ); } if ($bCreate && $bExtensions) { $sHTMLValue .= "<img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif\" onClick=\"oACWidget_{$this->iId}.CreateObject();\"/> "; $oPage->add_ready_script(<<<EOF \t\tif (\$('#ajax_{$this->iId}').length == 0) \t\t{ \t\t\t\$('body').append('<div id="ajax_{$this->iId}"></div>'); \t\t} EOF ); } if ($sDisplayStyle == 'select' || $sDisplayStyle == 'list') { $sHTMLValue .= "<span id=\"v_{$this->iId}\"></span>"; } $sHTMLValue .= "</span>"; // end of no wrap return $sHTMLValue; }
/** * Displays the status (SynchroLog) of the datasource in a graphical manner * @param $oPage WebPage * @return void */ protected function DisplayStatusTab(WebPage $oPage) { $oPage->SetCurrentTab(Dict::S('Core:SynchroStatus')); $sSelectSynchroLog = 'SELECT SynchroLog WHERE sync_source_id = :source_id'; $oSetSynchroLog = new CMDBObjectSet(DBObjectSearch::FromOQL($sSelectSynchroLog), array('start_date' => false), array('source_id' => $this->GetKey())); $oSetSynchroLog->SetLimit(100); // Display only the 100 latest runs if ($oSetSynchroLog->Count() > 0) { $oLastLog = $oSetSynchroLog->Fetch(); $sStartDate = $oLastLog->Get('start_date'); $oLastLog->Get('stats_nb_replica_seen'); $iLastLog = 0; $iDSid = $this->GetKey(); if ($oLastLog->Get('status') == 'running') { // Still running ! $oPage->p('<h2>' . Dict::Format('Core:Synchro:SynchroRunningStartedOn_Date', $sStartDate) . '</h2>'); } else { $sEndDate = $oLastLog->Get('end_date'); $iLastLog = $oLastLog->GetKey(); $oPage->p('<h2>' . Dict::Format('Core:Synchro:SynchroEndedOn_Date', $sEndDate) . '</h2>'); $sOQL = "SELECT SynchroReplica WHERE sync_source_id={$iDSid}"; $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL)); $iCountAllReplicas = $oSet->Count(); $sAllReplicas = "<a href=\"../synchro/replica.php?operation=oql&datasource={$iDSid}&oql={$sOQL}\">{$iCountAllReplicas}</a>"; $sOQL = "SELECT SynchroReplica WHERE sync_source_id={$iDSid} AND status_last_error !=''"; $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL)); $iCountAllErrors = $oSet->Count(); $sAllErrors = "<a href=\"../synchro/replica.php?operation=oql&datasource={$iDSid}&oql={$sOQL}\">{$iCountAllErrors}</a>"; $sOQL = "SELECT SynchroReplica WHERE sync_source_id={$iDSid} AND status_last_warning !=''"; $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL)); $iCountAllWarnings = $oSet->Count(); $sAllWarnings = "<a href=\"../synchro/replica.php?operation=oql&datasource={$iDSid}&oql={$sOQL}\">{$iCountAllWarnings}</a>"; $oPage->p('<h2>' . Dict::Format('Core:Synchro:ListReplicas_AllReplicas_Errors_Warnings', $sAllReplicas, $sAllErrors, $sAllWarnings) . '</h2>'); } $oPage->add('<table class="synoptics"><tr><td style="color:#333;vertical-align:top">'); // List all the log entries for the user to select $oPage->add('<h2 style="line-height:55px;">' . Dict::S('Core:Synchro:History') . '</h2>'); $oSetSynchroLog->Rewind(); $oPage->add('<select size="25" onChange="UpdateSynoptics(this.value);">'); $sSelected = ' selected'; // First log is selected by default $sScript = "var aSynchroLog = {\n"; while ($oLog = $oSetSynchroLog->Fetch()) { $sLogTitle = Dict::Format('Core:SynchroLogTitle', $oLog->Get('status'), $oLog->Get('start_date')); $oPage->add('<option value="' . $oLog->GetKey() . '"' . $sSelected . '>' . $sLogTitle . '</option>'); $sSelected = ''; // only the first log is selected by default $aData = $this->ProcessLog($oLog); $sScript .= '"' . $oLog->GetKey() . '": ' . json_encode($aData) . ",\n"; } $sScript .= "end: 'Done'"; $sScript .= "};\n"; $sScript .= <<<EOF \t\t\tvar sLastLog = '{$iLastLog}'; \tfunction ToggleSynoptics(sId, bShow) \t{ \t\tif (bShow) \t\t{ \t\t\t\$(sId).show(); \t\t} \t\telse \t\t{ \t\t\t\$(sId).hide(); \t\t} \t} \t \tfunction UpdateSynoptics(id) \t{ \t\tvar aValues = aSynchroLog[id]; \t\tif (aValues == undefined) return; \t\t \t\tfor (var sKey in aValues) \t\t{ \t\t\t\$('#c_'+sKey).html(aValues[sKey]); \t\t\tvar fOpacity = (aValues[sKey] == 0) ? 0.3 : 1; \t\t\t\$('#'+sKey).fadeTo("slow", fOpacity); \t\t} \t\t//alert('id = '+id+', lastLog='+sLastLog+', id==sLastLog: '+(id==sLastLog)+' obj_updated_errors: '+aValues['obj_updated_errors']); \t\tif ( (id == sLastLog) && (aValues['obj_new_errors'] > 0) ) \t\t{ \t\t\t\$('#new_errors_link').show(); \t\t} \t\telse \t\t{ \t\t\t\$('#new_errors_link').hide(); \t\t} \t\t \t\tif ( (id == sLastLog) && (aValues['obj_updated_errors'] > 0) ) \t\t{ \t\t\t\$('#updated_errors_link').show(); \t\t} \t\telse \t\t{ \t\t\t\$('#updated_errors_link').hide(); \t\t} \t\t \t\tif ( (id == sLastLog) && (aValues['obj_disappeared_errors'] > 0) ) \t\t{ \t\t\t\$('#disappeared_errors_link').show(); \t\t} \t\telse \t\t{ \t\t\t\$('#disappeared_errors_link').hide(); \t\t} \t\t \t\tToggleSynoptics('#cw_obj_created_warnings', aValues['obj_created_warnings'] > 0); \t\tToggleSynoptics('#cw_obj_new_updated_warnings', aValues['obj_new_updated_warnings'] > 0); \t\tToggleSynoptics('#cw_obj_new_unchanged_warnings', aValues['obj_new_unchanged_warnings'] > 0); \t\tToggleSynoptics('#cw_obj_updated_warnings', aValues['obj_updated_warnings'] > 0); \t\tToggleSynoptics('#cw_obj_unchanged_warnings', aValues['obj_unchanged_warnings'] > 0); \t} EOF; $oPage->add_script($sScript); $oPage->add('</select>'); $oPage->add('</td><td style="vertical-align:top;">'); // Now build the big "synoptics" view $aData = $this->ProcessLog($oLastLog); $sNbReplica = $this->GetIcon() . " " . Dict::Format('Core:Synchro:Nb_Replica', "<span id=\"c_nb_replica_total\">{$aData['nb_replica_total']}</span>"); $sNbObjects = MetaModel::GetClassIcon($this->GetTargetClass()) . " " . Dict::Format('Core:Synchro:Nb_Class:Objects', $this->GetTargetClass(), "<span id=\"c_nb_obj_total\">{$aData['nb_obj_total']}</span>"); $oPage->add(<<<EOF \t<table class="synoptics"> \t<tr class="synoptics_header"> \t<td>{$sNbReplica}</td><td> </td><td>{$sNbObjects}</td> \t</tr> \t<tr> EOF ); $sBaseOQL = "SELECT SynchroReplica WHERE sync_source_id=" . $this->GetKey() . " AND status_last_error!=''"; $oPage->add($this->HtmlBox('repl_ignored', $aData, '#999') . '<td colspan="2"> </td>'); $oPage->add("</tr>\n<tr>"); $oPage->add($this->HtmlBox('repl_disappeared', $aData, '#630', 'rowspan="4"') . '<td rowspan="4" class="arrow">=></td>' . $this->HtmlBox('obj_disappeared_no_action', $aData, '#333')); $oPage->add("</tr>\n<tr>"); $oPage->add($this->HtmlBox('obj_deleted', $aData, '#000')); $oPage->add("</tr>\n<tr>"); $oPage->add($this->HtmlBox('obj_obsoleted', $aData, '#630')); $oPage->add("</tr>\n<tr>"); $sOQL = urlencode($sBaseOQL . " AND status='obsolete'"); $oPage->add($this->HtmlBox('obj_disappeared_errors', $aData, '#C00', '', " <a style=\"color:#fff\" href=\"../synchro/replica.php?operation=oql&datasource={$iDSid}&oql={$sOQL}\" id=\"disappeared_errors_link\">Show</a>")); $oPage->add("</tr>\n<tr>"); $oPage->add($this->HtmlBox('repl_existing', $aData, '#093', 'rowspan="3"') . '<td rowspan="3" class="arrow">=></td>' . $this->HtmlBox('obj_unchanged', $aData, '#393')); $oPage->add("</tr>\n<tr>"); $oPage->add($this->HtmlBox('obj_updated', $aData, '#3C3')); $oPage->add("</tr>\n<tr>"); $sOQL = urlencode($sBaseOQL . " AND status='modified'"); $oPage->add($this->HtmlBox('obj_updated_errors', $aData, '#C00', '', " <a style=\"color:#fff\" href=\"../synchro/replica.php?operation=oql&datasource={$iDSid}&oql={$sOQL}\" id=\"updated_errors_link\">Show</a>")); $oPage->add("</tr>\n<tr>"); $oPage->add($this->HtmlBox('repl_new', $aData, '#339', 'rowspan="4"') . '<td rowspan="4" class="arrow">=></td>' . $this->HtmlBox('obj_new_unchanged', $aData, '#393')); $oPage->add("</tr>\n<tr>"); $oPage->add($this->HtmlBox('obj_new_updated', $aData, '#3C3')); $oPage->add("</tr>\n<tr>"); $oPage->add($this->HtmlBox('obj_created', $aData, '#339')); $oPage->add("</tr>\n<tr>"); $sOQL = urlencode($sBaseOQL . " AND status='new'"); $oPage->add($this->HtmlBox('obj_new_errors', $aData, '#C00', '', " <a style=\"color:#fff\" href=\"../synchro/replica.php?operation=oql&datasource={$iDSid}&oql={$sOQL}\" id=\"new_errors_link\">Show</a>")); $oPage->add("</tr>\n</table>\n"); $oPage->add('</td></tr></table>'); $oPage->add_ready_script("UpdateSynoptics('{$iLastLog}')"); } else { $oPage->p('<h2>' . Dict::S('Core:Synchro:NeverRun') . '</h2>'); } }
public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId) { $sHtml = ''; // Add the extra params into the filter if they make sense for such a filter $bDoSearch = utils::ReadParam('dosearch', false); if ($this->m_oSet == null) { $aQueryParams = array(); if (isset($aExtraParams['query_params'])) { $aQueryParams = $aExtraParams['query_params']; } if ($this->m_sStyle != 'links') { $oAppContext = new ApplicationContext(); $sClass = $this->m_oFilter->GetClass(); $aFilterCodes = array_keys(MetaModel::GetClassFilterDefs($sClass)); $aCallSpec = array($sClass, 'MapContextParam'); if (is_callable($aCallSpec)) { foreach ($oAppContext->GetNames() as $sContextParam) { $sParamCode = call_user_func($aCallSpec, $sContextParam); //Map context parameter to the value/filter code depending on the class if (!is_null($sParamCode)) { $sParamValue = $oAppContext->GetCurrentValue($sContextParam, null); if (!is_null($sParamValue)) { $aExtraParams[$sParamCode] = $sParamValue; } } } } foreach ($aFilterCodes as $sFilterCode) { $externalFilterValue = utils::ReadParam($sFilterCode, '', false, 'raw_data'); $condition = null; if (isset($aExtraParams[$sFilterCode])) { $condition = $aExtraParams[$sFilterCode]; } if ($bDoSearch && $externalFilterValue != "") { // Search takes precedence over context params... unset($aExtraParams[$sFilterCode]); if (!is_array($externalFilterValue)) { $condition = trim($externalFilterValue); } else { if (count($externalFilterValue) == 1) { $condition = trim($externalFilterValue[0]); } else { $condition = $externalFilterValue; } } } if (!is_null($condition)) { $sOpCode = null; // default operator if (is_array($condition)) { // Multiple values, add them as AND X IN (v1, v2, v3...) $sOpCode = 'IN'; } $this->AddCondition($sFilterCode, $condition, $sOpCode); } } if ($bDoSearch) { // Keep the table_id identifying this table if we're performing a search $sTableId = utils::ReadParam('_table_id_', null, false, 'raw_data'); if ($sTableId != null) { $aExtraParams['table_id'] = $sTableId; } } } $aOrderBy = array(); if (isset($aExtraParams['order_by'])) { // Convert the string describing the order_by parameter into an array // The syntax is +attCode1,-attCode2 // attCode1 => ascending, attCode2 => descending $aTemp = explode(',', $aExtraParams['order_by']); foreach ($aTemp as $sTemp) { $aMatches = array(); if (preg_match('/^([+-])?(.+)$/', $sTemp, $aMatches)) { $bAscending = true; if ($aMatches[1] == '-') { $bAscending = false; } $aOrderBy[$aMatches[2]] = $bAscending; } } } $this->m_oSet = new CMDBObjectSet($this->m_oFilter, $aOrderBy, $aQueryParams); } switch ($this->m_sStyle) { case 'count': if (isset($aExtraParams['group_by'])) { if (isset($aExtraParams['group_by_label'])) { $oGroupByExp = Expression::FromOQL($aExtraParams['group_by']); $sGroupByLabel = $aExtraParams['group_by_label']; } else { // Backward compatibility: group_by is simply a field id $sAlias = $this->m_oFilter->GetClassAlias(); $oGroupByExp = new FieldExpression($aExtraParams['group_by'], $sAlias); $sGroupByLabel = MetaModel::GetLabel($this->m_oFilter->GetClass(), $aExtraParams['group_by']); } $aGroupBy = array(); $aGroupBy['grouped_by_1'] = $oGroupByExp; $sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true); $aRes = CMDBSource::QueryToArray($sSql); $aGroupBy = array(); $aLabels = array(); $aValues = array(); $iTotalCount = 0; foreach ($aRes as $iRow => $aRow) { $sValue = $aRow['grouped_by_1']; $aValues[$iRow] = $sValue; $sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue); $aLabels[$iRow] = $sHtmlValue; $aGroupBy[$iRow] = (int) $aRow['_itop_count_']; $iTotalCount += $aRow['_itop_count_']; } $aData = array(); $oAppContext = new ApplicationContext(); $sParams = $oAppContext->GetForLink(); foreach ($aGroupBy as $iRow => $iCount) { // Build the search for this subset $oSubsetSearch = $this->m_oFilter->DeepClone(); $oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($aValues[$iRow])); $oSubsetSearch->AddConditionExpression($oCondition); $sFilter = urlencode($oSubsetSearch->serialize()); $aData[] = array('group' => $aLabels[$iRow], 'value' => "<a href=\"" . utils::GetAbsoluteUrlAppRoot() . "pages/UI.php?operation=search&dosearch=1&{$sParams}&filter={$sFilter}\">{$iCount}</a>"); // TO DO: add the context information } $aAttribs = array('group' => array('label' => $sGroupByLabel, 'description' => ''), 'value' => array('label' => Dict::S('UI:GroupBy:Count'), 'description' => Dict::S('UI:GroupBy:Count+'))); $sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection'; $sHtml .= $oPage->GetP(Dict::Format($sFormat, $iTotalCount)); $sHtml .= $oPage->GetTable($aAttribs, $aData); $oPage->add_ready_script("LoadGroupBySortOrder('{$sId}');\n\$('#{$sId} table.listResults').unbind('sortEnd.group_by').bind('sortEnd.group_by', function() { SaveGroupBySortOrder('{$sId}', \$(this)[0].config.sortList); })"); } else { // Simply count the number of elements in the set $iCount = $this->m_oSet->Count(); $sFormat = 'UI:CountOfObjects'; if (isset($aExtraParams['format'])) { $sFormat = $aExtraParams['format']; } $sHtml .= $oPage->GetP(Dict::Format($sFormat, $iCount)); } break; case 'join': $aDisplayAliases = isset($aExtraParams['display_aliases']) ? explode(',', $aExtraParams['display_aliases']) : array(); if (!isset($aExtraParams['group_by'])) { $sHtml .= $oPage->GetP(Dict::S('UI:Error:MandatoryTemplateParameter_group_by')); } else { $aGroupByFields = array(); $aGroupBy = explode(',', $aExtraParams['group_by']); foreach ($aGroupBy as $sGroupBy) { $aMatches = array(); if (preg_match('/^(.+)\\.(.+)$/', $sGroupBy, $aMatches) > 0) { $aGroupByFields[] = array('alias' => $aMatches[1], 'att_code' => $aMatches[2]); } } if (count($aGroupByFields) == 0) { $sHtml .= $oPage->GetP(Dict::Format('UI:Error:InvalidGroupByFields', $aExtraParams['group_by'])); } else { $aResults = array(); $aCriteria = array(); while ($aObjects = $this->m_oSet->FetchAssoc()) { $aKeys = array(); foreach ($aGroupByFields as $aField) { $sAlias = $aField['alias']; if (is_null($aObjects[$sAlias])) { $aKeys[$sAlias . '.' . $aField['att_code']] = ''; } else { $aKeys[$sAlias . '.' . $aField['att_code']] = $aObjects[$sAlias]->Get($aField['att_code']); } } $sCategory = implode($aKeys, ' '); $aResults[$sCategory][] = $aObjects; $aCriteria[$sCategory] = $aKeys; } $sHtml .= "<table>\n"; // Construct a new (parametric) query that will return the content of this block $oBlockFilter = $this->m_oFilter->DeepClone(); $aExpressions = array(); $index = 0; foreach ($aGroupByFields as $aField) { $aExpressions[] = '`' . $aField['alias'] . '`.`' . $aField['att_code'] . '` = :param' . $index++; } $sExpression = implode(' AND ', $aExpressions); $oExpression = Expression::FromOQL($sExpression); $oBlockFilter->AddConditionExpression($oExpression); $aExtraParams['menu'] = false; foreach ($aResults as $sCategory => $aObjects) { $sHtml .= "<tr><td><h1>{$sCategory}</h1></td></tr>\n"; if (count($aDisplayAliases) == 1) { $aSimpleArray = array(); foreach ($aObjects as $aRow) { $oObj = $aRow[$aDisplayAliases[0]]; if (!is_null($oObj)) { $aSimpleArray[] = $oObj; } } $oSet = CMDBObjectSet::FromArray($this->m_oFilter->GetClass(), $aSimpleArray); $sHtml .= "<tr><td>" . cmdbAbstractObject::GetDisplaySet($oPage, $oSet, $aExtraParams) . "</td></tr>\n"; } else { $index = 0; $aArgs = array(); foreach ($aGroupByFields as $aField) { $aArgs['param' . $index] = $aCriteria[$sCategory][$aField['alias'] . '.' . $aField['att_code']]; $index++; } $oSet = new CMDBObjectSet($oBlockFilter, array(), $aArgs); $sHtml .= "<tr><td>" . cmdbAbstractObject::GetDisplayExtendedSet($oPage, $oSet, $aExtraParams) . "</td></tr>\n"; } } $sHtml .= "</table>\n"; } } break; case 'list': $aClasses = $this->m_oSet->GetSelectedClasses(); $aAuthorizedClasses = array(); if (count($aClasses) > 1) { // Check the classes that can be read (i.e authorized) by this user... foreach ($aClasses as $sAlias => $sClassName) { if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $this->m_oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS)) { $aAuthorizedClasses[$sAlias] = $sClassName; } } if (count($aAuthorizedClasses) > 0) { if ($this->m_oSet->Count() > 0) { $sHtml .= cmdbAbstractObject::GetDisplayExtendedSet($oPage, $this->m_oSet, $aExtraParams); } else { // Empty set $sHtml .= $oPage->GetP(Dict::S('UI:NoObjectToDisplay')); } } else { // Not authorized $sHtml .= $oPage->GetP(Dict::S('UI:NoObjectToDisplay')); } } else { // The list is made of only 1 class of objects, actions on the list are possible if ($this->m_oSet->Count() > 0 && UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) { $sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $this->m_oSet, $aExtraParams); } else { $sHtml .= $oPage->GetP(Dict::S('UI:NoObjectToDisplay')); $sClass = $this->m_oFilter->GetClass(); $bDisplayMenu = isset($aExtraParams['menu']) ? $aExtraParams['menu'] == true : true; if ($bDisplayMenu) { if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES) { $sLinkTarget = ''; $oAppContext = new ApplicationContext(); $sParams = $oAppContext->GetForLink(); // 1:n links, populate the target object as a default value when creating a new linked object if (isset($aExtraParams['target_attr'])) { $sLinkTarget = ' target="_blank" '; $aExtraParams['default'][$aExtraParams['target_attr']] = $aExtraParams['object_id']; } $sDefault = ''; if (!empty($aExtraParams['default'])) { foreach ($aExtraParams['default'] as $sKey => $sValue) { $sDefault .= "&default[{$sKey}]={$sValue}"; } } $sHtml .= $oPage->GetP("<a{$sLinkTarget} href=\"" . utils::GetAbsoluteUrlAppRoot() . "pages/UI.php?operation=new&class={$sClass}&{$sParams}{$sDefault}\">" . Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sClass)) . "</a>\n"); } } } } break; case 'links': //$bDashboardMode = isset($aExtraParams['dashboard']) ? ($aExtraParams['dashboard'] == 'true') : false; //$bSelectMode = isset($aExtraParams['select']) ? ($aExtraParams['select'] == 'true') : false; if ($this->m_oSet->Count() > 0 && UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) { //$sLinkage = isset($aExtraParams['linkage']) ? $aExtraParams['linkage'] : ''; $sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $this->m_oSet, $aExtraParams); } else { $sClass = $this->m_oFilter->GetClass(); $oAttDef = MetaModel::GetAttributeDef($sClass, $this->m_aParams['target_attr']); $sTargetClass = $oAttDef->GetTargetClass(); $sHtml .= $oPage->GetP(Dict::Format('UI:NoObject_Class_ToDisplay', MetaModel::GetName($sTargetClass))); $bDisplayMenu = isset($this->m_aParams['menu']) ? $this->m_aParams['menu'] == true : true; if ($bDisplayMenu) { if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES) { $oAppContext = new ApplicationContext(); $sParams = $oAppContext->GetForLink(); $sDefaults = ''; if (isset($this->m_aParams['default'])) { foreach ($this->m_aParams['default'] as $sName => $sValue) { $sDefaults .= '&' . urlencode($sName) . '=' . urlencode($sValue); } } $sHtml .= $oPage->GetP("<a href=\"" . utils::GetAbsoluteUrlAppRoot() . "pages/UI.php?operation=modify_links&class={$sClass}&sParams&link_attr=" . $aExtraParams['link_attr'] . "&id=" . $aExtraParams['object_id'] . "&target_class={$sTargetClass}&addObjects=true{$sDefaults}\">" . Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sClass)) . "</a>\n"); } } } break; case 'details': while ($oObj = $this->m_oSet->Fetch()) { $sHtml .= $oObj->GetDetails($oPage); // Still used ??? } break; case 'actions': $sClass = $this->m_oFilter->GetClass(); $oAppContext = new ApplicationContext(); $bContextFilter = isset($aExtraParams['context_filter']) ? isset($aExtraParams['context_filter']) != 0 : false; if ($bContextFilter) { $aFilterCodes = array_keys(MetaModel::GetClassFilterDefs($this->m_oFilter->GetClass())); foreach ($oAppContext->GetNames() as $sFilterCode) { $sContextParamValue = $oAppContext->GetCurrentValue($sFilterCode, null); if (!is_null($sContextParamValue) && !empty($sContextParamValue) && MetaModel::IsValidFilterCode($sClass, $sFilterCode)) { $this->AddCondition($sFilterCode, $sContextParamValue); } } $aQueryParams = array(); if (isset($aExtraParams['query_params'])) { $aQueryParams = $aExtraParams['query_params']; } $this->m_oSet = new CMDBObjectSet($this->m_oFilter, array(), $aQueryParams); } $iCount = $this->m_oSet->Count(); $sHyperlink = utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?operation=search&' . $oAppContext->GetForLink() . '&filter=' . urlencode($this->m_oFilter->serialize()); $sHtml .= '<p><a class="actions" href="' . $sHyperlink . '">'; // Note: border set to 0 due to various browser interpretations (IE9 adding a 2px border) $sHtml .= MetaModel::GetClassIcon($sClass, true, 'float;left;margin-right:10px;border:0;'); $sHtml .= MetaModel::GetName($sClass) . ': ' . $iCount . '</a></p>'; $sParams = $oAppContext->GetForLink(); $sHtml .= '<p>'; if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY)) { $sHtml .= "<a href=\"" . utils::GetAbsoluteUrlAppRoot() . "pages/UI.php?operation=new&class={$sClass}&{$sParams}\">" . Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($sClass)) . "</a><br/>\n"; } $sHtml .= "<a href=\"" . utils::GetAbsoluteUrlAppRoot() . "pages/UI.php?operation=search_form&do_search=0&class={$sClass}&{$sParams}\">" . Dict::Format('UI:SearchFor_Class', MetaModel::GetName($sClass)) . "</a>\n"; $sHtml .= '</p>'; break; case 'summary': $sClass = $this->m_oFilter->GetClass(); $oAppContext = new ApplicationContext(); $sTitle = isset($aExtraParams['title[block]']) ? $aExtraParams['title[block]'] : ''; $sLabel = isset($aExtraParams['label[block]']) ? $aExtraParams['label[block]'] : ''; $sStateAttrCode = isset($aExtraParams['status[block]']) ? $aExtraParams['status[block]'] : 'status'; $sStatesList = isset($aExtraParams['status_codes[block]']) ? $aExtraParams['status_codes[block]'] : ''; $bContextFilter = isset($aExtraParams['context_filter']) ? isset($aExtraParams['context_filter']) != 0 : false; if ($bContextFilter) { $aFilterCodes = array_keys(MetaModel::GetClassFilterDefs($this->m_oFilter->GetClass())); foreach ($oAppContext->GetNames() as $sFilterCode) { $sContextParamValue = $oAppContext->GetCurrentValue($sFilterCode, null); if (!is_null($sContextParamValue) && !empty($sContextParamValue) && MetaModel::IsValidFilterCode($sClass, $sFilterCode)) { $this->AddCondition($sFilterCode, $sContextParamValue); } } $aQueryParams = array(); if (isset($aExtraParams['query_params'])) { $aQueryParams = $aExtraParams['query_params']; } $this->m_oSet = new CMDBObjectSet($this->m_oFilter, array(), $aQueryParams); } // Summary details $aCounts = array(); $aStateLabels = array(); if (!empty($sStateAttrCode) && !empty($sStatesList)) { $aStates = explode(',', $sStatesList); $oAttDef = MetaModel::GetAttributeDef($sClass, $sStateAttrCode); foreach ($aStates as $sStateValue) { $oFilter = $this->m_oFilter->DeepClone(); $oFilter->AddCondition($sStateAttrCode, $sStateValue, '='); $oSet = new DBObjectSet($oFilter); $aCounts[$sStateValue] = $oSet->Count(); $aStateLabels[$sStateValue] = htmlentities($oAttDef->GetValueLabel($sStateValue), ENT_QUOTES, 'UTF-8'); if ($aCounts[$sStateValue] == 0) { $aCounts[$sStateValue] = '-'; } else { $sHyperlink = utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?operation=search&' . $oAppContext->GetForLink() . '&filter=' . urlencode($oFilter->serialize()); $aCounts[$sStateValue] = "<a href=\"{$sHyperlink}\">{$aCounts[$sStateValue]}</a>"; } } } $sHtml .= '<div class="summary-details"><table><tr><th>' . implode('</th><th>', $aStateLabels) . '</th></tr>'; $sHtml .= '<tr><td>' . implode('</td><td>', $aCounts) . '</td></tr></table></div>'; // Title & summary $iCount = $this->m_oSet->Count(); $sHyperlink = utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?operation=search&' . $oAppContext->GetForLink() . '&filter=' . urlencode($this->m_oFilter->serialize()); $sHtml .= '<h1>' . Dict::S(str_replace('_', ':', $sTitle)) . '</h1>'; $sHtml .= '<a class="summary" href="' . $sHyperlink . '">' . Dict::Format(str_replace('_', ':', $sLabel), $iCount) . '</a>'; $sHtml .= '<div style="clear:both;"></div>'; break; case 'csv': $bAdvancedMode = utils::ReadParam('advanced', false); $sCsvFile = strtolower($this->m_oFilter->GetClass()) . '.csv'; $sDownloadLink = utils::GetAbsoluteUrlAppRoot() . 'webservices/export.php?expression=' . urlencode($this->m_oFilter->ToOQL(true)) . '&format=csv&filename=' . urlencode($sCsvFile); $sLinkToToggle = utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?operation=search&' . $oAppContext->GetForLink() . '&filter=' . urlencode($this->m_oFilter->serialize()) . '&format=csv'; if ($bAdvancedMode) { $sDownloadLink .= '&fields_advanced=1'; $sChecked = 'CHECKED'; } else { $sLinkToToggle = $sLinkToToggle . '&advanced=1'; $sChecked = ''; } $sAjaxLink = $sDownloadLink . '&charset=UTF-8'; // Includes &fields_advanced=1 if in advanced mode /* $sCSVData = cmdbAbstractObject::GetSetAsCSV($this->m_oSet, array('fields_advanced' => $bAdvancedMode)); $sCharset = MetaModel::GetConfig()->Get('csv_file_default_charset'); if ($sCharset == 'UTF-8') { $bLostChars = false; } else { $sConverted = @iconv('UTF-8', $sCharset, $sCSVData); $sRestored = @iconv($sCharset, 'UTF-8', $sConverted); $bLostChars = ($sRestored != $sCSVData); } if ($bLostChars) { $sCharsetNotice = " <span id=\"csv_charset_issue\">"; $sCharsetNotice .= '<img src="../images/error.png" style="vertical-align:middle"/>'; $sCharsetNotice .= "</span>"; $sTip = "<p>".htmlentities(Dict::S('UI:CSVExport:LostChars'), ENT_QUOTES, 'UTF-8')."</p>"; $sTip .= "<p>".htmlentities(Dict::Format('UI:CSVExport:LostChars+', $sCharset), ENT_QUOTES, 'UTF-8')."</p>"; $oPage->add_ready_script("$('#csv_charset_issue').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );"); } else { $sCharsetNotice = ''; } */ $sCharsetNotice = false; $sHtml .= "<div>"; $sHtml .= '<table style="width:100%" class="transparent">'; $sHtml .= '<tr>'; $sHtml .= '<td><a href="' . $sDownloadLink . '">' . Dict::Format('UI:Download-CSV', $sCsvFile) . '</a>' . $sCharsetNotice . '</td>'; $sHtml .= '<td style="text-align:right"><input type="checkbox" ' . $sChecked . ' onClick="window.location.href=\'' . $sLinkToToggle . '\'"> ' . Dict::S('UI:CSVExport:AdvancedMode') . '</td>'; $sHtml .= '</tr>'; $sHtml .= '</table>'; if ($bAdvancedMode) { $sHtml .= "<p>"; $sHtml .= htmlentities(Dict::S('UI:CSVExport:AdvancedMode+'), ENT_QUOTES, 'UTF-8'); $sHtml .= "</p>"; } $sHtml .= "</div>"; $sHtml .= "<div id=\"csv_content_loading\"><div style=\"width: 250px; height: 20px; background: url(../setup/orange-progress.gif); border: 1px #999 solid; margin-left:auto; margin-right: auto; text-align: center;\">" . Dict::S('UI:Loading') . "</div></div><textarea id=\"csv_content\" style=\"display:none;\">\n"; //$sHtml .= htmlentities($sCSVData, ENT_QUOTES, 'UTF-8'); $sHtml .= "</textarea>\n"; $oPage->add_ready_script("\$.post('{$sAjaxLink}', {}, function(data) { \$('#csv_content').html(data); \$('#csv_content_loading').hide(); \$('#csv_content').show();} );"); break; case 'modify': if (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_MODIFY, $this->m_oSet) == UR_ALLOWED_YES) { while ($oObj = $this->m_oSet->Fetch()) { $sHtml .= $oObj->GetModifyForm($oPage); } } break; case 'search': if (!$oPage->IsPrintableVersion()) { $sStyle = isset($aExtraParams['open']) && $aExtraParams['open'] == 'true' ? 'SearchDrawer' : 'SearchDrawer DrawerClosed'; $sHtml .= "<div id=\"ds_{$sId}\" class=\"{$sStyle}\">\n"; $oPage->add_ready_script(<<<EOF \t\t\$("#dh_{$sId}").click( function() { \t\t\t\$("#ds_{$sId}").slideToggle('normal', function() { \$("#ds_{$sId}").parent().resize(); FixSearchFormsDisposition(); \$("#dh_{$sId}").trigger('toggle_complete'); } ); \t\t\t\$("#dh_{$sId}").toggleClass('open'); \t\t}); EOF ); $aExtraParams['currentId'] = $sId; $sHtml .= cmdbAbstractObject::GetSearchForm($oPage, $this->m_oSet, $aExtraParams); $sHtml .= "</div>\n"; $sHtml .= "<div class=\"HRDrawer\"></div>\n"; $sHtml .= "<div id=\"dh_{$sId}\" class=\"DrawerHandle\">" . Dict::S('UI:SearchToggle') . "</div>\n"; } break; case 'open_flash_chart': static $iChartCounter = 0; $oAppContext = new ApplicationContext(); $sContext = $oAppContext->GetForLink(); if (!empty($sContext)) { $sContext = '&' . $sContext; } $sChartType = isset($aExtraParams['chart_type']) ? $aExtraParams['chart_type'] : 'pie'; $sTitle = isset($aExtraParams['chart_title']) ? $aExtraParams['chart_title'] : ''; $sGroupBy = isset($aExtraParams['group_by']) ? $aExtraParams['group_by'] : ''; $sGroupByExpr = isset($aExtraParams['group_by_expr']) ? '¶ms[group_by_expr]=' . $aExtraParams['group_by_expr'] : ''; $sFilter = $this->m_oFilter->serialize(); $sHtml .= "<div id=\"my_chart_{$sId}{$iChartCounter}\">If the chart does not display, <a href=\"http://get.adobe.com/flash/\" target=\"_blank\">install Flash</a></div>\n"; $oPage->add_script("function ofc_resize(left, width, top, height) { /* do nothing special */ }"); if (isset($aExtraParams['group_by_label'])) { $sUrl = urlencode(utils::GetAbsoluteUrlAppRoot() . "pages/ajax.render.php?operation=open_flash_chart¶ms[group_by]={$sGroupBy}{$sGroupByExpr}¶ms[group_by_label]={$aExtraParams['group_by_label']}¶ms[chart_type]={$sChartType}¶ms[chart_title]={$sTitle}¶ms[currentId]={$sId}&id={$sId}&filter=" . urlencode($sFilter)); } else { $sUrl = urlencode(utils::GetAbsoluteUrlAppRoot() . "pages/ajax.render.php?operation=open_flash_chart¶ms[group_by]={$sGroupBy}{$sGroupByExpr}¶ms[chart_type]={$sChartType}¶ms[chart_title]={$sTitle}¶ms[currentId]={$sId}&id={$sId}&filter=" . urlencode($sFilter)); } $oPage->add_ready_script("swfobject.embedSWF(\"../images/open-flash-chart.swf\", \"my_chart_{$sId}{$iChartCounter}\", \"100%\", \"300\",\"9.0.0\", \"expressInstall.swf\",\n\t\t\t\t{\"data-file\":\"" . $sUrl . "\"}, {wmode: 'transparent'} );\n"); $iChartCounter++; if (isset($aExtraParams['group_by'])) { if (isset($aExtraParams['group_by_label'])) { $oGroupByExp = Expression::FromOQL($aExtraParams['group_by']); $sGroupByLabel = $aExtraParams['group_by_label']; } else { // Backward compatibility: group_by is simply a field id $sAlias = $this->m_oFilter->GetClassAlias(); $oGroupByExp = new FieldExpression($aExtraParams['group_by'], $sAlias); $sGroupByLabel = MetaModel::GetLabel($this->m_oFilter->GetClass(), $aExtraParams['group_by']); } $aGroupBy = array(); $aGroupBy['grouped_by_1'] = $oGroupByExp; $sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true); $aRes = CMDBSource::QueryToArray($sSql); $aGroupBy = array(); $aLabels = array(); $aValues = array(); $iTotalCount = 0; foreach ($aRes as $iRow => $aRow) { $sValue = $aRow['grouped_by_1']; $aValues[$iRow] = $sValue; $sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue); $aLabels[$iRow] = $sHtmlValue; $aGroupBy[$iRow] = (int) $aRow['_itop_count_']; $iTotalCount += $aRow['_itop_count_']; } $aData = array(); $idx = 0; $aURLs = array(); foreach ($aGroupBy as $iRow => $iCount) { // Build the search for this subset $oSubsetSearch = $this->m_oFilter->DeepClone(); $oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($aValues[$iRow])); $oSubsetSearch->AddConditionExpression($oCondition); $aURLs[$idx] = $oSubsetSearch->serialize(); $idx++; } $sURLList = ''; foreach ($aURLs as $index => $sURL) { $sURLList .= "\taURLs[{$index}] = '" . utils::GetAbsoluteUrlAppRoot() . "pages/UI.php?operation=search&format=html{$sContext}&filter=" . urlencode($sURL) . "';\n"; } $oPage->add_script(<<<EOF function ofc_drill_down_{$sId}(index) { \tvar aURLs = new Array(); {$sURLList} \twindow.location.href=aURLs[index]; } EOF ); } break; case 'open_flash_chart_ajax': require_once APPROOT . '/pages/php-ofc-library/open-flash-chart.php'; $sChartType = isset($aExtraParams['chart_type']) ? $aExtraParams['chart_type'] : 'pie'; $sId = utils::ReadParam('id', ''); $oChart = new open_flash_chart(); switch ($sChartType) { case 'bars': $oChartElement = new bar_glass(); if (isset($aExtraParams['group_by'])) { if (isset($aExtraParams['group_by_label'])) { $oGroupByExp = Expression::FromOQL($aExtraParams['group_by']); $sGroupByLabel = $aExtraParams['group_by_label']; } else { // Backward compatibility: group_by is simply a field id $sAlias = $this->m_oFilter->GetClassAlias(); $oGroupByExp = new FieldExpression($aExtraParams['group_by'], $sAlias); $sGroupByLabel = MetaModel::GetLabel($this->m_oFilter->GetClass(), $aExtraParams['group_by']); } $aGroupBy = array(); $aGroupBy['grouped_by_1'] = $oGroupByExp; $sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true); $aRes = CMDBSource::QueryToArray($sSql); $aGroupBy = array(); $aLabels = array(); $iTotalCount = 0; foreach ($aRes as $iRow => $aRow) { $sValue = $aRow['grouped_by_1']; $sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue); $aLabels[$iRow] = strip_tags($sHtmlValue); $aGroupBy[$iRow] = (int) $aRow['_itop_count_']; $iTotalCount += $aRow['_itop_count_']; } $aData = array(); $aChartLabels = array(); $maxValue = 0; foreach ($aGroupBy as $iRow => $iCount) { $oBarValue = new bar_value($iCount); $oBarValue->on_click("ofc_drill_down_{$sId}"); $aData[] = $oBarValue; if ($iCount > $maxValue) { $maxValue = $iCount; } $aChartLabels[] = html_entity_decode($aLabels[$iRow], ENT_QUOTES, 'UTF-8'); } $oYAxis = new y_axis(); $aMagicValues = array(1, 2, 5, 10); $iMultiplier = 1; $index = 0; $iTop = $aMagicValues[$index % count($aMagicValues)] * $iMultiplier; while ($maxValue > $iTop) { $index++; $iTop = $aMagicValues[$index % count($aMagicValues)] * $iMultiplier; if ($index % count($aMagicValues) == 0) { $iMultiplier = $iMultiplier * 10; } } //echo "oYAxis->set_range(0, $iTop, $iMultiplier);\n"; $oYAxis->set_range(0, $iTop, $iMultiplier); $oChart->set_y_axis($oYAxis); $oChartElement->set_values($aData); $oXAxis = new x_axis(); $oXLabels = new x_axis_labels(); // set them vertical $oXLabels->set_vertical(); // set the label text $oXLabels->set_labels($aChartLabels); // Add the X Axis Labels to the X Axis $oXAxis->set_labels($oXLabels); $oChart->set_x_axis($oXAxis); } break; case 'pie': default: $oChartElement = new pie(); $oChartElement->set_start_angle(35); $oChartElement->set_animate(true); $oChartElement->set_tooltip('#label# - #val# (#percent#)'); $oChartElement->set_colours(array('#FF8A00', '#909980', '#2C2B33', '#CCC08D', '#596664')); if (isset($aExtraParams['group_by'])) { if (isset($aExtraParams['group_by_label'])) { $oGroupByExp = Expression::FromOQL($aExtraParams['group_by']); $sGroupByLabel = $aExtraParams['group_by_label']; } else { // Backward compatibility: group_by is simply a field id $sAlias = $this->m_oFilter->GetClassAlias(); $oGroupByExp = new FieldExpression($aExtraParams['group_by'], $sAlias); $sGroupByLabel = MetaModel::GetLabel($this->m_oFilter->GetClass(), $aExtraParams['group_by']); } $aGroupBy = array(); $aGroupBy['grouped_by_1'] = $oGroupByExp; $sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true); $aRes = CMDBSource::QueryToArray($sSql); $aGroupBy = array(); $aLabels = array(); $iTotalCount = 0; foreach ($aRes as $iRow => $aRow) { $sValue = $aRow['grouped_by_1']; $sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue); $aLabels[$iRow] = strip_tags($sHtmlValue); $aGroupBy[$iRow] = (int) $aRow['_itop_count_']; $iTotalCount += $aRow['_itop_count_']; } $aData = array(); foreach ($aGroupBy as $iRow => $iCount) { $sFlashLabel = html_entity_decode($aLabels[$iRow], ENT_QUOTES, 'UTF-8'); $PieValue = new pie_value($iCount, $sFlashLabel); //@@ BUG: not passed via ajax !!! $PieValue->on_click("ofc_drill_down_{$sId}"); $aData[] = $PieValue; } $oChartElement->set_values($aData); $oChart->x_axis = null; } } if (isset($aExtraParams['chart_title'])) { // The title has been given in an url, and urlencoded... // and urlencode transforms utf-8 into something similar to ISO-8859-1 // Example: é (C3A9 becomes %E9) // As a consequence, json_encode (called within open-flash-chart.php) // was returning 'null' and the graph was not displayed at all // To make sure that the graph is displayed AND to get a correct title // (at least for european characters) let's transform back into utf-8 ! $sTitle = iconv("ISO-8859-1", "UTF-8//IGNORE", $aExtraParams['chart_title']); // If the title is a dictionnary entry, fetch it $sTitle = Dict::S($sTitle); $oTitle = new title($sTitle); $oChart->set_title($oTitle); $oTitle->set_style("{font-size: 16px; font-family: Tahoma; font-weight: bold; text-align: center;}"); } $oChart->set_bg_colour('#FFFFFF'); $oChart->add_element($oChartElement); $sHtml = $oChart->toPrettyString(); break; default: // Unsupported style, do nothing. $sHtml .= Dict::format('UI:Error:UnsupportedStyleOfBlock', $this->m_sStyle); } return $sHtml; }
/** * Display the history of bulk imports */ static function DisplayImportHistory(WebPage $oPage, $bFromAjax = false, $bShowAll = false) { $sAjaxDivId = "CSVImportHistory"; if (!$bFromAjax) { $oPage->add('<div id="' . $sAjaxDivId . '">'); } $oPage->p(Dict::S('UI:History:BulkImports+') . ' <span id="csv_history_reload"></span>'); $oBulkChangeSearch = DBObjectSearch::FromOQL("SELECT CMDBChange WHERE origin IN ('csv-interactive', 'csv-import.php')"); $iQueryLimit = $bShowAll ? 0 : appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit()); $oBulkChanges = new DBObjectSet($oBulkChangeSearch, array('date' => false), array(), null, $iQueryLimit); $oAppContext = new ApplicationContext(); $bLimitExceeded = false; if ($oBulkChanges->Count() > appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit())) { $bLimitExceeded = true; if (!$bShowAll) { $iMaxObjects = appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit()); $oBulkChanges->SetLimit($iMaxObjects); } } $oBulkChanges->Seek(0); $aDetails = array(); while ($oChange = $oBulkChanges->Fetch()) { $sDate = '<a href="csvimport.php?step=10&changeid=' . $oChange->GetKey() . '&' . $oAppContext->GetForLink() . '">' . $oChange->Get('date') . '</a>'; $sUser = $oChange->GetUserName(); if (preg_match('/^(.*)\\(CSV\\)$/i', $oChange->Get('userinfo'), $aMatches)) { $sUser = $aMatches[1]; } else { $sUser = $oChange->Get('userinfo'); } $oOpSearch = DBObjectSearch::FromOQL("SELECT CMDBChangeOpCreate WHERE change = :change_id"); $oOpSet = new DBObjectSet($oOpSearch, array(), array('change_id' => $oChange->GetKey())); $iCreated = $oOpSet->Count(); // Get the class from the first item found (assumption: a CSV load is done for a single class) if ($oCreateOp = $oOpSet->Fetch()) { $sClass = $oCreateOp->Get('objclass'); } $oOpSearch = DBObjectSearch::FromOQL("SELECT CMDBChangeOpSetAttribute WHERE change = :change_id"); $oOpSet = new DBObjectSet($oOpSearch, array(), array('change_id' => $oChange->GetKey())); $aModified = array(); $aAttList = array(); while ($oModified = $oOpSet->Fetch()) { // Get the class (if not done earlier on object creation) $sClass = $oModified->Get('objclass'); $iKey = $oModified->Get('objkey'); $sAttCode = $oModified->Get('attcode'); $aAttList[$sClass][$sAttCode] = true; $aModified["{$sClass}::{$iKey}"] = true; } $iModified = count($aModified); // Assumption: there is only one class of objects being loaded // Then the last class found gives us the class for every object if ($iModified > 0 || $iCreated > 0) { $aDetails[] = array('date' => $sDate, 'user' => $sUser, 'class' => $sClass, 'created' => $iCreated, 'modified' => $iModified); } } $aConfig = array('date' => array('label' => Dict::S('UI:History:Date'), 'description' => Dict::S('UI:History:Date+')), 'user' => array('label' => Dict::S('UI:History:User'), 'description' => Dict::S('UI:History:User+')), 'class' => array('label' => Dict::S('Core:AttributeClass'), 'description' => Dict::S('Core:AttributeClass+')), 'created' => array('label' => Dict::S('UI:History:StatsCreations'), 'description' => Dict::S('UI:History:StatsCreations+')), 'modified' => array('label' => Dict::S('UI:History:StatsModifs'), 'description' => Dict::S('UI:History:StatsModifs+'))); if ($bLimitExceeded) { if ($bShowAll) { // Collapsible list $oPage->add('<p>' . Dict::Format('UI:CountOfResults', $oBulkChanges->Count()) . ' <a class="truncated" onclick="OnTruncatedHistoryToggle(false);">' . Dict::S('UI:CollapseList') . '</a></p>'); } else { // Truncated list $iMinDisplayLimit = appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit()); $sCollapsedLabel = Dict::Format('UI:TruncatedResults', $iMinDisplayLimit, $oBulkChanges->Count()); $sLinkLabel = Dict::S('UI:DisplayAll'); $oPage->add('<p>' . $sCollapsedLabel . ' <a class="truncated" onclick="OnTruncatedHistoryToggle(true);">' . $sLinkLabel . '</p>'); $oPage->add_ready_script(<<<EOF \t\$('#{$sAjaxDivId} table.listResults').addClass('truncated'); \t\$('#{$sAjaxDivId} table.listResults tr:last td').addClass('truncated'); EOF ); $sAppContext = $oAppContext->GetForLink(); $oPage->add_script(<<<EOF \tfunction OnTruncatedHistoryToggle(bShowAll) \t{ \t\t\$('#csv_history_reload').html('<img src="../images/indicator.gif"/>'); \t\t\$.get(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?{$sAppContext}', {operation: 'displayCSVHistory', showall: bShowAll}, function(data) \t\t\t{ \t\t\t\t\$('#{$sAjaxDivId}').html(data); \t\t\t\tvar table = \$('#{$sAjaxDivId} .listResults'); \t\t\t\ttable.tableHover(); // hover tables \t\t\t\ttable.tablesorter( { widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables \t\t\t} \t\t); \t} EOF ); } } else { // Normal display - full list without any decoration } $oPage->table($aConfig, $aDetails); if (!$bFromAjax) { $oPage->add('</div>'); } }
public function Render(WebPage $oPage, $aParams = array(), $bEditMode = false) { $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this->m_oObj)); $aTemplateFields = array(); preg_match_all('/\\$this->([a-z0-9_]+)\\$/', $this->m_sTemplate, $aMatches); foreach ($aMatches[1] as $sAttCode) { if (MetaModel::IsValidAttCode(get_class($this->m_oObj), $sAttCode)) { $aTemplateFields[] = $sAttCode; } else { $aParams['this->' . $sAttCode] = "<!--Unknown attribute: {$sAttCode}-->"; } } preg_match_all('/\\$this->field\\(([a-z0-9_]+)\\)\\$/', $this->m_sTemplate, $aMatches); foreach ($aMatches[1] as $sAttCode) { if (MetaModel::IsValidAttCode(get_class($this->m_oObj), $sAttCode)) { $aTemplateFields[] = $sAttCode; } else { $aParams['this->field(' . $sAttCode . ')'] = "<!--Unknown attribute: {$sAttCode}-->"; } } $aFieldsComments = isset($aParams['fieldsComments']) ? $aParams['fieldsComments'] : array(); $aFieldsMap = array(); $sClass = get_class($this->m_oObj); // Renders the fields used in the template foreach (MetaModel::ListAttributeDefs(get_class($this->m_oObj)) as $sAttCode => $oAttDef) { $aParams['this->label(' . $sAttCode . ')'] = $oAttDef->GetLabel(); $aParams['this->comments(' . $sAttCode . ')'] = isset($aFieldsComments[$sAttCode]) ? $aFieldsComments[$sAttCode] : ''; $iInputId = '2_' . $sAttCode; // TODO: generate a real/unique prefix... if (in_array($sAttCode, $aTemplateFields)) { if ($this->m_oObj->IsNew()) { $iFlags = $this->m_oObj->GetInitialStateAttributeFlags($sAttCode); } else { $iFlags = $this->m_oObj->GetAttributeFlags($sAttCode); } if ($iFlags & OPT_ATT_MANDATORY && $this->m_oObj->IsNew()) { $iFlags = $iFlags & ~OPT_ATT_READONLY; // Mandatory fields cannot be read-only when creating an object } if (!$oAttDef->IsWritable() || $sStateAttCode == $sAttCode) { $iFlags = $iFlags | OPT_ATT_READONLY; } if ($iFlags & OPT_ATT_HIDDEN) { $aParams['this->label(' . $sAttCode . ')'] = ''; $aParams['this->field(' . $sAttCode . ')'] = ''; $aParams['this->comments(' . $sAttCode . ')'] = ''; $aParams['this->' . $sAttCode] = ''; } else { if ($bEditMode && $iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE)) { // Check if the attribute is not read-only because of a synchro... $aReasons = array(); $sSynchroIcon = ''; if ($iFlags & OPT_ATT_SLAVE) { $iSynchroFlags = $this->m_oObj->GetSynchroReplicaFlags($sAttCode, $aReasons); $sSynchroIcon = " <img id=\"synchro_{$sInputId}\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>"; $sTip = ''; foreach ($aReasons as $aRow) { $sTip .= "<p>Synchronized with {$aRow['name']} - {$aRow['description']}</p>"; } $oPage->add_ready_script("\$('#synchro_{$iInputId}').qtip( { content: '{$sTip}', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );"); } // Attribute is read-only $sHTMLValue = "<span id=\"field_{$iInputId}\">" . $this->m_oObj->GetAsHTML($sAttCode); $sHTMLValue .= '<input type="hidden" id="' . $iInputId . '" name="attr_' . $sAttCode . '" value="' . htmlentities($this->m_oObj->Get($sAttCode), ENT_QUOTES, 'UTF-8') . '"/></span>'; $aFieldsMap[$sAttCode] = $iInputId; $aParams['this->comments(' . $sAttCode . ')'] = $sSynchroIcon; } if ($bEditMode && !($iFlags & OPT_ATT_READONLY)) { $aParams['this->field(' . $sAttCode . ')'] = "<span id=\"field_{$iInputId}\">" . $this->m_oObj->GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $this->m_oObj->Get($sAttCode), $this->m_oObj->GetEditValue($sAttCode), $iInputId, '', $iFlags, array('this' => $this->m_oObj)) . '</span>'; $aFieldsMap[$sAttCode] = $iInputId; } else { $aParams['this->field(' . $sAttCode . ')'] = $this->m_oObj->GetAsHTML($sAttCode); } $aParams['this->' . $sAttCode] = "<table class=\"field\"><tr><td class=\"label\">" . $aParams['this->label(' . $sAttCode . ')'] . ":</td><td>" . $aParams['this->field(' . $sAttCode . ')'] . "</td><td>" . $aParams['this->comments(' . $sAttCode . ')'] . "</td></tr></table>"; } } } // Renders the PlugIns used in the template preg_match_all('/\\$PlugIn:([A-Za-z0-9_]+)->properties\\(\\)\\$/', $this->m_sTemplate, $aMatches); $aPlugInProperties = $aMatches[1]; foreach ($aPlugInProperties as $sPlugInClass) { $oInstance = MetaModel::GetPlugins('iApplicationUIExtension', $sPlugInClass); if ($oInstance != null) { $offset = $oPage->start_capture(); $oInstance->OnDisplayProperties($this->m_oObj, $oPage, $bEditMode); $sContent = $oPage->end_capture($offset); $aParams["PlugIn:{$sPlugInClass}->properties()"] = $sContent; } else { $aParams["PlugIn:{$sPlugInClass}->properties()"] = "Missing PlugIn: {$sPlugInClass}"; } } $offset = $oPage->start_capture(); parent::Render($oPage, $aParams); $sContent = $oPage->end_capture($offset); // Remove empty table rows in case some attributes are hidden... $sContent = preg_replace('/<tr[^>]*>\\s*(<td[^>]*>\\s*<\\/td>)+\\s*<\\/tr>/im', '', $sContent); $oPage->add($sContent); return $aFieldsMap; }
protected function GetInteractiveFieldsWidget(WebPage $oP, $sWidgetId) { $oSet = new DBObjectSet($this->oSearch); $aSelectedClasses = $this->oSearch->GetSelectedClasses(); $aAuthorizedClasses = array(); foreach ($aSelectedClasses as $sAlias => $sClassName) { if (UserRights::IsActionAllowed($sClassName, UR_ACTION_BULK_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS)) { $aAuthorizedClasses[$sAlias] = $sClassName; } } $aAllFieldsByAlias = array(); foreach ($aAuthorizedClasses as $sAlias => $sClass) { $aAllFields = array(); if (count($aAuthorizedClasses) > 1) { $sShortAlias = $sAlias . '.'; } else { $sShortAlias = ''; } if ($this->IsValidField($sClass, 'id')) { $aAllFields[] = array('code' => $sShortAlias . 'id', 'unique_label' => $sShortAlias . Dict::S('Core:BulkExport:Identifier'), 'label' => $sShortAlias . 'id', 'subattr' => array(array('code' => $sShortAlias . 'id', 'unique_label' => $sShortAlias . Dict::S('Core:BulkExport:Identifier'), 'label' => $sShortAlias . 'id'), array('code' => $sShortAlias . 'friendlyname', 'unique_label' => $sShortAlias . Dict::S('Core:BulkExport:Friendlyname'), 'label' => $sShortAlias . Dict::S('Core:BulkExport:Friendlyname')))); } foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { if ($this->IsSubAttribute($sClass, $sAttCode, $oAttDef)) { continue; } if ($this->IsValidField($sClass, $sAttCode, $oAttDef)) { $sShortLabel = $oAttDef->GetLabel(); $sLabel = $sShortAlias . $oAttDef->GetLabel(); $aSubAttr = $this->GetSubAttributes($sClass, $sAttCode, $oAttDef); $aValidSubAttr = array(); foreach ($aSubAttr as $aSubAttDef) { if ($this->IsValidField($sClass, $aSubAttDef['code'], $aSubAttDef['attdef'])) { $aValidSubAttr[] = array('code' => $sShortAlias . $aSubAttDef['code'], 'label' => $aSubAttDef['label'], 'unique_label' => $aSubAttDef['unique_label']); } } $aAllFields[] = array('code' => $sShortAlias . $sAttCode, 'label' => $sShortLabel, 'unique_label' => $sLabel, 'subattr' => $aValidSubAttr); } } usort($aAllFields, array(get_class($this), 'SortOnLabel')); if (count($aAuthorizedClasses) > 1) { $sKey = MetaModel::GetName($sClass) . ' (' . $sAlias . ')'; } else { $sKey = MetaModel::GetName($sClass); } $aAllFieldsByAlias[$sKey] = $aAllFields; } $oP->add('<div id="' . $sWidgetId . '"></div>'); $JSAllFields = json_encode($aAllFieldsByAlias); $oSet = new DBObjectSet($this->oSearch); $iCount = $oSet->Count(); $iPreviewLimit = 3; $oSet->SetLimit($iPreviewLimit); $aSampleData = array(); while ($aRow = $oSet->FetchAssoc()) { $aSampleRow = array(); foreach ($aAuthorizedClasses as $sAlias => $sClass) { if (count($aAuthorizedClasses) > 1) { $sShortAlias = $sAlias . '.'; } else { $sShortAlias = ''; } if ($this->IsValidField($sClass, 'id')) { $aSampleRow[$sShortAlias . 'id'] = $this->GetSampleKey($aRow[$sAlias]); } foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { if ($this->IsValidField($sClass, $sAttCode, $oAttDef)) { $aSampleRow[$sShortAlias . $sAttCode] = $this->GetSampleData($aRow[$sAlias], $sAttCode); } } } $aSampleData[] = $aSampleRow; } $sJSSampleData = json_encode($aSampleData); $aLabels = array('preview_header' => Dict::S('Core:BulkExport:DragAndDropHelp'), 'empty_preview' => Dict::S('Core:BulkExport:EmptyPreview'), 'columns_order' => Dict::S('Core:BulkExport:ColumnsOrder'), 'columns_selection' => Dict::S('Core:BulkExport:AvailableColumnsFrom_Class'), 'check_all' => Dict::S('Core:BulkExport:CheckAll'), 'uncheck_all' => Dict::S('Core:BulkExport:UncheckAll'), 'no_field_selected' => Dict::S('Core:BulkExport:NoFieldSelected')); $sJSLabels = json_encode($aLabels); $oP->add_ready_script(<<<EOF \$('#{$sWidgetId}').tabularfieldsselector({fields: {$JSAllFields}, value_holder: '#tabular_fields', advanced_holder: '#tabular_advanced', sample_data: {$sJSSampleData}, total_count: {$iCount}, preview_limit: {$iPreviewLimit}, labels: {$sJSLabels} }); EOF ); }
protected function GetInteractiveFieldsWidget(WebPage $oP, $sWidgetId) { $oSet = new DBObjectSet($this->oSearch); $aSelectedClasses = $this->oSearch->GetSelectedClasses(); $aAuthorizedClasses = array(); foreach ($aSelectedClasses as $sAlias => $sClassName) { if (UserRights::IsActionAllowed($sClassName, UR_ACTION_BULK_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS)) { $aAuthorizedClasses[$sAlias] = $sClassName; } } $aAllFieldsByAlias = array(); $aAllAttCodes = array(); foreach ($aAuthorizedClasses as $sAlias => $sClass) { $aAllFields = array(); if (count($aAuthorizedClasses) > 1) { $sShortAlias = $sAlias . '.'; } else { $sShortAlias = ''; } if ($this->IsExportableField($sClass, 'id')) { $sFriendlyNameAttCode = MetaModel::GetFriendlyNameAttributeCode($sClass); if (is_null($sFriendlyNameAttCode)) { // The friendly name is made of several attribute $aSubAttr = array(array('attcodeex' => 'id', 'code' => $sShortAlias . 'id', 'unique_label' => $sShortAlias . Dict::S('UI:CSVImport:idField'), 'label' => $sShortAlias . 'id'), array('attcodeex' => 'friendlyname', 'code' => $sShortAlias . 'friendlyname', 'unique_label' => $sShortAlias . Dict::S('Core:FriendlyName-Label'), 'label' => $sShortAlias . Dict::S('Core:FriendlyName-Label'))); } else { // The friendly name has no added value $aSubAttr = array(); } $aAllFields[] = array('attcodeex' => 'id', 'code' => $sShortAlias . 'id', 'unique_label' => $sShortAlias . Dict::S('UI:CSVImport:idField'), 'label' => Dict::S('UI:CSVImport:idField'), 'subattr' => $aSubAttr); } foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { if ($this->IsSubAttribute($sClass, $sAttCode, $oAttDef)) { continue; } if ($this->IsExportableField($sClass, $sAttCode, $oAttDef)) { $sShortLabel = $oAttDef->GetLabel(); $sLabel = $sShortAlias . $oAttDef->GetLabel(); $aSubAttr = $this->GetSubAttributes($sClass, $sAttCode, $oAttDef); $aValidSubAttr = array(); foreach ($aSubAttr as $aSubAttDef) { $aValidSubAttr[] = array('attcodeex' => $aSubAttDef['code'], 'code' => $sShortAlias . $aSubAttDef['code'], 'label' => $aSubAttDef['label'], 'unique_label' => $sShortAlias . $aSubAttDef['unique_label']); } $aAllFields[] = array('attcodeex' => $sAttCode, 'code' => $sShortAlias . $sAttCode, 'label' => $sShortLabel, 'unique_label' => $sLabel, 'subattr' => $aValidSubAttr); } } usort($aAllFields, array(get_class($this), 'SortOnLabel')); if (count($aAuthorizedClasses) > 1) { $sKey = MetaModel::GetName($sClass) . ' (' . $sAlias . ')'; } else { $sKey = MetaModel::GetName($sClass); } $aAllFieldsByAlias[$sKey] = $aAllFields; foreach ($aAllFields as $aFieldSpec) { $sAttCode = $aFieldSpec['attcodeex']; if (count($aFieldSpec['subattr']) > 0) { foreach ($aFieldSpec['subattr'] as $aSubFieldSpec) { $aAllAttCodes[$sAlias][] = $aSubFieldSpec['attcodeex']; } } else { $aAllAttCodes[$sAlias][] = $sAttCode; } } } $oP->add('<div id="' . $sWidgetId . '"></div>'); $JSAllFields = json_encode($aAllFieldsByAlias); // First, fetch only the ids - the rest will be fetched by an object reload $oSet = new DBObjectSet($this->oSearch); $iCount = $oSet->Count(); foreach ($this->oSearch->GetSelectedClasses() as $sAlias => $sClass) { $aColumns[$sAlias] = array(); } $oSet->OptimizeColumnLoad($aColumns); $iPreviewLimit = 3; $oSet->SetLimit($iPreviewLimit); $aSampleData = array(); while ($aRow = $oSet->FetchAssoc()) { $aSampleRow = array(); foreach ($aAuthorizedClasses as $sAlias => $sClass) { if (count($aAuthorizedClasses) > 1) { $sShortAlias = $sAlias . '.'; } else { $sShortAlias = ''; } foreach ($aAllAttCodes[$sAlias] as $sAttCodeEx) { $oObj = $aRow[$sAlias]; $aSampleRow[$sShortAlias . $sAttCodeEx] = $oObj ? $this->GetSampleData($oObj, $sAttCodeEx) : ''; } } $aSampleData[] = $aSampleRow; } $sJSSampleData = json_encode($aSampleData); $aLabels = array('preview_header' => Dict::S('Core:BulkExport:DragAndDropHelp'), 'empty_preview' => Dict::S('Core:BulkExport:EmptyPreview'), 'columns_order' => Dict::S('Core:BulkExport:ColumnsOrder'), 'columns_selection' => Dict::S('Core:BulkExport:AvailableColumnsFrom_Class'), 'check_all' => Dict::S('Core:BulkExport:CheckAll'), 'uncheck_all' => Dict::S('Core:BulkExport:UncheckAll'), 'no_field_selected' => Dict::S('Core:BulkExport:NoFieldSelected')); $sJSLabels = json_encode($aLabels); $oP->add_ready_script(<<<EOF \$('#{$sWidgetId}').tabularfieldsselector({fields: {$JSAllFields}, value_holder: '#tabular_fields', advanced_holder: '#tabular_advanced', sample_data: {$sJSSampleData}, total_count: {$iCount}, preview_limit: {$iPreviewLimit}, labels: {$sJSLabels} }); EOF ); }
public function Render(WebPage $oP, $sFormId, $sRenderMode = 'dialog') { $bOpen = false; $sId = $this->oForm->GetFieldId($this->sCode); $sName = $this->oForm->GetFieldName($this->sCode); $sReadOnly = $this->IsReadOnly() ? 'readonly="readonly"' : ''; $aResult = array('label' => $this->sLabel, 'value' => "<input type=\"hidden\" id=\"{$sId}\" name=\"{$sName}\" {$sReadOnly} value=\"" . htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8') . "\">"); $sJSFields = json_encode(array_keys($this->aAllowedValues)); $oP->add_ready_script("\$('#{$sId}').sortable_field({aAvailableFields: {$sJSFields}});"); return $aResult; }
protected function DisplayEditInPlace(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete')) { $aAttribs = $this->GetTableConfig(); $oValue->Rewind(); $oPage->add('<table class="listContainer" id="' . $this->sInputid . '"><tr><td>'); $aData = array(); while ($oLinkObj = $oValue->Fetch()) { $aRow = array(); $aRow['form::select'] = '<input type="checkbox" class="selectList' . $this->sInputid . '" value="' . $oLinkObj->GetKey() . '"/>'; foreach ($this->aZlist as $sLinkedAttCode) { $aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode); } $aData[] = $aRow; } $oPage->table($aAttribs, $aData); $oPage->add('</td></tr></table>'); //listcontainer $sInputName = $sFormPrefix . 'attr_' . $this->sAttCode; $aLabels = array('delete' => Dict::S('UI:Button:Delete'), 'creation_title' => Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sLinkedClass)), 'create' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($this->sLinkedClass)), 'remove' => Dict::S('UI:Button:Remove'), 'add' => Dict::Format('UI:AddAnExisting_Class', MetaModel::GetName($this->sLinkedClass)), 'selection_title' => Dict::Format('UI:SelectionOf_Class', MetaModel::GetName($this->sLinkedClass))); $oContext = new ApplicationContext(); $sSubmitUrl = utils::GetAbsoluteUrlAppRoot() . 'pages/ajax.render.php?' . $oContext->GetForLink(); $sJSONLabels = json_encode($aLabels); $sJSONButtons = json_encode($aButtons); $sWizHelper = 'oWizardHelper' . $sFormPrefix; $oPage->add_ready_script("\$('#{$this->sInputid}').directlinks({class_name: '{$this->sClass}', att_code: '{$this->sAttCode}', input_name:'{$sInputName}', labels: {$sJSONLabels}, submit_to: '{$sSubmitUrl}', buttons: {$sJSONButtons}, oWizardHelper: {$sWizHelper} });"); }
public function Display(WebPage $oPage) { // Check if there are some manual steps required: $aManualSteps = array(); $aAvailableModules = SetupUtils::AnalyzeInstallation($this->oWizard); $sRootUrl = utils::GetAbsoluteUrlAppRoot(); $aSelectedModules = json_decode($this->oWizard->GetParameter('selected_modules'), true); foreach ($aSelectedModules as $sModuleId) { if (!empty($aAvailableModules[$sModuleId]['doc.manual_setup'])) { $aManualSteps[$aAvailableModules[$sModuleId]['label']] = $sRootUrl . $aAvailableModules[$sModuleId]['doc.manual_setup']; } } if (count($aManualSteps) > 0) { $oPage->add("<h2>Manual operations required</h2>"); $oPage->p("In order to complete the installation, the following manual operations are required:"); foreach ($aManualSteps as $sModuleLabel => $sUrl) { $oPage->p("<a href=\"{$sUrl}\" target=\"_blank\">Manual instructions for {$sModuleLabel}</a>"); } $oPage->add("<h2>Congratulations for installing " . ITOP_APPLICATION . "</h2>"); } else { $oPage->add("<h2>Congratulations for installing " . ITOP_APPLICATION . "</h2>"); $oPage->ok("The installation completed successfully."); } if ($this->oWizard->GetParameter('mode', '') == 'upgrade' && $this->oWizard->GetParameter('db_backup', false)) { $sBackupDestination = $this->oWizard->GetParameter('db_backup_path', ''); if (file_exists($sBackupDestination)) { // To mitigate security risks: pass only the filename without the extension, the download will add the extension itself $sTruncatedFilePath = preg_replace('/\\.zip$/', '', $sBackupDestination); $oPage->p('Your backup is ready'); $oPage->p('<a style="background:transparent;" href="' . utils::GetAbsoluteUrlAppRoot() . 'setup/ajax.dataloader.php?operation=async_action&step_class=WizStepDone¶ms[backup]=' . urlencode($sTruncatedFilePath) . '" target="_blank"><img src="../images/tar.png" style="border:0;vertical-align:middle;"> Download ' . basename($sBackupDestination) . '</a>'); } else { $oPage->p('<img src="../images/error.png"/> Warning: Backup creation failed !'); } } // Form goes here.. No back button since the job is done ! $oPage->add('<table style="width:600px;border:0;padding:0;"><tr>'); $oPage->add("<td><a style=\"background:transparent;padding:0;\" title=\"Free: Register your iTop version.\" href=\"http://www.combodo.com/register?product=iTop&version=" . urlencode(ITOP_VERSION . " revision " . ITOP_REVISION) . "\" target=\"_blank\"><img style=\"border:0\" src=\"../images/setup-register.gif\"/></td></a>"); $oPage->add("<td><a style=\"background:transparent;padding:0;\" title=\"Get Professional Support from Combodo\" href=\"http://www.combodo.com/itopsupport\" target=\"_blank\"><img style=\"border:0\" src=\"../images/setup-support.gif\"/></td></a>"); $oPage->add("<td><a style=\"background:transparent;padding:0;\" title=\"Get Professional Training from Combodo\" href=\"http://www.combodo.com/itoptraining\" target=\"_blank\"><img style=\"border:0\" src=\"../images/setup-training.gif\"/></td></a>"); $oPage->add('</tr></table>'); $sForm = '<form method="post" action="' . $this->oWizard->GetParameter('application_url') . 'pages/UI.php">'; $sForm .= '<input type="hidden" name="auth_user" value="' . htmlentities($this->oWizard->GetParameter('admin_user'), ENT_QUOTES, 'UTF-8') . '">'; $sForm .= '<input type="hidden" name="auth_pwd" value="' . htmlentities($this->oWizard->GetParameter('admin_pwd'), ENT_QUOTES, 'UTF-8') . '">'; $sForm .= "<p style=\"text-align:center;width:100%\"><button id=\"enter_itop\" type=\"submit\">Enter " . ITOP_APPLICATION . "</button></p>"; $sForm .= '</form>'; $sPHPVersion = phpversion(); $sMySQLVersion = SetupUtils::GetMySQLVersion($this->oWizard->GetParameter('db_server'), $this->oWizard->GetParameter('db_user'), $this->oWizard->GetParameter('db_pwd')); $oPage->add('<img style="border:0" src="http://www.combodo.com/stats/?p=' . urlencode(ITOP_APPLICATION) . '&v=' . urlencode(ITOP_VERSION) . '&php=' . urlencode($sPHPVersion) . '&mysql=' . urlencode($sMySQLVersion) . '&os=' . urlencode(PHP_OS) . '"/>'); $sForm = addslashes($sForm); $oPage->add_ready_script("\$('#wiz_form').after('{$sForm}');"); }
public function Display(WebPage $oP, $aExtraParams = array()) { $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); $sTargetClass = $oAttDef->GetTargetClass(); $oTargetObj = MetaModel::GetObject($sTargetClass, $this->m_iObjectId); $oP->set_title("iTop - " . MetaModel::GetName($this->m_sLinkedClass) . " objects linked with " . MetaModel::GetName(get_class($oTargetObj)) . ": " . $oTargetObj->GetRawName()); $oP->add("<div class=\"wizContainer\">\n"); $oP->add("<form method=\"post\">\n"); $oP->add("<div class=\"page_header\">\n"); $oP->add("<input type=\"hidden\" id=\"linksToRemove\" name=\"linksToRemove\" value=\"\">\n"); $oP->add("<input type=\"hidden\" name=\"operation\" value=\"do_modify_links\">\n"); $oP->add("<input type=\"hidden\" name=\"class\" value=\"{$this->m_sClass}\">\n"); $oP->add("<input type=\"hidden\" name=\"linkage\" value=\"{$this->m_sLinkageAttr}\">\n"); $oP->add("<input type=\"hidden\" name=\"object_id\" value=\"{$this->m_iObjectId}\">\n"); $oP->add("<input type=\"hidden\" name=\"linking_attcode\" value=\"{$this->m_sLinkingAttCode}\">\n"); $oP->add("<h1>" . Dict::Format('UI:ManageObjectsOf_Class_LinkedWith_Class_Instance', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName(get_class($oTargetObj)), "<span class=\"hilite\">" . $oTargetObj->GetHyperlink() . "</span>") . "</h1>\n"); $oP->add("</div>\n"); $oP->add_script(<<<EOF \t\tfunction OnSelectChange() \t\t{ \t\t\tvar nbChecked = \$('.selection:checked').length; \t\t\tif (nbChecked > 0) \t\t\t{ \t\t\t\t\$('#btnRemove').removeAttr('disabled'); \t\t\t} \t\t\telse \t\t\t{ \t\t\t\t\$('#btnRemove').attr('disabled','disabled'); \t\t\t} \t\t} \t\t \t\tfunction RemoveSelected() \t\t{ \t\t\t\$('.selection:checked').each( \t\t\t\tfunction() \t\t\t\t{ \t\t\t\t\t\$('#linksToRemove').val(\$('#linksToRemove').val() + ' ' + this.value); \t\t\t\t\t\$('#row_'+this.value).remove(); \t\t\t\t} \t\t\t); \t\t\t// Disable the button since all the selected items have been removed \t\t\t\$('#btnRemove').attr('disabled','disabled'); \t\t\t// Re-run the zebra plugin to properly highlight the remaining lines \t\t\t\$('.listResults').trigger('update'); \t\t\t \t\t} \t\t \t\tfunction AddObjects() \t\t{ \t\t\t// TO DO: compute the list of objects already linked with the current Object \t\t\t\$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { 'operation': 'addObjects', \t\t\t\t\t\t\t\t\t\t'class': '{$this->m_sClass}', \t\t\t\t\t\t\t\t\t\t'linkageAttr': '{$this->m_sLinkageAttr}', \t\t\t\t\t\t\t\t\t\t'linkedClass': '{$this->m_sLinkedClass}', \t\t\t\t\t\t\t\t\t\t'objectId': '{$this->m_iObjectId}' \t\t\t\t\t\t\t\t\t\t}, \t\t\t\tfunction(data) \t\t\t\t{ \t\t\t\t\t\$('#ModalDlg').html(data); \t\t\t\t\tdlgWidth = \$(document).width() - 100; \t\t\t\t\t\$('#ModalDlg').css('width', dlgWidth); \t\t\t\t\t\$('#ModalDlg').css('left', 50); \t\t\t\t\t\$('#ModalDlg').css('top', 50); \t\t\t\t\t\$('#ModalDlg').dialog( 'open' ); \t\t\t\t}, \t\t\t\t'html' \t\t\t); \t\t} \t\t \t\tfunction SearchObjectsToAdd(currentFormId) \t\t{ \t\t\tvar theMap = { 'class': '{$this->m_sClass}', \t\t\t\t\t\t 'linkageAttr': '{$this->m_sLinkageAttr}', \t\t\t\t\t\t 'linkedClass': '{$this->m_sLinkedClass}', \t\t\t\t\t\t 'objectId': '{$this->m_iObjectId}' \t\t\t\t\t\t } \t\t\tif (\$('#'+currentFormId+' :input[name=class]').val() != undefined) \t\t\t{ \t\t\t\ttheMap.linkedClass = \$('#'+currentFormId+' :input[name=class]').val(); \t\t\t} \t\t\t// Gather the parameters from the search form \t\t\t\$('#'+currentFormId+' :input').each( \t\t\t\tfunction(i) \t\t\t\t{ \t\t\t\t\tif (this.name != '') \t\t\t\t\t{ \t\t\t\t\t\ttheMap[this.name] = this.value; \t\t\t\t\t} \t\t\t\t} \t\t\t); \t\t\ttheMap['operation'] = 'searchObjectsToAdd'; \t\t\t \t\t\t// Run the query and display the results \t\t\t\$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap, \t\t\t\tfunction(data) \t\t\t\t{ \t\t\t\t\t\$('#SearchResultsToAdd').html(data); \t\t\t\t\t\$('#SearchResultsToAdd .listResults').tablesorter( { headers: {0: false}}, widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables \t\t\t\t\t \t\t\t\t}, \t\t\t\t'html' \t\t\t); \t\t\treturn false; \t\t} \t\t \t\tfunction DoAddObjects(currentFormId) \t\t{ \t\t\tvar theMap = { 'class': '{$this->m_sClass}', \t\t\t\t\t\t 'linkageAttr': '{$this->m_sLinkageAttr}', \t\t\t\t\t\t 'linkedClass': '{$this->m_sLinkedClass}', \t\t\t\t\t\t 'objectId': '{$this->m_iObjectId}' \t\t\t\t\t\t } \t\t\t \t\t\t// Gather the parameters from the search form \t\t\t\$('#'+currentFormId+' :input').each( \t\t\t\tfunction(i) \t\t\t\t{ \t\t\t\t\tif ( (this.name != '') && ((this.type != 'checkbox') || (this.checked)) ) \t\t\t\t\t{ \t\t\t\t\t\t//console.log(this.type); \t\t\t\t\t\tarrayExpr = /\\[\\]\$/; \t\t\t\t\t\tif (arrayExpr.test(this.name)) \t\t\t\t\t\t{ \t\t\t\t\t\t\t// Array \t\t\t\t\t\t\tif (theMap[this.name] == undefined) \t\t\t\t\t\t\t{ \t\t\t\t\t\t\t\ttheMap[this.name] = new Array(); \t\t\t\t\t\t\t} \t\t\t\t\t\t\ttheMap[this.name].push(this.value); \t\t\t\t\t\t} \t\t\t\t\t\telse \t\t\t\t\t\t{ \t\t\t\t\t\t\ttheMap[this.name] = this.value; \t\t\t\t\t\t} \t\t\t\t\t} \t\t\t\t} \t\t\t); \t\t\ttheMap['operation'] = 'doAddObjects'; \t\t\t \t\t\t// Run the query and display the results \t\t\t\$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap, \t\t\t\tfunction(data) \t\t\t\t{ \t\t\t\t\t//console.log('Data: ' + data); \t\t\t\t\tif (data != '') \t\t\t\t\t{ \t\t\t\t\t\t\$('#empty_row').remove(); \t\t\t\t\t} \t\t\t\t\t\$('.listResults tbody').append(data); \t\t\t\t\t\$('.listResults').trigger('update'); \t\t\t\t\t\$('.listResults').tablesorter( { headers: {0: false}}, widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables \t\t\t\t}, \t\t\t\t'html' \t\t\t); \t\t\t\$('#ModalDlg').dialog('close'); \t\t\treturn false; \t\t} \t\t \t\tfunction InitForm() \t\t{ \t\t\t// make sure that the form is clean \t\t\t\$('.selection').each( function() { this.checked = false; }); \t\t\t\$('#btnRemove').attr('disabled','disabled'); \t\t\t\$('#linksToRemove').val(''); \t\t} \t\t \t\tfunction SubmitHook() \t\t{ \t\t\tvar the_form = this; \t\t\tSearchObjectsToAdd(the_form.id); \t\t\treturn false; \t\t} EOF ); $oP->add_ready_script("InitForm();"); $oFilter = new DBObjectSearch($this->m_sClass); $oFilter->AddCondition($this->m_sLinkageAttr, $this->m_iObjectId, '='); $oSet = new DBObjectSet($oFilter); $aForm = array(); while ($oCurrentLink = $oSet->Fetch()) { $aRow = array(); $key = $oCurrentLink->GetKey(); $oLinkedObj = MetaModel::GetObject($this->m_sLinkedClass, $oCurrentLink->Get($this->m_sLinkingAttCode)); $aForm[$key] = $this->GetFormRow($oP, $oLinkedObj, $oCurrentLink); } //var_dump($aTableLabels); //var_dump($aForm); $this->DisplayFormTable($oP, $this->m_aTableConfig, $aForm); $oP->add("<span style=\"float:left;\"> <img src=\"../images/tv-item-last.gif\"> <input id=\"btnRemove\" type=\"button\" value=\"" . Dict::S('UI:RemoveLinkedObjectsOf_Class') . "\" onClick=\"RemoveSelected();\" >"); $oP->add(" <input id=\"btnAdd\" type=\"button\" value=\"" . Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sLinkedClass)) . "\" onClick=\"AddObjects();\"></span>\n"); $oP->add("<span style=\"float:right;\"><input id=\"btnCancel\" type=\"button\" value=\"" . Dict::S('UI:Button:Cancel') . "\" onClick=\"BackToDetails('" . $sTargetClass . "', " . $this->m_iObjectId . ");\">"); $oP->add(" <input id=\"btnOk\" type=\"submit\" value=\"" . Dict::S('UI:Button:Ok') . "\"></span>\n"); $oP->add("<span style=\"clear:both;\"><p> </p></span>\n"); $oP->add("</div>\n"); $oP->add("</form>\n"); if (isset($aExtraParams['StartWithAdd']) && $aExtraParams['StartWithAdd']) { $oP->add_ready_script("AddObjects();"); } }
function DisplayExpressionForm(WebPage $oP, $sAction, $sExpression = '', $sExceptionMessage = '') { $oP->add('<fieldset><legend>' . Dict::S('Core:BulkExport:ScopeDefinition') . '</legend>'); $oP->add('<form id="export-form" action="' . $sAction . '" method="post">'); $oP->add('<input type="hidden" name="interactive" value="1">'); $oP->add('<table style="width:100%" class="export_parameters">'); $sExpressionHint = empty($sExceptionMessage) ? '' : '<tr><td colspan="2">' . htmlentities($sExceptionMessage, ENT_QUOTES, 'UTF-8') . '</td></tr>'; $oP->add('<tr><td class="column-label"><span style="white-space: nowrap;"><input type="radio" name="query_mode" value="oql" id="radio_oql" checked><label for="radio_oql">' . Dict::S('Core:BulkExportLabelOQLExpression') . '</label></span></td>'); $oP->add('<td><textarea style="width:100%" cols="70" rows="8" name="expression" id="textarea_oql" placeholder="' . Dict::S('Core:BulkExportQueryPlaceholder') . '">' . htmlentities($sExpression, ENT_QUOTES, 'UTF-8') . '</textarea></td></tr>'); $oP->add($sExpressionHint); $oP->add('<tr><td class="column-label"><span style="white-space: nowrap;"><input type="radio" name="query_mode" value="phrasebook" id="radio_phrasebook"><label for="radio_phrasebook">' . Dict::S('Core:BulkExportLabelPhrasebookEntry') . '</label></span></td>'); $oP->add('<td><select name="query" id="select_phrasebook">'); $oP->add('<option value="">' . Dict::S('UI:SelectOne') . '</option>'); $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL'); $oQueries = new DBObjectSet($oSearch); while ($oQuery = $oQueries->Fetch()) { $oP->add('<option value="' . $oQuery->GetKey() . '">' . htmlentities($oQuery->Get('name'), ENT_QUOTES, 'UTF-8') . '</option>'); } $oP->add('</select></td></tr>'); $oP->add('<tr><td colspan="2" style="text-align:right"><button type="submit" id="next-btn">' . Dict::S('UI:Button:Next') . '</button></td></tr>'); $oP->add('</table>'); $oP->add('</form>'); $oP->add('</fieldset>'); $oP->p('<a target="_blank" href="' . utils::GetAbsoluteUrlAppRoot() . 'webservices/export-v2.php">' . Dict::S('Core:BulkExportCanRunNonInteractive') . '</a>'); $oP->p('<a target="_blank" href="' . utils::GetAbsoluteUrlAppRoot() . 'webservices/export.php">' . Dict::S('Core:BulkExportLegacyExport') . '</a>'); $sJSEmptyOQL = json_encode(Dict::S('Core:BulkExportMessageEmptyOQL')); $sJSEmptyQueryId = json_encode(Dict::S('Core:BulkExportMessageEmptyPhrasebookEntry')); $oP->add_ready_script(<<<EOF var colWidth = 0; \$('td.column-label').each(function() { \tvar jLabel = \$(this).find('span'); \tcolWidth = Math.max(colWidth, jLabel.width()); }); \$('td.column-label').each(function() { \tvar jLabel = \$(this).width(colWidth); }); \t\t \$('#textarea_oql').on('change keyup', function() { \t\$('#radio_oql').prop('checked', true); }); \$('#select_phrasebook').on('change', function() { \t\$('#radio_phrasebook').prop('checked', true); }); \$('#export-form').on('submit', function() { \tif (\$('#radio_oql').prop('checked')) \t{ \t\tvar sOQL = \$('#textarea_oql').val(); \t\tif (sOQL == '') \t\t{ \t\t\talert({$sJSEmptyOQL}); \t\t\treturn false; \t\t} \t} \telse \t{ \t\tvar sQueryId = \$('#select_phrasebook').val(); \t\tif (sQueryId == '') \t\t{ \t\t\talert({$sJSEmptyQueryId}); \t\t\treturn false; \t\t} \t} \treturn true; }); EOF ); }
public function DisplayAttachments($oObject, WebPage $oPage, $bEditMode = false) { // Exit here if the class is not allowed if (!$this->IsTargetObject($oObject)) { return; } $oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_class = :class AND item_id = :item_id"); $oSet = new DBObjectSet($oSearch, array(), array('class' => get_class($oObject), 'item_id' => $oObject->GetKey())); if ($this->GetAttachmentsPosition() == 'relations') { $sTitle = $oSet->Count() > 0 ? Dict::Format('Attachments:TabTitle_Count', $oSet->Count()) : Dict::S('Attachments:EmptyTabTitle'); $oPage->SetCurrentTab($sTitle); } $oPage->add_style(<<<EOF .attachment { \tdisplay: inline-block; \ttext-align:center; \tfloat:left; \tpadding:5px;\t } .attachment:hover { \tbackground-color: #e0e0e0; } .attachment img { \tborder: 0; } .attachment a { \ttext-decoration: none; \tcolor: #1C94C4; } .btn_hidden { \tdisplay: none; } EOF ); $oPage->add('<fieldset>'); $oPage->add('<legend>' . Dict::S('Attachments:FieldsetTitle') . '</legend>'); if ($bEditMode) { $sIsDeleteEnabled = $this->m_bDeleteEnabled ? 'true' : 'false'; $iTransactionId = $oPage->GetTransactionId(); $sClass = get_class($oObject); $sTempId = session_id() . '_' . $iTransactionId; $sDeleteBtn = Dict::S('Attachments:DeleteBtn'); $oPage->add_script(<<<EOF \tfunction RemoveNewAttachment(att_id) \t{ \t\t\$('#attachment_'+att_id).attr('name', 'removed_attachments[]'); \t\t\$('#display_attachment_'+att_id).hide(); \t\t\$('#attachment_plugin').trigger('remove_attachment', [att_id]); \t\treturn false; // Do not submit the form ! \t} \tfunction ajaxFileUpload() \t{ \t\t//starting setting some animation when the ajax starts and completes \t\t\$("#attachment_loading").ajaxStart(function(){ \t\t\t\$(this).show(); \t\t}).ajaxComplete(function(){ \t\t\t\$(this).hide(); \t\t}); \t\t \t\t/* \t\t\tprepareing ajax file upload \t\t\turl: the url of script file handling the uploaded files fileElementId: the file type of input element id and it will be the index of \$_FILES Array() \t\t\tdataType: it support json, xml \t\t\tsecureuri:use secure protocol \t\t\tsuccess: call back function when the ajax complete \t\t\terror: callback function when the ajax failed \t\t\t */ \t\t\$.ajaxFileUpload \t\t( \t\t\t{ \t\t\t\turl: GetAbsoluteUrlModulesRoot()+'itop-attachments/ajax.attachment.php?obj_class={$sClass}&temp_id={$sTempId}&operation=add', \t\t\t\tsecureuri:false, \t\t\t\tfileElementId:'file', \t\t\t\tdataType: 'json', \t\t\t\tsuccess: function (data, status) \t\t\t\t{ \t\t\t\t\tif(typeof(data.error) != 'undefined') \t\t\t\t\t{ \t\t\t\t\t\tif(data.error != '') \t\t\t\t\t\t{ \t\t\t\t\t\t\talert(data.error); \t\t\t\t\t\t} \t\t\t\t\t\telse \t\t\t\t\t\t{ \t\t\t\t\t\t\tvar sDownloadLink = GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?operation=download_document&class=Attachment&id='+data.att_id+'&field=contents'; \t\t\t\t\t\t\t\$('#attachments').append('<div class="attachment" id="display_attachment_'+data.att_id+'"><a href="'+sDownloadLink+'"><img src="'+data.icon+'"><br/>'+data.msg+'<input id="attachment_'+data.att_id+'" type="hidden" name="attachments[]" value="'+data.att_id+'"/></a><br/><input type="button" class="btn_hidden" value="{$sDeleteBtn}" onClick="RemoveNewAttachment('+data.att_id+');"/></div>'); \t\t\t\t\t\t\tif({$sIsDeleteEnabled}) \t\t\t\t\t\t\t{ \t\t\t\t\t\t\t\t\$('#display_attachment_'+data.att_id).hover( function() { \$(this).children(':button').toggleClass('btn_hidden'); } ); \t\t\t\t\t\t\t} \t\t\t\t\t\t\t\$('#attachment_plugin').trigger('add_attachment', [data.att_id, data.msg]); \t\t\t\t\t\t\t \t\t\t\t\t\t\t//alert(data.msg); \t\t\t\t\t\t} \t\t\t\t\t} \t\t\t\t}, \t\t\t\terror: function (data, status, e) \t\t\t\t{ \t\t\t\t\talert(e); \t\t\t\t} \t\t\t} \t\t) \t\t \t\treturn false; \t} EOF ); $oPage->add('<span id="attachments">'); while ($oAttachment = $oSet->Fetch()) { $iAttId = $oAttachment->GetKey(); $oDoc = $oAttachment->Get('contents'); $sFileName = $oDoc->GetFileName(); $sIcon = utils::GetAbsoluteUrlAppRoot() . AttachmentPlugIn::GetFileIcon($sFileName); $sDownloadLink = utils::GetAbsoluteUrlAppRoot() . 'pages/ajax.render.php?operation=download_document&class=Attachment&id=' . $iAttId . '&field=contents'; $oPage->add('<div class="attachment" id="attachment_' . $iAttId . '"><a href="' . $sDownloadLink . '"><img src="' . $sIcon . '"><br/>' . $sFileName . '<input type="hidden" name="attachments[]" value="' . $iAttId . '"/></a><br/> <input id="btn_remove_' . $iAttId . '" type="button" class="btn_hidden" value="Delete" onClick="$(\'#attachment_' . $iAttId . '\').remove();"/> </div>'); } // Suggested attachments are listed here but treated as temporary $aDefault = utils::ReadParam('default', array(), false, 'raw_data'); if (array_key_exists('suggested_attachments', $aDefault)) { $sSuggestedAttachements = $aDefault['suggested_attachments']; if (is_array($sSuggestedAttachements)) { $sSuggestedAttachements = implode(',', $sSuggestedAttachements); } $oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE id IN({$sSuggestedAttachements})"); $oSet = new DBObjectSet($oSearch, array()); if ($oSet->Count() > 0) { while ($oAttachment = $oSet->Fetch()) { // Mark the attachments as temporary attachments for the current object/form $oAttachment->Set('temp_id', $sTempId); $oAttachment->DBUpdate(); // Display them $iAttId = $oAttachment->GetKey(); $oDoc = $oAttachment->Get('contents'); $sFileName = $oDoc->GetFileName(); $sIcon = utils::GetAbsoluteUrlAppRoot() . AttachmentPlugIn::GetFileIcon($sFileName); $sDownloadLink = utils::GetAbsoluteUrlAppRoot() . 'pages/ajax.render.php?operation=download_document&class=Attachment&id=' . $iAttId . '&field=contents'; $oPage->add('<div class="attachment" id="display_attachment_' . $iAttId . '"><a href="' . $sDownloadLink . '"><img src="' . $sIcon . '"><br/>' . $sFileName . '<input type="hidden" name="attachments[]" value="' . $iAttId . '"/></a><br/> <input id="btn_remove_' . $iAttId . '" type="button" class="btn_hidden" value="Delete" onClick="RemoveNewAttachment(' . $iAttId . ');"/> </div>'); $oPage->add_ready_script("\$('#attachment_plugin').trigger('add_attachment', [{$iAttId}, '" . addslashes($sFileName) . "']);"); } } } $oPage->add('</span>'); $oPage->add('<div style="clear:both"></div>'); $sMaxUpload = $this->GetMaxUpload(); $oPage->p(Dict::S('Attachments:AddAttachment') . '<input type="file" name="file" id="file" onChange="ajaxFileUpload();"><span style="display:none;" id="attachment_loading"> <img src="../images/indicator.gif"></span> ' . $sMaxUpload); $oPage->p('<span style="display:none;" id="attachment_loading">Loading, please wait...</span>'); $oPage->p('<input type="hidden" id="attachment_plugin" name="attachment_plugin"/>'); $oPage->add('</fieldset>'); if ($this->m_bDeleteEnabled) { $oPage->add_ready_script('$(".attachment").hover( function() {$(this).children(":button").toggleClass("btn_hidden"); } );'); } } else { $oPage->add('<span id="attachments">'); if ($oSet->Count() == 0) { $oPage->add(Dict::S('Attachments:NoAttachment')); } else { while ($oAttachment = $oSet->Fetch()) { $iAttId = $oAttachment->GetKey(); $oDoc = $oAttachment->Get('contents'); $sFileName = $oDoc->GetFileName(); $sIcon = utils::GetAbsoluteUrlAppRoot() . AttachmentPlugIn::GetFileIcon($sFileName); $sDownloadLink = utils::GetAbsoluteUrlAppRoot() . 'pages/ajax.render.php?operation=download_document&class=Attachment&id=' . $iAttId . '&field=contents'; $oPage->add('<div class="attachment" id="attachment_' . $iAttId . '"><a href="' . $sDownloadLink . '"><img src="' . $sIcon . '"><br/>' . $sFileName . '</a><input type="hidden" name="attachments[]" value="' . $iAttId . '"/><br/> </div>'); } } } }
/** * Display the graph inside the given page, with the "filter" drawer above it * @param WebPage $oP * @param hash $aResults * @param string $sRelation * @param ApplicationContext $oAppContext * @param array $aExcludedObjects */ function Display(WebPage $oP, $aResults, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects = array(), $sObjClass = null, $iObjKey = null, $sContextKey, $aContextParams = array()) { $aContextDefs = static::GetContextDefinitions($sContextKey, true, $aContextParams); $aExcludedByClass = array(); foreach ($aExcludedObjects as $oObj) { if (!array_key_exists(get_class($oObj), $aExcludedByClass)) { $aExcludedByClass[get_class($oObj)] = array(); } $aExcludedByClass[get_class($oObj)][] = $oObj->GetKey(); } $oP->add("<div class=\"not-printable\">\n"); $oP->add("<div id=\"ds_flash\" class=\"SearchDrawer\" style=\"display:none;\">\n"); if (!$oP->IsPrintableVersion()) { $oP->add_ready_script(<<<EOF \t\$( "#tabbedContent_0" ).tabs({ heightStyle: "fill" }); EOF ); } $oP->add_ready_script(<<<EOF \t\$("#dh_flash").click( function() { \t\t\$("#ds_flash").slideToggle('normal', function() { \$("#ds_flash").parent().resize(); \$("#dh_flash").trigger('toggle_complete'); } ); \t\t\$("#dh_flash").toggleClass('open'); \t}); \$('#ReloadMovieBtn').button().button('disable'); EOF ); $aSortedElements = array(); foreach ($aResults as $sClassIdx => $aObjects) { foreach ($aObjects as $oCurrObj) { $sSubClass = get_class($oCurrObj); $aSortedElements[$sSubClass] = MetaModel::GetName($sSubClass); } } asort($aSortedElements); $idx = 0; foreach ($aSortedElements as $sSubClass => $sClassName) { $oP->add("<span style=\"padding-right:2em; white-space:nowrap;\"><input type=\"checkbox\" id=\"exclude_{$idx}\" name=\"excluded[]\" value=\"{$sSubClass}\" checked onChange=\"\$('#ReloadMovieBtn').button('enable')\"><label for=\"exclude_{$idx}\"> " . MetaModel::GetClassIcon($sSubClass) . " {$sClassName}</label></span> "); $idx++; } $oP->add("<p style=\"text-align:right\"><button type=\"button\" id=\"ReloadMovieBtn\" onClick=\"DoReload()\">" . Dict::S('UI:Button:Refresh') . "</button></p>"); $oP->add("</div>\n"); $oP->add("<div class=\"HRDrawer\"></div>\n"); $oP->add("<div id=\"dh_flash\" class=\"DrawerHandle\">" . Dict::S('UI:ElementsDisplayed') . "</div>\n"); $oP->add("</div>\n"); // class="not-printable" $aAdditionalContexts = array(); foreach ($aContextDefs as $sKey => $aDefinition) { $aAdditionalContexts[] = array('key' => $sKey, 'label' => Dict::S($aDefinition['dict']), 'oql' => $aDefinition['oql'], 'default' => array_key_exists('default', $aDefinition) && $aDefinition['default'] == 'yes'); } $sDirection = utils::ReadParam('d', 'horizontal'); $iGroupingThreshold = utils::ReadParam('g', 5); $oP->add_linked_script(utils::GetAbsoluteUrlAppRoot() . 'js/fraphael.js'); $oP->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot() . 'css/jquery.contextMenu.css'); $oP->add_linked_script(utils::GetAbsoluteUrlAppRoot() . 'js/jquery.contextMenu.js'); $oP->add_linked_script(utils::GetAbsoluteUrlAppRoot() . 'js/simple_graph.js'); try { $this->InitFromGraphviz(); $sExportAsPdfURL = ''; $sExportAsPdfURL = utils::GetAbsoluteUrlAppRoot() . 'pages/ajax.render.php?operation=relation_pdf&relation=' . $sRelation . '&direction=' . ($this->bDirectionDown ? 'down' : 'up'); $oAppcontext = new ApplicationContext(); $sContext = $oAppContext->GetForLink(); $sDrillDownURL = utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?operation=details&class=%1$s&id=%2$s&' . $sContext; $sExportAsDocumentURL = utils::GetAbsoluteUrlAppRoot() . 'pages/ajax.render.php?operation=relation_attachment&relation=' . $sRelation . '&direction=' . ($this->bDirectionDown ? 'down' : 'up'); $sLoadFromURL = utils::GetAbsoluteUrlAppRoot() . 'pages/ajax.render.php?operation=relation_json&relation=' . $sRelation . '&direction=' . ($this->bDirectionDown ? 'down' : 'up'); $sAttachmentExportTitle = ''; if ($sObjClass != null && $iObjKey != null) { $oTargetObj = MetaModel::GetObject($sObjClass, $iObjKey, false); if ($oTargetObj) { $sAttachmentExportTitle = Dict::Format('UI:Relation:AttachmentExportOptions_Name', $oTargetObj->GetName()); } } $sId = 'graph'; $sStyle = ''; if ($oP->IsPrintableVersion()) { // Optimize for printing on A4/Letter vertically $sStyle = 'margin-left:auto; margin-right:auto;'; $oP->add_ready_script("\$('.simple-graph').width(18/2.54*96).resizable({ stop: function() { \$(window).trigger('resized'); }});"); // Default width about 18 cm, since most browsers assume 96 dpi } $oP->add('<div id="' . $sId . '" class="simple-graph" style="' . $sStyle . '"></div>'); $aParams = array('source_url' => $sLoadFromURL, 'sources' => $this->bDirectionDown ? $this->aSourceObjects : $this->aSinkObjects, 'excluded' => $aExcludedByClass, 'grouping_threshold' => $iGroupingThreshold, 'export_as_pdf' => array('url' => $sExportAsPdfURL, 'label' => Dict::S('UI:Relation:ExportAsPDF')), 'export_as_attachment' => array('url' => $sExportAsDocumentURL, 'label' => Dict::S('UI:Relation:ExportAsAttachment'), 'obj_class' => $sObjClass, 'obj_key' => $iObjKey), 'drill_down' => array('url' => $sDrillDownURL, 'label' => Dict::S('UI:Relation:DrillDown')), 'labels' => array('export_pdf_title' => Dict::S('UI:Relation:PDFExportOptions'), 'export_as_attachment_title' => $sAttachmentExportTitle, 'export' => Dict::S('UI:Button:Export'), 'cancel' => Dict::S('UI:Button:Cancel'), 'title' => Dict::S('UI:RelationOption:Title'), 'untitled' => Dict::S('UI:RelationOption:Untitled'), 'include_list' => Dict::S('UI:RelationOption:IncludeList'), 'comments' => Dict::S('UI:RelationOption:Comments'), 'grouping_threshold' => Dict::S('UI:RelationOption:GroupingThreshold'), 'refresh' => Dict::S('UI:Button:Refresh'), 'check_all' => Dict::S('UI:SearchValue:CheckAll'), 'uncheck_all' => Dict::S('UI:SearchValue:UncheckAll'), 'none_selected' => Dict::S('UI:Relation:NoneSelected'), 'nb_selected' => Dict::S('UI:SearchValue:NbSelected'), 'additional_context_info' => Dict::S('UI:Relation:AdditionalContextInfo'), 'zoom' => Dict::S('UI:Relation:Zoom'), 'loading' => Dict::S('UI:Loading')), 'page_format' => array('label' => Dict::S('UI:Relation:PDFExportPageFormat'), 'values' => array('A3' => Dict::S('UI:PageFormat_A3'), 'A4' => Dict::S('UI:PageFormat_A4'), 'Letter' => Dict::S('UI:PageFormat_Letter'))), 'page_orientation' => array('label' => Dict::S('UI:Relation:PDFExportPageOrientation'), 'values' => array('P' => Dict::S('UI:PageOrientation_Portrait'), 'L' => Dict::S('UI:PageOrientation_Landscape'))), 'additional_contexts' => $aAdditionalContexts, 'context_key' => $sContextKey); if (!extension_loaded('gd')) { // PDF export requires GD unset($aParams['export_as_pdf']); } if (!extension_loaded('gd') || is_null($sObjClass) || is_null($iObjKey)) { // Export as Attachment requires GD (for building the PDF) AND a valid objclass/objkey couple unset($aParams['export_as_attachment']); } $oP->add_ready_script("\$('#{$sId}').simple_graph(" . json_encode($aParams) . ");"); } catch (Exception $e) { $oP->add('<div>' . $e->getMessage() . '</div>'); } $oP->add_script(<<<EOF \t\t \tfunction DoReload() \t{ \t\t\$('#ReloadMovieBtn').button('disable'); \t\ttry \t\t{ \t\t\tvar aExcluded = []; \t\t\t\$('input[name^=excluded]').each( function() { \t\t\t\tif (!\$(this).prop('checked')) \t\t\t\t{ \t\t\t\t\taExcluded.push(\$(this).val()); \t\t\t\t} \t\t\t} ); \t\t\t\$('#graph').simple_graph('option', {excluded_classes: aExcluded}); \t\t\t\$('#graph').simple_graph('reload'); \t\t} \t\tcatch(err) \t\t{ \t\t\talert(err); \t\t} \t} EOF ); }
/** * Special display where the case log uses the whole "screen" at the bottom of the "Properties" tab */ public function DisplayCaseLog(WebPage $oPage, $sAttCode, $sComment = '', $sPrefix = '', $bEditMode = false) { $oPage->SetCurrentTab(Dict::S('UI:PropertiesTab')); $sClass = get_class($this); if ($this->IsNew()) { $iFlags = $this->GetInitialStateAttributeFlags($sAttCode); } else { $iFlags = $this->GetAttributeFlags($sAttCode); } if ($iFlags & OPT_ATT_HIDDEN) { // The case log is hidden do nothing } else { $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode); $sInputId = $this->m_iFormId . '_' . $sAttCode; if (!$bEditMode || $iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE)) { // Check if the attribute is not read-only because of a synchro... $aReasons = array(); $sSynchroIcon = ''; if ($iFlags & OPT_ATT_SLAVE) { $iSynchroFlags = $this->GetSynchroReplicaFlags($sAttCode, $aReasons); $sSynchroIcon = " <img id=\"synchro_{$sInputId}\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>"; $sTip = ''; foreach ($aReasons as $aRow) { $sTip .= "<p>Synchronized with {$aRow['name']} - {$aRow['description']}</p>"; } $sTip = addslashes($sTip); $oPage->add_ready_script("\$('#synchro_{$sInputId}').qtip( { content: '{$sTip}', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );"); } // Attribute is read-only $sHTMLValue = $this->GetAsHTML($sAttCode); $sHTMLValue .= '<input type="hidden" id="' . $sInputId . '" name="attr_' . $sPrefix . $sAttCode . '" value="' . htmlentities($this->GetEditValue($sAttCode), ENT_QUOTES, 'UTF-8') . '"/>'; $aFieldsMap[$sAttCode] = $sInputId; $sComment .= $sSynchroIcon; } else { $sValue = $this->Get($sAttCode); $sDisplayValue = $this->GetEditValue($sAttCode); $aArgs = array('this' => $this, 'formPrefix' => $sPrefix); $sHTMLValue = ''; if ($sComment != '') { $sHTMLValue = '<span>' . $sComment . '</span><br/>'; } $sHTMLValue .= "<span id=\"field_{$sInputId}\">" . self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs) . '</span>'; $aFieldsMap[$sAttCode] = $sInputId; } //$aVal = array('label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().'</span>', 'value' => $sHTMLValue, 'comments' => $sComments, 'infos' => $sInfos); $oPage->add('<fieldset><legend>' . $oAttDef->GetLabel() . '</legend>'); $oPage->add($sHTMLValue); $oPage->add('</fieldset>'); } }
public function RenderIntoContent($sContent, WebPage $oPage) { // Render the tabs in the page (if any) foreach ($this->m_aTabs as $sTabContainerName => $aTabs) { $sTabs = ''; $sPrefix = $aTabs['prefix']; $container_index = 0; if (count($aTabs['tabs']) > 0) { if ($oPage->IsPrintableVersion()) { $oPage->add_ready_script(<<<EOF oHiddeableChapters = {}; EOF ); $sTabs = "<!-- tabs -->\n<div id=\"tabbedContent_{$sPrefix}{$container_index}\" class=\"light\">\n"; $i = 0; foreach ($aTabs['tabs'] as $sTabName => $aTabData) { $sTabNameEsc = addslashes($sTabName); $sTabId = "tab_{$sPrefix}{$container_index}{$i}"; switch ($aTabData['type']) { case 'ajax': $sTabHtml = ''; $sUrl = $aTabData['url']; $oPage->add_ready_script(<<<EOF \$.post('{$sUrl}', {printable: '1'}, function(data){ \t\$('#{$sTabId} > .printable-tab-content').append(data); }); EOF ); break; case 'html': default: $sTabHtml = $aTabData['html']; } $sTabs .= "<div class=\"printable-tab\" id=\"{$sTabId}\"><h2 class=\"printable-tab-title\">" . htmlentities($sTabName, ENT_QUOTES, 'UTF-8') . "</h2><div class=\"printable-tab-content\">" . $sTabHtml . "</div></div>\n"; $oPage->add_ready_script(<<<EOF oHiddeableChapters['{$sTabId}'] = '{$sTabNameEsc}'; EOF ); $i++; } $sTabs .= "</div>\n<!-- end of tabs-->\n"; } else { $sTabs = "<!-- tabs -->\n<div id=\"tabbedContent_{$sPrefix}{$container_index}\" class=\"light\">\n"; $sTabs .= "<ul>\n"; // Display the unordered list that will be rendered as the tabs $i = 0; foreach ($aTabs['tabs'] as $sTabName => $aTabData) { switch ($aTabData['type']) { case 'ajax': $sTabs .= "<li data-cache=\"" . ($aTabData['cache'] ? 'true' : 'false') . "\"><a href=\"{$aTabData['url']}\" class=\"tab\"><span>" . htmlentities($sTabName, ENT_QUOTES, 'UTF-8') . "</span></a></li>\n"; break; case 'html': default: $sTabs .= "<li><a href=\"#tab_{$sPrefix}{$container_index}{$i}\" class=\"tab\"><span>" . htmlentities($sTabName, ENT_QUOTES, 'UTF-8') . "</span></a></li>\n"; } $i++; } $sTabs .= "</ul>\n"; // Now add the content of the tabs themselves $i = 0; foreach ($aTabs['tabs'] as $sTabName => $aTabData) { switch ($aTabData['type']) { case 'ajax': // Nothing to add break; case 'html': default: $sTabs .= "<div id=\"tab_{$sPrefix}{$container_index}{$i}\">" . $aTabData['html'] . "</div>\n"; } $i++; } $sTabs .= "</div>\n<!-- end of tabs-->\n"; } } $sContent = str_replace("\$Tabs:{$sTabContainerName}\$", $sTabs, $sContent); $container_index++; } return $sContent; }