Completed
Push — master ( 8849ee...a1b70f )
by Andreas
24:35
created

render_csv()   B

Complexity

Conditions 11
Paths 24

Size

Total Lines 47
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 0
Metric Value
cc 11
eloc 30
nc 24
nop 0
dl 0
loc 47
ccs 0
cts 31
cp 0
crap 132
rs 7.3166
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @package midcom.baseclasses
4
 * @author The Midgard Project, http://www.midgard-project.org
5
 * @copyright The Midgard Project, http://www.midgard-project.org
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
use midcom\datamanager\datamanager;
10
use Symfony\Component\HttpFoundation\StreamedResponse;
11
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
12
13
/**
14
 * Generic CSV export handler baseclass
15
 *
16
 * @package midcom.baseclasses
17
 */
18
abstract class midcom_baseclasses_components_handler_dataexport extends midcom_baseclasses_components_handler
19
{
20
    /**
21
     * The Datamanager of the objects to export.
22
     *
23
     * @var datamanager[] Array of datamanager instances
24
     */
25
    private $_datamanagers = [];
26
27
    /**
28
     * Flag indicating whether or not the GUID of the first type should be included in exports.
29
     *
30
     * @var boolean
31
     */
32
    public $include_guid = true;
33
34
    /**
35
     * Flag indicating whether or not totals for number fields should be generated
36
     *
37
     * @var boolean
38
     */
39
    public $include_totals = false;
40
41
    /**
42
     * @var array
43
     */
44
    public $csv = [];
45
46
    protected $_schema;
47
48
    private $_rows = [];
49
50
    private $_totals = [];
51
52
    private $schemas = [];
53
54
    /**
55
     * @param mixed $handler_id The ID of the handler.
56
     * @param array $args The argument list.
57
     * @param array $data The local request data.
58
     * @return midcom\datamanager\schemadb[]
59
     */
60
    abstract public function _load_schemadbs($handler_id, array &$args, array &$data);
61
62
    /**
63
     * @param mixed $handler_id The ID of the handler.
64
     * @param array $args The argument list.
65
     * @param array $data The local request data.
66
     * @return array
67
     */
68
    abstract public function _load_data($handler_id, array &$args, array &$data);
69
70
    /**
71
     * @param mixed $handler_id The ID of the handler.
72
     * @param array $args The argument list.
73
     * @param array $data The local request data.
74
     */
75 1
    public function _handler_csv($handler_id, array $args, array &$data)
76
    {
77 1
        midcom::get()->auth->require_valid_user();
78 1
        $this->_load_datamanagers($this->_load_schemadbs($handler_id, $args, $data));
79
80 1
        if (empty($args[0])) {
81
            //We do not have filename in URL, generate one and redirect
82
            if (empty($data['filename'])) {
83
                $data['filename'] = preg_replace('/[^a-z0-9-]/i', '_', strtolower($this->_topic->extra)) . '_' . date('Y-m-d') . '.csv';
84
            }
85
            if (strpos(midcom_connection::get_url('uri'), '/', strlen(midcom_connection::get_url('uri')) - 2)) {
86
                $data['filename'] = '/' . $data['filename'];
87
            }
88
            return new midcom_response_relocate(midcom_connection::get_url('uri') . $data['filename']);
89
        }
90
91 1
        midcom::get()->disable_limits();
92
93 1
        $rows = $this->_load_data($handler_id, $args, $data);
94 1
        if (count($this->_datamanagers) == 1) {
95
            foreach ($rows as $row) {
96
                $this->_rows[] = [$row];
97
            }
98
        } else {
99 1
            $this->_rows = $rows;
100
        }
101
102 1
        if (empty($data['filename'])) {
103
            $data['filename'] = str_replace('.csv', '', $args[0]);
104
        }
105
106 1
        $this->_init_csv_variables();
107
108 1
        $response = new StreamedResponse([$this, 'render_csv']);
109 1
        $response->headers->set('Content-Disposition', $response->headers->makeDisposition(
110 1
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
111 1
            $data['filename'])
112
        );
113 1
        $response->headers->set('Content-Type', $this->csv['mimetype']);
114 1
        return $response;
115
    }
116
117
    public function render_csv()
118
    {
119
        // Make real sure we're dumping data live
120
        midcom::get()->cache->content->enable_live_mode();
121
122
        // Dump headers
123
        reset($this->_datamanagers);
124
        $first_type = key($this->_datamanagers);
125
        $multiple_types = count($this->_datamanagers) > 1;
126
        $row = [];
127
        if ($this->include_guid) {
128
            $row[] = $first_type . ' GUID';
129
        }
130
131
        foreach ($this->_datamanagers as $type => $datamanager) {
132
            foreach ($datamanager->get_schema($this->schemas[$type])->get('fields') as $name => $config) {
133
                $title = $config['title'];
134
                if (   $this->include_totals
135
                    && $config['type'] == 'number') {
136
                    $this->_totals[$type . '-' . $name] = 0;
137
                }
138
                $title = $this->_l10n->get($title);
139
                if ($multiple_types) {
140
                    $title = $this->_l10n->get($type) . ': ' . $title;
141
                }
142
                $row[] = $title;
143
            }
144
        }
145
        $output = fopen("php://output", 'w');
146
        $this->_print_row($row, $output);
147
148
        $this->_dump_rows($output);
149
150
        if ($this->include_totals) {
151
            $row = [];
152
            foreach ($this->_datamanagers as $type => $datamanager) {
153
                foreach ($datamanager->get_schema()->get('fields') as $name => $config) {
154
                    $value = "";
155
                    if ($config['type'] == 'number') {
156
                        $value = $this->_totals[$type . '-' . $name];
157
                    }
158
                    $row[] = $value;
159
                }
160
            }
161
            $this->_print_row($row, $output);
162
        }
163
        fclose($output);
0 ignored issues
show
Bug introduced by
It seems like $output can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

163
        fclose(/** @scrutinizer ignore-type */ $output);
Loading history...
164
    }
165
166
    /**
167
     * Internal helper, loads the datamanagers for the given types. Any error triggers a 500.
168
     */
169 1
    private function _load_datamanagers(array $schemadbs)
170
    {
171 1
        if (empty($this->_schema)) {
172
            throw new midcom_error('Export schema ($this->_schema) must be defined');
173
        }
174 1
        foreach ($schemadbs as $type => $schemadb) {
175 1
            $this->_datamanagers[$type] = new datamanager($schemadb);
176
177 1
            if ($schemadb->has($this->_schema)) {
178
                $this->schemas[$type] = $this->_schema;
179
            } else {
180 1
                $this->schemas[$type] = $schemadb->get_first()->get_name();
181
            }
182
        }
183 1
    }
184
185
    private function _dump_rows($output)
186
    {
187
        reset($this->_datamanagers);
188
        $first_type = key($this->_datamanagers);
189
        // Output each row
190
        foreach ($this->_rows as $num => $row) {
191
            $data = [];
192
            foreach ($this->_datamanagers as $type => $datamanager) {
193
                if (!array_key_exists($type, $row)) {
194
                    debug_add("row #{$num} does not have {$type} set", MIDCOM_LOG_INFO);
195
                    $target_size = count($datamanager->get_schema($this->schemas[$type])->get('fields')) + count($data);
196
                    $data = array_pad($data, $target_size, '');
197
                    continue;
198
                }
199
                $object =& $row[$type];
200
201
                $datamanager->set_storage($object, $this->schemas[$type]);
202
203
                if (   $this->include_guid
204
                    && $type == $first_type) {
205
                    $data[] = $object->guid;
206
                }
207
208
                $csvdata = $datamanager->get_content_csv();
209
                foreach ($datamanager->get_schema()->get('fields') as $fieldname => $config) {
210
                    if (   $this->include_totals
211
                        && $config['type'] == 'number') {
212
                        $this->_totals[$type . '-' . $fieldname] += $csvdata[$fieldname];
213
                    }
214
                    $data[] = $csvdata[$fieldname];
215
                }
216
            }
217
            $this->_print_row($data, $output);
218
        }
219
    }
220
221
    private function _print_row(array $row, $output)
222
    {
223
        $row = array_map([$this, 'encode_csv'], $row);
224
        fputcsv($output, $row, $this->csv['s'], $this->csv['q']);
225
    }
226
227 1
    private function _init_csv_variables()
228
    {
229
        // FIXME: Use global configuration
230 1
        $this->csv['s'] = $this->_config->get('csv_export_separator') ?: ';';
231 1
        $this->csv['q'] = $this->_config->get('csv_export_quote') ?: '"';
232 1
        $this->csv['mimetype'] = $this->_config->get('csv_export_content_type') ?: 'application/csv';
233 1
        $this->csv['charset'] = $this->_config->get('csv_export_charset');
234
235 1
        if (empty($this->csv['charset'])) {
236
            // Default to ISO-LATIN-15 (Latin-1 with EURO sign etc)
237 1
            $this->csv['charset'] = 'ISO-8859-15';
238 1
            if (   isset($_SERVER['HTTP_USER_AGENT'])
239 1
                && !preg_match('/Windows/i', $_SERVER['HTTP_USER_AGENT'])) {
240
                // Except when not on windows, then default to UTF-8
241 1
                $this->csv['charset'] = 'UTF-8';
242
            }
243
        }
244 1
    }
245
246
    private function encode_csv($data)
247
    {
248
        /* START: Quick'n'Dirty on-the-fly charset conversion */
249
        if ($this->csv['charset'] !== 'UTF-8') {
250
            $to_charset = "{$this->csv['charset']}//TRANSLIT";
251
            $stat = @iconv('UTF-8', $to_charset, $data);
252
            if (!empty($stat)) {
253
                $data = $stat;
254
            }
255
        }
256
        /* END: Quick'n'Dirty on-the-fly charset conversion */
257
258
        return $data;
259
    }
260
}
261