Completed
Pull Request — master (#238)
by Luc
09:35 queued 04:39
created

Offer::syncLabels()   B

Complexity

Conditions 5
Paths 9

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
c 0
b 0
f 0
rs 8.8571
cc 5
eloc 7
nc 9
nop 1
1
<?php
2
3
namespace CultuurNet\UDB3\Offer;
4
5
use Broadway\EventSourcing\EventSourcedAggregateRoot;
6
use CultureFeed_Cdb_Item_Base;
7
use CultuurNet\UDB3\BookingInfo;
8
use CultuurNet\UDB3\ContactPoint;
9
use CultuurNet\UDB3\Label;
10
use CultuurNet\UDB3\LabelCollection;
11
use CultuurNet\UDB3\Media\Image;
12
use CultuurNet\UDB3\Offer\Commands\Image\AbstractUpdateImage;
13
use CultuurNet\UDB3\Language;
14
use CultuurNet\UDB3\Offer\Events\AbstractBookingInfoUpdated;
15
use CultuurNet\UDB3\Offer\Events\AbstractContactPointUpdated;
16
use CultuurNet\UDB3\Offer\Events\AbstractDescriptionTranslated;
17
use CultuurNet\UDB3\Offer\Events\AbstractDescriptionUpdated;
18
use CultuurNet\UDB3\Offer\Events\AbstractLabelAdded;
19
use CultuurNet\UDB3\Offer\Events\AbstractLabelDeleted;
20
use CultuurNet\UDB3\Offer\Events\AbstractOfferDeleted;
21
use CultuurNet\UDB3\Offer\Events\AbstractOrganizerDeleted;
22
use CultuurNet\UDB3\Offer\Events\AbstractOrganizerUpdated;
23
use CultuurNet\UDB3\Offer\Events\AbstractPriceInfoUpdated;
24
use CultuurNet\UDB3\Offer\Events\AbstractTypicalAgeRangeDeleted;
25
use CultuurNet\UDB3\Offer\Events\AbstractTypicalAgeRangeUpdated;
26
use CultuurNet\UDB3\Offer\Events\Image\AbstractImageAdded;
27
use CultuurNet\UDB3\Offer\Events\Image\AbstractImageRemoved;
28
use CultuurNet\UDB3\Offer\Events\Image\AbstractImageUpdated;
29
use CultuurNet\UDB3\Offer\Events\Image\AbstractMainImageSelected;
30
use CultuurNet\UDB3\Offer\Events\AbstractTitleTranslated;
31
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractApproved;
32
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractFlaggedAsDuplicate;
33
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractFlaggedAsInappropriate;
34
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractPublished;
35
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractRejected;
36
use CultuurNet\UDB3\PriceInfo\PriceInfo;
37
use Exception;
38
use ValueObjects\Identity\UUID;
39
use ValueObjects\String\String as StringLiteral;
40
41
abstract class Offer extends EventSourcedAggregateRoot
42
{
43
    const DUPLICATE_REASON = 'duplicate';
44
    const INAPPROPRIATE_REASON = 'inappropriate';
45
46
    /**
47
     * @var LabelCollection
48
     */
49
    protected $labels;
50
51
    /**
52
     * @var UUID[]
53
     */
54
    protected $mediaObjects = [];
55
56
    /**
57
     * @var UUID
58
     */
59
    protected $mainImageId;
60
61
    /**
62
     * @var string
63
     *
64
     * Organizer ids can come from UDB2 which does not strictly use UUIDs.
65
     */
66
    protected $organizerId;
67
68
    /**
69
     * @var WorkflowStatus
70
     */
71
    protected $workflowStatus;
72
73
    /**
74
     * @var StringLiteral|null
75
     */
76
    protected $rejectedReason;
77
78
    /**
79
     * @var PriceInfo
80
     */
81
    protected $priceInfo;
82
83
    /**
84
     * Offer constructor.
85
     */
86
    public function __construct()
87
    {
88
        $this->resetLabels();
89
    }
90
91
    /**
92
     * @return LabelCollection
93
     */
94
    public function getLabels()
95
    {
96
        return $this->labels;
97
    }
98
99
    /**
100
     * Get the id of the main image if one is selected for this offer.
101
     *
102
     * @return UUID|null
103
     */
104
    protected function getMainImageId()
105
    {
106
        return $this->mainImageId;
107
    }
108
109
    /**
110
     * @param Label $label
111
     */
112
    public function addLabel(Label $label)
113
    {
114
        if (!$this->labels->contains($label)) {
115
            $this->apply(
116
                $this->createLabelAddedEvent($label)
117
            );
118
        }
119
    }
120
121
    /**
122
     * @param Label $label
123
     */
124
    public function deleteLabel(Label $label)
125
    {
126
        if ($this->labels->contains($label)) {
127
            $this->apply(
128
                $this->createLabelDeletedEvent($label)
129
            );
130
        }
131
    }
132
133
    /**
134
     * @param LabelCollection $newLabelCollection
135
     */
136
    public function syncLabels(LabelCollection $newLabelCollection)
137
    {
138
        // Apply the label added, part of the new label collections but not the existing.
139
        foreach ($newLabelCollection->asArray() as $newLabel) {
140
            if (!$this->labels->contains($newLabel)) {
141
                $this->apply($this->createLabelAddedEvent($newLabel));
142
            }
143
        }
144
145
        // Apply the label removed, part of the existing label collection but not of the old.
146
        foreach ($this->labels->asArray() as $oldLabel) {
147
            if (!$newLabelCollection->contains($oldLabel)) {
148
                $this->apply($this->createLabelDeletedEvent($oldLabel));
149
            }
150
        }
151
    }
152
153
    /**
154
     * @param Language $language
155
     * @param StringLiteral $title
156
     */
157
    public function translateTitle(Language $language, StringLiteral $title)
158
    {
159
        $this->apply(
160
            $this->createTitleTranslatedEvent($language, $title)
161
        );
162
    }
163
164
    /**
165
     * @param Language $language
166
     * @param StringLiteral $description
167
     */
168
    public function translateDescription(Language $language, StringLiteral $description)
169
    {
170
        $this->apply(
171
            $this->createDescriptionTranslatedEvent($language, $description)
172
        );
173
    }
174
175
176
    /**
177
     * @param string $description
178
     */
179
    public function updateDescription($description)
180
    {
181
        $this->apply(
182
            $this->createDescriptionUpdatedEvent($description)
183
        );
184
    }
185
186
    /**
187
     * @param string $typicalAgeRange
188
     */
189
    public function updateTypicalAgeRange($typicalAgeRange)
190
    {
191
        $this->apply(
192
            $this->createTypicalAgeRangeUpdatedEvent($typicalAgeRange)
193
        );
194
    }
195
196
    public function deleteTypicalAgeRange()
197
    {
198
        $this->apply(
199
            $this->createTypicalAgeRangeDeletedEvent()
200
        );
201
    }
202
203
    /**
204
     * @param string $organizerId
205
     */
206
    public function updateOrganizer($organizerId)
207
    {
208
        if ($this->organizerId !== $organizerId) {
209
            $this->apply(
210
                $this->createOrganizerUpdatedEvent($organizerId)
211
            );
212
        }
213
    }
214
215
    /**
216
     * Delete the given organizer.
217
     *
218
     * @param string $organizerId
219
     */
220
    public function deleteOrganizer($organizerId)
221
    {
222
        if ($this->organizerId === $organizerId) {
223
            $this->apply(
224
                $this->createOrganizerDeletedEvent($organizerId)
225
            );
226
        }
227
    }
228
229
    /**
230
     * Updated the contact info.
231
     * @param ContactPoint $contactPoint
232
     */
233
    public function updateContactPoint(ContactPoint $contactPoint)
234
    {
235
        $this->apply(
236
            $this->createContactPointUpdatedEvent($contactPoint)
237
        );
238
    }
239
240
    /**
241
     * Updated the booking info.
242
     *
243
     * @param BookingInfo $bookingInfo
244
     */
245
    public function updateBookingInfo(BookingInfo $bookingInfo)
246
    {
247
        $this->apply(
248
            $this->createBookingInfoUpdatedEvent($bookingInfo)
249
        );
250
    }
251
252
    /**
253
     * @param PriceInfo $priceInfo
254
     */
255
    public function updatePriceInfo(PriceInfo $priceInfo)
256
    {
257
        if (is_null($this->priceInfo) || $priceInfo->serialize() !== $this->priceInfo->serialize()) {
258
            $this->apply(
259
                $this->createPriceInfoUpdatedEvent($priceInfo)
260
            );
261
        }
262
    }
263
264
    /**
265
     * @param AbstractPriceInfoUpdated $priceInfoUpdated
266
     */
267
    protected function applyPriceInfoUpdated(AbstractPriceInfoUpdated $priceInfoUpdated)
268
    {
269
        $this->priceInfo = $priceInfoUpdated->getPriceInfo();
270
    }
271
272
    /**
273
     * @param AbstractLabelAdded $labelAdded
274
     */
275
    protected function applyLabelAdded(AbstractLabelAdded $labelAdded)
276
    {
277
        $newLabel = $labelAdded->getLabel();
278
279
        if (!$this->labels->contains($newLabel)) {
280
            $this->labels = $this->labels->with($newLabel);
281
        }
282
    }
283
284
    /**
285
     * @param AbstractLabelDeleted $labelDeleted
286
     */
287
    protected function applyLabelDeleted(AbstractLabelDeleted $labelDeleted)
288
    {
289
        $this->labels = $this->labels->without(
290
            $labelDeleted->getLabel()
291
        );
292
    }
293
294
    protected function resetLabels()
295
    {
296
        $this->labels = new LabelCollection();
297
    }
298
299
    /**
300
     * @param Image $image
301
     * @return boolean
302
     */
303
    private function containsImage(Image $image)
304
    {
305
        $equalImages = array_filter(
306
            $this->mediaObjects,
307
            function ($existingMediaObjectId) use ($image) {
308
                return $image
309
                    ->getMediaObjectId()
310
                    ->sameValueAs($existingMediaObjectId);
311
            }
312
        );
313
314
        return !empty($equalImages);
315
    }
316
317
    /**
318
     * Add a new image.
319
     *
320
     * @param Image $image
321
     */
322
    public function addImage(Image $image)
323
    {
324
        if (!$this->containsImage($image)) {
325
            $this->apply(
326
                $this->createImageAddedEvent($image)
327
            );
328
        }
329
    }
330
331
    /**
332
     * @param AbstractUpdateImage $updateImageCommand
333
     */
334
    public function updateImage(AbstractUpdateImage $updateImageCommand)
335
    {
336
        $this->apply(
337
            $this->createImageUpdatedEvent($updateImageCommand)
338
        );
339
    }
340
341
    /**
342
     * Remove an image.
343
     *
344
     * @param Image $image
345
     */
346
    public function removeImage(Image $image)
347
    {
348
        if ($this->containsImage($image)) {
349
            $this->apply(
350
                $this->createImageRemovedEvent($image)
351
            );
352
        }
353
    }
354
355
    /**
356
     * Make an existing image of the item the main image.
357
     *
358
     * @param Image $image
359
     */
360
    public function selectMainImage(Image $image)
361
    {
362
        if (!$this->containsImage($image)) {
363
            throw new \InvalidArgumentException('You can not select a random image to be main, it has to be added to the item.');
364
        }
365
366
        if ($this->mainImageId !== $image->getMediaObjectId()) {
367
            $this->apply(
368
                $this->createMainImageSelectedEvent($image)
369
            );
370
        }
371
    }
372
373
    /**
374
     * Delete the offer.
375
     */
376
    public function delete()
377
    {
378
        $this->apply(
379
            $this->createOfferDeletedEvent()
380
        );
381
    }
382
383
    /**
384
     * @param CultureFeed_Cdb_Item_Base $cdbItem
385
     */
386
    protected function importWorkflowStatus(CultureFeed_Cdb_Item_Base $cdbItem)
387
    {
388
        try {
389
            $workflowStatus = WorkflowStatus::fromNative($cdbItem->getWfStatus());
390
        } catch (\InvalidArgumentException $exception) {
391
            $workflowStatus = WorkflowStatus::READY_FOR_VALIDATION();
392
        }
393
        $this->workflowStatus = $workflowStatus;
394
    }
395
396
    /**
397
     * Publish the offer when it has workflowstatus draft.
398
     * @param \DateTimeInterface $publicationDate
399
     */
400
    public function publish(\DateTimeInterface $publicationDate)
401
    {
402
        $this->guardPublish() ?: $this->apply(
403
            $this->createPublishedEvent($publicationDate)
404
        );
405
    }
406
407
    /**
408
     * @return bool
409
     * @throws Exception
410
     */
411 View Code Duplication
    private function guardPublish()
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...
412
    {
413
        if ($this->workflowStatus === WorkflowStatus::READY_FOR_VALIDATION()) {
414
            return true; // nothing left to do if the offer has already been published
415
        }
416
417
        if ($this->workflowStatus !== WorkflowStatus::DRAFT()) {
418
            throw new Exception('You can not publish an offer that is not draft');
419
        }
420
421
        return false;
422
    }
423
424
    /**
425
     * Approve the offer when it's waiting for validation.
426
     */
427
    public function approve()
428
    {
429
        $this->guardApprove() ?: $this->apply($this->createApprovedEvent());
430
    }
431
432
    /**
433
     * @return bool
434
     * @throws Exception
435
     */
436 View Code Duplication
    private function guardApprove()
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...
437
    {
438
        if ($this->workflowStatus === WorkflowStatus::APPROVED()) {
439
            return true; // nothing left to do if the offer has already been approved
440
        }
441
442
        if ($this->workflowStatus !== WorkflowStatus::READY_FOR_VALIDATION()) {
443
            throw new Exception('You can not approve an offer that is not ready for validation');
444
        }
445
446
        return false;
447
    }
448
449
    /**
450
     * Reject an offer that is waiting for validation with a given reason.
451
     * @param StringLiteral $reason
452
     */
453
    public function reject(StringLiteral $reason)
454
    {
455
        $this->guardRejection($reason) ?: $this->apply($this->createRejectedEvent($reason));
456
    }
457
458
    public function flagAsDuplicate()
459
    {
460
        $reason = new StringLiteral(self::DUPLICATE_REASON);
461
        $this->guardRejection($reason) ?: $this->apply($this->createFlaggedAsDuplicate());
462
    }
463
464
    public function flagAsInappropriate()
465
    {
466
        $reason = new StringLiteral(self::INAPPROPRIATE_REASON);
467
        $this->guardRejection($reason) ?: $this->apply($this->createFlaggedAsInappropriate());
468
    }
469
470
    /**
471
     * @param StringLiteral $reason
472
     * @return bool
473
     *  false when the offer can still be rejected, true when the offer is already rejected for the same reason
474
     * @throws Exception
475
     */
476
    private function guardRejection(StringLiteral $reason)
477
    {
478
        if ($this->workflowStatus === WorkflowStatus::REJECTED()) {
479
            if ($this->rejectedReason && $reason->sameValueAs($this->rejectedReason)) {
480
                return true; // nothing left to do if the offer has already been rejected for the same reason
481
            } else {
482
                throw new Exception('The offer has already been rejected for another reason: ' . $this->rejectedReason);
483
            }
484
        }
485
486
        if ($this->workflowStatus !== WorkflowStatus::READY_FOR_VALIDATION()) {
487
            throw new Exception('You can not reject an offer that is not ready for validation');
488
        }
489
490
        return false;
491
    }
492
493
    /**
494
     * @param AbstractPublished $published
495
     */
496
    protected function applyPublished(AbstractPublished $published)
0 ignored issues
show
Unused Code introduced by
The parameter $published is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
497
    {
498
        $this->workflowStatus = WorkflowStatus::READY_FOR_VALIDATION();
499
    }
500
501
    /**
502
     * @param AbstractApproved $approved
503
     */
504
    protected function applyApproved(AbstractApproved $approved)
0 ignored issues
show
Unused Code introduced by
The parameter $approved is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
505
    {
506
        $this->workflowStatus = WorkflowStatus::APPROVED();
507
    }
508
509
    /**
510
     * @param AbstractRejected $rejected
511
     */
512
    protected function applyRejected(AbstractRejected $rejected)
513
    {
514
        $this->rejectedReason = $rejected->getReason();
515
        $this->workflowStatus = WorkflowStatus::REJECTED();
516
    }
517
518
    /**
519
     * @param AbstractFlaggedAsDuplicate $flaggedAsDuplicate
520
     */
521
    protected function applyFlaggedAsDuplicate(AbstractFlaggedAsDuplicate $flaggedAsDuplicate)
0 ignored issues
show
Unused Code introduced by
The parameter $flaggedAsDuplicate is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
522
    {
523
        $this->rejectedReason = new StringLiteral(self::DUPLICATE_REASON);
524
        $this->workflowStatus = WorkflowStatus::REJECTED();
525
    }
526
527
    /**
528
     * @param AbstractFlaggedAsInappropriate $flaggedAsInappropriate
529
     */
530
    protected function applyFlaggedAsInappropriate(AbstractFlaggedAsInappropriate $flaggedAsInappropriate)
0 ignored issues
show
Unused Code introduced by
The parameter $flaggedAsInappropriate is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
531
    {
532
        $this->rejectedReason = new StringLiteral(self::INAPPROPRIATE_REASON);
533
        $this->workflowStatus = WorkflowStatus::REJECTED();
534
    }
535
536 View Code Duplication
    protected function applyImageAdded(AbstractImageAdded $imageAdded)
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...
537
    {
538
        $imageId = $imageAdded->getImage()->getMediaObjectId();
539
        $this->mediaObjects[] = $imageId;
540
541
        if (count($this->mediaObjects) === 1) {
542
            $this->mainImageId = $imageId;
543
        }
544
    }
545
546 View Code Duplication
    protected function applyImageRemoved(AbstractImageRemoved $imageRemoved)
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...
547
    {
548
        $this->mediaObjects = array_diff(
549
            $this->mediaObjects,
550
            [$imageRemoved->getImage()->getMediaObjectId()]
551
        );
552
553
        $oldestImageId = reset($this->mediaObjects);
554
        if ($oldestImageId) {
555
            $this->mainImageId = $oldestImageId;
556
        }
557
    }
558
559
    protected function applyMainImageSelected(AbstractMainImageSelected $mainImageSelected)
560
    {
561
        $this->mainImageId = $mainImageSelected->getImage()->getMediaObjectId();
562
    }
563
564
    protected function applyOrganizerUpdated(AbstractOrganizerUpdated $organizerUpdated)
565
    {
566
        $this->organizerId = $organizerUpdated->getOrganizerId();
567
    }
568
569
    protected function applyOrganizerDeleted(AbstractOrganizerDeleted $organizerDeleted)
0 ignored issues
show
Unused Code introduced by
The parameter $organizerDeleted is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
570
    {
571
        $this->organizerId = null;
572
    }
573
574
    /**
575
     * @param Label $label
576
     * @return AbstractLabelAdded
577
     */
578
    abstract protected function createLabelAddedEvent(Label $label);
579
580
    /**
581
     * @param Label $label
582
     * @return AbstractLabelDeleted
583
     */
584
    abstract protected function createLabelDeletedEvent(Label $label);
585
586
    /**
587
     * @param Language $language
588
     * @param StringLiteral $title
589
     * @return AbstractTitleTranslated
590
     */
591
    abstract protected function createTitleTranslatedEvent(Language $language, StringLiteral $title);
592
593
    /**
594
     * @param Language $language
595
     * @param StringLiteral $description
596
     * @return AbstractDescriptionTranslated
597
     */
598
    abstract protected function createDescriptionTranslatedEvent(Language $language, StringLiteral $description);
599
600
    /**
601
     * @param Image $image
602
     * @return AbstractImageAdded
603
     */
604
    abstract protected function createImageAddedEvent(Image $image);
605
606
    /**
607
     * @param Image $image
608
     * @return AbstractImageRemoved
609
     */
610
    abstract protected function createImageRemovedEvent(Image $image);
611
612
    /**
613
     * @param AbstractUpdateImage $updateImageCommand
614
     * @return AbstractImageUpdated
615
     */
616
    abstract protected function createImageUpdatedEvent(
617
        AbstractUpdateImage $updateImageCommand
618
    );
619
620
    /**
621
     * @param Image $image
622
     * @return AbstractMainImageSelected
623
     */
624
    abstract protected function createMainImageSelectedEvent(Image $image);
625
626
    /**
627
     * @return AbstractOfferDeleted
628
     */
629
    abstract protected function createOfferDeletedEvent();
630
631
    /**
632
     * @param string $description
633
     * @return AbstractDescriptionUpdated
634
     */
635
    abstract protected function createDescriptionUpdatedEvent($description);
636
637
    /**
638
     * @param string $typicalAgeRange
639
     * @return AbstractTypicalAgeRangeUpdated
640
     */
641
    abstract protected function createTypicalAgeRangeUpdatedEvent($typicalAgeRange);
642
643
    /**
644
     * @return AbstractTypicalAgeRangeDeleted
645
     */
646
    abstract protected function createTypicalAgeRangeDeletedEvent();
647
648
    /**
649
     * @param string $organizerId
650
     * @return AbstractOrganizerUpdated
651
     */
652
    abstract protected function createOrganizerUpdatedEvent($organizerId);
653
654
    /**
655
     * @param string $organizerId
656
     * @return AbstractOrganizerDeleted
657
     */
658
    abstract protected function createOrganizerDeletedEvent($organizerId);
659
660
    /**
661
     * @param ContactPoint $contactPoint
662
     * @return AbstractContactPointUpdated
663
     */
664
    abstract protected function createContactPointUpdatedEvent(ContactPoint $contactPoint);
665
666
    /**
667
     * @param BookingInfo $bookingInfo
668
     * @return AbstractBookingInfoUpdated
669
     */
670
    abstract protected function createBookingInfoUpdatedEvent(BookingInfo $bookingInfo);
671
672
    /**
673
     * @param PriceInfo $priceInfo
674
     * @return AbstractPriceInfoUpdated
675
     */
676
    abstract protected function createPriceInfoUpdatedEvent(PriceInfo $priceInfo);
677
678
    /**
679
     * @param \DateTimeInterface $publicationDate
680
     * @return AbstractPublished
681
     */
682
    abstract protected function createPublishedEvent(\DateTimeInterface $publicationDate);
683
684
    /**
685
     * @return AbstractApproved
686
     */
687
    abstract protected function createApprovedEvent();
688
689
    /**
690
     * @param StringLiteral $reason
691
     * @return AbstractRejected
692
     */
693
    abstract protected function createRejectedEvent(StringLiteral $reason);
694
695
    /**
696
     * @return AbstractFlaggedAsDuplicate
697
     */
698
    abstract protected function createFlaggedAsDuplicate();
699
700
    /**
701
     * @return AbstractFlaggedAsInappropriate
702
     */
703
    abstract protected function createFlaggedAsInappropriate();
704
}
705