protected function GetHistoryTable(WebPage $oPage, DBObjectSet $oSet)
 {
     $sHtml = '';
     // First the latest change that the user is allowed to see
     $oSet->Rewind();
     // Reset the pointer to the beginning of the set
     $aChanges = array();
     while ($oChangeOp = $oSet->Fetch()) {
         $sChangeDescription = $oChangeOp->GetDescription();
         if ($sChangeDescription != '') {
             // The change is visible for the current user
             $changeId = $oChangeOp->Get('change');
             $aChanges[$changeId]['date'] = $oChangeOp->Get('date');
             $aChanges[$changeId]['userinfo'] = $oChangeOp->Get('userinfo');
             if (!isset($aChanges[$changeId]['log'])) {
                 $aChanges[$changeId]['log'] = array();
             }
             $aChanges[$changeId]['log'][] = $sChangeDescription;
         }
     }
     $aAttribs = array('date' => array('label' => Dict::S('UI:History:Date'), 'description' => Dict::S('UI:History:Date+')), 'userinfo' => array('label' => Dict::S('UI:History:User'), 'description' => Dict::S('UI:History:User+')), 'log' => array('label' => Dict::S('UI:History:Changes'), 'description' => Dict::S('UI:History:Changes+')));
     $aValues = array();
     foreach ($aChanges as $aChange) {
         $aValues[] = array('date' => $aChange['date'], 'userinfo' => htmlentities($aChange['userinfo'], ENT_QUOTES, 'UTF-8'), 'log' => "<ul><li>" . implode('</li><li>', $aChange['log']) . "</li></ul>");
     }
     $sHtml .= $oPage->GetTable($aAttribs, $aValues);
     return $sHtml;
 }
 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} });");
 }
    /**
     * Get the HTML fragment corresponding to the linkset editing widget
     * @param WebPage $oP The web page used for all the output
     * @param DBObjectSet The initial value of the linked set
     * @param Hash $aArgs Extra context arguments
     * @param string $sFormPrefix prefix of the fields in the current form
     * @param DBObject $oCurrentObj the current object to which the linkset is related
     * @return string The HTML fragment to be inserted into the page
     */
    public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
    {
        $sHtmlValue = '';
        $sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode);
        $sHtmlValue .= "<div id=\"linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix}\">\n";
        $sHtmlValue .= "<input type=\"hidden\" id=\"{$sFormPrefix}{$this->m_iInputId}\">\n";
        $oValue->Rewind();
        $aForm = array();
        while ($oCurrentLink = $oValue->Fetch()) {
            $aRow = array();
            $oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote));
            if ($oCurrentLink->IsNew()) {
                $key = -$oLinkedObj->GetKey();
                $aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj);
            } else {
                $key = $oCurrentLink->GetKey();
                $aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj);
            }
        }
        $sHtmlValue .= $this->DisplayFormTable($oPage, $this->m_aTableConfig, $aForm);
        $sDuplicates = $this->m_bDuplicatesAllowed ? 'true' : 'false';
        $sWizHelper = 'oWizardHelper' . $sFormPrefix;
        $oPage->add_ready_script(<<<EOF
\t\toWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', {$sDuplicates}, {$sWizHelper}, '{$this->m_sExtKeyToRemote}');
\t\toWidget{$this->m_iInputId}.Init();
\t\t\$('#{$this->m_iInputId}').bind('update_value', function() { \$(this).val(oWidget{$this->m_iInputId}.GetUpdatedValue()); })
EOF
);
        $sHtmlValue .= "<span style=\"float:left;\">&nbsp;&nbsp;&nbsp;<img src=\"../images/tv-item-last.gif\">&nbsp;&nbsp;<input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnRemove\" type=\"button\" value=\"" . Dict::S('UI:RemoveLinkedObjectsOf_Class') . "\" onClick=\"oWidget{$this->m_iInputId}.RemoveSelected();\" >";
        $sHtmlValue .= "&nbsp;&nbsp;&nbsp;<input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnAdd\" type=\"button\" value=\"" . Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sRemoteClass)) . "\" onClick=\"oWidget{$this->m_iInputId}.AddObjects();\"><span id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_indicatorAdd\"></span></span>\n";
        $sHtmlValue .= "<span style=\"clear:both;\"><p>&nbsp;</p></span>\n";
        $sHtmlValue .= "</div>\n";
        $oPage->add_at_the_end("<div id=\"dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}\"></div>");
        // To prevent adding forms inside the main form
        return $sHtmlValue;
    }
 /**
  * Compare two sets of objects to determine if their content is identical or not.
  * 
  * Limitation:
  * Works only on objects written to the DB, since we rely on their identifiers
  * 
  * @param DBObjectSet $oObjectSet
  * @return boolean True if the sets are identical, false otherwise
  */
 public function HasSameContents(DBObjectSet $oObjectSet)
 {
     if ($this->GetRootClass() != $oObjectSet->GetRootClass()) {
         return false;
     }
     if ($this->Count() != $oObjectSet->Count()) {
         return false;
     }
     $aId2Row = array();
     $bRet = true;
     $iCurrPos = $this->m_iCurrRow;
     // Save the cursor
     $idx = 0;
     // Optimization: we retain the first $iMaxObjects objects in memory
     // to speed up the comparison of small sets (see below: $oObject->Equals($oSibling))
     $iMaxObjects = 20;
     $aCachedObjects = array();
     while ($oObj = $this->Fetch()) {
         $aId2Row[$oObj->GetKey()] = $idx;
         if ($idx <= $iMaxObjects) {
             $aCachedObjects[$idx] = $oObj;
         }
         $idx++;
     }
     $oObjectSet->Rewind();
     while ($oObject = $oObjectSet->Fetch()) {
         $iObjectKey = $oObject->GetKey();
         if ($iObjectKey < 0) {
             $bRet = false;
             break;
         }
         if (!array_key_exists($iObjectKey, $aId2Row)) {
             $bRet = false;
             break;
         }
         $iRow = $aId2Row[$iObjectKey];
         if (array_key_exists($iRow, $aCachedObjects)) {
             // Cache hit
             $oSibling = $aCachedObjects[$iRow];
         } else {
             // Go fetch it from the DB, unless it's an object added in-memory
             $oSibling = $this->GetObjectAt($iRow);
         }
         if (!$oObject->Equals($oSibling)) {
             $bRet = false;
             break;
         }
     }
     $this->Seek($iCurrPos);
     // Restore the cursor
     return $bRet;
 }