diff --git a/application/utils.inc.php b/application/utils.inc.php
index ab9b3d216..b1f2d2160 100644
--- a/application/utils.inc.php
+++ b/application/utils.inc.php
@@ -802,6 +802,7 @@ class utils
// Bulk export actions
$aResult[] = new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '$sDataTableId', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")");
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '$sDataTableId', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")");
+ $aResult[] = new JSPopupMenuItem('UI:Menu:ExportPDF', Dict::S('UI:Menu:ExportPDF'), "ExportListDlg('$sOQL', '$sDataTableId', 'pdf', ".json_encode(Dict::S('UI:Menu:ExportPDF')).")");
}
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL')");
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php
index 16f8ace95..428d7acc8 100644
--- a/core/attributedef.class.inc.php
+++ b/core/attributedef.class.inc.php
@@ -728,7 +728,42 @@ class AttributeLinkedSet extends AttributeDefinition
public function GetAsXML($sValue, $oHostObject = null, $bLocalize = true)
{
- return "Sorry, no yet implemented";
+ if (is_object($sValue) && ($sValue instanceof DBObjectSet))
+ {
+ $sValue->Rewind();
+ $sRes = "\n";
+ while ($oObj = $sValue->Fetch())
+ {
+ $sObjClass = get_class($oObj);
+ $sRes .= "<$sObjClass id=\"".$oObj->GetKey()."\">\n";
+ // Show only relevant information (hide the external key to the current object)
+ $aAttributes = array();
+ foreach(MetaModel::ListAttributeDefs($sObjClass) as $sAttCode => $oAttDef)
+ {
+ if ($sAttCode == 'finalclass')
+ {
+ if ($sObjClass == $this->GetLinkedClass())
+ {
+ // Simplify the output if the exact class could be determined implicitely
+ continue;
+ }
+ }
+ if ($sAttCode == $this->GetExtKeyToMe()) continue;
+ if ($oAttDef->IsExternalField()) continue;
+ if (!$oAttDef->IsDirectField()) continue;
+ if (!$oAttDef->IsScalar()) continue;
+ $sAttValue = $oObj->GetAsXML($sAttCode, $bLocalize);
+ $sRes .= "<$sAttCode>$sAttValue$sAttCode>\n";
+ }
+ $sRes .= "$sObjClass>\n";
+ }
+ $sRes .= "\n";
+ }
+ else
+ {
+ $sRes = '';
+ }
+ return $sRes;
}
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
@@ -761,7 +796,7 @@ class AttributeLinkedSet extends AttributeDefinition
if ($oAttDef->IsExternalField()) continue;
if (!$oAttDef->IsDirectField()) continue;
if (!$oAttDef->IsScalar()) continue;
- $sAttValue = $oObj->GetAsCSV($sAttCode, $sSepValue, '');
+ $sAttValue = $oObj->GetAsCSV($sAttCode, $sSepValue, '', $bLocalize);
if (strlen($sAttValue) > 0)
{
$sAttributeData = str_replace($sAttributeQualifier, $sAttributeQualifier.$sAttributeQualifier, $sAttCode.$sSepValue.$sAttValue);
@@ -897,7 +932,8 @@ class AttributeLinkedSet extends AttributeDefinition
{
throw new CoreException('Wrong attribute code for link attribute specification', array('class' => $sTargetClass, 'attcode' => $sAttCode));
}
- $aValues[$sAttCode] = $sValue;
+ $oAttDef = MetaModel::GetAttributeDef($sTargetClass, $sAttCode);
+ $aValues[$sAttCode] = $oAttDef->MakeValueFromString($sValue, $bLocalizedValue, $sSepItem, $sSepAttribute, $sSepValue, $sAttributeQualifier);
}
}
@@ -4530,6 +4566,10 @@ class AttributeStopWatch extends AttributeDefinition
switch($sThresholdCode)
{
case 'deadline':
+ if ($value != '')
+ {
+ $sRet = $sTextQualifier.date(self::GetDateFormat(true /*full*/), $value).$sTextQualifier;
+ }
break;
case 'passed':
diff --git a/core/csvbulkexport.class.inc.php b/core/csvbulkexport.class.inc.php
index b236c1c80..bc9d7ad90 100644
--- a/core/csvbulkexport.class.inc.php
+++ b/core/csvbulkexport.class.inc.php
@@ -57,6 +57,26 @@ class CSVBulkExport extends TabularBulkExport
$this->aStatusInfo['charset'] = strtoupper(utils::ReadParam('character-set', 'UTF-8', true, 'raw_data'));
}
+
+ protected function SuggestField($aAliases, $sClass, $sAlias, $sAttCode)
+ {
+ switch($sAttCode)
+ {
+ case 'id': // replace 'id' by 'friendlyname'
+ $sAttCode = 'friendlyname';
+ break;
+
+ default:
+ $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
+ if ($oAttDef instanceof AttributeExternalKey)
+ {
+ $sAttCode .= '_friendlyname';
+ }
+ }
+
+ return parent::SuggestField($aAliases, $sClass, $sAlias, $sAttCode);
+ }
+
public function EnumFormParts()
{
return array_merge(parent::EnumFormParts(), array('csv_options' => array('separator', 'character-set', 'text-qualifier', 'no_localize') ,'interactive_fields_csv' => array('interactive_fields_csv')));
@@ -153,6 +173,7 @@ class CSVBulkExport extends TabularBulkExport
$aAuthorizedClasses[$sAlias] = $sClassName;
}
}
+ $aAliases = array_keys($aAuthorizedClasses);
$aData = array();
foreach($this->aStatusInfo['fields'] as $sExtendedAttCode)
{
@@ -163,46 +184,47 @@ class CSVBulkExport extends TabularBulkExport
}
else
{
- $sAlias = reset($aAuthorizedClasses);
+ $sAlias = reset($aAliases);
$sAttCode = $sExtendedAttCode;
}
- if (!array_key_exists($sAlias, $aAuthorizedClasses))
+ if (!in_array($sAlias, $aAliases))
{
- throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", array_keys($aAuthorizedClasses))."'");
+ throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", $aAliases)."'");
}
$sClass = $aAuthorizedClasses[$sAlias];
- if ($this->aStatusInfo['localize'])
+ switch($sAttCode)
{
- switch($sAttCode)
+ case 'id':
+ $sLabel = 'id';
+ break;
+
+ default:
+ $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
+ if ($oAttDef instanceof AttributeExternalField)
{
- case 'id':
- if (count($aAuthorizedClasses) > 1)
- {
- $aData[] = $sAlias.'.id';
- }
- else
- {
- $aData[] = 'id';
- }
- break;
-
- default:
- $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
- $sLabel = $this->aStatusInfo['localize'] ? $oAttDef->GetLabel() : $sAttCode;
- if (count($aAuthorizedClasses) > 1)
- {
- $aData[] = $sAlias.'.'.$sLabel;
- }
- else
- {
- $aData[] = $sLabel;
- }
+ if ($this->aStatusInfo['localize'])
+ {
+ $sStar = $oAttDef->IsNullAllowed() ? '' : '*';
+ $sLabel = $oAttDef->GetKeyAttDef()->GetLabel().$sStar.'->'.$oAttDef->GetExtAttDef()->GetLabel();
+ }
+ else
+ {
+ $sLabel = $oAttDef->GetKeyAttDef()->GetCode().'->'.$oAttDef->GetExtAttDef()->GetCode();
+ }
}
+ else
+ {
+ $sLabel = $this->aStatusInfo['localize'] ? $oAttDef->GetLabel() : $sAttCode;
+ }
+ }
+ if (count($aAuthorizedClasses) > 1)
+ {
+ $aData[] = $sAlias.'.'.$sLabel;
}
else
{
- $aData[] = $sExtendedAttCode;
+ $aData[] = $sLabel;
}
}
$sFrom = array("\r\n", $this->aStatusInfo['text_qualifier']);
@@ -239,6 +261,7 @@ class CSVBulkExport extends TabularBulkExport
$aAuthorizedClasses[$sAlias] = $sClassName;
}
}
+ $aAliases = array_keys($aAuthorizedClasses);
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
$aAliasByField = array();
@@ -254,13 +277,13 @@ class CSVBulkExport extends TabularBulkExport
}
else
{
- $sAlias = reset($aAuthorizedClasses);
+ $sAlias = reset($aAliases);
$sAttCode = $sExtendedAttCode;
}
- if (!array_key_exists($sAlias, $aAuthorizedClasses))
+ if (!in_array($sAlias, $aAliases))
{
- throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", array_keys($aAuthorizedClasses))."'");
+ throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", $aAliases)."'");
}
if (!array_key_exists($sAlias, $aColumnsToLoad))
diff --git a/core/excelbulkexport.class.inc.php b/core/excelbulkexport.class.inc.php
index 69743db5d..1d33bccd1 100644
--- a/core/excelbulkexport.class.inc.php
+++ b/core/excelbulkexport.class.inc.php
@@ -102,6 +102,14 @@ class ExcelBulkExport extends TabularBulkExport
$this->aStatusInfo['total'] = $oSet->Count();
$aSelectedClasses = $this->oSearch->GetSelectedClasses();
+ foreach($aSelectedClasses as $sAlias => $sClassName)
+ {
+ if (UserRights::IsActionAllowed($sClassName, UR_ACTION_BULK_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
+ {
+ $aAuthorizedClasses[$sAlias] = $sClassName;
+ }
+ }
+ $aAliases = array_keys($aAuthorizedClasses);
$aTableHeaders = array();
foreach($this->aStatusInfo['fields'] as $sExtendedAttCode)
{
@@ -112,12 +120,12 @@ class ExcelBulkExport extends TabularBulkExport
}
else
{
- $sAlias = reset($aSelectedClasses);
+ $sAlias = reset($aAliases);
$sAttCode = $sExtendedAttCode;
}
- if (!array_key_exists($sAlias, $aSelectedClasses))
+ if (!in_array($sAlias, $aAliases))
{
- throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", array_keys($aSelectedClasses))."'");
+ throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", $aAliases)."'");
}
$sClass = $aSelectedClasses[$sAlias];
@@ -170,6 +178,7 @@ class ExcelBulkExport extends TabularBulkExport
$hFile = fopen($this->aStatusInfo['tmp_file'], 'ab');
$oSet = new DBObjectSet($this->oSearch);
$aSelectedClasses = $this->oSearch->GetSelectedClasses();
+ $aAliases = array_keys($aSelectedClasses);
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
$aAliasByField = array();
@@ -185,13 +194,13 @@ class ExcelBulkExport extends TabularBulkExport
}
else
{
- $sAlias = reset($aSelectedClasses);
+ $sAlias = reset($aAliases);
$sAttCode = $sExtendedAttCode;
}
- if (!array_key_exists($sAlias, $aSelectedClasses))
+ if (!in_array($sAlias, $aAliases))
{
- throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", array_keys($aSelectedClasses))."'");
+ throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", $aAliases)."'");
}
if (!array_key_exists($sAlias, $aColumnsToLoad))
@@ -224,7 +233,22 @@ class ExcelBulkExport extends TabularBulkExport
break;
default:
- $sField = $aRow[$aAttCode['alias']]->Get($aAttCode['attcode']);
+ $value = $aRow[$aAttCode['alias']]->Get($aAttCode['attcode']);
+ 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($aRow[$aAttCode['alias']]), $aAttCode['attcode']);
+ $sField = $oAttDef->GetAsCSV($value, '', '', $aRow[$aAttCode['alias']]);
+ }
+ else
+ {
+ $oAttDef = MetaModel::GetAttributeDef(get_class($aRow[$aAttCode['alias']]), $aAttCode['attcode']);
+ $sField = $oAttDef->GetEditValue($value, $aRow[$aAttCode['alias']]);
+ }
}
$aData[] = $sField;
}
diff --git a/core/htmlbulkexport.class.inc.php b/core/htmlbulkexport.class.inc.php
index be3ff36ad..fe27256fb 100644
--- a/core/htmlbulkexport.class.inc.php
+++ b/core/htmlbulkexport.class.inc.php
@@ -56,12 +56,22 @@ class HTMLBulkExport extends TabularBulkExport
public function GetHeader()
{
+ $sData = '';
+
$oSet = new DBObjectSet($this->oSearch);
$this->aStatusInfo['status'] = 'running';
$this->aStatusInfo['position'] = 0;
$this->aStatusInfo['total'] = $oSet->Count();
$aSelectedClasses = $this->oSearch->GetSelectedClasses();
+ foreach($aSelectedClasses as $sAlias => $sClassName)
+ {
+ if (UserRights::IsActionAllowed($sClassName, UR_ACTION_BULK_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
+ {
+ $aAuthorizedClasses[$sAlias] = $sClassName;
+ }
+ }
+ $aAliases = array_keys($aAuthorizedClasses);
$aData = array();
foreach($this->aStatusInfo['fields'] as $sExtendedAttCode)
{
@@ -72,12 +82,13 @@ class HTMLBulkExport extends TabularBulkExport
}
else
{
- $sAlias = reset($aSelectedClasses);
+
+ $sAlias = reset($aAliases);
$sAttCode = $sExtendedAttCode;
}
- if (!array_key_exists($sAlias, $aSelectedClasses))
+ if (!in_array($sAlias, $aAliases))
{
- throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", array_keys($aSelectedClasses))."'");
+ throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", $aAliases)."'");
}
$sClass = $aSelectedClasses[$sAlias];
@@ -106,7 +117,7 @@ class HTMLBulkExport extends TabularBulkExport
}
}
}
- $sData = "
\n";
+ $sData .= "
\n";
$sData .= "\n";
$sData .= "
\n";
foreach($aData as $sLabel)
@@ -126,6 +137,7 @@ class HTMLBulkExport extends TabularBulkExport
$oSet = new DBObjectSet($this->oSearch);
$aSelectedClasses = $this->oSearch->GetSelectedClasses();
+ $aAliases = array_keys($aSelectedClasses);
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
$aAliasByField = array();
@@ -141,13 +153,13 @@ class HTMLBulkExport extends TabularBulkExport
}
else
{
- $sAlias = reset($aSelectedClasses);
+ $sAlias = reset($aAliases);
$sAttCode = $sExtendedAttCode;
}
- if (!array_key_exists($sAlias, $aSelectedClasses))
+ if (!in_array($sAlias, $aAliases))
{
- throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", array_keys($aSelectedClasses))."'");
+ throw new Exception("Invalid alias '$sAlias' for the column '$sExtendedAttCode'. Availables aliases: '".implode("', '", $aAliases)."'");
}
if (!array_key_exists($sAlias, $aColumnsToLoad))
@@ -170,7 +182,7 @@ class HTMLBulkExport extends TabularBulkExport
while($aRow = $oSet->FetchAssoc())
{
set_time_limit($iLoopTimeLimit);
- $sFirstAlias = reset($aSelectedClasses);
+ $sFirstAlias = reset($aAliases);
$sHilightClass = $aRow[$sFirstAlias]->GetHilightClass();
if ($sHilightClass != '')
{
@@ -222,7 +234,6 @@ class HTMLBulkExport extends TabularBulkExport
{
$sData = "\n";
$sData .= "
\n";
-
return $sData;
}
diff --git a/core/tabularbulkexport.class.inc.php b/core/tabularbulkexport.class.inc.php
index f1640c558..82b228c19 100644
--- a/core/tabularbulkexport.class.inc.php
+++ b/core/tabularbulkexport.class.inc.php
@@ -266,18 +266,6 @@ EOF
return true; //$oAttDef->IsScalar();
}
- /**
- * Tells if the specified field is part of the "advanced" fields
- * @param unknown $sClass
- * @param unknown $sAttCode
- * @param AttributeDefinition $oAttDef Can be null when $sAttCode == 'id'
- * @return boolean
- */
- protected function IsAdvancedValidField($sClass, $sAttCode, $oAttDef = null)
- {
- return (($sAttCode == 'id') || ($oAttDef instanceof AttributeExternalKey));
- }
-
protected function GetSampleData(DBObject $oObj, $sAttCode)
{
if ($oObj == null) return '';
@@ -306,10 +294,14 @@ EOF
if ($oQueries->Count() > 0)
{
$oQuery = $oQueries->Fetch();
- $sFields = trim($oQuery->Get('fields'));
- if ($sFields === '')
+ if (($sFields === null) || ($sFields === ''))
{
- throw new BulkExportMissingParameterException('fields');
+ // No 'fields' parameter supplied, take the fields from the query phrasebook definition
+ $sFields = trim($oQuery->Get('fields'));
+ if ($sFields === '')
+ {
+ throw new BulkExportMissingParameterException('fields');
+ }
}
}
else
@@ -318,6 +310,12 @@ EOF
}
}
- $this->aStatusInfo['fields'] = explode(',', $sFields);
+ $aFields = explode(',', $sFields);
+ $this->aStatusInfo['fields'] = array();
+ foreach($aFields as $sField)
+ {
+ // Trim the values since it's too temping to write: fields=name, first_name, org_name instead of fields=name,first_name,org_name
+ $this->aStatusInfo['fields'][] = trim($sField);
+ }
}
}
diff --git a/core/xmlbulkexport.class.inc.php b/core/xmlbulkexport.class.inc.php
index 022a8a16b..f6b28d40b 100644
--- a/core/xmlbulkexport.class.inc.php
+++ b/core/xmlbulkexport.class.inc.php
@@ -133,13 +133,10 @@ class XMLBulkExport extends BulkExport
}
else
{
- if ($oAttDef->IsWritable())
+ if ($oAttDef->IsWritable() )
{
- if (!$oAttDef->IsLinkSet())
- {
- $sValue = $oObj->GetAsXML($sAttCode, $bLocalize);
- $sData .= "<$sAttCode>$sValue$sAttCode>\n";
- }
+ $sValue = $oObj->GetAsXML($sAttCode, $bLocalize);
+ $sData .= "<$sAttCode>$sValue$sAttCode>\n";
}
}
}
diff --git a/css/light-grey.css b/css/light-grey.css
index e3aa0e624..b21edd240 100644
--- a/css/light-grey.css
+++ b/css/light-grey.css
@@ -1962,9 +1962,8 @@ table.export_parameters td {
}
-.table_preview .nodragtable-sortable li {
- border: 1px #555555 solid;
- font-size: 10pt;
+.table_preview .drag-handle {
+ cursor: move;
}
diff --git a/css/light-grey.scss b/css/light-grey.scss
index 4a53a929a..814cb531a 100644
--- a/css/light-grey.scss
+++ b/css/light-grey.scss
@@ -1450,7 +1450,6 @@ table.export_parameters td {
padding-right: 0.25em;
font-size: 10pt;
}
-.table_preview .nodragtable-sortable li {
- border: 1px $grey-color solid;
- font-size: 10pt;
+.table_preview .drag-handle {
+ cursor: move;
}
\ No newline at end of file
diff --git a/dictionaries/de.dictionary.itop.core.php b/dictionaries/de.dictionary.itop.core.php
index 93017524f..1b31f9753 100644
--- a/dictionaries/de.dictionary.itop.core.php
+++ b/dictionaries/de.dictionary.itop.core.php
@@ -582,4 +582,7 @@ Operatoren:
'Core:BulkExportLabelPhrasebookEntry' => 'Query Phrasebook Entry:~~',
'Core:BulkExportMessageEmptyOQL' => 'Please enter a valid OQL query.~~',
'Core:BulkExportMessageEmptyPhrasebookEntry' => 'Please select a valid phrasebook entry.~~',
+ 'Core:BulkExportQueryPlaceholder' => 'Type an OQL query here...~~',
+ 'Core:BulkExportCanRunNonInteractive' => 'Click here to run the export in non-interactive mode.~~',
+ 'Core:BulkExportLegacyExport' => 'Click here to access the legacy export.~~',
));
diff --git a/dictionaries/de.dictionary.itop.ui.php b/dictionaries/de.dictionary.itop.ui.php
index d5769d3fd..8aedeb59e 100644
--- a/dictionaries/de.dictionary.itop.ui.php
+++ b/dictionaries/de.dictionary.itop.ui.php
@@ -1020,5 +1020,7 @@ Wenn Aktionen mit Trigger verknüpft sind, bekommt jede Aktion eine Auftragsnumm
'UI:CurrentObjectLockExpired_Explanation' => 'The lock to prevent concurrent modifications of the object has expired. You can no longer submit your modification since other users are now allowed to modify this object.~~',
'UI:ConcurrentLockKilled' => 'The lock preventing modifications on the current object has been deleted.~~',
'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !~~',
+
+ 'UI:Menu:ExportPDF' => 'Export as PDF...~~',
));
?>
diff --git a/dictionaries/dictionary.itop.core.php b/dictionaries/dictionary.itop.core.php
index e5a31b6e9..14ab263a5 100644
--- a/dictionaries/dictionary.itop.core.php
+++ b/dictionaries/dictionary.itop.core.php
@@ -831,5 +831,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Core:BulkExportLabelPhrasebookEntry' => 'Query Phrasebook Entry:',
'Core:BulkExportMessageEmptyOQL' => 'Please enter a valid OQL query.',
'Core:BulkExportMessageEmptyPhrasebookEntry' => 'Please select a valid phrasebook entry.',
-
+ 'Core:BulkExportQueryPlaceholder' => 'Type an OQL query here...',
+ 'Core:BulkExportCanRunNonInteractive' => 'Click here to run the export in non-interactive mode.',
+ 'Core:BulkExportLegacyExport' => 'Click here to access the legacy export.',
));
diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php
index 08a94027f..226c90729 100644
--- a/dictionaries/dictionary.itop.ui.php
+++ b/dictionaries/dictionary.itop.ui.php
@@ -1286,5 +1286,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:CurrentObjectLockExpired_Explanation' => 'The lock to prevent concurrent modifications of the object has expired. You can no longer submit your modification since other users are now allowed to modify this object.',
'UI:ConcurrentLockKilled' => 'The lock preventing modifications on the current object has been deleted.',
'UI:Menu:KillConcurrentLock' => 'Kill the Concurrent Modification Lock !',
+
+ 'UI:Menu:ExportPDF' => 'Export as PDF...',
));
?>
diff --git a/dictionaries/fr.dictionary.itop.core.php b/dictionaries/fr.dictionary.itop.core.php
index 106430408..5db781c81 100644
--- a/dictionaries/fr.dictionary.itop.core.php
+++ b/dictionaries/fr.dictionary.itop.core.php
@@ -689,7 +689,10 @@ Opérateurs :
'Core:BulkExport:OptionNoLocalize' => 'Ne pas traduire les valeurs (pour les champs de type "Enum")',
'Core:BulkExport:ScopeDefinition' => 'Définition des objets à exporter',
'Core:BulkExportLabelOQLExpression' => 'Requête OQL:',
- 'Core:BulkExportLabelPhrasebookEntry' => 'Entrée dans le livre des requêtes:',
+ 'Core:BulkExportLabelPhrasebookEntry' => 'Entrée du livre des requêtes:',
'Core:BulkExportMessageEmptyOQL' => 'Veuillez saisir une requête OQL valide.',
'Core:BulkExportMessageEmptyPhrasebookEntry' => 'Veuillez sélectionner une entrée dans le livre des requêtes.',
+ 'Core:BulkExportQueryPlaceholder' => 'Saisissez une requête OQL...',
+ 'Core:BulkExportCanRunNonInteractive' => 'Cliquez ici pour exécuter l\'export en mode non-interactif.',
+ 'Core:BulkExportLegacyExport' => 'Cliquez ici pour exécuter l\'ancienne version de l\'export.',
));
diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php
index 4b3e5d148..c8b16404c 100644
--- a/dictionaries/fr.dictionary.itop.ui.php
+++ b/dictionaries/fr.dictionary.itop.ui.php
@@ -1128,5 +1128,6 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:CurrentObjectLockExpired_Explanation' => 'Le verrouillage interdisant les modifications concurrentes a expiré. Vos modifications ne peuvent pas être acceptées car d\'autres utilisateurs peuvent modifier cet objet.',
'UI:ConcurrentLockKilled' => 'Le verrouillage en édition de l\'objet courant a été supprimé.',
'UI:Menu:KillConcurrentLock' => 'Supprimer le verrouillage !',
-));
-?>
\ No newline at end of file
+
+ 'UI:Menu:ExportPDF' => 'Exporter en PDF...',
+));
\ No newline at end of file
diff --git a/js/tabularfieldsselector.js b/js/tabularfieldsselector.js
index f80b14962..f97d443ad 100644
--- a/js/tabularfieldsselector.js
+++ b/js/tabularfieldsselector.js
@@ -36,11 +36,10 @@ $(function()
this.element.parent().bind('validate', function() { me.validate(); });
this.aSelected = [];
- var sContent = '';
for(var i in this.options.fields)
{
- sContent += '