Exports further improved:

- Support reconciliation keys for every external key
- Better support for Case logs and multiline text fields (both in the preview and in the results)
- Do not repeat identical columns in the list of proposed columns. Examples with UserRequest: friendlyname is equivalent to ref, UserRequest::caller_name is equivalent to UserRequest::caller_id->name
- Optimized the preview for huge data sets (OptimizeColumnLoad)
- Cosmetics on the preview
- Labels for ids aligned with the labels used by the CSV import feature
- Fixed Stop Watch output for PDF/HTML/spreadsheet formats

SVN:trunk[3712]
This commit is contained in:
Romain Quetiez
2015-09-03 16:16:17 +00:00
parent b6341741c3
commit 5425f55af7
30 changed files with 464 additions and 147 deletions

View File

@@ -102,9 +102,11 @@ class PDFPage extends WebPage
{
$this->add_style(
<<<EOF
table {
padding: 2pt;
}
table.listResults td {
border: 0.5pt solid #000 ;
padding: 0.5pt;
}
table.listResults th {
background-color: #eee;

View File

@@ -220,7 +220,7 @@ Class XLSXWriter
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"/>');
fwrite($fd, '</cellStyleXfs>');
fwrite($fd, '<cellXfs count="4">');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"/>');
fwrite($fd, '<xf applyAlignment="1" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"><alignment wrapText="1"/></xf>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="165" xfId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="166" xfId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="167" xfId="0"/>');

View File

@@ -281,9 +281,13 @@ abstract class BulkExport
return array(); // return array('csv' => Dict::S('UI:ExportFormatCSV'));
}
public function SetHttpHeaders(WebPage $oPage)
{
}
public function GetHeader()
{
}
abstract public function GetNextChunk(&$aStatus);
public function GetFooter()

View File

@@ -58,7 +58,7 @@ class CSVBulkExport extends TabularBulkExport
}
protected function SuggestField($aAliases, $sClass, $sAlias, $sAttCode)
protected function SuggestField($sClass, $sAttCode)
{
switch($sAttCode)
{
@@ -74,7 +74,7 @@ class CSVBulkExport extends TabularBulkExport
}
}
return parent::SuggestField($aAliases, $sClass, $sAlias, $sAttCode);
return parent::SuggestField($sClass, $sAttCode);
}
public function EnumFormParts()
@@ -170,13 +170,19 @@ class CSVBulkExport extends TabularBulkExport
protected function GetSampleData($oObj, $sAttCode)
{
if ($oObj)
return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
}
protected function GetValue($oObj, $sAttCode)
{
switch($sAttCode)
{
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
}
else
{
$sRet = '';
case 'id':
$sRet = $oObj->GetKey();
break;
default:
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
}
return $sRet;
}

View File

@@ -392,7 +392,7 @@ abstract class DBSearch
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, $bBeautifulSQL);
if ($sClassAlias == '_itop_')
{
echo $sRes."<br/>\n";
IssueLog::Info('SQL Query (_itop_): '.$sRes);
}
}
catch (MissingQueryArgument $e)

View File

@@ -67,7 +67,7 @@ class ExcelBulkExport extends TabularBulkExport
}
}
protected function SuggestField($aAliases, $sClass, $sAlias, $sAttCode)
protected function SuggestField($sClass, $sAttCode)
{
switch($sAttCode)
{
@@ -83,7 +83,41 @@ class ExcelBulkExport extends TabularBulkExport
}
}
return parent::SuggestField($aAliases, $sClass, $sAlias, $sAttCode);
return parent::SuggestField($sClass, $sAttCode);
}
protected function GetSampleData($oObj, $sAttCode)
{
return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
}
protected function GetValue($oObj, $sAttCode)
{
switch($sAttCode)
{
case 'id':
$sRet = $oObj->GetKey();
break;
default:
$value = $oObj->Get($sAttCode);
if ($value instanceOf ormCaseLog)
{
// Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it!
$sRet = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText()));
}
else if ($value instanceOf DBObjectSet)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
}
else
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetEditValue($value, $oObj);
}
}
return $sRet;
}
public function GetHeader()
@@ -155,30 +189,7 @@ class ExcelBulkExport extends TabularBulkExport
$sField = '';
if ($oObj)
{
switch($sAttCode)
{
case 'id':
$sField = $oObj->GetKey();
break;
default:
$value = $oObj->Get($sAttCode);
if ($value instanceOf ormCaseLog)
{
// Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it!
$sField = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText()));
}
else if ($value instanceOf DBObjectSet)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sField = $oAttDef->GetAsCSV($value, '', '', $oObj);
}
else
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sField = $oAttDef->GetEditValue($value, $oObj);
}
}
$sField = $this->GetValue($oObj, $sAttCode);
}
$aData[] = $sField;
}

View File

@@ -51,13 +51,31 @@ class HTMLBulkExport extends TabularBulkExport
protected function GetSampleData($oObj, $sAttCode)
{
if ($oObj)
return $this->GetValue($oObj, $sAttCode);
}
protected function GetValue($oObj, $sAttCode)
{
switch($sAttCode)
{
$sRet = $oObj->GetAsHTML($sAttCode);
}
else
{
$sRet = '';
case 'id':
$sRet = $oObj->GetHyperlink();
break;
default:
$value = $oObj->Get($sAttCode);
if ($value instanceof ormCaseLog)
{
$sRet = $value->GetAsSimpleHtml();
}
elseif ($value instanceof ormStopWatch)
{
$sRet = $value->GetTimeSpent();
}
else
{
$sRet = $oObj->GetAsHtml($sAttCode);
}
}
return $sRet;
}
@@ -125,15 +143,7 @@ class HTMLBulkExport extends TabularBulkExport
$sField = '';
if ($oObj)
{
switch($sAttCode)
{
case 'id':
$sField = $aRow[$sAlias]->GetHyperlink();
break;
default:
$sField = $aRow[$sAlias]->GetAsHtml($sAttCode);
}
$sField = $this->GetValue($oObj, $sAttCode);
}
$sValue = ($sField === '') ? '&nbsp;' : $sField;
$sData .= "<td>$sValue</td>";

View File

@@ -429,6 +429,25 @@ abstract class MetaModel
return $oNameExpr;
}
/**
* Returns the friendly name IIF it is equivalent to a single attribute
*/
final static public function GetFriendlyNameAttributeCode($sClass)
{
$aNameSpec = self::GetNameSpec($sClass);
$sFormat = trim($aNameSpec[0]);
$aAttributes = $aNameSpec[1];
if (($sFormat != '') && ($sFormat != '%1$s'))
{
return null;
}
if (count($aAttributes) > 1)
{
return null;
}
return reset($aAttributes);
}
final static public function GetStateAttributeCode($sClass)
{
self::_check_subclass($sClass);

View File

@@ -212,6 +212,81 @@ class ormCaseLog {
return $sHtml;
}
/**
* Produces an HTML representation, aimed at being used to produce a PDF with TCPDF (no table)
*/
public function GetAsSimpleHtml()
{
$sStyleCaseLogHeader = '';
$sStyleCaseLogEntry = '';
$sHtml = '<ul class="case_log_simple_html">';
$iPos = 0;
$aIndex = $this->m_aIndex;
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
{
$iPos += $aIndex[$index]['separator_length'];
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
$iPos += $aIndex[$index]['text_length'];
$sEntry = '<li>';
// Workaround: PHP < 5.3 cannot unserialize correctly DateTime objects,
// therefore we have changed the format. To preserve the compatibility with existing
// installations of iTop, both format are allowed:
// the 'date' item is either a DateTime object, or a unix timestamp
if (is_int($aIndex[$index]['date']))
{
// Unix timestamp
$sDate = date(Dict::S('UI:CaseLog:DateFormat'),$aIndex[$index]['date']);
}
elseif (is_object($aIndex[$index]['date']))
{
if (version_compare(phpversion(), '5.3.0', '>='))
{
// DateTime
$sDate = $aIndex[$index]['date']->format(Dict::S('UI:CaseLog:DateFormat'));
}
else
{
// No Warning... but the date is unknown
$sDate = '';
}
}
$sEntry .= sprintf(Dict::S('UI:CaseLog:Header_Date_UserName'), '<span class="caselog_header_date">'.$sDate.'</span>', '<span class="caselog_header_user">'.$aIndex[$index]['user_name'].'</span>');
$sEntry .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
$sEntry .= $sTextEntry;
$sEntry .= '</div>';
$sEntry .= '</li>';
$sHtml = $sHtml.$sEntry;
}
// Process the case of an eventual remainder (quick migration of AttributeText fields)
if ($iPos < (strlen($this->m_sLog) - 1))
{
$sTextEntry = substr($this->m_sLog, $iPos);
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
if (count($this->m_aIndex) == 0)
{
$sHtml .= '<li>';
$sHtml .= $sTextEntry;
$sHtml .= '</li>';
}
else
{
$sHtml .= '<li>';
$sHtml .= Dict::S('UI:CaseLog:InitialValue');
$sHtml .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
$sHtml .= $sTextEntry;
$sHtml .= '</div>';
$sHtml .= '</li>';
}
}
$sHtml .= '</ul>';
return $sHtml;
}
/**
* Produces an HTML representation, aimed at being used within the iTop framework
*/

View File

@@ -124,6 +124,9 @@ class PDFBulkExport extends HTMLBulkExport
$sData = parent::GetFooter();
$oPage = new PDFPage(Dict::Format('Core:BulkExportOf_Class', MetaModel::GetName($this->oSearch->GetClass())), $this->aStatusInfo['page_size'], $this->aStatusInfo['page_orientation']);
$oPDF = $oPage->get_tcpdf();
$oPDF->SetFont('dejavusans', '', 8, '', true);
$oPage->add(file_get_contents($this->aStatusInfo['tmp_file']));
$oPage->add($sData);

View File

@@ -63,17 +63,57 @@ class SpreadsheetBulkExport extends TabularBulkExport
protected function GetSampleData($oObj, $sAttCode)
{
if ($oObj)
return $this->GetValue($oObj, $sAttCode);
}
protected function GetValue($oObj, $sAttCode)
{
switch($sAttCode)
{
$sRet = $oObj->GetAsHTML($sAttCode);
}
else
{
$sRet = '';
case 'id':
$sRet = $oObj->GetKey();
break;
default:
$value = $oObj->Get($sAttCode);
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
if ($value instanceof ormCaseLog)
{
$sRet = str_replace("\n", "<br/>", htmlentities($value->__toString(), ENT_QUOTES, 'UTF-8'));
}
elseif ($value instanceof ormStopWatch)
{
$sRet = $value->GetTimeSpent();
}
elseif ($oAttDef instanceof AttributeString)
{
$sRet = $oObj->GetAsHTML($sAttCode);
}
else
{
if ($this->bLocalizeOutput)
{
$sRet = htmlentities($oObj->GetEditValue(), ENT_QUOTES, 'UTF-8');
}
else
{
$sRet = htmlentities($value, ENT_QUOTES, 'UTF-8');
}
}
}
return $sRet;
}
public function SetHttpHeaders(WebPage $oPage)
{
// Integration within MS-Excel web queries + HTTPS + IIS:
// MS-IIS set these header values with no-cache... while Excel fails to do the job if using HTTPS
// Then the fix is to force the reset of header values Pragma and Cache-control
$oPage->add_header("Pragma:", true);
$oPage->add_header("Cache-control:", true);
}
public function GetHeader()
{
$oSet = new DBObjectSet($this->oSearch);
@@ -99,8 +139,14 @@ class SpreadsheetBulkExport extends TabularBulkExport
$aData[] = $sColLabel;
}
}
else
{
$aData[] = $sColLabel;
}
}
$sData = "<table border=\"1\">\n";
$sData = '';
$sData .= '<style>table br {mso-data-placement:same-cell;}</style>'; // Trick for Excel: keep line breaks inside the same cell !
$sData .= "<table border=\"1\">\n";
$sData .= "<tr>\n";
foreach($aData as $sLabel)
{
@@ -137,14 +183,14 @@ class SpreadsheetBulkExport extends TabularBulkExport
$oObj = $aRow[$sAlias];
if ($oObj == null)
{
$sData .= "<td x:str>$sField</td>";
$sData .= "<td x:str></td>";
continue;
}
switch($sAttCode)
{
case 'id':
$sField = $oObj->GetName();
$sField = $oObj->GetKey();
$sData .= "<td>$sField</td>";
break;
@@ -165,22 +211,14 @@ class SpreadsheetBulkExport extends TabularBulkExport
// Trick for Excel: treat the content as text even if it begins with an equal sign
$sData .= "<td x:str>$sField</td>";
}
else if($oAttDef instanceof AttributeString)
{
$sField = $oObj->GetAsHTML($sAttCode);
$sData .= "<td x:str>$sField</td>";
}
else
{
$rawValue = $oObj->Get($sAttCode);
// Due to custom formatting rules, empty friendlynames may be rendered as non-empty strings
// let's fix this and make sure we render an empty string if the key == 0
if ($oAttDef instanceof AttributeFriendlyName)
{
$sKeyAttCode = $oAttDef->GetKeyAttCode();
if ($sKeyAttCode != 'id')
{
if ($oObj->Get($sKeyAttCode) == 0)
{
$sValue = '';
}
}
}
if ($this->bLocalizeOutput)
{
$sField = htmlentities($oFinalAttDef->GetEditValue($rawValue), ENT_QUOTES, 'UTF-8');

View File

@@ -47,12 +47,13 @@ abstract class TabularBulkExport extends BulkExport
break;
default:
return parent:: DisplayFormPart($oP, $sPartId);
return parent::DisplayFormPart($oP, $sPartId);
}
}
protected function SuggestFields($aSuggestedFields)
{
$aRet = array();
// By defaults all fields are Ok, nothing gets translated but
// you can overload this method if some fields are better exported
// (in a given format) by using an alternate field, for example id => friendlyname
@@ -71,20 +72,75 @@ abstract class TabularBulkExport extends BulkExport
$sAttCode = $sField;
$sClass = reset($aAliases);
}
$aSuggestedFields[$idx] = $this->SuggestField($aAliases, $sClass, $sAlias, $sAttCode);
$sMostRelevantField = $this->SuggestField($sClass, $sAttCode);
$sAttCodeEx = $this->NormalizeFieldSpec($sClass, $sMostRelevantField);
// Remove the aliases (if any) from the field names to make them compatible
// with the 'short' notation used in this case by the widget
if (count($aAliases) > 1)
{
$sAttCodeEx = $sAlias.'.'.$sAttCodeEx;
}
$aRet[] = $sAttCodeEx;
}
return $aSuggestedFields;
return $aRet;
}
protected function SuggestField($aAliases, $sClass, $sAlias, $sAttCode)
protected function SuggestField($sClass, $sAttCode)
{
// Remove the aliases (if any) from the field names to make them compatible
// with the 'short' notation used in this case by the widget
if (count($aAliases) == 1)
return $sAttCode;
}
/**
* Given a field spec, get the most relevant (unique) representation
* Examples for a user request:
* - friendlyname => ref
* - org_name => org_id->name
* - org_id_friendlyname => org_id=>name
* - caller_name => caller_id->name
* - caller_id_friendlyname => caller_id->friendlyname
*/
protected function NormalizeFieldSpec($sClass, $sField)
{
$sRet = $sField;
if ($sField == 'id')
{
return $sAttCode;
$sRet = 'id';
}
return $sAlias.'.'.$sAttCode;
elseif ($sField == 'friendlyname')
{
$sFriendlyNameAttCode = MetaModel::GetFriendlyNameAttributeCode($sClass);
if (!is_null($sFriendlyNameAttCode))
{
// The friendly name is made of a single attribute
$sRet = $sFriendlyNameAttCode;
}
}
else
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sField);
if ($oAttDef instanceof AttributeFriendlyName)
{
$oKeyAttDef = MetaModel::GetAttributeDef($sClass, $oAttDef->GetKeyAttCode());
$sRemoteClass = $oKeyAttDef->GetTargetClass();
$sFriendlyNameAttCode = MetaModel::GetFriendlyNameAttributeCode($sRemoteClass);
if (is_null($sFriendlyNameAttCode))
{
// The friendly name is made of several attributes
$sRet = $oAttDef->GetKeyAttCode().'->friendlyname';
}
else
{
// The friendly name is made of a single attribute
$sRet = $oAttDef->GetKeyAttCode().'->'.$sFriendlyNameAttCode;
}
}
elseif ($oAttDef->IsExternalField())
{
$sRet = $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode();
}
}
return $sRet;
}
protected function IsSubAttribute($sClass, $sAttCode, $oAttDef)
@@ -99,19 +155,64 @@ abstract class TabularBulkExport extends BulkExport
{
case 'AttributeExternalKey':
case 'AttributeHierarchicalKey':
$aResult[] = array('code' => $sAttCode, 'unique_label' => $oAttDef->GetLabel(), 'label' => Dict::S('Core:BulkExport:Identifier'), 'attdef' => $oAttDef);
$aResult[] = array('code' => $sAttCode.'_friendlyname', 'unique_label' => $oAttDef->GetLabel().'->'.Dict::S('Core:FriendlyName-Label'), 'label' => Dict::S('Core:FriendlyName-Label'), 'attdef' => MetaModel::GetAttributeDef($sClass, $sAttCode.'_friendlyname'));
$bAddFriendlyName = true;
$oKeyAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sRemoteClass = $oKeyAttDef->GetTargetClass();
$sFriendlyNameAttCode = MetaModel::GetFriendlyNameAttributeCode($sRemoteClass);
if (!is_null($sFriendlyNameAttCode))
{
// The friendly name is made of a single attribute, check if that attribute is present as an external field
foreach(MetaModel::ListAttributeDefs($sClass) as $sSubAttCode => $oSubAttDef)
{
if ($oSubAttDef instanceof AttributeExternalField)
{
if (($oSubAttDef->GetKeyAttCode() == $sAttCode) && ($oSubAttDef->GetExtAttCode() == $sFriendlyNameAttCode))
{
$bAddFriendlyName = false;
}
}
}
}
$aResult[$sAttCode] = array('code' => $sAttCode, 'unique_label' => $oAttDef->GetLabel(), 'label' => Dict::S('UI:CSVImport:idField'), 'attdef' => $oAttDef);
if ($bAddFriendlyName)
{
if ($this->IsExportableField($sClass, $sAttCode.'->friendlyname'))
{
$aResult[$sAttCode.'->friendlyname'] = array('code' => $sAttCode.'->friendlyname', 'unique_label' => $oAttDef->GetLabel().'->'.Dict::S('Core:FriendlyName-Label'), 'label' => Dict::S('Core:FriendlyName-Label'), 'attdef' => MetaModel::GetAttributeDef($sClass, $sAttCode.'_friendlyname'));
}
}
foreach(MetaModel::ListAttributeDefs($sClass) as $sSubAttCode => $oSubAttDef)
{
if ($oSubAttDef instanceof AttributeExternalField)
{
if ($oSubAttDef->GetKeyAttCode() == $sAttCode)
if ($this->IsExportableField($sClass, $sSubAttCode, $oSubAttDef))
{
$aResult[] = array('code' => $sSubAttCode, 'unique_label' => $oAttDef->GetLabel().'->'.$oSubAttDef->GetExtAttDef()->GetLabel(), 'label' => $oSubAttDef->GetExtAttDef()->GetLabel(), 'attdef' => $oSubAttDef);
if ($oSubAttDef->GetKeyAttCode() == $sAttCode)
{
$sAttCodeEx = $sAttCode.'->'.$oSubAttDef->GetExtAttCode();
$aResult[$sAttCodeEx] = array('code' => $sAttCodeEx, 'unique_label' => $oAttDef->GetLabel().'->'.$oSubAttDef->GetExtAttDef()->GetLabel(), 'label' => MetaModel::GetLabel($sRemoteClass, $oSubAttDef->GetExtAttCode()), 'attdef' => $oSubAttDef);
}
}
}
}
// Add the reconciliation keys
foreach(MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode)
{
$sAttCodeEx = $sAttCode.'->'.$sRemoteAttCode;
if (!array_key_exists($sAttCodeEx, $aResult))
{
$oRemoteAttDef = MetaModel::GetAttributeDef($sRemoteClass, $sRemoteAttCode);
if ($this->IsExportableField($sRemoteClass, $sRemoteAttCode, $oRemoteAttDef))
{
$aResult[$sAttCodeEx] = array('code' => $sAttCodeEx, 'unique_label' => $oAttDef->GetLabel().'->'.$oRemoteAttDef->GetLabel(), 'label' => MetaModel::GetLabel($sRemoteClass, $sRemoteAttCode), 'attdef' => $oRemoteAttDef);
}
}
}
break;
case 'AttributeStopWatch':
@@ -121,12 +222,14 @@ abstract class TabularBulkExport extends BulkExport
{
if ($oSubAttDef->GetParentAttCode() == $sAttCode)
{
$aResult[] = array('code' => $sSubAttCode, 'unique_label' => $oSubAttDef->GetLabel(), 'label' => $oSubAttDef->GetLabel(), 'attdef' => $oSubAttDef);
if ($this->IsExportableField($sClass, $sSubAttCode, $oSubAttDef))
{
$aResult[$sSubAttCode] = array('code' => $sSubAttCode, 'unique_label' => $oSubAttDef->GetLabel(), 'label' => $oSubAttDef->GetLabel(), 'attdef' => $oSubAttDef);
}
}
}
}
break;
}
return $aResult;
}
@@ -144,6 +247,7 @@ abstract class TabularBulkExport extends BulkExport
}
}
$aAllFieldsByAlias = array();
$aAllAttCodes = array();
foreach($aAuthorizedClasses as $sAlias => $sClass)
{
$aAllFields = array();
@@ -155,18 +259,29 @@ abstract class TabularBulkExport extends BulkExport
{
$sShortAlias = '';
}
if ($this->IsValidField($sClass, 'id'))
if ($this->IsExportableField($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:FriendlyName-Label'), 'label' => $sShortAlias.Dict::S('Core:FriendlyName-Label')),
));
$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->IsValidField($sClass, $sAttCode, $oAttDef))
if ($this->IsExportableField($sClass, $sAttCode, $oAttDef))
{
$sShortLabel = $oAttDef->GetLabel();
$sLabel = $sShortAlias.$oAttDef->GetLabel();
@@ -174,12 +289,9 @@ abstract class TabularBulkExport extends BulkExport
$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']);
}
$aValidSubAttr[] = array('attcodeex' => $aSubAttDef['code'], 'code' => $sShortAlias.$aSubAttDef['code'], 'label' => $aSubAttDef['label'], 'unique_label' => $sShortAlias.$aSubAttDef['unique_label']);
}
$aAllFields[] = array('code' => $sShortAlias.$sAttCode, 'label' => $sShortLabel, 'unique_label' => $sLabel, 'subattr' => $aValidSubAttr);
$aAllFields[] = array('attcodeex' => $sAttCode, 'code' => $sShortAlias.$sAttCode, 'label' => $sShortLabel, 'unique_label' => $sLabel, 'subattr' => $aValidSubAttr);
}
}
usort($aAllFields, array(get_class($this), 'SortOnLabel'));
@@ -192,12 +304,36 @@ abstract class TabularBulkExport extends BulkExport
$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();
@@ -215,16 +351,10 @@ abstract class TabularBulkExport extends BulkExport
$sShortAlias = '';
}
if ($this->IsValidField($sClass, 'id'))
foreach ($aAllAttCodes[$sAlias] as $sAttCodeEx)
{
$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);
}
$oObj = $aRow[$sAlias];
$aSampleRow[$sShortAlias.$sAttCodeEx] = $oObj ? $this->GetSampleData($oObj, $sAttCodeEx) : '';
}
}
$aSampleData[] = $aSampleRow;
@@ -256,28 +386,26 @@ EOF
* Tells if the specified field can be exported
* @param unknown $sClass
* @param unknown $sAttCode
* @param AttributeDefinition $oAttDef Can be null when $sAttCode == 'id'
* @param AttributeDefinition $oAttDef Can be null in case the attribute definition has not been fetched by the caller
* @return boolean
*/
protected function IsValidField($sClass, $sAttCode, $oAttDef = null)
protected function IsExportableField($sClass, $sAttCode, $oAttDef = null)
{
if ($sAttCode == 'id') return true;
if (is_null($oAttDef))
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
}
if ($oAttDef instanceof AttributeLinkedSet) return false;
return true; //$oAttDef->IsScalar();
}
protected function GetSampleData($oObj, $sAttCode)
{
if ($oObj == null) return '';
if ($sAttCode == 'id') return $oObj->GetKey();
return $oObj->GetEditValue($sAttCode);
}
protected function GetSampleKey($oObj)
{
if ($oObj == null) return '';
return $oObj->GetKey();
}
public function ReadParameters()
{
parent::ReadParameters();
@@ -358,7 +486,7 @@ EOF
catch (Exception $e)
{
throw new Exception("Wrong field specification '$sFieldSpec': ".$e->getMessage());
}
}
}
else
{

View File

@@ -70,14 +70,7 @@ class XMLBulkExport extends BulkExport
protected function GetSampleData($oObj, $sAttCode)
{
if ($oObj)
{
$sRet = $oObj->GetAsXML($sAttCode);
}
else
{
$sRet = '';
}
$sRet = ($sAttCode == 'id') ? $oObj->GetKey() : $oObj->GetAsXML($sAttCode);
return $sRet;
}

View File

@@ -1956,6 +1956,9 @@ table.export_parameters td {
.table_preview > table {
border-collapse: collapse;
max-height: 150px;
overflow: auto;
display: block;
}
@@ -1968,11 +1971,21 @@ table.export_parameters td {
}
.table_preview > table > tbody > tr > td {
vertical-align: top;
}
.table_preview .drag-handle {
cursor: move;
}
.table_preview div.text-preview {
white-space: pre-wrap;
}
.graph_zoom {
display: inline-block;
float: right;

View File

@@ -1445,8 +1445,12 @@ div.ui-dialog-header {
table.export_parameters td {
padding-right: 2em;
}
.table_preview > table {
border-collapse: collapse;
max-height: 150px;
overflow: auto;
display: block;
}
.table_preview > table > thead > tr > th, .table_preview > table > tbody > tr > td {
border: 1px $grey-color solid;
@@ -1455,9 +1459,15 @@ table.export_parameters td {
padding-right: 0.25em;
font-size: 10pt;
}
.table_preview > table > tbody > tr > td {
vertical-align: top;
}
.table_preview .drag-handle {
cursor: move;
}
.table_preview div.text-preview {
white-space: pre-wrap;
}
.graph_zoom {
display: inline-block;
float: right;

View File

@@ -2446,7 +2446,6 @@ Operators:<br/>
'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
'Core:BulkExport:Identifier' => 'Identifier~~',
'Core:BulkExportOf_Class' => '%1$s Export~~',
'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
'Core:BulkExport:ExportResult' => 'Result of the export:~~',

View File

@@ -558,7 +558,6 @@ Operatoren:<br/>
'Core:BulkExport:MissingParameter_Param' => 'Fehlender Parameter "%1$s"',
'Core:BulkExport:InvalidParameter_Query' => 'ungültiger Wert für den Paramter "query". Es gibt keinen Eintrag in der Query-Bibliothek, der zu der id "%1$s" korrespondiert.',
'Core:BulkExport:ExportFormatPrompt' => 'Exportformat:',
'Core:BulkExport:Identifier' => 'Identifikator',
'Core:BulkExportOf_Class' => '%1$s-Export',
'Core:BulkExport:ClickHereToDownload_FileName' => 'Klicken Sie hier um %1$s herunterzuladen',
'Core:BulkExport:ExportResult' => 'Ergebnis ses Exportvorgangs:',

View File

@@ -810,7 +810,6 @@ Dict::Add('EN US', 'English', 'English', array(
'Core:BulkExport:MissingParameter_Param' => 'Missing parameter "%1$s"',
'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter "query". There is no Query Phrasebook corresponding to the id: "%1$s".',
'Core:BulkExport:ExportFormatPrompt' => 'Export format:',
'Core:BulkExport:Identifier' => 'Identifier',
'Core:BulkExportOf_Class' => '%1$s Export',
'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s',
'Core:BulkExport:ExportResult' => 'Result of the export:',

View File

@@ -804,7 +804,6 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
'Core:BulkExport:Identifier' => 'Identifier~~',
'Core:BulkExportOf_Class' => '%1$s Export~~',
'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
'Core:BulkExport:ExportResult' => 'Result of the export:~~',

View File

@@ -667,7 +667,6 @@ Opérateurs :<br/>
'Core:BulkExport:MissingParameter_Param' => 'Il manque le paramètre "%1$s"',
'Core:BulkExport:InvalidParameter_Query' => 'Valeur incorrecte pour le paramètre "query". Il n\'existe aucune entrée dans le livre des requêtes pour l\'identifiant: "%1$s"',
'Core:BulkExport:ExportFormatPrompt' => 'Format d\'export:',
'Core:BulkExport:Identifier' => 'Identifiant',
'Core:BulkExportOf_Class' => 'Export de: %1$s',
'Core:BulkExport:ClickHereToDownload_FileName' => 'Cliquez ici pour télécharger %1$s',
'Core:BulkExport:ExportResult' => 'Résultat de l\'export:',

View File

@@ -561,7 +561,6 @@ Operators:<br/>
'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
'Core:BulkExport:Identifier' => 'Identifier~~',
'Core:BulkExportOf_Class' => '%1$s Export~~',
'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
'Core:BulkExport:ExportResult' => 'Result of the export:~~',

View File

@@ -793,7 +793,6 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
'Core:BulkExport:Identifier' => 'Identifier~~',
'Core:BulkExportOf_Class' => '%1$s Export~~',
'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
'Core:BulkExport:ExportResult' => 'Result of the export:~~',

View File

@@ -583,7 +583,6 @@ Operators:<br/>
'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
'Core:BulkExport:Identifier' => 'Identifier~~',
'Core:BulkExportOf_Class' => '%1$s Export~~',
'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
'Core:BulkExport:ExportResult' => 'Result of the export:~~',

View File

@@ -813,7 +813,6 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
'Core:BulkExport:Identifier' => 'Identifier~~',
'Core:BulkExportOf_Class' => '%1$s Export~~',
'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
'Core:BulkExport:ExportResult' => 'Result of the export:~~',

View File

@@ -806,7 +806,6 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
'Core:BulkExport:Identifier' => 'Identifier~~',
'Core:BulkExportOf_Class' => '%1$s Export~~',
'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
'Core:BulkExport:ExportResult' => 'Result of the export:~~',

View File

@@ -801,7 +801,6 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
'Core:BulkExport:Identifier' => 'Identifier~~',
'Core:BulkExportOf_Class' => '%1$s Export~~',
'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
'Core:BulkExport:ExportResult' => 'Result of the export:~~',

View File

@@ -733,7 +733,6 @@ Operators:<br/>
'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
'Core:BulkExport:Identifier' => 'Identifier~~',
'Core:BulkExportOf_Class' => '%1$s Export~~',
'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
'Core:BulkExport:ExportResult' => 'Result of the export:~~',

View File

@@ -732,7 +732,6 @@ Operators:<br/>
'Core:BulkExport:MissingParameter_Param' => 'Missing parameter \"%1$s\"~~',
'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter \"query\". There is no Query Phrasebook corresponding to the id: \"%1$s\".~~',
'Core:BulkExport:ExportFormatPrompt' => 'Export format:~~',
'Core:BulkExport:Identifier' => 'Identifier~~',
'Core:BulkExportOf_Class' => '%1$s Export~~',
'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s~~',
'Core:BulkExport:ExportResult' => 'Result of the export:~~',

View File

@@ -489,6 +489,7 @@ function CheckParameters($sExpression, $sQueryId, $sFormat)
function DoExport(Page $oP, BulkExport $oExporter, $bInteractive = false)
{
$oExporter->SetHttpHeaders($oP);
$exportResult = $oExporter->GetHeader();
$aStatus = array();
do

View File

@@ -185,10 +185,15 @@ $aOperations = array(
);
$aOperations = array(
array(
'operation' => 'core/get', // operation code
'class' => 'PhysicalDevice',
'key' => 'SELECT PhysicalDevice WHERE id < 3',
'output_fields' => '*+', // list of fields to show in the results (* or a,b,c)
'operation' => 'core/update', // operation code
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
'class' => 'Server',
'key' => 'SELECT Server WHERE name="Server1"',
'output_fields' => 'id, friendlyname, description', // list of fields to show in the results (* or a,b,c)
// Values for the object to create
'fields' => array(
'description' => 'Issue #'.time(),
),
),
);
$aOperations = array(
@@ -213,6 +218,15 @@ $aXXXOperations = array(
'password' => 'admin',
),
);
$aOperations = array(
array(
'operation' => 'core/delete', // operation code
'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
'class' => 'Server',
'key' => 'SELECT Server',
'simulate' => false,
),
);
if (false)
{
@@ -225,8 +239,11 @@ else
}
$aData = array();
$aData['auth_user'] = 'admin';
$aData['auth_pwd'] = 'admin';
$aData['auth_user'] = 'no-export';
$aData['auth_pwd'] = 'no-export';
//$aData['auth_user'] = 'admin';
//$aData['auth_pwd'] = 'admin';
foreach ($aOperations as $iOp => $aOperation)
{