Completed
Push — master ( 1c5a06...2e51ef )
by Simon
08:14
created

PartialSubmissionJob::sendEmail()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 16
rs 9.4285
cc 3
eloc 10
nc 3
nop 0
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\Email\Email;
11
use SilverStripe\Core\Injector\Injector;
12
use SilverStripe\Dev\Debug;
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
     * @return string
47
     */
48
    public function getTitle()
49
    {
50
        return _t(__CLASS__ . '.Title', 'Export partial submissions to Email');
51
    }
52
53
    /**
54
     * Do some processing yourself!
55
     */
56
    public function process()
57
    {
58
        $this->config = SiteConfig::current_site_config();
59
        $this->validateEmails();
60
61
        if (!$this->config->SendDailyEmail ||
62
            !count($this->addresses)
63
        ) {
64
            $this->addMessage(_t(__CLASS__ . '.EmailError', 'Can not process without valid email'));
65
            $this->isComplete = true;
66
67
            return;
68
        }
69
70
        $userDefinedForms = $this->getParents();
71
72
        /** @var UserDefinedForm $form */
73
        foreach ($userDefinedForms as $form) {
74
            $fileName = _t(__CLASS__ . '.Export', 'Export of ') .
75
                $form->Title . ' - ' .
76
                DBDatetime::now()->Format(DBDatetime::ISO_DATETIME);
77
            $file = '/tmp/' . $fileName . '.csv';
78
            $this->files[] = $file;
79
            $this->buildCSV($file, $form);
80
        }
81
82
        $this->sendEmail();
83
84
85
        $this->isComplete = true;
86
    }
87
88
    /**
89
     * Only add valid email addresses
90
     */
91
    protected function validateEmails()
92
    {
93
        $email = $this->config->SendMailTo;
94
        $result = Email::is_valid_address($email);
95
        if ($result) {
96
            $this->addresses[] = $email;
97
        }
98
        if (strpos(',', $email) !== false) {
99
            $emails = explode(',', $email);
100
            foreach ($emails as $address) {
101
                $result = Email::is_valid_address(trim($address));
102
                if ($result) {
103
                    $this->addresses[] = trim($address);
104
                } else {
105
                    $this->addMessage($address . _t(__CLASS__ . '.invalidMail', ' is not a valid email address'));
106
                }
107
            }
108
        }
109
    }
110
111
    /**
112
     * @return ArrayList
113
     */
114
    protected function getParents()
115
    {
116
        /** @var DataList|PartialFormSubmission[] $exportForms */
117
        $allSubmissions = PartialFormSubmission::get()->filter(['IsSend' => false]);
118
        /** @var ArrayList|UserDefinedForm[]|ElementForm[] $parents */
119
        $userDefinedForms = ArrayList::create();
120
121
        /** @var PartialFormSubmission $submission */
122
        foreach ($allSubmissions as $submission) {
123
            // Due to having to support Elemental ElementForm, we need to manually get the parent
124
            // It's a bit a pickle, but it works
125
            $parentClass = $submission->ParentClass;
126
            $parent = $parentClass::get()->byID($submission->UserDefinedFormID);
127
            if ($parent &&
128
                $parent->ExportPartialSubmissions &&
129
                !$userDefinedForms->find('ID', $parent->ID)
130
            ) {
131
                $userDefinedForms->push($parent);
132
            }
133
            $submission->destroy();
134
        }
135
136
        return $userDefinedForms;
137
    }
138
139
    /**
140
     * @param $file
141
     * @param $form
142
     */
143
    protected function buildCSV($file, $form)
144
    {
145
        $resource = fopen($file, 'w+');
146
        /** @var PartialFormSubmission $submissions */
147
        $submissions = PartialFormSubmission::get()->filter(['UserDefinedFormID' => $form->ID]);
148
        $headerFields = $form
149
            ->Fields()
150
            ->exclude(['Name:PartialMatch' => 'EditableFormStep'])
151
            ->column('Title');
152
        fputcsv($resource, $headerFields);
153
154
        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...
155
            $this->processSubmissions($form, $submissions, $resource);
156
        }
157
        fclose($resource);
158
    }
159
160
    /**
161
     * @param $form
162
     * @param $submissions
163
     * @param $resource
164
     */
165
    protected function processSubmissions($form, $submissions, $resource)
166
    {
167
        $editableFields = $form->Fields()->map('Name', 'Title')->toArray();
168
        $submitted = [];
169
        foreach ($submissions as $submission) {
170
            $values = $submission->PartialFields()->map('Name', 'Value')->toArray();
171
            $i = 0;
172
            foreach ($editableFields as $field => $title) {
173
                $submitted[] = '';
174
                if (isset($values[$field])) {
175
                    $submitted[] = $values[$field];
176
                }
177
                $i++;
178
            }
179
            fputcsv($resource, $submitted);
180
            $submission->IsSend = true;
181
            $submission->write();
182
        }
183
    }
184
185
    /**
186
     * @throws \Exception
187
     */
188
    public function afterComplete()
189
    {
190
        // Remove the files created in the process
191
        foreach ($this->files as $file) {
192
            unlink($file);
193
        }
194
195
        parent::afterComplete();
196
        if ($this->config->CleanupAfterSend) {
197
            $this->cleanupSubmissions();
198
        }
199
        if ($this->config->SendDailyEmail) {
200
            $this->createNewJob();
201
        }
202
    }
203
204
    /**
205
     * Remove submissions that have been sent out
206
     */
207
    protected function cleanupSubmissions()
208
    {
209
        /** @var DataList|PartialFormSubmission[] $forms */
210
        $forms = PartialFormSubmission::get()->filter(['IsSend' => true]);
211
        foreach ($forms as $form) {
212
            /** @var DataList|PartialFieldSubmission[] $fields */
213
            $fields = PartialFieldSubmission::get()->filter(['ID' => $form->PartialFields()->column('ID')]);
214
            $fields->removeAll();
215
            $form->delete();
216
            $form->destroy();
217
        }
218
    }
219
220
    /**
221
     * Create a new queued job for tomorrow
222
     * @throws \Exception
223
     */
224
    protected function createNewJob()
225
    {
226
        $job = new self();
227
        /** @var QueuedJobService $queuedJob */
228
        $queuedJob = Injector::inst()->get(QueuedJobService::class);
229
        $tomorrow = $this->getTomorrow();
230
        $queuedJob->queueJob($job, $tomorrow);
231
    }
232
233
    /**
234
     * @return DBDatetime|static
235
     * @throws \Exception
236
     */
237 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...
238
    {
239
        $dateTime = new DateTime(DBDatetime::now());
240
        $interval = new DateInterval('P1D');
241
        $tomorrow = $dateTime->add($interval);
242
        $dbDateTime = DBDatetime::create();
243
        $dbDateTime->setValue($tomorrow->format('Y-m-d 00:00:00'));
244
245
        return $dbDateTime;
246
    }
247
248
    /**
249
     * @return array
250
     */
251
    public function getMessages()
252
    {
253
        return $this->messages;
254
    }
255
256
    /**
257
     * @return array
258
     */
259
    public function getAddresses()
260
    {
261
        return $this->addresses;
262
    }
263
264
    public function addAddress($address)
265
    {
266
        if (Email::is_valid_address($address)) {
267
            $this->addresses[] = $address;
268
        }
269
    }
270
271
    protected function sendEmail()
272
    {
273
        foreach ($this->addresses as $address) {
274
            /** @var Email $mail */
275
            $mail = Email::create();
276
            $mail->setSubject('Partial form submissions of ' . DBDatetime::now()->Format(DBDatetime::ISO_DATETIME));
277
            foreach ($this->files as $file) {
278
                $mail->addAttachment($file);
279
            }
280
281
            $mail->setFrom('[email protected]');
282
            $mail->setTo($address);
283
            $mail->setBody('Please see attached CSV files');
284
            Debug::dump($mail->send());
285
        }
286
    }
287
}
288