export xslx

This commit is contained in:
yuri
2017-05-02 15:14:18 +03:00
parent 1659e583ea
commit e0ebb44e64
12 changed files with 539 additions and 34 deletions
@@ -258,6 +258,14 @@ class Record extends Base
$params['attributeList'] = $data['attributeList'];
}
if (isset($data['fieldList'])) {
$params['fieldList'] = $data['fieldList'];
}
if (isset($data['format'])) {
$params['format'] = $data['format'];
}
return array(
'id' => $this->getRecordService()->export($params)
);
+65
View File
@@ -0,0 +1,65 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Export;
use \Espo\Core\Exceptions\Error;
class Csv extends \Espo\Core\Injectable
{
protected $dependencyList = [
'config',
'preferences'
];
public function process($entityType, $params, $dataList)
{
if (!is_array($params['attributeList'])) {
throw new Error();
}
$attributeList = $params['attributeList'];
$delimiter = $this->getInjection('preferences')->get('exportDelimiter');
if (empty($delimiter)) {
$delimiter = $this->getInjection('config')->get('exportDelimiter', ';');
}
$fp = fopen('php://temp', 'w');
fputcsv($fp, $attributeList, $delimiter);
foreach ($dataList as $row) {
fputcsv($fp, $row, $delimiter);
}
rewind($fp);
$csv = stream_get_contents($fp);
fclose($fp);
return $csv;
}
}
+302
View File
@@ -0,0 +1,302 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2017 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Export;
use \Espo\Core\Exceptions\Error;
class Xlsx extends \Espo\Core\Injectable
{
protected $dependencyList = [
'language',
'metadata',
'config'
];
protected function getConfig()
{
return $this->getInjection('config');
}
protected function getMetadata()
{
return $this->getInjection('metadata');
}
public function process($entityType, $params, $dataList)
{
if (!is_array($params['fieldList'])) {
throw new Error();
}
$phpExcel = new \PHPExcel();
$sheet = $phpExcel->setActiveSheetIndex(0);
if (isset($params['exportName'])) {
$exportName = $params['exportName'];
} else {
$exportName = $this->getInjection('language')->translate($entityType, 'scopeNamesPlural');
}
$sheet->setTitle($exportName);
$fieldList = $params['fieldList'];
$titleStyle = array(
'alignment' => array(
'vertical' => \PHPExcel_Style_Alignment::VERTICAL_CENTER
),
'font' => array(
'bold' => true,
'size' => 18
)
);
$dateStyle = array(
'alignment' => array(
'vertical' => \PHPExcel_Style_Alignment::VERTICAL_CENTER
),
'font' => array(
'size' => 16
)
);
$sheet->setCellValue('B1', $exportName);
$sheet->setCellValue('C1', date('D M j G:i:s Y'));
$sheet->getRowDimension('1')->setRowHeight(40);
$sheet->getStyle('B1')->applyFromArray($titleStyle);
$sheet->getStyle('C1')->applyFromArray($dateStyle);
//\PHPExcel_Shared_Font::setTrueTypeFontPath('/usr/share/fonts/truetype/msttcorefonts/');
//\PHPExcel_Shared_Font::setAutoSizeMethod(\PHPExcel_Shared_Font::AUTOSIZE_METHOD_EXACT);
$azRange = range('A', 'Z');
$rowNumber = 2;
$linkColumns = [];
foreach ($fieldList as $i => $name) {
$col = $azRange[$i];
$defs = $this->getInjection('metadata')->get(['entityDefs', $entityType, 'fields', $name]);
if (!$defs) {
$defs['type'] = 'base';
}
$label = $this->getInjection('language')->translate($name, 'fields', $entityType);
$sheet->setCellValue($col . $rowNumber, $label);
$sheet->getColumnDimension($col)->setAutoSize(true);
if ($defs['type'] == 'phone'
|| $defs['type'] == 'email'
|| $defs['type'] == 'url'
|| $defs['type'] == 'link'
|| $defs['type'] == 'linkParent'
|| $defs['type'] == 'belongsTo'
|| $defs['type'] == 'belongsToParent') {
$linkColumns[] = $col;
} else if ($name == 'name') {
if (in_array('id', $fieldList)) {
$linkColumns[] = $col;
}
}
}
$col = chr(ord($col) - 1);
$headerStyle = array(
'font' => array(
'bold' => true,
'size' => 12
)
);
$sheet->getStyle("A$rowNumber:$col$rowNumber")->applyFromArray($headerStyle);
$sheet->setAutoFilter("A$rowNumber:$col$rowNumber");
$rowNumber++;
foreach ($dataList as $row) {
$i = 0;
foreach ($fieldList as $i => $name) {
$col = $azRange[$i];
$defs = $this->getInjection('metadata')->get(['entityDefs', $entityType, 'fields', $name]);
if (!$defs) {
$defs['type'] = 'base';
}
$link = null;
if ($defs['type'] == 'link' || $defs['type'] == 'linkParent') {
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
} else if ($defs['type'] == 'belongsTo') {
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
} else if ($defs['type'] == 'belongsToParent') {
$sheet->setCellValue("$col$rowNumber", $row[$name.'Name']);
} else if ($defs['type'] == 'int') {
$sheet->setCellValue("$col$rowNumber", $row[$name] ?: 0);
} else if ($defs['type'] == 'personName') {
if (!empty($row['name'])) {
$sheet->setCellValue("$col$rowNumber", $row['name']);
} else {
$personName = '';
if (!empty($row['firstName'])) {
$personName .= $row['firstName'];
}
if (!empty($row['lastName'])) {
if (!empty($row['firstName'])) {
$personName .= ' ';
}
$personName .= $row['lastName'];
}
$sheet->setCellValue("$col$rowNumber", $personName);
}
} else if ($defs['type'] == 'date') {
if (isset($row[$name])) {
$sheet->setCellValue("$col$rowNumber", \PHPExcel_Shared_Date::PHPToExcel(strtotime($row[$name])));
}
} else if ($defs['type'] == 'datetime' || $defs['type'] == 'datetimeOptional') {
if (isset($row[$name])) {
$sheet->setCellValue("$col$rowNumber", \PHPExcel_Shared_Date::PHPToExcel(strtotime($row[$name])));
}
} else if ($defs['type'] == 'image') {
} else if ($defs['type'] == 'file') {
} else {
if (array_key_exists($name, $row)) {
$sheet->setCellValue("$col$rowNumber", $row[$name]);
}
}
$link = false;
if ($name == 'name') {
if (array_key_exists('id', $row)) {
$link = $this->getConfig()->getSiteUrl() . "/#".$entityType . "/view/" . $row['id'];
}
} else if ($defs['type'] == 'url') {
if (array_key_exists($name, $row) && filter_var($row[$name], FILTER_VALIDATE_URL)) {
$link = $row[$name];
}
} else if ($defs['type'] == 'link') {
if (array_key_exists($name.'Id', $row)) {
$foreignEntity = $this->getMetadata()->get(['entityDefs', $entityType, 'links', $name, 'entity']);
if ($foreignEntity) {
$link = $this->getConfig()->getSiteUrl() . "/#" . $foreignEntity. "/view/". $row[$name.'Id'];
}
}
} else if ($defs['type'] == 'linkParent') {
if (array_key_exists($name.'Id', $row) && array_key_exists($name.'Type', $row)) {
$link = $this->getConfig()->getSiteUrl() . "/#".$row[$name.'Type']."/view/". $row[$name.'Id'];
}
} else if ($defs['type'] == 'phone') {
if (array_key_exists($name, $row)) {
$link = "tel:".$row[$name];
}
} else if ($defs['type'] == 'email' && array_key_exists($name, $row)) {
if (array_key_exists($name, $row)) {
$link = "mailto:".$row[$name];
}
}
if ($link) {
$sheet->getCell("$col$rowNumber")->getHyperlink()->setUrl($link);
$sheet->getCell("$col$rowNumber")->getHyperlink()->setTooltip($link);
}
}
$rowNumber++;
}
foreach ($fieldList as $i => $name) {
$col = $azRange[$i];
$defs = $this->getInjection('metadata')->get(['entityDefs', $entityType, 'fields', $name]);
if (!$defs) {
$defs['type'] = 'base';
}
if ($col == 'A') {
$sheet->getStyle("A2:A$rowNumber")
->getNumberFormat()
->setFormatCode(\PHPExcel_Style_NumberFormat::FORMAT_TEXT);
} else {
switch($defs['type']) {
case 'currency': {
$sheet->getStyle($col.'3:'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode('£#,##0_-');
} break;
case 'int': {
$sheet->getStyle($col.'3:'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode('0');
} break;
case 'date': {
$sheet->getStyle($col.'3:'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode('dd/mm/yyyy');
} break;
case 'datetime': {
$sheet->getStyle($col.'3:'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode('h:mm dd/mm/yyyy');
} break;
case 'datetimeOptional': {
$sheet->getStyle($col.'3:'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode('h:mm dd/mm/yyyy');
} break;
default: {
$sheet->getStyle($col.'3:'.$col.$rowNumber)
->getNumberFormat()
->setFormatCode('@');
} break;
}
}
}
$linkStyle = [
'font' => [
'color' => ['rgb' => '0000FF'],
'underline' => 'single'
]
];
foreach ($linkColumns as $linkColumn) {
$sheet->getStyle($linkColumn.'3:'.$linkColumn.$rowNumber)->applyFromArray($linkStyle);
}
$tempOutput = tempnam('/tmp/', 'ESPO');
$objWriter = \PHPExcel_IOFactory::createWriter($phpExcel, 'Excel2007');
$objWriter->save($tempOutput);
$fp = fopen($tempOutput, 'r');
$xlsx = stream_get_contents($fp);
unlink($tempOutput);
return $xlsx;
}
}
@@ -1,6 +1,13 @@
{
"fields": {
"useCustomFieldList": "Custom Field List",
"fieldList": "Field List"
"exportAllFields": "Export all fields",
"fieldList": "Field List",
"format": "Format"
},
"options": {
"format": {
"csv": "CSV",
"xlsx": "XLSX (Excel)"
}
}
}
@@ -0,0 +1,20 @@
{
"formatList": [
"csv",
"xlsx"
],
"formatDefs": {
"csv": {
"mimeType": "text/csv",
"fileExtension": "csv"
},
"xlsx": {
"mimeType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"fileExtension": "xlsx"
}
},
"exportFormatClassNameMap": {
"csv": "\\Espo\\Core\\Export\\Csv",
"xlsx": "\\Espo\\Core\\Export\\Xlsx"
}
}
+40 -14
View File
@@ -53,7 +53,8 @@ class Record extends \Espo\Core\Services\Base
'fileManager',
'selectManagerFactory',
'preferences',
'fileStorageManager'
'fileStorageManager',
'injectableFactory'
);
protected $getEntityBeforeUpdate = false;
@@ -1271,6 +1272,16 @@ class Record extends \Espo\Core\Services\Base
public function export(array $params)
{
if (array_key_exists('format', $params)) {
$format = $params['format'];
} else {
$format = 'csv';
}
if (!in_array($format, $this->getMetadata()->get(['app', 'export', 'formatList']))) {
throw new Error('Not supported export format.');
}
if (array_key_exists('collection', $params)) {
$collection = $params['collection'];
} else {
@@ -1364,31 +1375,46 @@ class Record extends \Espo\Core\Services\Base
$arr[] = $row;
}
$delimiter = $this->getPreferences()->get('exportDelimiter');
if (empty($delimiter)) {
$delimiter = $this->getConfig()->get('exportDelimiter', ';');
if (is_null($attributeList)) {
$attributeList = [];
}
$fp = fopen('php://temp', 'w');
fputcsv($fp, array_keys($arr[0]), $delimiter);
foreach ($arr as $row) {
fputcsv($fp, $row, $delimiter);
if (!array_key_exists('fieldList', $params)) {
$fieldList = array_keys($fieldList);
array_unshift($fieldList, 'id');
} else {
$fieldList = $params['fieldList'];
}
rewind($fp);
$csv = stream_get_contents($fp);
fclose($fp);
$fileName = "Export_{$this->entityType}.csv";
$mimeType = $this->getMetadata()->get(['app', 'export', 'formatDefs', $format, 'mimeType']);
$fileExtension = $this->getMetadata()->get(['app', 'export', 'formatDefs', $format, 'fileExtension']);
$fileName = "Export_{$this->entityType}." . $fileExtension;
$className = $this->getMetadata()->get(['app', 'export', 'exportFormatClassNameMap', $format]);
if (empty($className)) {
throw new Error();
}
$exportObj = $this->getInjection('injectableFactory')->createByClassName($className);
$exportParams = array(
'attributeList' => $attributeList,
'fileName ' => $fileName
);
$exportParams['fieldList'] = $fieldList;
if (array_key_exists('exportName', $params)) {
$exportParams['exportName'] = $params['exportName'];
}
$contents = $exportObj->process($this->entityType, $exportParams, $arr);
$attachment = $this->getEntityManager()->getEntity('Attachment');
$attachment->set('name', $fileName);
$attachment->set('role', 'Export File');
$attachment->set('type', 'text/csv');
$attachment->set('type', $mimeType);
$this->getEntityManager()->saveEntity($attachment);
if (!empty($attachment->id)) {
$this->getInjection('fileStorageManager')->putContents($attachment, $csv);
$this->getInjection('fileStorageManager')->putContents($attachment, $contents);
// TODO cron job to remove file
return $attachment->id;
}
+11 -4
View File
@@ -1,7 +1,14 @@
<div class="cell">
<label class="control-label" data-name="useCustomFieldList">{{translate 'useCustomFieldList' category='fields' scope='Export'}}</label>
<div class="field" data-name="useCustomFieldList">{{{useCustomFieldList}}}</div>
<div class="cell form-group" data-name="format">
<label class="control-label" data-name="format">{{translate 'format' category='fields' scope='Export'}}</label>
<div class="field" data-name="format">{{{format}}}</div>
</div>
<div class="cell">
<div class="cell form-group" data-name="exportAllFields">
<label class="control-label" data-name="exportAllFields">{{translate 'exportAllFields' category='fields' scope='Export'}}</label>
<div class="field" data-name="exportAllFields">{{{exportAllFields}}}</div>
</div>
<div class="cell form-group" data-name="fieldList">
<label class="control-label" data-name="fieldList">{{translate 'fieldList' category='fields' scope='Export'}}</label>
<div class="field" data-name="fieldList">{{{fieldList}}}</div>
</div>
+8 -3
View File
@@ -59,7 +59,11 @@ Espo.define('views/export/modals/export', ['views/modal', 'model'], function (De
if (this.options.fieldList) {
this.model.set('fieldList', this.options.fieldList);
this.model.set('useCustomFieldList', true);
this.model.set('exportAllFields', false);
this.model.set('format', 'csv');
} else {
this.model.set('exportAllFields', true);
this.model.set('format', 'csv');
}
this.createView('record', 'views/export/record/record', {
@@ -75,10 +79,11 @@ Espo.define('views/export/modals/export', ['views/modal', 'model'], function (De
if (this.getView('record').validate()) return;
var returnData = {
useCustomFieldList: data.useCustomFieldList
exportAllFields: data.exportAllFields,
format: data.format
};
if (data.useCustomFieldList) {
if (!data.exportAllFields) {
var attributeList = [];
data.fieldList.forEach(function (item) {
if (item === 'id') {
+13 -7
View File
@@ -64,7 +64,7 @@ Espo.define('views/export/record/record', 'views/record/base', function (Dep) {
translatedOptions[item] = this.getLanguage().translate(item, 'fields', this.scope);
}, this);
this.createField('useCustomFieldList', 'views/fields/bool', {
this.createField('exportAllFields', 'views/fields/bool', {
});
var setFieldList = this.model.get('fieldList') || [];
@@ -79,7 +79,6 @@ Espo.define('views/export/record/record', 'views/record/base', function (Dep) {
var foreignScope = this.getMetadata().get(['entityDefs', this.scope, 'links', arr[0], 'entity']);
if (!foreignScope) return;
translatedOptions[item] = this.getLanguage().translate(arr[0], 'links', this.scope) + '.' + this.getLanguage().translate(arr[1], 'fields', foreignScope);
}, this);
@@ -89,17 +88,24 @@ Espo.define('views/export/record/record', 'views/record/base', function (Dep) {
options: fieldList
});
this.controlVisibility();
this.listenTo(this.model, 'change:useCustomFieldList', function () {
this.controlVisibility();
this.createField('format', 'views/fields/enum', {
options: this.getMetadata().get('app.export.formatList')
});
this.controlAllFields();
this.listenTo(this.model, 'change:exportAllFields', function () {
this.controlAllFields();
}, this);
},
controlVisibility: function () {
if (this.model.get('useCustomFieldList')) {
controlAllFields: function () {
if (!this.model.get('exportAllFields')) {
this.showField('fieldList');
//this.setFieldOptionList('format', this.getMetadata().get('app.export.formatList'));
} else {
this.hideField('fieldList');
//this.model.set('format', 'csv');
//this.setFieldOptionList('format', ['csv']);
}
}
+2 -1
View File
@@ -359,10 +359,11 @@ Espo.define('views/record/list', 'view', function (Dep) {
this.createView('dialogExport', 'views/export/modals/export', o, function (view) {
view.render();
this.listenToOnce(view, 'proceed', function (dialogData) {
if (dialogData.useCustomFieldList) {
if (!dialogData.exportAllFields) {
data.attributeList = dialogData.attributeList;
data.fieldList = dialogData.fieldList;
}
data.format = dialogData.format;
this.ajaxPostRequest(url, data).then(function (data) {
if ('id' in data) {
window.location = this.getBasePath() + '?entryPoint=download&id=' + data.id;
+2 -1
View File
@@ -13,7 +13,8 @@
"zendframework/zend-servicemanager": "2.6.0",
"tecnickcom/tcpdf": "^6.2",
"php-mime-mail-parser/php-mime-mail-parser": "^2.5",
"zbateson/mail-mime-parser": "^0.4.1"
"zbateson/mail-mime-parser": "^0.4.1",
"phpoffice/phpexcel": "^1.8"
},
"autoload": {
"psr-0": {
Generated
+59 -2
View File
@@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "6e1e3327b6081f402280ed7ad0c324f3",
"content-hash": "c98074c42af6b9e3396ae058bc602bea",
"hash": "064c61d7e728851cad5d703c85ade74d",
"content-hash": "02c2bb32ee1f881379ee82f5d375419d",
"packages": [
{
"name": "composer/semver",
@@ -766,6 +766,63 @@
],
"time": "2016-10-07 16:46:22"
},
{
"name": "phpoffice/phpexcel",
"version": "1.8.1",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PHPExcel.git",
"reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPOffice/PHPExcel/zipball/372c7cbb695a6f6f1e62649381aeaa37e7e70b32",
"reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32",
"shasum": ""
},
"require": {
"ext-xml": "*",
"ext-xmlwriter": "*",
"php": ">=5.2.0"
},
"type": "library",
"autoload": {
"psr-0": {
"PHPExcel": "Classes/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL"
],
"authors": [
{
"name": "Maarten Balliauw",
"homepage": "http://blog.maartenballiauw.be"
},
{
"name": "Mark Baker"
},
{
"name": "Franck Lefevre",
"homepage": "http://blog.rootslabs.net"
},
{
"name": "Erik Tilt"
}
],
"description": "PHPExcel - OpenXML - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
"homepage": "http://phpexcel.codeplex.com",
"keywords": [
"OpenXML",
"excel",
"php",
"spreadsheet",
"xls",
"xlsx"
],
"time": "2015-05-01 07:00:55"
},
{
"name": "psr/log",
"version": "1.0.0",