Completed
Push — master ( 996639...c6213a )
by Bram
02:34
created

Attendee::getName()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.2
c 0
b 0
f 0
cc 4
eloc 7
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 Member;
23
use ReadonlyField;
24
use SSViewer;
25
use Tab;
26
use TabSet;
27
use ViewableData;
28
29
/**
30
 * Class Attendee
31
 *
32
 * @package Broarm\EventTickets
33
 *
34
 * @property string    Title
35
 * @property string    FirstName
36
 * @property string    Surname
37
 * @property string    Email
38
 * @property string    TicketCode
39
 * @property boolean   TicketReceiver
40
 * @property boolean   CheckedIn
41
 * @property FieldList SavableFields    Field to be set in AttendeesField
42
 *
43
 * @property int       TicketID
44
 * @property int       TicketQRCodeID
45
 * @property int       TicketFileID
46
 * @property int       ReservationID
47
 * @property int       EventID
48
 * @property int       MemberID
49
 *
50
 * @method Reservation Reservation
51
 * @method Ticket Ticket
52
 * @method Image TicketQRCode
53
 * @method File TicketFile
54
 * @method Member Member
55
 * @method CalendarEvent|TicketExtension Event
56
 */
57
class Attendee extends DataObject
58
{
59
    /**
60
     * Set this to true when you want to have a QR code that opens the check in page and validates the code.
61
     * The validation is only done with proper authorisation so guest cannot check themselves in by mistake.
62
     * By default only the ticket number is translated to an QR code. (for use with USB QR scanners)
63
     *
64
     * @var bool
65
     */
66
    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...
67
68
    private static $savable_fields = array(
0 ignored issues
show
Unused Code introduced by
The property $savable_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...
69
        'FirstName' => 'TextField',
70
        'Surname' => 'TextField',
71
        'Email' => 'EmailField',
72
    );
73
74
    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...
75
        'Title',
76
        'Email'
77
    );
78
79
    private static $db = array(
1 ignored issue
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...
80
        'Title' => 'Varchar(255)',
81
        'FirstName' => 'Varchar(255)',
82
        'Surname' => 'Varchar(255)',
83
        'Email' => 'Varchar(255)',
84
        'TicketReceiver' => 'Boolean',
85
        'TicketCode' => 'Varchar(255)',
86
        'CheckedIn' => 'Boolean'
87
    );
88
89
    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...
90
        'TicketCode' => 'unique("TicketCode")'
91
    );
92
93
    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...
94
        'Reservation' => 'Broarm\EventTickets\Reservation',
95
        'Ticket' => 'Broarm\EventTickets\Ticket',
96
        'Event' => 'CalendarEvent',
97
        'Member' => 'Member',
98
        'TicketQRCode' => 'Image',
99
        'TicketFile' => 'File'
100
    );
101
102
    private static $summary_fields = array(
1 ignored issue
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...
103
        'Title' => 'Name',
104
        'Email' => 'Email',
105
        'TicketCode' => 'Ticket',
106
        'CheckedInSummary' => 'Checked in',
107
    );
108
109
    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...
110
    {
111
        $fields = new FieldList(new TabSet('Root', $mainTab = new Tab('Main')));
112
        $fields->addFieldsToTab('Root.Main', array(
113
            ReadonlyField::create('Title', _t('Attendee.Name', 'Name')),
114
            ReadonlyField::create('Email', _t('Attendee.Email', 'Email')),
115
            ReadonlyField::create('TicketCode', _t('Attendee.Ticket', 'Ticket')),
116
            ReadonlyField::create('MyCheckedIn', _t('Attendee.CheckedIn', 'Checked in'),
117
                $this->dbObject('CheckedIn')->Nice()),
118
            $reservationFileField = ReadonlyField::create(
119
                'ReservationFile',
120
                _t('Attendee.Reservation', 'Reservation'),
121
                "<a class='readonly' href='{$this->Reservation()->TicketFile()->Link()}' target='_blank'>Download reservation PDF</a>"
0 ignored issues
show
Documentation Bug introduced by
The method Reservation does not exist on object<Broarm\EventTickets\Attendee>? 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...
122
            )
123
        ));
124
        $reservationFileField->dontEscape = true;
125
        $this->extend('updateCMSFields', $fields);
126
        return $fields;
127
    }
128
129
    /**
130
     * Set the title and ticket code before writing
131
     */
132
    public function onBeforeWrite()
133
    {
134
        // Set the title of the attendee
135
        $this->Title = $this->getName();
136
137
        // Generate the ticket code
138
        if ($this->exists() && empty($this->TicketCode)) {
139
            $this->TicketCode = $this->generateTicketCode();
140
        }
141
142
        parent::onBeforeWrite();
143
    }
144
145
    /**
146
     * Delete any stray files before deleting the object
147
     */
148
    public function onBeforeDelete()
149
    {
150
        // If an attendee is deleted from the guest list remove it's qr code
151
        // after deleting the code it's not validatable anymore, simply here for cleanup
152
        if ($this->TicketQRCode()->exists()) {
0 ignored issues
show
Documentation Bug introduced by
The method TicketQRCode does not exist on object<Broarm\EventTickets\Attendee>? 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...
153
            $this->TicketQRCode()->delete();
0 ignored issues
show
Documentation Bug introduced by
The method TicketQRCode does not exist on object<Broarm\EventTickets\Attendee>? 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...
154
        }
155
156
        // cleanup the ticket file
157
        if ($this->TicketFile()->exists()) {
0 ignored issues
show
Bug introduced by
The method TicketFile() does not exist on Broarm\EventTickets\Attendee. Did you maybe mean createTicketFile()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
158
            $this->TicketFile()->delete();
0 ignored issues
show
Bug introduced by
The method TicketFile() does not exist on Broarm\EventTickets\Attendee. Did you maybe mean createTicketFile()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
159
        }
160
161
        parent::onBeforeDelete();
162
    }
163
164
    /**
165
     * Get the table fields for this attendee
166
     *
167
     * @return ArrayList
168
     */
169
    public function getTableFields()
170
    {
171
        $fields = new ArrayList();
172
        foreach (self::config()->get('table_fields') as $field) {
173
            $data = new ViewableData();
174
            $data->Header = _t("Attendee.$field", $field);
175
            $data->Value = $this->getField($field);
176
            $fields->add($data);
177
        }
178
        return $fields;
179
    }
180
181
    /**
182
     * Get the unnamespaced singular name for display in the CMS
183
     *
184
     * @return string
185
     */
186
    public function singular_name()
187
    {
188
        $name = explode('\\', parent::singular_name());
189
        return trim(end($name));
190
    }
191
192
    /**
193
     * Get the combined first and last nave for dispay on the ticket and attendee list
194
     *
195
     * @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...
196
     */
197
    public function getName()
198
    {
199
        if (!empty($this->FirstName)) {
200
            return trim("$this->FirstName $this->Surname");
201
        } elseif ($this->Reservation()->MainContact()->exists() && $mainContact = $this->Reservation()->MainContact()) {
0 ignored issues
show
Documentation Bug introduced by
The method Reservation does not exist on object<Broarm\EventTickets\Attendee>? 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...
202
            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...
203
        } else {
204
            return null;
205
        }
206
    }
207
208
    /**
209
     * Return the checked in state for use in grid fields
210
     *
211
     * @return LiteralField
212
     */
213
    public function getCheckedInSummary()
214
    {
215
        $checkedInNice = $this->dbObject('CheckedIn')->Nice();
216
        $checkedIn = $this->CheckedIn
217
            ? "<span style='color: #3adb76;'>$checkedInNice</span>"
218
            : "<span style='color: #cc4b37;'>$checkedInNice</span>";
219
220
        return new LiteralField('CheckedIn', $checkedIn);
221
    }
222
223
    /**
224
     * Generate a unique ticket id
225
     * Serves as the base for the QR code and ticket file
226
     *
227
     * @return string
228
     */
229
    public function generateTicketCode()
230
    {
231
        return uniqid($this->ID);
232
    }
233
234
    /**
235
     * Create a QRCode for the attendee based on the Ticket code
236
     *
237
     * @param Folder $folder
238
     *
239
     * @return Image
240
     */
241
    public function createQRCode(Folder $folder)
242
    {
243
        $relativeFilePath = "/{$folder->Filename}{$this->TicketCode}.png";
244
        $absoluteFilePath = Director::baseFolder() . $relativeFilePath;
245
246
        if (!$image = Image::get()->find('Filename', $relativeFilePath)) {
247
            // Generate the QR code
248
            $renderer = new BaconQrCode\Renderer\Image\Png();
249
            $renderer->setHeight(256);
250
            $renderer->setWidth(256);
251
            $writer = new BaconQrCode\Writer($renderer);
252
            if (self::config()->get('qr_as_link')) {
253
                $writer->writeFile($this->Event()->AbsoluteLink("checkin/$this->TicketCode"), $absoluteFilePath);
0 ignored issues
show
Documentation Bug introduced by
The method Event does not exist on object<Broarm\EventTickets\Attendee>? 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...
254
            } else {
255
                $writer->writeFile($this->TicketCode, $absoluteFilePath);
256
            }
257
258
            // Store the image in an image object
259
            $image = Image::create();
260
            $image->ParentID = $folder->ID;
261
            $image->OwnerID = (Member::currentUser()) ? Member::currentUser()->ID : 0;
262
            $image->Title = $this->TicketCode;
263
            $image->setFilename($relativeFilePath);
264
            $image->write();
265
266
            // Attach the QR code to the Attendee
267
            $this->TicketQRCodeID = $image->ID;
268
            $this->write();
269
        }
270
271
        return $image;
272
    }
273
274
    /**
275
     * Creates a printable ticket for the attendee
276
     *
277
     * @param Folder $folder
278
     *
279
     * @return File
280
     */
281
    public function createTicketFile(Folder $folder)
282
    {
283
        // Find or make a folder
284
        $relativeFilePath = "/{$folder->Filename}{$this->TicketCode}.pdf";
285
        $absoluteFilePath = Director::baseFolder() . $relativeFilePath;
286
287
        if (!$file = File::get()->find('Filename', $relativeFilePath)) {
288
            $file = File::create();
289
            $file->ParentID = $folder->ID;
290
            $file->OwnerID = (Member::currentUser()) ? Member::currentUser()->ID : 0;
291
            $file->Title = $this->TicketCode;
292
            $file->setFilename($relativeFilePath);
293
            $file->write();
294
        }
295
296
        // Set the template and parse the data
297
        $template = new SSViewer('PrintableTicket');
298
        $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...
299
300
        // Create a DomPDF instance
301
        $domPDF = new Dompdf();
302
        $domPDF->loadHtml($html);
303
        $domPDF->setPaper('A4');
304
        $domPDF->getOptions()->setDpi(150);
305
        $domPDF->render();
306
307
        // Save the pdf stream as a file
308
        file_put_contents($absoluteFilePath, $domPDF->output());
309
310
        // Attach the ticket file to the Attendee
311
        $this->TicketFileID = $file->ID;
312
        $this->write();
313
314
        return $file;
315
    }
316
317
    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...
318
    {
319
        return $this->Reservation()->canView($member);
0 ignored issues
show
Documentation Bug introduced by
The method Reservation does not exist on object<Broarm\EventTickets\Attendee>? 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...
320
    }
321
322
    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...
323
    {
324
        return $this->Reservation()->canEdit($member);
0 ignored issues
show
Documentation Bug introduced by
The method Reservation does not exist on object<Broarm\EventTickets\Attendee>? 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...
325
    }
326
327
    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...
328
    {
329
        return $this->Reservation()->canDelete($member);
0 ignored issues
show
Documentation Bug introduced by
The method Reservation does not exist on object<Broarm\EventTickets\Attendee>? 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...
330
    }
331
332
    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...
333
    {
334
        return $this->Reservation()->canCreate($member);
0 ignored issues
show
Documentation Bug introduced by
The method Reservation does not exist on object<Broarm\EventTickets\Attendee>? 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...
335
    }
336
}
337