Passed
Branch master (249862)
by Adam
07:51
created

TopicController   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 277
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 277
rs 9
c 0
b 0
f 0
wmc 35

9 Methods

Rating   Name   Duplication   Size   Complexity  
A findByObject() 0 4 1
A mark() 0 11 2
A getFlags() 0 3 1
A getParsers() 0 6 1
F index() 0 126 18
A getActivities() 0 13 2
B prompt() 0 21 5
A getWarnings() 0 14 3
A subscribe() 0 11 2
1
<?php
2
3
namespace Coyote\Http\Controllers\Forum;
4
5
use Coyote\Forum;
6
use Coyote\Forum\Reason;
7
use Coyote\Http\Factories\CacheFactory;
8
use Coyote\Http\Factories\FlagFactory;
9
use Coyote\Repositories\Contracts\StreamRepositoryInterface as StreamRepository;
10
use Coyote\Repositories\Contracts\UserRepositoryInterface as User;
11
use Coyote\Repositories\Criteria\Forum\OnlyThoseWithAccess;
12
use Coyote\Repositories\Criteria\Post\ObtainSubscribers;
13
use Coyote\Repositories\Criteria\Post\WithTrashed;
14
use Coyote\Services\Elasticsearch\Builders\Forum\MoreLikeThisBuilder;
15
use Coyote\Services\Forum\TreeBuilder;
16
use Coyote\Services\Parser\Parsers\ParserInterface;
17
use Illuminate\Http\Request;
18
use Illuminate\Pagination\LengthAwarePaginator;
19
use Coyote\Topic;
20
21
class TopicController extends BaseController
22
{
23
    use CacheFactory, FlagFactory;
24
25
    /**
26
     * @var \Illuminate\Contracts\Auth\Access\Gate
27
     */
28
    private $gate;
29
30
    /**
31
     * @param Request $request
32
     * @param \Coyote\Forum $forum
33
     * @param \Coyote\Topic $topic
34
     * @return \Illuminate\View\View
35
     */
36
    public function index(Request $request, $forum, $topic)
37
    {
38
        // get the topic (and forum) mark time value from middleware
39
        // @see \Coyote\Http\Middleware\ScrollToPost
40
        $markTime = $request->attributes->get('mark_time');
41
42
        $this->gate = $this->getGateFactory();
43
44
        // current page...
45
        $page = (int) $request->get('page');
46
        // number of answers
47
        $replies = $topic->replies;
48
        // number of posts per one page
49
        $perPage = $this->postsPerPage($request);
50
51
        // user with forum-update ability WILL see every post
52
        if ($this->gate->allows('delete', $forum)) {
53
            $this->post->pushCriteria(new WithTrashed());
54
            // user is able to see real number of posts in this topic
55
            $replies = $topic->replies_real;
56
        }
57
58
        // user wants to show certain post. we need to calculate page number based on post id.
59
        if ($request->has('p')) {
60
            $page = $this->post->getPage(min(2147483647, (int) $request->get('p')), $topic->id, $perPage);
61
        }
62
63
        // build "more like this" block. it's important to send elasticsearch query before
64
        // send SQL query to database because search() method exists only in Model and not Builder class.
65
        $mlt = $this->getCacheFactory()->remember('mlt-post:' . $topic->id, 60 * 24, function () use ($topic) {
66
            $this->forum->pushCriteria(new OnlyThoseWithAccess());
67
68
            $builder = new MoreLikeThisBuilder($topic, $this->forum->pluck('id'));
69
70
            // search related topics
71
            $mlt = $this->topic->search($builder);
0 ignored issues
show
Bug introduced by
The method search() does not exist on Coyote\Repositories\Cont...opicRepositoryInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Coyote\Repositories\Cont...opicRepositoryInterface. ( Ignorable by Annotation )

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

71
            /** @scrutinizer ignore-call */ 
72
            $mlt = $this->topic->search($builder);
Loading history...
72
73
            // it's important to reset criteria for the further queries
74
            $this->forum->resetCriteria();
75
            return $mlt;
76
        });
77
78
        $this->post->pushCriteria(new ObtainSubscribers($this->userId));
79
80
        // magic happens here. get posts for given topic (including first post for every page)
81
        /* @var \Illuminate\Support\Collection $posts */
82
        $posts = $this->post->takeForTopic($topic->id, $topic->first_post_id, $page, $perPage);
83
        $paginate = new LengthAwarePaginator($posts, $replies, $perPage, $page, ['path' => ' ']);
84
85
        start_measure('Parsing...');
86
        $parser = $this->getParsers();
87
88
        /** @var \Coyote\Post $post */
89
        foreach ($posts as &$post) {
90
            // parse post or get it from cache
91
            $post->text = $parser['post']->parse($post->text);
92
93
            if ((auth()->guest() || (auth()->check() && $this->auth->allow_sig)) && $post->sig) {
94
                $post->sig = $parser['sig']->parse($post->sig);
95
            }
96
97
            foreach ($post->comments as &$comment) {
98
                $comment->text = $parser['comment']->setUserId($comment->user_id)->parse($comment->text);
0 ignored issues
show
Bug introduced by
The method setUserId() does not exist on Coyote\Services\Parser\Parsers\ParserInterface. It seems like you code against a sub-type of Coyote\Services\Parser\Parsers\ParserInterface such as Coyote\Services\Parser\Parsers\Emphasis. ( Ignorable by Annotation )

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

98
                $comment->text = $parser['comment']->/** @scrutinizer ignore-call */ setUserId($comment->user_id)->parse($comment->text);
Loading history...
99
            }
100
101
            $post->setRelation('topic', $topic);
102
            $post->setRelation('forum', $forum);
103
        }
104
105
        stop_measure('Parsing...');
106
107
        $postsId = $posts->pluck('id')->toArray();
108
        $dateTimeString = $posts->last()->created_at->toDateTimeString();
109
110
        if ($markTime[Topic::class] < $dateTimeString && $markTime[Forum::class] < $dateTimeString) {
111
            // mark topic as read. the date MUST be data of last post on this page
112
            $topic->markAsRead($dateTimeString, $this->guestId);
113
            $isUnread = true;
114
115
            if ($markTime[Forum::class] < $dateTimeString) {
116
                $isUnread = $this->topic->isUnread(
117
                    $forum->id,
118
                    $markTime[Forum::class],
119
                    $this->guestId
120
                );
121
            }
122
123
            if (!$isUnread) {
124
                $this->forum->markAsRead($forum->id, $this->guestId);
125
            }
126
        }
127
128
        // create forum list for current user (according to user's privileges)
129
        $this->pushForumCriteria();
130
131
        $treeBuilder = new TreeBuilder();
132
        $forumList = $treeBuilder->listBySlug($this->forum->list());
133
134
        $this->breadcrumb->push($topic->subject, route('forum.topic', [$forum->slug, $topic->id, $topic->slug]));
135
136
        $flags = $activities = [];
137
138
        if ($this->gate->allows('delete', $forum) || $this->gate->allows('move', $forum)) {
139
            $reasonList = Reason::pluck('name', 'id')->toArray();
140
141
            if ($this->gate->allows('delete', $forum)) {
142
                $flags = $this->getFlags($postsId);
143
                $activities = $this->getActivities($postsId);
144
            }
145
146
            $this->forum->skipCriteria(true);
147
            $adminForumList = $treeBuilder->listBySlug($this->forum->list());
148
        }
149
150
        // informacje o powodzie zablokowania watku, przeniesienia itp
151
        $warnings = $this->getWarnings($topic);
152
153
        $form = $this->getForm($forum, $topic);
154
155
        return $this->view(
156
            'forum.topic',
157
            compact('posts', 'forum', 'topic', 'paginate', 'forumList', 'adminForumList', 'reasonList', 'form', 'mlt', 'flags', 'warnings', 'activities')
158
        )->with([
159
            'markTime'      => $markTime[Topic::class] ? $markTime[Topic::class] : $markTime[Forum::class],
160
            'subscribers'   => $this->userId ? $topic->subscribers()->pluck('topic_id', 'user_id') : [],
161
            'author_id'     => $posts[0]->user_id
162
        ]);
163
    }
164
165
    /**
166
     * @return ParserInterface[]
167
     */
168
    private function getParsers()
169
    {
170
        return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('post' => a...' => app('parser.sig')) returns the type array<string,Coyote\Serv...r\Factories\SigFactory> which is incompatible with the documented return type Coyote\Services\Parser\Parsers\ParserInterface[].
Loading history...
171
            'post'      => app('parser.post'),
172
            'comment'   => app('parser.comment'),
173
            'sig'       => app('parser.sig')
174
        ];
175
    }
176
177
    /**
178
     * @param array $postsId
179
     * @return mixed
180
     */
181
    private function getFlags($postsId)
182
    {
183
        return $this->getFlagFactory()->takeForPosts($postsId);
184
    }
185
186
    /**
187
     * @param int[] $postsId
188
     * @return array
189
     */
190
    private function getActivities($postsId)
191
    {
192
        $activities = [];
193
194
        // here we go. if user has delete ability, for sure he/she would like to know
195
        // why posts were deleted and by whom
196
        $collection = $this->findByObject('post', $postsId, 'delete');
197
198
        foreach ($collection->sortByDesc('created_at')->groupBy('object.id') as $row) {
199
            $activities[$row->first()['object.id']] = $row->first();
200
        }
201
202
        return $activities;
203
    }
204
205
    /**
206
     * @param \Coyote\Topic $topic
207
     * @return array
208
     */
209
    private function getWarnings($topic)
210
    {
211
        $warnings = [];
212
213
        // if topic is locked we need to fetch information when and by whom
214
        if ($topic->is_locked) {
215
            $warnings['lock'] = $this->findByObject('topic', $topic->id, 'lock')->last();
216
        }
217
218
        if ($topic->prev_forum_id) {
219
            $warnings['move'] = $this->findByObject('topic', $topic->id, 'move')->last();
220
        }
221
222
        return $warnings;
223
    }
224
225
    /**
226
     * @param \Coyote\Topic $topic
227
     * @return \Symfony\Component\HttpFoundation\Response
228
     */
229
    public function subscribe($topic)
230
    {
231
        $subscriber = $topic->subscribers()->forUser($this->userId)->first();
232
233
        if ($subscriber) {
234
            $subscriber->delete();
235
        } else {
236
            $topic->subscribers()->create(['user_id' => $this->userId]);
237
        }
238
239
        return response($topic->subscribers()->count());
0 ignored issues
show
Bug Best Practice introduced by
The expression return response($topic->subscribers()->count()) also could return the type Illuminate\Contracts\Routing\ResponseFactory which is incompatible with the documented return type Symfony\Component\HttpFoundation\Response.
Loading history...
240
    }
241
242
    /**
243
     * @param $id
244
     * @param User $user
245
     * @param Request $request
246
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
247
     */
248
    public function prompt($id, User $user, Request $request)
249
    {
250
        $this->validate($request, ['q' => 'username']);
251
        $usersId = [];
252
253
        $posts = $this->post->findAllBy('topic_id', $id, ['id', 'user_id']);
254
        $posts->load('comments'); // load comments assigned to posts
255
256
        foreach ($posts as $post) {
257
            if ($post->user_id) {
258
                $usersId[] = $post->user_id;
259
            }
260
261
            foreach ($post->comments as $comment) {
262
                if ($comment->user_id) {
263
                    $usersId[] = $comment->user_id;
264
                }
265
            }
266
        }
267
268
        return view('components.prompt')->with('users', $user->lookupName($request['q'], array_unique($usersId)));
269
    }
270
271
    /**
272
     * @param \Coyote\Topic $topic
273
     */
274
    public function mark($topic)
275
    {
276
        // pobranie daty i godziny ostatniego razy gdy uzytkownik przeczytal to forum
277
        $forumMarkTime = $topic->forum->markTime($this->guestId);
278
279
        // mark topic as read
280
        $topic->markAsRead($topic->last_post_created_at, $this->guestId);
281
        $isUnread = $this->topic->isUnread($topic->forum_id, $forumMarkTime, $this->guestId);
282
283
        if (!$isUnread) {
284
            $this->forum->markAsRead($topic->forum_id, $this->guestId);
285
        }
286
    }
287
288
    /**
289
     * @param string $object
290
     * @param $id
291
     * @param string $verb
292
     * @return mixed
293
     */
294
    protected function findByObject($object, $id, $verb)
295
    {
296
        return app(StreamRepository::class)->findWhere(
297
            ['object.objectType' => $object, 'object.id' => $id, 'verb' => $verb]
298
        );
299
    }
300
}
301