Completed
Push — master ( 29242b...64551a )
by Simon
01:44
created

PartialSubmissionJob::buildCSV()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 16
rs 9.4285
cc 2
eloc 11
nc 2
nop 2
1
<?php
2
3
namespace Firesphere\PartialUserforms\Jobs;
4
5
use DateInterval;
6
use DateTime;
7
use DNADesign\ElementalUserForms\Model\ElementForm;
8
use Firesphere\PartialUserforms\Models\PartialFieldSubmission;
9
use Firesphere\PartialUserforms\Models\PartialFormSubmission;
10
use SilverStripe\Control\Director;
11
use SilverStripe\Control\Email\Email;
12
use SilverStripe\Core\Injector\Injector;
13
use SilverStripe\Dev\Debug;
14
use SilverStripe\ORM\ArrayList;
15
use SilverStripe\ORM\DataList;
16
use SilverStripe\ORM\FieldType\DBDatetime;
17
use SilverStripe\SiteConfig\SiteConfig;
18
use SilverStripe\UserForms\Model\UserDefinedForm;
19
use Symbiote\QueuedJobs\Services\AbstractQueuedJob;
20
use Symbiote\QueuedJobs\Services\QueuedJobService;
21
22
/**
23
 * Class PartialSubmissionJob
24
 * @package Firesphere\PartialUserforms\Jobs
25
 */
26
class PartialSubmissionJob extends AbstractQueuedJob
27
{
28
29
    /**
30
     * The generated CSV files
31
     * @var array
32
     */
33
    protected $files = [];
34
35
    /**
36
     * @var SiteConfig
37
     */
38
    protected $config;
39
40
    /**
41
     * @var array
42
     */
43
    protected $addresses;
44
45
46
    /**
47
     * @return string
48
     */
49
    public function getTitle()
50
    {
51
        return _t(__CLASS__ . '.Title', 'Export partial submissions to Email');
52
    }
53
54
    /**
55
     * Do some processing yourself!
56
     */
57
    public function process()
58
    {
59
        $this->config = SiteConfig::current_site_config();
60
        $this->validateEmails();
61
62
        if (!$this->config->SendDailyEmail) {
63
            $this->addMessage(_t(__CLASS__ . '.NotActive', 'Daily exports are not enabled'));
64
            $this->isComplete = true;
65
66
            return;
67
        }
68
        if (!count($this->addresses)) {
69
            $this->addMessage(_t(__CLASS__ . '.EmailError', 'Can not process without valid email'));
70
            $this->isComplete = true;
71
72
            return;
73
        }
74
75
        $userDefinedForms = $this->getParents();
76
77
        /** @var UserDefinedForm $form */
78
        foreach ($userDefinedForms as $form) {
79
            $fileName = _t(__CLASS__ . '.Export', 'Export of ') .
80
                $form->Title . ' - ' .
81
                DBDatetime::now()->Format(DBDatetime::ISO_DATETIME);
82
            $file = '/tmp/' . $fileName . '.csv';
83
            $this->files[] = $file;
84
            $this->buildCSV($file, $form);
85
        }
86
87
        $this->sendEmail();
88
89
        $this->isComplete = true;
90
    }
91
92
    /**
93
     * Only add valid email addresses
94
     */
95
    protected function validateEmails()
96
    {
97
        $email = $this->config->SendMailTo;
98
        $result = Email::is_valid_address($email);
99
        if ($result) {
100
            $this->addresses[] = $email;
101
        }
102
        if (strpos($email, ',') !== false) {
103
            $emails = explode(',', $email);
104
            foreach ($emails as $address) {
105
                $result = Email::is_valid_address(trim($address));
106
                if ($result) {
107
                    $this->addresses[] = trim($address);
108
                } else {
109
                    $this->addMessage($address . _t(__CLASS__ . '.invalidMail', ' is not a valid email address'));
110
                }
111
            }
112
        }
113
    }
114
115
    /**
116
     * @return ArrayList
117
     */
118
    protected function getParents()
119
    {
120
        /** @var DataList|PartialFormSubmission[] $exportForms */
121
        $allSubmissions = PartialFormSubmission::get()->filter(['IsSend' => false]);
122
        /** @var ArrayList|UserDefinedForm[]|ElementForm[] $parents */
123
        $userDefinedForms = ArrayList::create();
124
125
        /** @var PartialFormSubmission $submission */
126
        /** @noinspection ForeachSourceInspection */
127
        foreach ($allSubmissions as $submission) {
128
            // Due to having to support Elemental ElementForm, we need to manually get the parent
129
            // It's a bit a pickle, but it works
130
            $parentClass = $submission->ParentClass;
131
            $parent = $parentClass::get()->byID($submission->UserDefinedFormID);
132
            if ($parent &&
133
                $parent->ExportPartialSubmissions &&
134
                !$userDefinedForms->find('ID', $parent->ID)
135
            ) {
136
                $userDefinedForms->push($parent);
137
            }
138
            $submission->destroy();
139
        }
140
141
        return $userDefinedForms;
142
    }
143
144
    /**
145
     * @param $file
146
     * @param $form
147
     */
148
    protected function buildCSV($file, $form)
149
    {
150
        $resource = fopen($file, 'w+');
151
        /** @var PartialFormSubmission $submissions */
152
        $submissions = PartialFormSubmission::get()->filter(['UserDefinedFormID' => $form->ID]);
153
        $headerFields = $form
154
            ->Fields()
155
            ->exclude(['Name:PartialMatch' => 'EditableFormStep'])
156
            ->column('Title');
157
        fputcsv($resource, $headerFields);
158
159
        if ($submissions->count()) {
0 ignored issues
show
Documentation Bug introduced by
The method count does not exist on object<Firesphere\Partia...\PartialFormSubmission>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
160
            $this->processSubmissions($form, $submissions, $resource);
161
        }
162
        fclose($resource);
163
    }
164
165
    /**
166
     * @param $form
167
     * @param $submissions
168
     * @param $resource
169
     */
170
    protected function processSubmissions($form, $submissions, $resource)
171
    {
172
        $editableFields = $form->Fields()->map('Name', 'Title')->toArray();
173
        $submitted = [];
174
        foreach ($submissions as $submission) {
175
            $values = $submission->PartialFields()->map('Name', 'Value')->toArray();
176
            $i = 0;
177
            foreach ($editableFields as $field => $title) {
178
                $submitted[] = '';
179
                if (isset($values[$field])) {
180
                    $submitted[] = $values[$field];
181
                }
182
                $i++;
183
            }
184
            fputcsv($resource, $submitted);
185
            $submission->IsSend = true;
186
            $submission->write();
187
        }
188
    }
189
190
    /**
191
     * @throws \Exception
192
     */
193
    public function afterComplete()
194
    {
195
        // Remove the files created in the process
196
        foreach ($this->files as $file) {
197
            unlink($file);
198
        }
199
200
        parent::afterComplete();
201
        if ($this->config->CleanupAfterSend) {
202
            $this->cleanupSubmissions();
203
        }
204
        if ($this->config->SendDailyEmail) {
205
            $this->createNewJob();
206
        }
207
    }
208
209
    /**
210
     * Remove submissions that have been sent out
211
     */
212
    protected function cleanupSubmissions()
213
    {
214
        /** @var DataList|PartialFormSubmission[] $forms */
215
        $forms = PartialFormSubmission::get()->filter(['IsSend' => true]);
216
        foreach ($forms as $form) {
217
            /** @var DataList|PartialFieldSubmission[] $fields */
218
            $fields = PartialFieldSubmission::get()->filter(['ID' => $form->PartialFields()->column('ID')]);
219
            $fields->removeAll();
220
            $form->delete();
221
            $form->destroy();
222
        }
223
    }
224
225
    /**
226
     * Create a new queued job for tomorrow
227
     * @throws \Exception
228
     */
229
    protected function createNewJob()
230
    {
231
        $job = new self();
232
        /** @var QueuedJobService $queuedJob */
233
        $queuedJob = Injector::inst()->get(QueuedJobService::class);
234
        $tomorrow = $this->getTomorrow();
235
        $queuedJob->queueJob($job, $tomorrow);
236
    }
237
238
    /**
239
     * @return DBDatetime|static
240
     * @throws \Exception
241
     */
242 View Code Duplication
    protected function getTomorrow()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
243
    {
244
        $dateTime = new DateTime(DBDatetime::now());
245
        $interval = new DateInterval('P1D');
246
        $tomorrow = $dateTime->add($interval);
247
        $dbDateTime = DBDatetime::create();
248
        $dbDateTime->setValue($tomorrow->format('Y-m-d 00:00:00'));
249
250
        return $dbDateTime;
251
    }
252
253
    /**
254
     * @return array
255
     */
256
    public function getMessages()
257
    {
258
        return $this->messages;
259
    }
260
261
    /**
262
     * @return array
263
     */
264
    public function getAddresses()
265
    {
266
        return $this->addresses;
267
    }
268
269
    public function addAddress($address)
270
    {
271
        if (Email::is_valid_address($address)) {
272
            $this->addresses[] = $address;
273
        }
274
    }
275
276
    protected function sendEmail()
277
    {
278
        /** @var Email $mail */
279
        $mail = Email::create();
280
        $mail->setSubject('Partial form submissions of ' . DBDatetime::now()->Format(DBDatetime::ISO_DATETIME));
281
        foreach ($this->files as $file) {
282
            $mail->addAttachment($file);
283
        }
284
285
286
        $from = $this->config->SendMailFrom ?: 'site@' . Director::host();
287
        $mail->setFrom($from);
288
        $mail->setTo($this->addresses);
289
        $mail->setBody('Please see attached CSV files');
290
        $mail->send();
291
    }
292
}
293