Completed
Push — master ( 01d8d1...58ca4d )
by Simon
01:47
created

PartialSubmissionJob::getTomorrow()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 7

Duplication

Lines 10
Ratio 100 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 10
loc 10
rs 9.4285
cc 1
eloc 7
nc 1
nop 0
1
<?php
2
3
namespace Firesphere\PartialUserforms\Jobs;
4
5
use DateInterval;
6
use DateTime;
7
use Firesphere\PartialUserforms\Models\PartialFieldSubmission;
8
use Firesphere\PartialUserforms\Models\PartialFormSubmission;
9
use Firesphere\PartialUserforms\Services\DateService;
10
use SilverStripe\Control\Director;
11
use SilverStripe\Control\Email\Email;
12
use SilverStripe\Core\Injector\Injector;
13
use SilverStripe\ORM\ArrayList;
14
use SilverStripe\ORM\DataList;
15
use SilverStripe\ORM\FieldType\DBDatetime;
16
use SilverStripe\SiteConfig\SiteConfig;
17
use SilverStripe\UserForms\Model\UserDefinedForm;
18
use Symbiote\QueuedJobs\Services\AbstractQueuedJob;
19
use Symbiote\QueuedJobs\Services\QueuedJobService;
20
21
/**
22
 * Class PartialSubmissionJob
23
 * @package Firesphere\PartialUserforms\Jobs
24
 */
25
class PartialSubmissionJob extends AbstractQueuedJob
26
{
27
28
    /**
29
     * The generated CSV files
30
     * @var array
31
     */
32
    protected $files = [];
33
34
    /**
35
     * @var SiteConfig
36
     */
37
    protected $config;
38
39
    /**
40
     * @var array
41
     */
42
    protected $addresses;
43
44
45
    /**
46
     * Prepare the data
47
     */
48
    public function setup()
49
    {
50
        parent::setup();
51
        $this->config = SiteConfig::current_site_config();
52
        $this->validateEmails();
53
    }
54
55
    /**
56
     * Only add valid email addresses
57
     */
58
    protected function validateEmails()
59
    {
60
        $email = $this->config->SendMailTo;
61
        $result = Email::is_valid_address($email);
62
        if ($result) {
63
            $this->addresses[] = $email;
64
        }
65
        if (strpos($email, ',') !== false) {
66
            $emails = explode(',', $email);
67
            foreach ($emails as $address) {
68
                $result = Email::is_valid_address(trim($address));
69
                if ($result) {
70
                    $this->addresses[] = trim($address);
71
                } else {
72
                    $this->addMessage($address . _t(__CLASS__ . '.invalidMail', ' is not a valid email address'));
73
                }
74
            }
75
        }
76
    }
77
78
    /**
79
     * @return string
80
     */
81
    public function getTitle()
82
    {
83
        return _t(__CLASS__ . '.Title', 'Export partial submissions to Email');
84
    }
85
86
    /**
87
     * Do some processing yourself!
88
     */
89
    public function process()
90
    {
91
        if (!$this->config->SendDailyEmail) {
92
            $this->addMessage(_t(__CLASS__ . '.NotActive', 'Daily exports are not enabled'));
93
            $this->isComplete = true;
94
95
            return;
96
        }
97
        if (!count($this->addresses)) {
98
            $this->addMessage(_t(__CLASS__ . '.EmailError', 'Can not process without valid email'));
99
            $this->isComplete = true;
100
101
            return;
102
        }
103
104
        $userDefinedForms = $this->getParents();
105
106
        /** @var UserDefinedForm $form */
107
        foreach ($userDefinedForms as $form) {
108
            $fileName = _t(__CLASS__ . '.Export', 'Export of ') .
109
                $form->Title . ' - ' .
110
                DBDatetime::now()->Format(DBDatetime::ISO_DATETIME);
111
            $file = '/tmp/' . $fileName . '.csv';
112
            $this->files[] = $file;
113
            $this->buildCSV($file, $form);
114
        }
115
116
        $this->sendEmail();
117
118
        $this->isComplete = true;
119
    }
120
121
    /**
122
     * @return ArrayList
123
     */
124
    protected function getParents()
125
    {
126
        /** @var DataList|PartialFormSubmission[] $exportForms */
127
        $allSubmissions = PartialFormSubmission::get()->filter(['IsSend' => false]);
128
        /** @var ArrayList|UserDefinedForm[] $parents */
129
        $userDefinedForms = ArrayList::create();
130
131
        /** @var PartialFormSubmission $submission */
132
        /** @noinspection ForeachSourceInspection */
133
        foreach ($allSubmissions as $submission) {
134
            // Due to having to support Elemental ElementForm, we need to manually get the parent
135
            // It's a bit a pickle, but it works
136
            $parentClass = $submission->ParentClass;
137
            $parent = $parentClass::get()->byID($submission->UserDefinedFormID);
138
            if ($parent &&
139
                $parent->ExportPartialSubmissions &&
140
                !$userDefinedForms->find('ID', $parent->ID)
141
            ) {
142
                $userDefinedForms->push($parent);
143
            }
144
            $submission->destroy();
145
        }
146
147
        return $userDefinedForms;
148
    }
149
150
    /**
151
     * @param $file
152
     * @param $form
153
     */
154
    protected function buildCSV($file, $form)
155
    {
156
        $resource = fopen($file, 'w+');
157
        /** @var DataList|PartialFormSubmission[] $submissions */
158
        $submissions = PartialFormSubmission::get()->filter(['UserDefinedFormID' => $form->ID]);
159
        $headerFields = $form
160
            ->Fields()
161
            ->exclude(['Name:PartialMatch' => 'EditableFormStep'])
162
            ->column('Title');
163
        fputcsv($resource, $headerFields);
164
165
        if ($submissions->count()) {
166
            $this->processSubmissions($form, $submissions, $resource);
167
        }
168
        fclose($resource);
169
    }
170
171
    /**
172
     * @param $form
173
     * @param $submissions
174
     * @param $resource
175
     */
176
    protected function processSubmissions($form, $submissions, $resource)
177
    {
178
        $editableFields = $form->Fields()->map('Name', 'Title')->toArray();
179
        $submitted = [];
180
        foreach ($submissions as $submission) {
181
            $values = $submission->PartialFields()->map('Name', 'Value')->toArray();
182
            $i = 0;
183
            foreach ($editableFields as $field => $title) {
184
                $submitted[] = '';
185
                if (isset($values[$field])) {
186
                    $submitted[] = $values[$field];
187
                }
188
                $i++;
189
            }
190
            fputcsv($resource, $submitted);
191
            $submission->IsSend = true;
192
            $submission->write();
193
        }
194
    }
195
196
    protected function sendEmail()
197
    {
198
        /** @var Email $mail */
199
        $mail = Email::create();
200
        $mail->setSubject('Partial form submissions of ' . DBDatetime::now()->Format(DBDatetime::ISO_DATETIME));
201
        foreach ($this->files as $file) {
202
            $mail->addAttachment($file);
203
        }
204
        $from = $this->config->SendMailFrom ?: 'site@' . Director::host();
205
206
        $mail->setFrom($from);
207
        foreach ($this->addresses as $address) {
208
            $mail->setTo($address);
209
            $mail->setBody('Please see attached CSV files');
210
            $mail->send();
211
        }
212
    }
213
214
    /**
215
     * @throws \Exception
216
     */
217
    public function afterComplete()
218
    {
219
        // Remove the files created in the process
220
        foreach ($this->files as $file) {
221
            unlink($file);
222
        }
223
224
        parent::afterComplete();
225
        if ($this->config->CleanupAfterSend) {
226
            $this->cleanupSubmissions();
227
        }
228
        if ($this->config->SendDailyEmail) {
229
            $this->createNewJob();
230
        }
231
    }
232
233
    /**
234
     * Remove submissions that have been sent out
235
     */
236
    protected function cleanupSubmissions()
237
    {
238
        /** @var DataList|PartialFormSubmission[] $forms */
239
        $forms = PartialFormSubmission::get()->filter(['IsSend' => true]);
240
        foreach ($forms as $form) {
241
            /** @var DataList|PartialFieldSubmission[] $fields */
242
            $fields = PartialFieldSubmission::get()->filter(['ID' => $form->PartialFields()->column('ID')]);
243
            $fields->removeAll();
244
            $form->delete();
245
            $form->destroy();
246
        }
247
    }
248
249
    /**
250
     * Create a new queued job for tomorrow
251
     * @throws \Exception
252
     */
253
    protected function createNewJob()
254
    {
255
        $job = new self();
256
        /** @var QueuedJobService $queuedJob */
257
        $queuedJob = Injector::inst()->get(QueuedJobService::class);
258
        $tomorrow = DateService::getTomorrow();
259
        $queuedJob->queueJob($job, $tomorrow->Format(DBDatetime::ISO_DATETIME));
0 ignored issues
show
Bug introduced by
The method Format does only exist in SilverStripe\ORM\FieldType\DBDatetime, but not in Firesphere\PartialUserforms\Services\DateService.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
260
    }
261
262
    /**
263
     * @return array
264
     */
265
    public function getMessages()
266
    {
267
        return $this->messages;
268
    }
269
270
    /**
271
     * @return array
272
     */
273
    public function getAddresses()
274
    {
275
        return $this->addresses;
276
    }
277
278
    /**
279
     * @param string $address
280
     */
281
    public function addAddress($address)
282
    {
283
        if (Email::is_valid_address($address)) {
284
            $this->addresses[] = $address;
285
        }
286
    }
287
288
    public function getConfig()
289
    {
290
        return $this->config;
291
    }
292
}
293