Completed
Push — master ( 1525fc...e38edc )
by Bram
06:54
created

Attendee::getName()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 3
nop 0
1
<?php
2
/**
3
 * Attendee.php
4
 *
5
 * @author Bram de Leeuw
6
 * Date: 09/03/17
7
 */
8
9
namespace Broarm\EventTickets;
10
11
use ArrayList;
12
use BaconQrCode;
13
use CalendarEvent;
14
use DataObject;
15
use Director;
16
use Dompdf\Dompdf;
17
use FieldList;
18
use File;
19
use Folder;
20
use Image;
21
use LiteralField;
22
use ManyManyList;
23
use Member;
24
use ReadonlyField;
25
use SSViewer;
26
use Tab;
27
use TabSet;
28
use TextField;
29
use ViewableData;
30
31
/**
32
 * Class Attendee
33
 *
34
 * @package Broarm\EventTickets
35
 *
36
 * @property string    Title
37
 * @property string    FirstName
38
 * @property string    Surname
39
 * @property string    Email
40
 * @property string    TicketCode
41
 * @property boolean   TicketReceiver
42
 * @property boolean   CheckedIn
43
 * @property FieldList SavableFields    Field to be set in AttendeesField
44
 *
45
 * @property int       TicketID
46
 * @property int       TicketQRCodeID
47
 * @property int       TicketFileID
48
 * @property int       ReservationID
49
 * @property int       EventID
50
 * @property int       MemberID
51
 *
52
 * @method Reservation Reservation()
53
 * @method Ticket Ticket()
54
 * @method Image TicketQRCode()
55
 * @method File TicketFile()
56
 * @method Member Member()
57
 * @method CalendarEvent|TicketExtension Event()
58
 * @method ManyManyList Fields()
59
 */
60
class Attendee extends DataObject
61
{
62
    /**
63
     * Set this to true when you want to have a QR code that opens the check in page and validates the code.
64
     * The validation is only done with proper authorisation so guest cannot check themselves in by mistake.
65
     * By default only the ticket number is translated to an QR code. (for use with USB QR scanners)
66
     *
67
     * @var bool
68
     */
69
    private static $qr_as_link = false;
1 ignored issue
show
Unused Code introduced by
The property $qr_as_link is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
70
71
    private static $default_fields = array(
1 ignored issue
show
Unused Code introduced by
The property $default_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
72
        'FirstName' => array(
73
            'FieldType' => 'TextField'
74
        ),
75
        'Surname' => array(
76
            'FieldType' => 'TextField'
77
        ),
78
        'Email' => array(
79
            'FieldType' => 'TextField'
80
        )
81
    );
82
83
    private static $table_fields = array(
1 ignored issue
show
Unused Code introduced by
The property $table_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
84
        'Title',
85
        'Email'
86
    );
87
88
    private static $db = array(
2 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
89
        'Title' => 'Varchar(255)',
90
        'TicketReceiver' => 'Boolean',
91
        'TicketCode' => 'Varchar(255)',
92
        'CheckedIn' => 'Boolean'
93
    );
94
95
    private static $indexes = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $indexes is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
96
        'TicketCode' => 'unique("TicketCode")'
97
    );
98
99
    private static $has_one = array(
2 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $has_one is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
100
        'Reservation' => 'Broarm\EventTickets\Reservation',
101
        'Ticket' => 'Broarm\EventTickets\Ticket',
102
        'Event' => 'CalendarEvent',
103
        'Member' => 'Member',
104
        'TicketQRCode' => 'Image',
105
        'TicketFile' => 'File'
106
    );
107
108
    private static $many_many = array(
2 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $many_many is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
109
        'Fields' => 'Broarm\EventTickets\AttendeeExtraField'
110
    );
111
112
    private static $many_many_extraFields = array(
2 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $many_many_extraFields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
113
        'Fields' => array(
114
            'Value' => 'Varchar(255)'
115
        )
116
    );
117
118
    private static $summary_fields = array(
2 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $summary_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
119
        'Title' => 'Name',
120
        'Ticket.Title' => 'Ticket',
121
        'TicketCode' => 'Ticket #',
122
        'CheckedIn.Nice' => 'Checked in',
123
    );
124
125
    public function getCMSFields()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
126
    {
127
        $fields = new FieldList(new TabSet('Root', $mainTab = new Tab('Main')));
128
129
        $fields->addFieldsToTab('Root.Main', array(
130
            ReadonlyField::create('TicketCode', _t('Attendee.Ticket', 'Ticket')),
131
            ReadonlyField::create('MyCheckedIn', _t('Attendee.CheckedIn', 'Checked in'), $this->dbObject('CheckedIn')->Nice())
132
        ));
133
134
        foreach ($this->Fields() as $field) {
135
            $fields->addFieldToTab(
136
                'Root.Main',
137
                ReadonlyField::create("{$field->FieldName}_Preview", $field->Title, $field->getValue())
138
            );
139
        }
140
141
        if ($this->TicketFile()->exists()) {
142
            $fields->addFieldToTab('Root.Main', $reservationFileField = ReadonlyField::create(
143
                'ReservationFile',
144
                _t('Attendee.Reservation', 'Reservation'),
145
                "<a class='readonly' href='{$this->TicketFile()->Link()}' target='_blank'>Download reservation PDF</a>"
146
            ));
147
            $reservationFileField->dontEscape = true;
148
        }
149
150
        $this->extend('updateCMSFields', $fields);
151
        return $fields;
152
    }
153
154
    /**
155
     * Utility method for fetching the default field, FirstName, value
156
     *
157
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
158
     */
159 View Code Duplication
    public function getFirstName()
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...
160
    {
161
        if ($firstName = $this->Fields()->find('FieldName', 'FirstName')) {
162
            return (string)$firstName->getField('Value');
163
        }
164
165
        return null;
166
    }
167
168
    /**
169
     * Utility method for fetching the default field, Surname, value
170
     *
171
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
172
     */
173 View Code Duplication
    public function getSurname()
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...
174
    {
175
        if ($surname = $this->Fields()->find('FieldName', 'Surname')) {
176
            return (string)$surname->getField('Value');
177
        }
178
179
        return null;
180
    }
181
182
    /**
183
     * Get the combined first and last nave for dispay on the ticket and attendee list
184
     *
185
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
186
     */
187
    public function getName()
188
    {
189
        $mainContact = $this->Reservation()->MainContact();
190
        if (!empty($this->getSurname())) {
191
            return trim("{$this->getFirstName()} {$this->getSurname()}");
192
        } elseif ($mainContact->exists() && !empty($mainContact->getSurname())) {
193
            return _t('Attendee.GUEST_OF', 'Guest of {name}', null, array('name' => $mainContact->getName()));
0 ignored issues
show
Documentation introduced by
array('name' => $mainContact->getName()) is of type array<string,?,{"name":"?"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
194
        } else {
195
            return null;
196
        }
197
    }
198
199
    /**
200
     * Utility method for fetching the default field, Email, value
201
     *
202
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
203
     */
204 View Code Duplication
    public function getEmail()
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...
205
    {
206
        if ($email = $this->Fields()->find('FieldName', 'Email')) {
207
            return (string)$email->getField('Value');
208
        }
209
210
        return null;
211
    }
212
213
    /**
214
     * Set the title and ticket code before writing
215
     */
216
    public function onBeforeWrite()
217
    {
218
        // Set the title of the attendee
219
        $this->Title = $this->getName();
220
221
        // Generate the ticket code
222
        if ($this->exists() && empty($this->TicketCode)) {
223
            $this->TicketCode = $this->generateTicketCode();
224
        }
225
226
        parent::onBeforeWrite();
227
    }
228
229
    /**
230
     * Delete any stray files before deleting the object
231
     */
232
    public function onBeforeDelete()
233
    {
234
        // If an attendee is deleted from the guest list remove it's qr code
235
        // after deleting the code it's not validatable anymore, simply here for cleanup
236
        if ($this->TicketQRCode()->exists()) {
237
            $this->TicketQRCode()->delete();
238
        }
239
240
        // cleanup the ticket file
241
        if ($this->TicketFile()->exists()) {
242
            $this->TicketFile()->delete();
243
        }
244
245
        parent::onBeforeDelete();
246
    }
247
248
    /**
249
     * Get the table fields for this attendee
250
     *
251
     * @return ArrayList
252
     */
253
    public function getTableFields()
254
    {
255
        $fields = new ArrayList();
256
        foreach (self::config()->get('table_fields') as $field) {
257
            $data = new ViewableData();
258
            $data->Header = _t("Attendee.$field", $field);
259
            $data->Value = $this->$field;
260
            $fields->add($data);
261
        }
262
        return $fields;
263
    }
264
265
    /**
266
     * Get the unnamespaced singular name for display in the CMS
267
     *
268
     * @return string
269
     */
270
    public function singular_name()
271
    {
272
        $name = explode('\\', parent::singular_name());
273
        return trim(end($name));
274
    }
275
276
    /**
277
     * Generate a unique ticket id
278
     * Serves as the base for the QR code and ticket file
279
     *
280
     * @return string
281
     */
282
    public function generateTicketCode()
283
    {
284
        return uniqid($this->ID);
285
    }
286
287
    /**
288
     * Create a QRCode for the attendee based on the Ticket code
289
     *
290
     * @param Folder $folder
291
     *
292
     * @return Image
293
     */
294
    public function createQRCode(Folder $folder)
295
    {
296
        $relativeFilePath = "/{$folder->Filename}{$this->TicketCode}.png";
297
        $absoluteFilePath = Director::baseFolder() . $relativeFilePath;
298
299
        if (!$image = Image::get()->find('Filename', $relativeFilePath)) {
300
            // Generate the QR code
301
            $renderer = new BaconQrCode\Renderer\Image\Png();
302
            $renderer->setHeight(256);
303
            $renderer->setWidth(256);
304
            $writer = new BaconQrCode\Writer($renderer);
305
            if (self::config()->get('qr_as_link')) {
306
                $writer->writeFile($this->Event()->AbsoluteLink("checkin/$this->TicketCode"), $absoluteFilePath);
307
            } else {
308
                $writer->writeFile($this->TicketCode, $absoluteFilePath);
309
            }
310
311
            // Store the image in an image object
312
            $image = Image::create();
313
            $image->ParentID = $folder->ID;
314
            $image->OwnerID = (Member::currentUser()) ? Member::currentUser()->ID : 0;
315
            $image->Title = $this->TicketCode;
316
            $image->setFilename($relativeFilePath);
317
            $image->write();
318
319
            // Attach the QR code to the Attendee
320
            $this->TicketQRCodeID = $image->ID;
321
            $this->write();
322
        }
323
324
        return $image;
325
    }
326
327
    /**
328
     * Creates a printable ticket for the attendee
329
     *
330
     * @param Folder $folder
331
     *
332
     * @return File
333
     */
334
    public function createTicketFile(Folder $folder)
335
    {
336
        // Find or make a folder
337
        $relativeFilePath = "/{$folder->Filename}{$this->TicketCode}.pdf";
338
        $absoluteFilePath = Director::baseFolder() . $relativeFilePath;
339
340
        if (!$file = File::get()->find('Filename', $relativeFilePath)) {
341
            $file = File::create();
342
            $file->ParentID = $folder->ID;
343
            $file->OwnerID = (Member::currentUser()) ? Member::currentUser()->ID : 0;
344
            $file->Title = $this->TicketCode;
345
            $file->setFilename($relativeFilePath);
346
            $file->write();
347
        }
348
349
        // Set the template and parse the data
350
        $template = new SSViewer('PrintableTicket');
351
        $html = $template->process($this->data());// getViewableData());
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
352
353
        // Create a DomPDF instance
354
        $domPDF = new Dompdf();
355
        $domPDF->loadHtml($html);
356
        $domPDF->setPaper('A4');
357
        $domPDF->getOptions()->setDpi(150);
358
        $domPDF->render();
359
360
        // Save the pdf stream as a file
361
        file_put_contents($absoluteFilePath, $domPDF->output());
362
363
        // Attach the ticket file to the Attendee
364
        $this->TicketFileID = $file->ID;
365
        $this->write();
366
367
        return $file;
368
    }
369
370
    public function canView($member = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
371
    {
372
        return $this->Reservation()->canView($member);
373
    }
374
375
    public function canEdit($member = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
376
    {
377
        return $this->Reservation()->canEdit($member);
378
    }
379
380
    public function canDelete($member = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
381
    {
382
        return $this->Reservation()->canDelete($member);
383
    }
384
385
    public function canCreate($member = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
386
    {
387
        return $this->Reservation()->canCreate($member);
388
    }
389
}
390