UserPostingTrait   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 155
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 49
c 0
b 0
f 0
dl 0
loc 155
rs 10
wmc 24

10 Methods

Rating   Name   Duplication   Size   Complexity  
A isAnsweringForbidden() 0 8 2
A isBookmarked() 0 3 1
A isEditingAllowed() 0 3 1
A setCurrentUser() 0 3 1
A isEditingAsUserAllowed() 0 6 1
A getCurrentUser() 0 3 1
A isUnread() 0 10 2
B _isEditingAllowed() 0 39 11
A isIgnored() 0 3 1
A hasNewAnswers() 0 13 3
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Saito - The Threaded Web Forum
7
 *
8
 * @copyright Copyright (c) the Saito Project Developers
9
 * @link https://github.com/Schlaefer/Saito
10
 * @license http://opensource.org/licenses/MIT
11
 */
12
13
namespace Saito\Posting\UserPosting;
14
15
use Cake\Core\Configure;
16
use Saito\Posting\Basic\BasicPostingInterface;
17
use Saito\User\CurrentUser\CurrentUserInterface;
18
use Saito\User\Permission\ResourceAI;
19
20
/**
21
 * Implements UserPostingInterface
22
 */
23
trait UserPostingTrait
24
{
25
    /**
26
     * @var array
27
     */
28
    private $_userPostingTraitUnreadCache = [];
29
30
    /**
31
     * @var CurrentUserInterface
32
     */
33
    private $_CurrentUser;
34
35
    /**
36
     * {@inheritDoc}
37
     */
38
    public function getCurrentUser(): CurrentUserInterface
39
    {
40
        return $this->_CurrentUser;
41
    }
42
43
    /**
44
     * {@inheritDoc}
45
     */
46
    public function setCurrentUser(CurrentUserInterface $CurrentUser): void
47
    {
48
        $this->_CurrentUser = $CurrentUser;
49
    }
50
51
    /**
52
     * {@inheritDoc}
53
     */
54
    public function isAnsweringForbidden()
55
    {
56
        if ($this->isLocked()) {
0 ignored issues
show
Bug introduced by
It seems like isLocked() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

56
        if ($this->/** @scrutinizer ignore-call */ isLocked()) {
Loading history...
57
            return 'locked';
58
        }
59
        $permission = $this->_CurrentUser->getCategories()->permission('answer', $this->get('category'));
0 ignored issues
show
Bug introduced by
It seems like get() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

59
        $permission = $this->_CurrentUser->getCategories()->permission('answer', $this->/** @scrutinizer ignore-call */ get('category'));
Loading history...
60
61
        return !$permission;
62
    }
63
64
    /**
65
     * {@inheritDoc}
66
     */
67
    public function isBookmarked(): bool
68
    {
69
        return $this->_CurrentUser->hasBookmarked($this->get('id'));
70
    }
71
72
    /**
73
     * {@inheritDoc}
74
     */
75
    public function isEditingAllowed(): bool
76
    {
77
        return $this->_isEditingAllowed($this, $this->_CurrentUser);
0 ignored issues
show
Bug introduced by
$this of type Saito\Posting\UserPosting\UserPostingTrait is incompatible with the type Saito\Posting\Basic\BasicPostingInterface expected by parameter $posting of Saito\Posting\UserPostin...it::_isEditingAllowed(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

77
        return $this->_isEditingAllowed(/** @scrutinizer ignore-type */ $this, $this->_CurrentUser);
Loading history...
78
    }
79
80
    /**
81
     * {@inheritDoc}
82
     */
83
    public function isEditingAsUserAllowed(): bool
84
    {
85
        $MockedUser = clone $this->_CurrentUser;
86
        $MockedUser->set('user_type', 'user');
87
88
        return $this->_isEditingAllowed($this, $MockedUser);
0 ignored issues
show
Bug introduced by
$this of type Saito\Posting\UserPosting\UserPostingTrait is incompatible with the type Saito\Posting\Basic\BasicPostingInterface expected by parameter $posting of Saito\Posting\UserPostin...it::_isEditingAllowed(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

88
        return $this->_isEditingAllowed(/** @scrutinizer ignore-type */ $this, $MockedUser);
Loading history...
89
    }
90
91
    /**
92
     * Check if editing on the posting is forbidden.
93
     *
94
     * @param BasicPostingInterface $posting The posting.
95
     * @param CurrentUserInterface $User The user.
96
     * @return bool|string string if a reason is available.
97
     */
98
    protected function _isEditingAllowed(BasicPostingInterface $posting, CurrentUserInterface $User)
99
    {
100
        if ($User->isLoggedIn() !== true) {
101
            return false;
102
        }
103
104
        if ($User->permission('saito.core.posting.edit.unrestricted')) {
105
            return true;
106
        }
107
108
        /// Check category
109
        $action = $posting->isRoot() ? 'thread' : 'answer';
110
        $categoryAllowed = $User->getCategories()
111
            ->permission($action, $posting->get('category_id'));
112
        if (!$categoryAllowed) {
113
            return false;
114
        }
115
116
        $editPeriod = Configure::read('Saito.Settings.edit_period') * 60;
117
        $timeLimit = $editPeriod + ($posting->get('time')->format('U'));
118
        $isOverTime = time() > $timeLimit;
119
120
        $isOwn = $User->permission(
121
            'saito.core.posting.edit',
122
            (new ResourceAI())->onOwner($posting->get('user_id'))
123
        );
124
125
        if (!$isOverTime && $isOwn && !$this->isLocked()) {
126
            // Normal posting without special conditions.
127
            return true;
128
        }
129
130
        if ($User->permission('saito.core.posting.edit.restricted')) {
131
            if (!$isOwn || $posting->isPinned()) {
132
                return true;
133
            }
134
        }
135
136
        return false;
137
    }
138
139
    /**
140
     * {@inheritDoc}
141
     */
142
    public function isIgnored(): bool
143
    {
144
        return $this->_CurrentUser->ignores($this->get('user_id'));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_CurrentUs...($this->get('user_id')) could return the type array which is incompatible with the type-hinted return boolean. Consider adding an additional type-check to rule them out.
Loading history...
145
    }
146
147
    /**
148
     * {@inheritDoc}
149
     */
150
    public function isUnread(): bool
151
    {
152
        if (!isset($this->_userPostingTraitUnreadCache['isUnread'])) {
153
            $id = $this->get('id');
154
            $time = $this->get('time');
155
            $this->_userPostingTraitUnreadCache['isUnread'] = !$this->getCurrentUser()
156
                ->getReadPostings()->isRead($id, $time);
157
        }
158
159
        return $this->_userPostingTraitUnreadCache['isUnread'];
160
    }
161
162
    /**
163
     * {@inheritDoc}
164
     */
165
    public function hasNewAnswers(): bool
166
    {
167
        if (!$this->isRoot()) {
0 ignored issues
show
Bug introduced by
It seems like isRoot() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

167
        if (!$this->/** @scrutinizer ignore-call */ isRoot()) {
Loading history...
168
            throw new \RuntimeException(
169
                'Posting with id ' . $this->get('id') . ' is no root posting.'
170
            );
171
        }
172
        if (!$this->_CurrentUser->get('last_refresh')) {
173
            return false;
174
        }
175
176
        return $this->_CurrentUser->get('last_refresh_unix') < strtotime(
177
            $this->get('last_answer')
178
        );
179
    }
180
}
181