Completed
Push — master ( 664340...0a8ee2 )
by Damian
02:27
created

Blog_Controller::category()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 7

Duplication

Lines 13
Ratio 92.86 %
Metric Value
dl 13
loc 14
rs 9.4286
cc 2
eloc 7
nc 2
nop 0
1
<?php
2
3
/**
4
 * Blog Holder
5
 *
6
 * @package silverstripe
7
 * @subpackage blog
8
 *
9
 * @method HasManyList Tags() List of tags in this blog
10
 * @method HasManyList Categories() List of categories in this blog
11
 * @method ManyManyList Editors() List of editors
12
 * @method ManyManyList Writers() List of writers
13
 * @method ManyManyList Contributors() List of contributors
14
 */
15
class Blog extends Page implements PermissionProvider
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
16
{
17
    /**
18
     * Permission for user management.
19
     *
20
     * @var string
21
     */
22
    const MANAGE_USERS = 'BLOG_MANAGE_USERS';
23
24
    /**
25
     * If true, users assigned as editor, writer, or contributor will be automatically granted
26
     * CMS_ACCESS_CMSMain permission. If false, only users with this permission already may be
27
     * assigned.
28
     *
29
     * @config
30
     *
31
     * @var boolean
32
     */
33
    private static $grant_user_access = true;
0 ignored issues
show
Unused Code introduced by
The property $grant_user_access 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...
34
35
    /**
36
     * Permission to either require, or grant to users assigned to work on this blog.
37
     *
38
     * @config
39
     *
40
     * @var string
41
     */
42
    private static $grant_user_permission = 'CMS_ACCESS_CMSMain';
0 ignored issues
show
Unused Code introduced by
The property $grant_user_permission 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...
43
44
    /**
45
     * Group code to assign newly granted users to.
46
     *
47
     * @config
48
     *
49
     * @var string
50
     */
51
    private static $grant_user_group = 'blog-users';
0 ignored issues
show
Unused Code introduced by
The property $grant_user_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...
52
53
    /**
54
     * @var array
55
     */
56
    private static $db = array(
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...
57
        'PostsPerPage' => 'Int',
58
    );
59
60
    /**
61
     * @var array
62
     */
63
    private static $has_many = array(
0 ignored issues
show
Unused Code introduced by
The property $has_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...
64
        'Tags' => 'BlogTag',
65
        'Categories' => 'BlogCategory',
66
    );
67
68
    /**
69
     * @var array
70
     */
71
    private static $many_many = array(
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...
72
        'Editors' => 'Member',
73
        'Writers' => 'Member',
74
        'Contributors' => 'Member',
75
    );
76
77
    /**
78
     * @var array
79
     */
80
    private static $allowed_children = array(
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...
81
        'BlogPost',
82
    );
83
84
    /**
85
     * @var array
86
     */
87
    private static $extensions = array(
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...
88
        'BlogFilter',
89
    );
90
91
    /**
92
     * @var array
93
     */
94
    private static $defaults = array(
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...
95
        'ProvideComments' => false,
96
        'PostsPerPage' => 10,
97
    );
98
99
    /**
100
     * @var string
101
     */
102
    private static $description = 'Adds a blog to your website.';
0 ignored issues
show
Unused Code introduced by
The property $description 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
104
    /**
105
     * {@inheritdoc}
106
     */
107
    public function getCMSFields()
108
    {
109
        Requirements::css(BLOGGER_DIR . '/css/cms.css');
110
        Requirements::javascript(BLOGGER_DIR . '/js/cms.js');
111
112
        $self =& $this;
113
114
        $this->beforeUpdateCMSFields(function ($fields) use ($self) {
115
            if (!$self->canEdit()) {
116
                return;
117
            }
118
119
            $categories = GridField::create(
120
                'Categories',
121
                _t('Blog.Categories', 'Categories'),
122
                $self->Categories(),
123
                GridFieldCategorisationConfig::create(15, $self->Categories()->sort('Title'), 'BlogCategory', 'Categories', 'BlogPosts')
124
            );
125
126
            $tags = GridField::create(
127
                'Tags',
128
                _t('Blog.Tags', 'Tags'),
129
                $self->Tags(),
130
                GridFieldCategorisationConfig::create(15, $self->Tags()->sort('Title'), 'BlogTag', 'Tags', 'BlogPosts')
131
            );
132
133
            /**
134
             * @var FieldList $fields
135
             */
136
            $fields->addFieldsToTab('Root.Categorisation', array(
137
                $categories,
138
                $tags
139
            ));
140
141
            $fields->findOrMakeTab('Root.Categorisation')->addExtraClass('blog-cms-categorisation');
142
        });
143
144
        return parent::getCMSFields();
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150
    public function canEdit($member = null)
151
    {
152
        $member = $this->getMember($member);
153
154
        if ($this->isEditor($member)) {
155
            return true;
156
        }
157
158
        return parent::canEdit($member);
159
    }
160
161
    /**
162
     * @param null|int|Member $member
163
     *
164
     * @return null|Member
165
     */
166 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...
167
    {
168
        if (!$member) {
169
            $member = Member::currentUser();
170
        }
171
172
        if (is_numeric($member)) {
173
            $member = Member::get()->byID($member);
174
        }
175
176
        return $member;
177
    }
178
179
    /**
180
     * Check if this member is an editor of the blog.
181
     *
182
     * @param Member $member
183
     *
184
     * @return bool
185
     */
186
    public function isEditor($member)
187
    {
188
        $isEditor = $this->isMemberOf($member, $this->Editors());
189
        $this->extend('updateIsEditor', $isEditor, $member);
190
191
        return $isEditor;
192
    }
193
194
    /**
195
     * Determine if the given member belongs to the given relation.
196
     *
197
     * @param Member $member
198
     * @param DataList $relation
199
     *
200
     * @return bool
201
     */
202 View Code Duplication
    protected function isMemberOf($member, $relation)
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...
203
    {
204
        if (!$member || !$member->exists()) {
205
            return false;
206
        }
207
208
        if ($relation instanceof UnsavedRelationList) {
209
            return in_array($member->ID, $relation->getIDList());
210
        }
211
212
        return $relation->byID($member->ID) !== null;
213
    }
214
215
    /**
216
     * Determine the role of the given member.
217
     *
218
     * Call be called via template to determine the current user.
219
     *
220
     * @example "Hello $RoleOf($CurrentMember.ID)"
221
     *
222
     * @param int|Member $member
223
     *
224
     * @return null|string
225
     */
226
    public function RoleOf($member)
227
    {
228
        if (is_numeric($member)) {
229
            $member = DataObject::get_by_id('Member', $member);
230
        }
231
232
        if (!$member) {
233
            return null;
234
        }
235
236
        if ($this->isEditor($member)) {
237
            return _t('Blog.EDITOR', 'Editor');
238
        }
239
240
        if ($this->isWriter($member)) {
241
            return _t('Blog.WRITER', 'Writer');
242
        }
243
244
        if ($this->isContributor($member)) {
245
            return _t('Blog.CONTRIBUTOR', 'Contributor');
246
        }
247
248
        return null;
249
    }
250
251
    /**
252
     * Check if this member is a writer of the blog.
253
     *
254
     * @param Member $member
255
     *
256
     * @return bool
257
     */
258
    public function isWriter($member)
259
    {
260
        $isWriter = $this->isMemberOf($member, $this->Writers());
261
        $this->extend('updateIsWriter', $isWriter, $member);
262
263
        return $isWriter;
264
    }
265
266
    /**
267
     * Check if this member is a contributor of the blog.
268
     *
269
     * @param Member $member
270
     *
271
     * @return bool
272
     */
273
    public function isContributor($member)
274
    {
275
        $isContributor = $this->isMemberOf($member, $this->Contributors());
276
        $this->extend('updateIsContributor', $isContributor, $member);
277
278
        return $isContributor;
279
    }
280
281
    /**
282
     * {@inheritdoc}
283
     */
284
    public function canAddChildren($member = null)
285
    {
286
        $member = $this->getMember($member);
287
288
        if ($this->isEditor($member) || $this->isWriter($member) || $this->isContributor($member)) {
289
            return true;
290
        }
291
292
        return parent::canAddChildren($member);
293
    }
294
295
    /**
296
     * {@inheritdoc}
297
     */
298
    public function getSettingsFields()
299
    {
300
        $fields = parent::getSettingsFields();
301
302
        $fields->addFieldToTab('Root.Settings',
303
            NumericField::create('PostsPerPage', _t('Blog.PostsPerPage', 'Posts Per Page'))
304
        );
305
306
        $members = $this->getCandidateUsers()->map()->toArray();
307
308
        $editorField = ListboxField::create('Editors', 'Editors', $members)
309
            ->setMultiple(true)
310
            ->setRightTitle('<a class="toggle-description">help</a>')
311
            ->setDescription('
312
				An editor has control over specific Blogs, and all posts included within it. Short of being able to assign other editors to a blog, they are able to handle most changes to their assigned blog.<br />
313
				<br />
314
				Editors have these permissions:<br />
315
				<br />
316
				Update or publish any BlogPost in their Blog<br />
317
				Update or publish their Blog<br />
318
				Assign/unassign writers to their Blog<br />
319
				Assign/unassign contributors to their Blog<br />
320
				Assign/unassign any member as an author of a particular BlogPost
321
			');
322
323
        if (!$this->canEditEditors()) {
324
            $editorField = $editorField->performDisabledTransformation();
325
        }
326
327
        $writerField = ListboxField::create('Writers', 'Writers', $members)
328
            ->setMultiple(true)
329
            ->setRightTitle('<a class="toggle-description">help</a>')
330
            ->setDescription('
331
				A writer has full control over creating, editing and publishing BlogPosts they have authored or have been assigned to. Writers are unable to edit BlogPosts to which they are not assigned.<br />
332
				<br />
333
				Writers have these permissions:<br />
334
				<br />
335
				Update or publish any BlogPost they have authored or have been assigned to<br />
336
				Assign/unassign any member as an author of a particular BlogPost they have authored or have been assigned to
337
			');
338
339
        if (!$this->canEditWriters()) {
340
            $writerField = $writerField->performDisabledTransformation();
341
        }
342
343
        $contributorField = ListboxField::create('Contributors', 'Contributors', $members)
344
            ->setMultiple(true)
345
            ->setRightTitle('<a class="toggle-description">help</a>')
346
            ->setDescription('
347
				Contributors have the ability to create or edit BlogPosts, but are unable to publish without authorisation of an editor. They are also unable to assign other contributing authors to any of their BlogPosts.<br />
348
				<br />
349
				Contributors have these permissions:<br />
350
				<br />
351
				Update any BlogPost they have authored or have been assigned to
352
			');
353
354
        if (!$this->canEditContributors()) {
355
            $contributorField = $contributorField->performDisabledTransformation();
356
        }
357
358
        $fields->addFieldsToTab('Root.Users', array(
359
            $editorField,
360
            $writerField,
361
            $contributorField
362
        ));
363
364
        return $fields;
365
    }
366
367
    /**
368
     * Gets the list of user candidates to be assigned to assist with this blog.
369
     *
370
     * @return SS_List
371
     */
372
    protected function getCandidateUsers()
373
    {
374
        if ($this->config()->grant_user_access) {
375
            $list = Member::get();
376
            $this->extend('updateCandidateUsers', $list);
377
            return $list;
378
        } else {
379
            return Permission::get_members_by_permission(
380
                $this->config()->grant_user_permission
381
            );
382
        }
383
    }
384
385
    /**
386
     * Determine if this user can edit the editors list.
387
     *
388
     * @param int|Member $member
389
     *
390
     * @return bool
391
     */
392
    public function canEditEditors($member = null)
393
    {
394
        $member = $this->getMember($member);
395
396
        $extended = $this->extendedCan('canEditEditors', $member);
397
398
        if ($extended !== null) {
399
            return $extended;
400
        }
401
402
        return Permission::checkMember($member, self::MANAGE_USERS);
0 ignored issues
show
Bug Compatibility introduced by
The expression \Permission::checkMember...r, self::MANAGE_USERS); of type boolean|string|null adds the type string to the return on line 402 which is incompatible with the return type documented by Blog::canEditEditors of type boolean.
Loading history...
403
    }
404
405
    /**
406
     * Determine if this user can edit writers list.
407
     *
408
     * @param int|Member $member
409
     *
410
     * @return boolean
411
     */
412 View Code Duplication
    public function canEditWriters($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...
413
    {
414
        $member = $this->getMember($member);
415
416
        $extended = $this->extendedCan('canEditWriters', $member);
417
418
        if ($extended !== null) {
419
            return $extended;
420
        }
421
422
        if ($this->isEditor($member)) {
423
            return true;
424
        }
425
426
        return Permission::checkMember($member, self::MANAGE_USERS);
0 ignored issues
show
Bug Compatibility introduced by
The expression \Permission::checkMember...r, self::MANAGE_USERS); of type boolean|string|null adds the type string to the return on line 426 which is incompatible with the return type documented by Blog::canEditWriters of type boolean.
Loading history...
427
    }
428
429
    /**
430
     * Determines if this user can edit the contributors list.
431
     *
432
     * @param int|Member $member
433
     *
434
     * @return boolean
435
     */
436 View Code Duplication
    public function canEditContributors($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...
437
    {
438
        $member = $this->getMember($member);
439
440
        $extended = $this->extendedCan('canEditContributors', $member);
441
442
        if ($extended !== null) {
443
            return $extended;
444
        }
445
446
        if ($this->isEditor($member)) {
447
            return true;
448
        }
449
450
        return Permission::checkMember($member, self::MANAGE_USERS);
0 ignored issues
show
Bug Compatibility introduced by
The expression \Permission::checkMember...r, self::MANAGE_USERS); of type boolean|string|null adds the type string to the return on line 450 which is incompatible with the return type documented by Blog::canEditContributors of type boolean.
Loading history...
451
    }
452
453
    /**
454
     * Returns BlogPosts for a given date period.
455
     *
456
     * @param int $year
457
     * @param null|int $month
458
     * @param null|int $day
459
     *
460
     * @return DataList
461
     */
462
    public function getArchivedBlogPosts($year, $month = null, $day = null)
463
    {
464
        $query = $this->getBlogPosts()->dataQuery();
465
466
        $stage = $query->getQueryParam('Versioned.stage');
467
468
        if ($stage) {
469
            $stage = '_' . $stage;
470
        }
471
472
        $query->innerJoin('BlogPost', sprintf('"SiteTree%s"."ID" = "BlogPost%s"."ID"', $stage, $stage));
473
474
        $query->where(sprintf('YEAR("PublishDate") = \'%s\'', Convert::raw2sql($year)));
475
476
        if ($month) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $month of type null|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
477
            $query->where(sprintf('MONTH("PublishDate") = \'%s\'', Convert::raw2sql($month)));
478
479
            if ($day) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $day of type null|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
480
                $query->where(sprintf('DAY("PublishDate") = \'%s\'', Convert::raw2sql($day)));
481
            }
482
        }
483
484
        return $this->getBlogPosts()->setDataQuery($query);
485
    }
486
487
    /**
488
     * Return blog posts.
489
     *
490
     * @return DataList of BlogPost objects
491
     */
492
    public function getBlogPosts()
493
    {
494
        $blogPosts = BlogPost::get()->filter('ParentID', $this->ID);
495
496
        $this->extend('updateGetBlogPosts', $blogPosts);
497
498
        return $blogPosts;
499
    }
500
501
    /**
502
     * Get a link to a Member profile.
503
     *
504
     * @param string $urlSegment
505
     *
506
     * @return string
507
     */
508
    public function ProfileLink($urlSegment)
509
    {
510
        return Controller::join_links($this->Link(), 'profile', $urlSegment);
511
    }
512
513
    /**
514
     * This sets the title for our gridfield.
515
     *
516
     * @return string
517
     */
518
    public function getLumberjackTitle()
519
    {
520
        return _t('Blog.LumberjackTitle', 'Blog Posts');
521
    }
522
523
    /**
524
     * This overwrites lumberjacks default gridfield config.
525
     *
526
     * @return GridFieldConfig
527
     */
528
    public function getLumberjackGridFieldConfig()
529
    {
530
        return GridFieldConfig_BlogPost::create();
531
    }
532
533
    /**
534
     * {@inheritdoc}
535
     */
536
    public function providePermissions()
537
    {
538
        return array(
539
            Blog::MANAGE_USERS => array(
540
                'name' => _t(
541
                    'Blog.PERMISSION_MANAGE_USERS_DESCRIPTION',
542
                    'Manage users for individual blogs'
543
                ),
544
                'help' => _t(
545
                    'Blog.PERMISSION_MANAGE_USERS_HELP',
546
                    'Allow assignment of Editors, Writers, or Contributors to blogs'
547
                ),
548
                'category' => _t('Blog.PERMISSIONS_CATEGORY', 'Blog permissions'),
549
                'sort' => 100
550
            )
551
        );
552
    }
553
554
    /**
555
     * {@inheritdoc}
556
     */
557
    protected function onBeforeWrite()
558
    {
559
        parent::onBeforeWrite();
560
        $this->assignGroup();
561
    }
562
563
    /**
564
     * Assign users as necessary to the blog group.
565
     */
566
    protected function assignGroup()
567
    {
568
        if (!$this->config()->grant_user_access) {
569
            return;
570
        }
571
572
        $group = $this->getUserGroup();
573
574
        // Must check if the method exists or else an error occurs when changing page type
575
        if ($this->hasMethod('Editors')) {
576
            foreach (array($this->Editors(), $this->Writers(), $this->Contributors()) as $levels) {
577
                foreach ($levels as $user) {
578
                    if (!$user->inGroup($group)) {
579
                        $user->Groups()->add($group);
580
                    }
581
                }
582
            }
583
        }
584
    }
585
586
    /**
587
     * Gets or creates the group used to assign CMS access.
588
     *
589
     * @return Group
590
     */
591
    protected function getUserGroup()
592
    {
593
        $code = $this->config()->grant_user_group;
594
595
        $group = Group::get()->filter('Code', $code)->first();
596
597
        if ($group) {
598
            return $group;
599
        }
600
601
        $group = new Group();
602
        $group->Title = 'Blog users';
603
        $group->Code = $code;
604
605
        $group->write();
606
607
        $permission = new Permission();
608
        $permission->Code = $this->config()->grant_user_permission;
609
610
        $group->Permissions()->add($permission);
611
612
        return $group;
613
    }
614
}
615
616
/**
617
 * @package silverstripe
618
 * @subpackage blog
619
 */
620
class Blog_Controller extends Page_Controller
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
621
{
622
    /**
623
     * @var array
624
     */
625
    private static $allowed_actions = array(
0 ignored issues
show
Unused Code introduced by
The property $allowed_actions 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...
626
        'archive',
627
        'tag',
628
        'category',
629
        'rss',
630
        'profile',
631
    );
632
633
    /**
634
     * @var array
635
     */
636
    private static $url_handlers = array(
0 ignored issues
show
Unused Code introduced by
The property $url_handlers 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...
637
        'tag/$Tag!' => 'tag',
638
        'category/$Category!' => 'category',
639
        'archive/$Year!/$Month/$Day' => 'archive',
640
        'profile/$URLSegment!' => 'profile',
641
    );
642
643
    /**
644
     * @var array
645
     */
646
    private static $casting = array(
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...
647
        'MetaTitle' => 'Text',
648
        'FilterDescription' => 'Text',
649
    );
650
651
    /**
652
     * The current Blog Post DataList query.
653
     *
654
     * @var DataList
655
     */
656
    protected $blogPosts;
657
658
    /**
659
     * @return string
660
     */
661
    public function index()
662
    {
663
        /**
664
         * @var Blog $dataRecord
665
         */
666
        $dataRecord = $this->dataRecord;
667
668
        $this->blogPosts = $dataRecord->getBlogPosts();
669
670
        return $this->render();
671
    }
672
673
    /**
674
     * Renders a Blog Member's profile.
675
     *
676
     * @return SS_HTTPResponse
677
     */
678
    public function profile()
679
    {
680
        $profile = $this->getCurrentProfile();
681
682
        if (!$profile) {
683
            return $this->httpError(404, 'Not Found');
684
        }
685
686
        $this->blogPosts = $this->getCurrentProfilePosts();
687
688
        return $this->render();
689
    }
690
691
    /**
692
     * Get the Member associated with the current URL segment.
693
     *
694
     * @return null|Member
695
     */
696
    public function getCurrentProfile()
697
    {
698
        $urlSegment = $this->request->param('URLSegment');
699
700
        if ($urlSegment) {
701
            return Member::get()
702
                ->filter('URLSegment', $urlSegment)
703
                ->first();
704
        }
705
706
        return null;
707
    }
708
709
    /**
710
     * Get posts related to the current Member profile.
711
     *
712
     * @return null|DataList
713
     */
714
    public function getCurrentProfilePosts()
715
    {
716
        $profile = $this->getCurrentProfile();
717
718
        if ($profile) {
719
            return $profile->BlogPosts()->filter('ParentID', $this->ID);
720
        }
721
722
        return null;
723
    }
724
725
    /**
726
     * Renders an archive for a specified date. This can be by year or year/month.
727
     *
728
     * @return null|SS_HTTPResponse
729
     */
730
    public function archive()
731
    {
732
        /**
733
         * @var Blog $dataRecord
734
         */
735
        $dataRecord = $this->dataRecord;
736
737
        $year = $this->getArchiveYear();
738
        $month = $this->getArchiveMonth();
739
        $day = $this->getArchiveDay();
740
741
        if ($this->request->param('Month') && !$month) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $month of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
742
            $this->httpError(404, 'Not Found');
743
        }
744
745
        if ($month && $this->request->param('Day') && !$day) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $month of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $day of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
746
            $this->httpError(404, 'Not Found');
747
        }
748
749
        if ($year) {
750
            $this->blogPosts = $dataRecord->getArchivedBlogPosts($year, $month, $day);
751
752
            return $this->render();
753
        }
754
755
        $this->httpError(404, 'Not Found');
756
757
        return null;
758
    }
759
760
    /**
761
     * Fetches the archive year from the url.
762
     *
763
     * @return int
764
     */
765
    public function getArchiveYear()
766
    {
767
        if ($this->request->param('Year')) {
768
            if (preg_match('/^[0-9]{4}$/', $year = $this->request->param('Year'))) {
769
                return (int) $year;
770
            }
771
        } elseif ($this->request->param('Action') == 'archive') {
772
            return SS_Datetime::now()->Year();
773
        }
774
775
        return null;
776
    }
777
778
    /**
779
     * Fetches the archive money from the url.
780
     *
781
     * @return null|int
782
     */
783
    public function getArchiveMonth()
784
    {
785
        $month = $this->request->param('Month');
786
787
        if (preg_match('/^[0-9]{1,2}$/', $month)) {
788
            if ($month > 0 && $month < 13) {
789
                if (checkdate($month, 01, $this->getArchiveYear())) {
790
                    return (int) $month;
791
                }
792
            }
793
        }
794
795
        return null;
796
    }
797
798
    /**
799
     * Fetches the archive day from the url.
800
     *
801
     * @return null|int
802
     */
803
    public function getArchiveDay()
804
    {
805
        $day = $this->request->param('Day');
806
807
        if (preg_match('/^[0-9]{1,2}$/', $day)) {
808
            if (checkdate($this->getArchiveMonth(), $day, $this->getArchiveYear())) {
809
                return (int) $day;
810
            }
811
        }
812
813
        return null;
814
    }
815
816
    /**
817
     * Renders the blog posts for a given tag.
818
     *
819
     * @return null|SS_HTTPResponse
820
     */
821 View Code Duplication
    public function tag()
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...
822
    {
823
        $tag = $this->getCurrentTag();
824
825
        if ($tag) {
826
            $this->blogPosts = $tag->BlogPosts();
827
            return $this->render();
828
        }
829
830
        $this->httpError(404, 'Not Found');
831
832
        return null;
833
    }
834
835
    /**
836
     * Tag Getter for use in templates.
837
     *
838
     * @return null|BlogTag
839
     */
840 View Code Duplication
    public function getCurrentTag()
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...
841
    {
842
        /**
843
         * @var Blog $dataRecord
844
         */
845
        $dataRecord = $this->dataRecord;
846
        $tag = $this->request->param('Tag');
847
        if ($tag) {
848
            return $dataRecord->Tags()
849
                ->filter('URLSegment', array($tag, rawurlencode($tag)))
850
                ->first();
851
        }
852
        return null;
853
    }
854
855
    /**
856
     * Renders the blog posts for a given category.
857
     *
858
     * @return null|SS_HTTPResponse
859
     */
860 View Code Duplication
    public function category()
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...
861
    {
862
        $category = $this->getCurrentCategory();
863
864
        if ($category) {
865
            $this->blogPosts = $category->BlogPosts();
866
867
            return $this->render();
868
        }
869
870
        $this->httpError(404, 'Not Found');
871
872
        return null;
873
    }
874
875
    /**
876
     * Category Getter for use in templates.
877
     *
878
     * @return null|BlogCategory
879
     */
880 View Code Duplication
    public function getCurrentCategory()
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...
881
    {
882
        /**
883
         * @var Blog $dataRecord
884
         */
885
        $dataRecord = $this->dataRecord;
886
        $category = $this->request->param('Category');
887
        if ($category) {
888
            return $dataRecord->Categories()
889
                ->filter('URLSegment', array($category, rawurlencode($category)))
890
                ->first();
891
        }
892
        return null;
893
    }
894
895
    /**
896
     * Get the meta title for the current action.
897
     *
898
     * @return string
899
     */
900
    public function getMetaTitle()
901
    {
902
        $title = $this->data()->getTitle();
903
        $filter = $this->getFilterDescription();
904
905
        if ($filter) {
906
            $title = sprintf('%s - %s', $title, $filter);
907
        }
908
909
        $this->extend('updateMetaTitle', $title);
910
911
        return $title;
912
    }
913
914
    /**
915
     * Returns a description of the current filter.
916
     *
917
     * @return string
918
     */
919
    public function getFilterDescription()
920
    {
921
        $items = array();
922
923
        $list = $this->PaginatedList();
924
        $currentPage = $list->CurrentPage();
925
926
        if ($currentPage > 1) {
927
            $items[] = _t(
928
                'Blog.FILTERDESCRIPTION_PAGE',
929
                'Page {page}',
930
                null,
931
                array(
0 ignored issues
show
Documentation introduced by
array('page' => $currentPage) is of type array<string,double|inte...age":"double|integer"}>, 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...
932
                    'page' => $currentPage,
933
                )
934
            );
935
        }
936
937
        if ($author = $this->getCurrentProfile()) {
938
            $items[] = _t(
939
                'Blog.FILTERDESCRIPTION_AUTHOR',
940
                'By {author}',
941
                null,
942
                array(
0 ignored issues
show
Documentation introduced by
array('author' => $author->Title) is of type array<string,?,{"author":"?"}>, 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...
943
                    'author' => $author->Title,
944
                )
945
            );
946
        }
947
948
        if ($tag = $this->getCurrentTag()) {
949
            $items[] = _t(
950
                'Blog.FILTERDESCRIPTION_TAG',
951
                'Tagged with {tag}',
952
                null,
953
                array(
0 ignored issues
show
Documentation introduced by
array('tag' => $tag->Title) is of type array<string,?,{"tag":"?"}>, 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...
954
                    'tag' => $tag->Title,
955
                )
956
            );
957
        }
958
959
        if ($category = $this->getCurrentCategory()) {
960
            $items[] = _t(
961
                'Blog.FILTERDESCRIPTION_CATEGORY',
962
                'In category {category}',
963
                null,
964
                array(
0 ignored issues
show
Documentation introduced by
array('category' => $category->Title) is of type array<string,?,{"category":"?"}>, 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...
965
                    'category' => $category->Title,
966
                )
967
            );
968
        }
969
970
        if ($this->owner->getArchiveYear()) {
971
            if ($this->owner->getArchiveDay()) {
972
                $date = $this->owner->getArchiveDate()->Nice();
973
            } elseif ($this->owner->getArchiveMonth()) {
974
                $date = $this->owner->getArchiveDate()->format('F, Y');
975
            } else {
976
                $date = $this->owner->getArchiveDate()->format('Y');
977
            }
978
979
            $items[] = _t(
980
                'Blog.FILTERDESCRIPTION_DATE',
981
                'In {date}',
982
                null,
983
                array(
0 ignored issues
show
Documentation introduced by
array('date' => $date) is of type array<string,?,{"date":"?"}>, 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...
984
                    'date' => $date,
985
                )
986
            );
987
        }
988
989
        $result = '';
990
991
        if ($items) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $items of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
992
            $result = implode(', ', $items);
993
        }
994
995
        $this->extend('updateFilterDescription', $result);
996
997
        return $result;
998
    }
999
1000
    /**
1001
     * Returns a list of paginated blog posts based on the BlogPost dataList.
1002
     *
1003
     * @return PaginatedList
1004
     */
1005
    public function PaginatedList()
1006
    {
1007
        $allPosts = $this->blogPosts ?: new ArrayList();
1008
1009
        $posts = new PaginatedList($allPosts);
1010
1011
        // Set appropriate page size
1012
        if ($this->PostsPerPage > 0) {
1013
            $pageSize = $this->PostsPerPage;
1014
        } elseif ($count = $allPosts->count()) {
1015
            $pageSize = $count;
1016
        } else {
1017
            $pageSize = 99999;
1018
        }
1019
        $posts->setPageLength($pageSize);
1020
1021
        // Set current page
1022
        $start = $this->request->getVar($posts->getPaginationGetVar());
1023
        $posts->setPageStart($start);
1024
1025
        return $posts;
1026
    }
1027
1028
    /**
1029
     * Displays an RSS feed of blog posts.
1030
     *
1031
     * @return string
1032
     */
1033
    public function rss()
1034
    {
1035
        /**
1036
         * @var Blog $dataRecord
1037
         */
1038
        $dataRecord = $this->dataRecord;
1039
1040
        $this->blogPosts = $dataRecord->getBlogPosts();
1041
1042
        $rss = new RSSFeed($this->blogPosts, $this->Link(), $this->MetaTitle, $this->MetaDescription);
1043
1044
        $this->extend('updateRss', $rss);
1045
1046
        return $rss->outputToBrowser();
1047
    }
1048
1049
    /**
1050
     * Returns the current archive date.
1051
     *
1052
     * @return null|Date
1053
     */
1054
    public function getArchiveDate()
1055
    {
1056
        $year = $this->getArchiveYear();
1057
        $month = $this->getArchiveMonth();
1058
        $day = $this->getArchiveDay();
1059
1060
        if ($year) {
1061
            if ($month) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $month of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1062
                $date = sprintf('%s-%s-01', $year, $month);
1063
1064
                if ($day) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $day of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1065
                    $date = sprintf('%s-%s-%s', $year, $month, $day);
1066
                }
1067
            } else {
1068
                $date = sprintf('%s-01-01', $year);
1069
            }
1070
1071
            return DBField::create_field('Date', $date);
1072
        }
1073
1074
        return null;
1075
    }
1076
1077
    /**
1078
     * Returns a link to the RSS feed.
1079
     *
1080
     * @return string
1081
     */
1082
    public function getRSSLink()
1083
    {
1084
        return $this->Link('rss');
1085
    }
1086
}
1087