Completed
Push — master ( 906332...92d568 )
by Hamish
02:01
created

GenerateCSVJob::getGridField()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 43
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 43
rs 8.8571
cc 2
eloc 23
nc 2
nop 0
1
<?php
2
3
class GenerateCSVJob extends AbstractQueuedJob {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4
5
    public function __construct() {
6
        $this->ID = uniqid();
7
        $this->Seperator = ',';
8
        $this->IncludeHeader = true;
9
        $this->HeadersOutput = false;
10
        $this->totalSteps = 1;
11
    }
12
13
    public function getJobType() {
14
        return QueuedJob::QUEUED;
15
    }
16
17
    public function getTitle() {
18
        return "Export a CSV of a Gridfield";
19
    }
20
21
    public function getSignature() {
22
        return md5(get_class($this) . '-' . $this->ID);
23
    }
24
25
    function setGridField($gridField) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
26
        $this->GridFieldName = $gridField->getName();
27
        $this->GridFieldURL = $gridField->Link();
28
    }
29
30
    function setSession($session) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
31
        // None of the gridfield actions are needed, and they make the stored session bigger, so pull
32
        // them out.
33
        $actionkeys = array_filter(array_keys($session), function ($i) {
34
            return strpos($i, 'gf_') === 0;
35
        });
36
37
        $session = array_diff_key($session, array_flip($actionkeys));
38
39
        // This causes problems with logins
40
        unset($session['HTTP_USER_AGENT']);
41
42
        $this->Session = $session;
43
    }
44
45
    function setColumns($columns) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
46
        $this->Columns = $columns;
47
    }
48
49
    function setSeparator($seperator) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
50
        $this->Separator = $seperator;
51
    }
52
53
    function setIncludeHeader($includeHeader) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
54
        $this->IncludeHeader = $includeHeader;
55
    }
56
57
    protected function getGridField() {
58
        $session = $this->Session;
59
60
        // Store state in session, and pass ID to client side.
61
        $state = array(
62
            'grid'       => $this->GridFieldName,
63
            'actionName' => 'findgridfield',
64
            'args'       => null
65
        );
66
67
        // Ensure $id doesn't contain only numeric characters
68
        $id = 'gf_' . substr(md5(serialize($state)), 0, 8);
69
70
        // Add new form action into session for GridField to find when Director::test is called below
71
        $session[$id] = $state;
72
        $session['SecurityID'] = '1';
73
74
        // Construct the URL
75
        $actionKey = 'action_gridFieldAlterAction?' . http_build_query(['StateID' => $id]);
76
        $actionValue = 'Find Gridfield';
77
78
        $url = $this->GridFieldURL . '?' .http_build_query([
79
            $actionKey => $actionValue,
80
            'SecurityID' => 1
81
        ]);
82
83
        // Restore into the current session the user the job is exporting as
84
        Session::set("loggedInAs", $session['loggedInAs']);
85
86
        // Then make a sub-query that should return a special SS_HTTPResponse with the gridfield object
87
        $res = Director::test($url, null, new Session($session), 'GET');
88
89
        // Great, it did, we can return it
90
        if ($res instanceof GridFieldQueuedExportButton_Response) {
91
            $gridField = $res->getGridField();
92
            $gridField->getConfig()->removeComponentsByType('GridFieldPaginator');
93
            $gridField->getConfig()->removeComponentsByType('GridFieldPageCount');
94
95
            return $gridField;
96
        } else {
97
            user_error('Couldn\'t restore GridField', E_USER_ERROR);
98
        }
99
    }
100
101
    protected function outputHeader($gridField, $columns) {
102
        $fileData = '';
103
        $separator = $this->Separator;
104
105
        $headers = array();
106
107
        // determine the CSV headers. If a field is callable (e.g. anonymous function) then use the
108
        // source name as the header instead
109
        foreach ($columns as $columnSource => $columnHeader) {
110
            $headers[] = (!is_string($columnHeader) && is_callable($columnHeader)) ? $columnSource : $columnHeader;
111
        }
112
113
        $fileData .= "\"" . implode("\"{$separator}\"", array_values($headers)) . "\"";
114
        $fileData .= "\n";
115
116
        file_put_contents('/tmp/' . $this->getSignature() . '.csv', $fileData, FILE_APPEND);
117
    }
118
119
    protected function outputRows($gridField, $columns, $start, $count) {
120
        $fileData = '';
121
        $separator = $this->Separator;
122
123
        $items = $gridField->getManipulatedList();
124
        $items = $items->limit($count, $start);
125
126
        foreach ($items as $item) {
127
            if (!$item->hasMethod('canView') || $item->canView()) {
128
                $columnData = array();
129
130
                foreach ($columns as $columnSource => $columnHeader) {
131
                    if (!is_string($columnHeader) && is_callable($columnHeader)) {
132
                        if ($item->hasMethod($columnSource)) {
133
                            $relObj = $item->{$columnSource}();
134
                        } else {
135
                            $relObj = $item->relObject($columnSource);
136
                        }
137
138
                        $value = $columnHeader($relObj);
139
                    } else {
140
                        $value = $gridField->getDataFieldValue($item, $columnSource);
141
142
                        if ($value === null) {
143
                            $value = $gridField->getDataFieldValue($item, $columnHeader);
144
                        }
145
                    }
146
147
                    $value = str_replace(array("\r", "\n"), "\n", $value);
148
                    $columnData[] = '"' . str_replace('"', '""', $value) . '"';
149
                }
150
151
                $fileData .= implode($separator, $columnData);
152
                $fileData .= "\n";
153
            }
154
155
            if ($item->hasMethod('destroy')) {
156
                $item->destroy();
157
            }
158
        }
159
160
        file_put_contents('/tmp/' . $this->getSignature() . '.csv', $fileData, FILE_APPEND);
161
    }
162
163
164
    public function setup() {
165
        $gridField = $this->getGridField();
166
        $this->totalSteps = $gridField->getManipulatedList()->count();
167
    }
168
169
    /**
170
     * Generate export fields for CSV.
171
     *
172
     * @param GridField $gridField
0 ignored issues
show
Bug introduced by
There is no parameter named $gridField. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
173
     * @return array
174
     */
175
    public function process() {
176
        $gridField = $this->getGridField();
177
        $columns = $this->Columns ?: singleton($gridField->getModelClass())->summaryFields();
178
179
        if ($this->IncludeHeader && !$this->HeadersOutput) {
180
            $this->outputHeader($gridField, $columns);
181
            $this->HeadersOutput = true;
182
        }
183
184
        $this->outputRows($gridField, $columns, $this->currentStep, 100);
185
186
        $this->currentStep += 100;
187
188
        if ($this->currentStep >= $this->totalSteps) {
189
            $this->isComplete = true;
190
        }
191
    }
192
}
193