Completed
Push — master ( c7767b...f548dd )
by Daniel
11:31
created

GridFieldExportButton::getCsvSeparator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Adds an "Export list" button to the bottom of a {@link GridField}.
5
 *
6
 * @package forms
7
 * @subpackage fields-gridfield
8
 */
9
10
class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionProvider, GridField_URLHandler {
11
12
	/**
13
	 * @var array Map of a property name on the exported objects, with values being the column title in the CSV file.
14
	 * Note that titles are only used when {@link $csvHasHeader} is set to TRUE.
15
	 */
16
	protected $exportColumns;
17
18
	/**
19
	 * @var string
20
	 */
21
	protected $csvSeparator = ",";
22
23
	/**
24
	 * @var string
25
	 */
26
	protected $csvEnclosure = '"';
27
28
	/**
29
	 * @var boolean
30
	 */
31
	protected $csvHasHeader = true;
32
33
	/**
34
	 * Fragment to write the button to
35
	 */
36
	protected $targetFragment;
37
38
	/**
39
	 * @param string $targetFragment The HTML fragment to write the button into
40
	 * @param array $exportColumns The columns to include in the export
41
	 */
42
	public function __construct($targetFragment = "after", $exportColumns = null) {
43
		$this->targetFragment = $targetFragment;
44
		$this->exportColumns = $exportColumns;
0 ignored issues
show
Documentation Bug introduced by
It seems like $exportColumns can be null. However, the property $exportColumns is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
45
	}
46
47
	/**
48
	 * Place the export button in a <p> tag below the field
49
	 */
50
	public function getHTMLFragments($gridField) {
51
		$button = new GridField_FormAction(
52
			$gridField,
53
			'export',
54
			_t('TableListField.CSVEXPORT', 'Export to CSV'),
55
			'export',
56
			null
57
		);
58
		$button->setAttribute('data-icon', 'download-csv');
59
		$button->addExtraClass('no-ajax font-icon-down-circled action_export');
60
		$button->setForm($gridField->getForm());
61
		return array(
62
			$this->targetFragment => '<p class="grid-csv-button">' . $button->Field() . '</p>',
63
		);
64
	}
65
66
	/**
67
	 * export is an action button
68
	 */
69
	public function getActions($gridField) {
70
		return array('export');
71
	}
72
73
	public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
74
		if($actionName == 'export') {
75
			return $this->handleExport($gridField);
76
		}
77
	}
78
79
	/**
80
	 * it is also a URL
81
	 */
82
	public function getURLHandlers($gridField) {
83
		return array(
84
			'export' => 'handleExport',
85
		);
86
	}
87
88
	/**
89
	 * Handle the export, for both the action button and the URL
90
 	 */
91
	public function handleExport($gridField, $request = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
92
		$now = Date("d-m-Y-H-i");
93
		$fileName = "export-$now.csv";
94
95
		if($fileData = $this->generateExportFileData($gridField)){
96
			return SS_HTTPRequest::send_file($fileData, $fileName, 'text/csv');
97
		}
98
	}
99
100
	/**
101
	 * Return the columns to export
102
	 *
103
	 * @param GridField $gridField
104
	 *
105
	 * @return array
106
	 */
107
	protected function getExportColumnsForGridField(GridField $gridField) {
108
		if($this->exportColumns) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->exportColumns of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
109
			$exportColumns = $this->exportColumns;
110
		} else if($dataCols = $gridField->getConfig()->getComponentByType('GridFieldDataColumns')) {
111
			$exportColumns = $dataCols->getDisplayFields($gridField);
112
		} else {
113
			$exportColumns = singleton($gridField->getModelClass())->summaryFields();
114
		}
115
116
		return $exportColumns;
117
	}
118
119
	/**
120
	 * Generate export fields for CSV.
121
	 *
122
	 * @param GridField $gridField
123
	 * @return array
124
	 */
125
	public function generateExportFileData($gridField) {
126
		$csvColumns = $this->getExportColumnsForGridField($gridField);
127
		$fileData = array();
128
129
		if($this->csvHasHeader) {
130
			$headers = array();
131
132
			// determine the CSV headers. If a field is callable (e.g. anonymous function) then use the
133
			// source name as the header instead
134
			foreach($csvColumns as $columnSource => $columnHeader) {
135
				$headers[] = (!is_string($columnHeader) && is_callable($columnHeader)) ? $columnSource : $columnHeader;
136
			}
137
138
			$fileData[] = $headers;
139
		}
140
141
		//Remove GridFieldPaginator as we're going to export the entire list.
142
		$gridField->getConfig()->removeComponentsByType('GridFieldPaginator');
143
144
		$items = $gridField->getManipulatedList();
145
146
		// @todo should GridFieldComponents change behaviour based on whether others are available in the config?
147
		foreach($gridField->getConfig()->getComponents() as $component){
148
			if($component instanceof GridFieldFilterHeader || $component instanceof GridFieldSortableHeader) {
149
				$items = $component->getManipulatedData($gridField, $items);
150
			}
151
		}
152
153
		foreach($items->limit(null) as $item) {
154
			if(!$item->hasMethod('canView') || $item->canView()) {
155
				$columnData = array();
156
157
				foreach($csvColumns as $columnSource => $columnHeader) {
158
					if(!is_string($columnHeader) && is_callable($columnHeader)) {
159
						if($item->hasMethod($columnSource)) {
160
							$relObj = $item->{$columnSource}();
161
						} else {
162
							$relObj = $item->relObject($columnSource);
163
						}
164
165
						$value = $columnHeader($relObj);
166
					} else {
167
						$value = $gridField->getDataFieldValue($item, $columnSource);
168
169
						if($value === null) {
170
							$value = $gridField->getDataFieldValue($item, $columnHeader);
171
						}
172
					}
173
174
					$columnData[] = $value;
175
				}
176
177
				$fileData[] = $columnData;
178
			}
179
180
			if($item->hasMethod('destroy')) {
181
				$item->destroy();
182
			}
183
		}
184
185
		// Convert the $fileData array into csv by capturing fputcsv's output
186
		$csv = fopen('php://temp', 'r+');
187
		foreach($fileData as $line) {
188
			fputcsv($csv, $line, $this->csvSeparator, $this->csvEnclosure);
189
		}
190
		rewind($csv);
191
		return stream_get_contents($csv);
192
	}
193
194
	/**
195
	 * @return array
196
	 */
197
	public function getExportColumns() {
198
		return $this->exportColumns;
199
	}
200
201
	/**
202
	 * @param array
203
	 */
204
	public function setExportColumns($cols) {
205
		$this->exportColumns = $cols;
206
		return $this;
207
	}
208
209
	/**
210
	 * @return string
211
	 */
212
	public function getCsvSeparator() {
213
		return $this->csvSeparator;
214
	}
215
216
	/**
217
	 * @param string
218
	 */
219
	public function setCsvSeparator($separator) {
220
		$this->csvSeparator = $separator;
221
		return $this;
222
	}
223
224
	/**
225
	 * @return string
226
	 */
227
	public function getCsvEnclosure() {
228
		return $this->csvEnclosure;
229
	}
230
231
	/**
232
	 * @param string
233
	 */
234
	public function setCsvEnclosure($enclosure) {
235
		$this->csvEnclosure = $enclosure;
236
		return $this;
237
	}
238
239
	/**
240
	 * @return boolean
241
	 */
242
	public function getCsvHasHeader() {
243
		return $this->csvHasHeader;
244
	}
245
246
	/**
247
	 * @param boolean
248
	 */
249
	public function setCsvHasHeader($bool) {
250
		$this->csvHasHeader = $bool;
251
		return $this;
252
	}
253
254
255
}
256