Completed
Pull Request — master (#512)
by
unknown
01:45
created

BlogPost::getDate()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 4
nc 3
nop 0
1
<?php
2
3
namespace SilverStripe\Blog\Model;
4
5
use Page;
6
use SilverStripe\AssetAdmin\Forms\UploadField;
7
use SilverStripe\Assets\Image;
8
use SilverStripe\Control\Controller;
9
use SilverStripe\Core\Config\Config;
10
use SilverStripe\Forms\DatetimeField;
11
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
12
use SilverStripe\Forms\ListboxField;
13
use SilverStripe\Forms\TextField;
14
use SilverStripe\Forms\ToggleCompositeField;
15
use SilverStripe\ORM\ArrayList;
16
use SilverStripe\ORM\FieldType\DBDatetime;
17
use SilverStripe\ORM\UnsavedRelationList;
18
use SilverStripe\Security\Group;
19
use SilverStripe\Security\Member;
20
use SilverStripe\Security\Permission;
21
use SilverStripe\Security\Security;
22
use SilverStripe\TagField\TagField;
23
use SilverStripe\Versioned\Versioned;
24
use SilverStripe\View\ArrayData;
25
use SilverStripe\View\Requirements;
26
27
/**
28
 * An individual blog post.
29
 *
30
 * @method ManyManyList Categories()
31
 * @method ManyManyList Tags()
32
 * @method ManyManyList Authors()
33
 * @method Blog Parent()
34
 *
35
 * @property string $PublishDate
36
 * @property string $AuthorNames
37
 * @property int $ParentID
38
 */
39
class BlogPost extends Page
40
{
41
    /**
42
     * Same as above, but for list of users that can be
43
     * given credit in the author field for blog posts
44
     * @var string|bool false or group code
45
     */
46
    private static $restrict_authors_to_group = false;
0 ignored issues
show
Unused Code introduced by
The property $restrict_authors_to_group 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...
47
48
    /**
49
     * {@inheritDoc}
50
     * @var string
51
     */
52
    private static $table_name = 'BlogPost';
0 ignored issues
show
Unused Code introduced by
The property $table_name 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...
53
54
    /**
55
     * @var array
56
     */
57
    private static $db = [
0 ignored issues
show
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...
58
        'PublishDate' => 'Datetime',
59
        'AuthorNames' => 'Varchar(1024)',
60
        'Summary'     => 'HTMLText'
61
    ];
62
63
    /**
64
     * @var array
65
     */
66
    private static $has_one = [
0 ignored issues
show
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...
67
        'FeaturedImage' => Image::class
68
    ];
69
70
    /**
71
     * @var array
72
     */
73
    private static $owns = [
0 ignored issues
show
Unused Code introduced by
The property $owns 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...
74
        'FeaturedImage',
75
    ];
76
77
    /**
78
     * @var array
79
     */
80
    private static $many_many = [
0 ignored issues
show
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...
81
        'Categories' => BlogCategory::class,
82
        'Tags'       => BlogTag::class,
83
        'Authors'    => Member::class
84
    ];
85
86
    /**
87
     * @var array
88
     */
89
    private static $defaults = [
0 ignored issues
show
Unused Code introduced by
The property $defaults 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
        'ShowInMenus'     => false,
91
        'InheritSideBar'  => true,
92
        'ProvideComments' => true
93
    ];
94
95
    /**
96
     * @var array
97
     */
98
    private static $extensions = [
0 ignored issues
show
Unused Code introduced by
The property $extensions 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...
99
        BlogPostFilter::class
100
    ];
101
102
    /**
103
     * @var array
104
     */
105
    private static $searchable_fields = [
0 ignored issues
show
Unused Code introduced by
The property $searchable_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...
106
        'Title'
107
    ];
108
109
    /**
110
     * @var array
111
     */
112
    private static $summary_fields = [
0 ignored issues
show
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...
113
        'Title'
114
    ];
115
116
    /**
117
     * @var array
118
     */
119
    private static $casting = [
0 ignored issues
show
Unused Code introduced by
The property $casting 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...
120
        'Excerpt' => 'HTMLText',
121
        'Date' => 'DBDatetime'
122
    ];
123
124
    /**
125
     * @var array
126
     */
127
    private static $allowed_children = [];
0 ignored issues
show
Unused Code introduced by
The property $allowed_children 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...
128
129
    /**
130
     * The default sorting lists BlogPosts with an empty PublishDate at the top.
131
     *
132
     * @var string
133
     */
134
    private static $default_sort = '"PublishDate" IS NULL DESC, "PublishDate" DESC';
0 ignored issues
show
Unused Code introduced by
The property $default_sort 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...
135
136
    /**
137
     * @var bool
138
     */
139
    private static $can_be_root = false;
0 ignored issues
show
Unused Code introduced by
The property $can_be_root 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...
140
141
    /**
142
     * This will display or hide the current class from the SiteTree. This variable can be
143
     * configured using YAML.
144
     *
145
     * @var bool
146
     */
147
    private static $show_in_sitetree = false;
0 ignored issues
show
Unused Code introduced by
The property $show_in_sitetree 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...
148
149
    /**
150
     * Determine the role of the given member.
151
     *
152
     * Call be called via template to determine the current user.
153
     *
154
     * @example "Hello $RoleOf($CurrentMember.ID)"
155
     *
156
     * @param null|int|Member $member
157
     *
158
     * @return null|string
159
     */
160
    public function RoleOf($member = null)
161
    {
162
        $member = $this->getMember($member);
163
164
        if (!$member) {
165
            return null;
166
        }
167
168
        if ($this->isAuthor($member)) {
169
            return _t(__CLASS__ . '.AUTHOR', 'Author');
170
        }
171
172
        $parent = $this->Parent();
173
174
        if ($parent instanceof Blog) {
175
            return $parent->RoleOf($member);
176
        }
177
178
        return null;
179
    }
180
181
    /**
182
     * Determine if the given member is an author of this post.
183
     *
184
     * @param null|Member $member
185
     *
186
     * @return bool
187
     */
188 View Code Duplication
    public function isAuthor($member = null)
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...
189
    {
190
        if (!$member || !$member->exists()) {
191
            return false;
192
        }
193
194
        $list = $this->Authors();
195
196
        if ($list instanceof UnsavedRelationList) {
197
            return in_array($member->ID, $list->getIDList());
198
        }
199
200
        return $list->byID($member->ID) !== null;
201
    }
202
203
    /**
204
     * {@inheritdoc}
205
     */
206
    public function getCMSFields()
207
    {
208
        Requirements::css('silverstripe/blog:client/dist/styles/main.css');
209
        Requirements::javascript('silverstripe/blog:client/dist/js/main.bundle.js');
210
211
        $this->beforeUpdateCMSFields(function ($fields) {
212
            $uploadField = UploadField::create('FeaturedImage', _t(__CLASS__ . '.FeaturedImage', 'Featured Image'));
213
            $uploadField->getValidator()->setAllowedExtensions(['jpg', 'jpeg', 'png', 'gif']);
214
215
            /**
216
             * @var FieldList $fields
217
             */
218
            $fields->insertAfter($uploadField, 'Content');
219
220
            $summary = HtmlEditorField::create('Summary', false);
221
            $summary->setRows(5);
222
            $summary->setDescription(_t(
223
                __CLASS__ . '.SUMMARY_DESCRIPTION',
224
                'If no summary is specified the first 30 words will be used.'
225
            ));
226
227
            $summaryHolder = ToggleCompositeField::create(
228
                'CustomSummary',
229
                _t(__CLASS__ . '.CUSTOMSUMMARY', 'Add A Custom Summary'),
230
                [
231
                    $summary,
232
                ]
233
            );
234
            $summaryHolder->setHeadingLevel(4);
235
            $summaryHolder->addExtraClass('custom-summary');
236
237
            $fields->insertAfter($summaryHolder, 'FeaturedImage');
238
239
            $urlSegment = $fields->dataFieldByName('URLSegment');
240
            $urlSegment->setURLPrefix($this->Parent()->RelativeLink());
241
242
            $fields->removeFieldsFromTab('Root.Main', [
243
                'MenuTitle',
244
                'URLSegment',
245
            ]);
246
247
            $authorField = ListboxField::create(
248
                'Authors',
249
                _t(__CLASS__ . '.Authors', 'Authors'),
250
                $this->getCandidateAuthors()->map()->toArray()
251
            );
252
253
            $authorNames = TextField::create(
254
                'AuthorNames',
255
                _t(__CLASS__ . '.AdditionalCredits', 'Additional Credits'),
256
                null,
257
                1024
258
            )->setDescription(
259
                _t(
260
                    __CLASS__ . '.AdditionalCredits_Description',
261
                    'If some authors of this post don\'t have CMS access, enter their name(s) here. '.
262
                    'You can separate multiple names with a comma.'
263
                )
264
            );
265
266
            if (!$this->canEditAuthors()) {
267
                $authorField = $authorField->performDisabledTransformation();
268
                $authorNames = $authorNames->performDisabledTransformation();
269
            }
270
271
            $publishDate = DatetimeField::create('PublishDate', _t(__CLASS__ . '.PublishDate', 'Publish Date'));
272
273
            if (!$this->PublishDate) {
274
                $publishDate->setDescription(
275
                    _t(
276
                        __CLASS__ . '.PublishDate_Description',
277
                        'Will be set to "now" if published without a value.'
278
                    )
279
                );
280
            }
281
282
            // Get categories and tags
283
            $parent = $this->Parent();
284
            $categories = $parent instanceof Blog
285
                ? $parent->Categories()
286
                : BlogCategory::get();
287
            $tags = $parent instanceof Blog
288
                ? $parent->Tags()
289
                : BlogTag::get();
290
291
            // @todo: Reimplement the sidebar
292
            // $options = BlogAdminSidebar::create(
293
            $fields->addFieldsToTab(
294
                'Root.PostOptions',
295
                [
296
                    $publishDate,
297
                    $urlSegment,
298
                    TagField::create(
299
                        'Categories',
300
                        _t(__CLASS__ . '.Categories', 'Categories'),
301
                        $categories,
302
                        $this->Categories()
303
                    )
304
                        ->setCanCreate($this->canCreateCategories())
305
                        ->setShouldLazyLoad(true),
306
                    TagField::create(
307
                        'Tags',
308
                        _t(__CLASS__ . '.Tags', 'Tags'),
309
                        $tags,
310
                        $this->Tags()
311
                    )
312
                        ->setCanCreate($this->canCreateTags())
313
                        ->setShouldLazyLoad(true),
314
                    $authorField,
315
                    $authorNames
316
                ]
317
            );
318
            // )->setTitle('Post Options');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% 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...
319
            // $options->setName('blog-admin-sidebar');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% 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...
320
            // $fields->insertBefore($options, 'Root');
0 ignored issues
show
Unused Code Comprehensibility introduced by
73% 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...
321
        });
322
323
        $fields = parent::getCMSFields();
324
325
        $fields->fieldByName('Root')->setTemplate('TabSet_holder');
326
327
        $fields->fieldByName('Root.PostOptions')
328
            ->setTitle(_t(__CLASS__ . '.PostOptions', 'Post Options'));
329
330
        return $fields;
331
    }
332
333
    /**
334
     * Gets the list of author candidates to be assigned as authors of this blog post.
335
     *
336
     * @return SS_List
337
     */
338
    public function getCandidateAuthors()
339
    {
340
        if ($this->config()->get('restrict_authors_to_group')) {
341
            return Group::get()->filter('Code', $this->config()->get('restrict_authors_to_group'))->first()->Members();
342
        }
343
344
        $list = Member::get();
345
        $this->extend('updateCandidateAuthors', $list);
346
        return $list;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $list; (SilverStripe\ORM\DataList) is incompatible with the return type documented by SilverStripe\Blog\Model\...st::getCandidateAuthors of type SilverStripe\Blog\Model\SS_List.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
347
    }
348
349
    /**
350
     * Determine if this user can edit the authors list.
351
     *
352
     * @param null|int|Member $member
353
     *
354
     * @return bool
355
     */
356
    public function canEditAuthors($member = null)
357
    {
358
        $member = $this->getMember($member);
359
360
        $extended = $this->extendedCan('canEditAuthors', $member);
361
362
        if ($extended !== null) {
363
            return $extended;
364
        }
365
366
        $parent = $this->Parent();
367
368
        if ($parent instanceof Blog && $parent->exists()) {
369
            if ($parent->isEditor($member)) {
370
                return true;
371
            }
372
373
            if ($parent->isWriter($member) && $this->isAuthor($member)) {
374
                return true;
375
            }
376
        }
377
378
        return Permission::checkMember($member, Blog::MANAGE_USERS);
0 ignored issues
show
Bug Compatibility introduced by
The expression \SilverStripe\Security\P...el\Blog::MANAGE_USERS); of type boolean|string adds the type string to the return on line 378 which is incompatible with the return type documented by SilverStripe\Blog\Model\BlogPost::canEditAuthors of type boolean.
Loading history...
379
    }
380
381
    /**
382
     * @param null|int|Member $member
383
     *
384
     * @return null|Member
385
     */
386 View Code Duplication
    protected function getMember($member = null)
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...
387
    {
388
        if (!$member) {
389
            $member = Security::getCurrentUser();
390
        }
391
392
        if (is_numeric($member)) {
393
            $member = Member::get()->byID($member);
394
        }
395
396
        return $member;
397
    }
398
399
    /**
400
     * Determine whether user can create new categories.
401
     *
402
     * @param null|int|Member $member
403
     *
404
     * @return bool
405
     */
406 View Code Duplication
    public function canCreateCategories($member = null)
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...
407
    {
408
        $member = $this->getMember($member);
409
410
        $parent = $this->Parent();
411
412
        if (!$parent || !$parent->exists() || !($parent instanceof Blog)) {
413
            return false;
414
        }
415
416
        if ($parent->isEditor($member)) {
417
            return true;
418
        }
419
420
        return Permission::checkMember($member, 'ADMIN');
421
    }
422
423
    /**
424
     * Determine whether user can create new tags.
425
     *
426
     * @param null|int|Member $member
427
     *
428
     * @return bool
429
     */
430 View Code Duplication
    public function canCreateTags($member = null)
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...
431
    {
432
        $member = $this->getMember($member);
433
434
        $parent = $this->Parent();
435
436
        if (!$parent || !$parent->exists() || !($parent instanceof Blog)) {
437
            return false;
438
        }
439
440
        if ($parent->isEditor($member)) {
441
            return true;
442
        }
443
444
        if ($parent->isWriter($member)) {
445
            return true;
446
        }
447
448
        return Permission::checkMember($member, 'ADMIN');
449
    }
450
451
    /**
452
     * {@inheritdoc}
453
     *
454
     * Update the PublishDate to now if the BlogPost would otherwise be published without a date.
455
     */
456
    public function onBeforePublish()
457
    {
458
        /**
459
         * @var DBDatetime $publishDate
460
         */
461
        $publishDate = $this->dbObject('PublishDate');
462
463
        if (!$publishDate->getValue()) {
464
            $this->PublishDate = DBDatetime::now()->getValue();
465
            $this->write();
466
        }
467
    }
468
469
    /**
470
     * {@inheritdoc}
471
     *
472
     * Sets blog relationship on all categories and tags assigned to this post.
473
     */
474
    public function onAfterWrite()
475
    {
476
        parent::onAfterWrite();
477
478
        foreach ($this->Categories() as $category) {
479
            /**
480
             * @var BlogCategory $category
481
             */
482
            $category->BlogID = $this->ParentID;
483
            $category->write();
484
        }
485
486
        foreach ($this->Tags() as $tag) {
487
            /**
488
             * @var BlogTag $tag
489
             */
490
            $tag->BlogID = $this->ParentID;
491
            $tag->write();
492
        }
493
    }
494
495
    /**
496
     * {@inheritdoc}
497
     */
498
    public function canView($member = null)
499
    {
500
        $member = $this->getMember($member);
501
502
        if (!parent::canView($member)) {
503
            return false;
504
        }
505
506
        if ($this->canEdit($member)) {
507
            return true;
508
        }
509
510
        // If on draft stage, user has permission to view draft, so show it
511
        if (Versioned::get_stage() === Versioned::DRAFT) {
512
            return true;
513
        }
514
515
        /**
516
         * @var DBDatetime $publishDate
517
         */
518
        $publishDate = $this->dbObject('PublishDate');
519
        if (!$publishDate->exists()) {
520
            return false;
521
        }
522
523
        return !$publishDate->InFuture();
524
    }
525
526
    /**
527
     * {@inheritdoc}
528
     */
529
    public function canPublish($member = null)
530
    {
531
        $member = $this->getMember($member);
532
533
        if (Permission::checkMember($member, 'ADMIN')) {
534
            return true;
535
        }
536
537
        $extended = $this->extendedCan('canPublish', $member);
538
539
        if ($extended !== null) {
540
            return $extended;
541
        }
542
543
        $parent = $this->Parent();
544
545
        if ($parent instanceof Blog && $parent->exists()) {
546
            if ($parent->isEditor($member)) {
547
                return true;
548
            }
549
550
            if ($parent->isWriter($member) && $this->isAuthor($member)) {
551
                return true;
552
            }
553
554
            if ($parent->isContributor($member)) {
555
                return parent::canEdit($member);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (canEdit() instead of canPublish()). Are you sure this is correct? If so, you might want to change this to $this->canEdit().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
556
            }
557
        }
558
559
        return $this->canEdit($member);
560
    }
561
562
    /**
563
     * {@inheritdoc}
564
     */
565
    public function canEdit($member = null)
566
    {
567
        $member = $this->getMember($member);
568
569
        if (parent::canEdit($member)) {
570
            return true;
571
        }
572
573
        $parent = $this->Parent();
574
575
        if (!$parent || !$parent->exists() || !($parent instanceof Blog)) {
576
            return false;
577
        }
578
579
        if ($parent->isEditor($member)) {
580
            return true;
581
        }
582
583
        if (!$parent->isWriter($member) && !$parent->isContributor($member)) {
584
            return false;
585
        }
586
587
        return $this->isAuthor($member);
588
    }
589
590
    /**
591
     * Returns the post excerpt.
592
     *
593
     * @param int $wordsToDisplay
594
     *
595
     * @return string
596
     */
597
    public function Excerpt($wordsToDisplay = 30)
598
    {
599
        /** @var HTMLText $content */
600
        $content = $this->dbObject('Content');
601
602
        return $content->Summary($wordsToDisplay);
603
    }
604
605
    /**
606
     * Returns a monthly archive link for the current blog post.
607
     *
608
     * @param string $type
609
     *
610
     * @return string
611
     */
612
    public function getMonthlyArchiveLink($type = 'day')
613
    {
614
        /**
615
         * @var DBDatetime $date
616
         */
617
        $date = $this->dbObject('PublishDate');
618
619
        if ($type != 'year') {
620
            if ($type == 'day') {
621
                return Controller::join_links(
622
                    $this->Parent()->Link('archive'),
623
                    $date->format('Y'),
624
                    $date->format('m'),
625
                    $date->format('d')
626
                );
627
            }
628
629
            return Controller::join_links($this->Parent()->Link('archive'), $date->format('Y'), $date->format('m'));
630
        }
631
632
        return Controller::join_links($this->Parent()->Link('archive'), $date->format('Y'));
633
    }
634
635
    /**
636
     * Returns a yearly archive link for the current blog post.
637
     *
638
     * @return string
639
     */
640
    public function getYearlyArchiveLink()
641
    {
642
        /**
643
         * @var DBDatetime $date
644
         */
645
        $date = $this->dbObject('PublishDate');
646
647
        return Controller::join_links($this->Parent()->Link('archive'), $date->format('Y'));
648
    }
649
650
    /**
651
     * Resolves static and dynamic authors linked to this post.
652
     *
653
     * @return ArrayList
654
     */
655
    public function getCredits()
656
    {
657
        $list = ArrayList::create();
658
659
        $list->merge($this->getDynamicCredits());
660
        $list->merge($this->getStaticCredits());
661
662
        return $list->sort('Name');
663
    }
664
665
    /**
666
     * Resolves dynamic authors linked to this post.
667
     *
668
     * @return ArrayList
669
     */
670
    protected function getDynamicCredits()
671
    {
672
        // Find best page to host user profiles
673
        $parent = $this->Parent();
674
        if (! ($parent instanceof Blog)) {
675
            $parent = Blog::get()->first();
676
        }
677
678
        // If there is no parent blog, return list undecorated
679
        if (!$parent) {
680
            $items = $this->Authors()->toArray();
681
            return ArrayList::create($items);
682
        }
683
684
        // Update all authors
685
        $items = ArrayList::create();
686
        foreach ($this->Authors() as $author) {
687
            // Add link for each author
688
            $author = $author->customise([
689
                'URL' => $parent->ProfileLink($author->URLSegment),
690
            ]);
691
            $items->push($author);
692
        }
693
694
        return $items;
695
    }
696
697
    /**
698
     * Resolves static authors linked to this post.
699
     *
700
     * @return ArrayList
701
     */
702
    protected function getStaticCredits()
703
    {
704
        $items = ArrayList::create();
705
706
        $authors = array_filter(preg_split('/\s*,\s*/', $this->AuthorNames));
707
708
        foreach ($authors as $author) {
709
            $item = ArrayData::create([
710
                'Name' => $author,
711
            ]);
712
713
            $items->push($item);
714
        }
715
716
        return $items;
717
    }
718
719
    /**
720
     * Checks to see if User Profiles has been disabled via config
721
     *
722
     * @return bool
723
     */
724
    public function getProfilesDisabled()
725
    {
726
        return Config::inst()->get(BlogController::class, 'disable_profiles');
727
    }
728
729
    /**
730
     * Sets the label for BlogPost.Title to 'Post Title' (Rather than 'Page name').
731
     *
732
     * @param bool $includeRelations
733
     *
734
     * @return array
735
     */
736
    public function fieldLabels($includeRelations = true)
737
    {
738
        $labels = parent::fieldLabels($includeRelations);
739
740
        $labels['Title'] = _t(__CLASS__ . '.PageTitleLabel', 'Post Title');
741
742
        return $labels;
743
    }
744
745
    /**
746
     * Proxy method for displaying the publish date in rss feeds.
747
     * @see https://github.com/silverstripe/silverstripe-blog/issues/394
748
     *
749
     * @return string|null
750
     */
751
    public function getDate()
752
    {
753
        if ($this->hasDatabaseField('Date')) {
754
            return $this->getField('Date');
755
        }
756
        return !empty($this->PublishDate) ? $this->PublishDate : null;
757
    }
758
    
759
    /**
760
     * Provides a rough estimate of how long this post will take to read based on wikipedias answer to "How fast can a
761
     * human read" of 200wpm. Source https://en.wikipedia.org/wiki/Speed_reading
762
     *
763
     * @return string
764
     */
765
    public function getMinutesToRead()
766
    {
767
        $wpm = 200;
768
        $wordCount = count(array_filter(explode(" ", strip_tags($this->Content))));
769
770
        return round($wordCount / $wpm, 0);
771
    }
772
773
    /**
774
     * {@inheritdoc}
775
     */
776
    protected function onBeforeWrite()
777
    {
778
        parent::onBeforeWrite();
779
780
        if (!$this->exists() && ($member = Security::getCurrentUser())) {
781
            $this->Authors()->add($member);
782
        }
783
    }
784
}
785