/**
  * 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') . '"/>&nbsp;<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/>&nbsp;<input id="btn_remove_' . $iAttId . '" type="button" class="btn_hidden" value="Delete" onClick="RemoveAttachment(' . $iAttId . ');"/>&nbsp;</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/>&nbsp;<input id="btn_remove_' . $iAttId . '" type="button" class="btn_hidden" value="Delete" onClick="RemoveAttachment(' . $iAttId . ');"/>&nbsp;</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">&nbsp;<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/>&nbsp;&nbsp;</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}\"/>&nbsp;";
            $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();\"/>&nbsp;";
            // 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();\"/>&nbsp;";
            $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();\"/>&nbsp;";
            $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() . "&nbsp;" . Dict::Format('Core:Synchro:Nb_Replica', "<span id=\"c_nb_replica_total\">{$aData['nb_replica_total']}</span>");
            $sNbObjects = MetaModel::GetClassIcon($this->GetTargetClass()) . "&nbsp;" . 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>&nbsp;</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">&nbsp;</td>');
            $oPage->add("</tr>\n<tr>");
            $oPage->add($this->HtmlBox('repl_disappeared', $aData, '#630', 'rowspan="4"') . '<td rowspan="4" class="arrow">=&gt;</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">=&gt;</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">=&gt;</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 = "&nbsp;&nbsp;<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 . '\'">&nbsp;' . 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']) ? '&params[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&params[group_by]={$sGroupBy}{$sGroupByExpr}&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]={$sChartType}&params[chart_title]={$sTitle}&params[currentId]={$sId}&id={$sId}&filter=" . urlencode($sFilter));
                } else {
                    $sUrl = urlencode(utils::GetAbsoluteUrlAppRoot() . "pages/ajax.render.php?operation=open_flash_chart&params[group_by]={$sGroupBy}{$sGroupByExpr}&params[chart_type]={$sChartType}&params[chart_title]={$sTitle}&params[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()) . '&nbsp;&nbsp;<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 . '&nbsp;&nbsp;<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 = "&nbsp;<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
);
    }
Beispiel #12
0
 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&params[backup]=' . urlencode($sTruncatedFilePath) . '" target="_blank"><img src="../images/tar.png" style="border:0;vertical-align:middle;">&nbsp;Download ' . basename($sBackupDestination) . '</a>');
         } else {
             $oPage->p('<img src="../images/error.png"/>&nbsp;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;\">&nbsp;&nbsp;&nbsp;<img src=\"../images/tv-item-last.gif\">&nbsp;&nbsp;<input id=\"btnRemove\" type=\"button\" value=\"" . Dict::S('UI:RemoveLinkedObjectsOf_Class') . "\" onClick=\"RemoveSelected();\" >");
        $oP->add("&nbsp;&nbsp;&nbsp;<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("&nbsp;&nbsp;&nbsp;<input id=\"btnOk\" type=\"submit\" value=\"" . Dict::S('UI:Button:Ok') . "\"></span>\n");
        $oP->add("<span style=\"clear:both;\"><p>&nbsp;</p></span>\n");
        $oP->add("</div>\n");
        $oP->add("</form>\n");
        if (isset($aExtraParams['StartWithAdd']) && $aExtraParams['StartWithAdd']) {
            $oP->add_ready_script("AddObjects();");
        }
    }
Beispiel #16
0
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/>&nbsp;<input id="btn_remove_' . $iAttId . '" type="button" class="btn_hidden" value="Delete" onClick="$(\'#attachment_' . $iAttId . '\').remove();"/>&nbsp;</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/>&nbsp;<input id="btn_remove_' . $iAttId . '" type="button" class="btn_hidden" value="Delete" onClick="RemoveNewAttachment(' . $iAttId . ');"/>&nbsp;</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">&nbsp;<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/>&nbsp;&nbsp;</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}\">&nbsp;" . MetaModel::GetClassIcon($sSubClass) . "&nbsp;{$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 = "&nbsp;<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;
    }