/** * Bulk export: Tabular export: abstract base class for all "tabular" exports. * Provides the user interface for selecting the column to be exported * * @copyright Copyright (C) 2015 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ abstract class TabularBulkExport extends BulkExport { public function EnumFormParts() { return array_merge(parent::EnumFormParts(), array('tabular_fields' => array('fields'))); } public function DisplayFormPart(WebPage $oP, $sPartId) { switch($sPartId) { case 'tabular_fields': $sFields = utils::ReadParam('fields', '', true, 'raw_data'); $sSuggestedFields = utils::ReadParam('suggested_fields', null, true, 'raw_data'); if (($sSuggestedFields !== null) && ($sSuggestedFields !== '')) { $aSuggestedFields = explode(',', $sSuggestedFields); $sFields = implode(',', $this->SuggestFields($aSuggestedFields)); } $oP->add(''); break; default: 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 $aAliases = $this->oSearch->GetSelectedClasses(); foreach($aSuggestedFields as $idx => $sField) { if (preg_match('/^([^\\.]+)\\.(.+)$/', $sField, $aMatches)) { $sAlias = $aMatches[1]; $sAttCode = $aMatches[2]; $sClass = $aAliases[$sAlias]; } else { $sAlias = ''; $sAttCode = $sField; $sClass = reset($aAliases); } $sMostRelevantField = $this->SuggestField($sClass, $sAttCode); $sAttCodeEx = MetaModel::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 $aRet; } protected function SuggestField($sClass, $sAttCode) { return $sAttCode; } protected function IsSubAttribute($sClass, $sAttCode, $oAttDef) { return (($oAttDef instanceof AttributeFriendlyName) || ($oAttDef instanceof AttributeExternalField) || ($oAttDef instanceof AttributeSubItem)); } protected function GetSubAttributes($sClass, $sAttCode, $oAttDef) { $aResult = array(); switch(get_class($oAttDef)) { case 'AttributeExternalKey': case 'AttributeHierarchicalKey': $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 ($this->IsExportableField($sClass, $sSubAttCode, $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': foreach(MetaModel::ListAttributeDefs($sClass) as $sSubAttCode => $oSubAttDef) { if ($oSubAttDef instanceof AttributeSubItem) { if ($oSubAttDef->GetParentAttCode() == $sAttCode) { if ($this->IsExportableField($sClass, $sSubAttCode, $oSubAttDef)) { $aResult[$sSubAttCode] = array('code' => $sSubAttCode, 'unique_label' => $oSubAttDef->GetLabel(), 'label' => $oSubAttDef->GetLabel(), 'attdef' => $oSubAttDef); } } } } break; } return $aResult; } 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('
'); $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( <<