Total Complexity | 63 |
Total Lines | 522 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like EntriesController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use EntriesController, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
45 | class EntriesController extends AppController |
||
46 | { |
||
47 | use RequestActionTrait; |
||
|
|||
48 | |||
49 | public $helpers = ['Posting', 'Text']; |
||
50 | |||
51 | /** |
||
52 | * {@inheritDoc} |
||
53 | */ |
||
54 | public function initialize() |
||
62 | } |
||
63 | |||
64 | /** |
||
65 | * posting index |
||
66 | * |
||
67 | * @return void|\Cake\Http\Response |
||
68 | */ |
||
69 | public function index() |
||
70 | { |
||
71 | Stopwatch::start('Entries->index()'); |
||
72 | |||
73 | //= determine user sort order |
||
74 | $sortKey = 'last_answer'; |
||
75 | if (!$this->CurrentUser->get('user_sort_last_answer')) { |
||
76 | $sortKey = 'time'; |
||
77 | } |
||
78 | $order = ['fixed' => 'DESC', $sortKey => 'DESC']; |
||
79 | |||
80 | //= get threads |
||
81 | $threads = $this->Threads->paginate($order, $this->CurrentUser); |
||
82 | $this->set('entries', $threads); |
||
83 | |||
84 | $currentPage = (int)$this->request->getQuery('page') ?: 1; |
||
85 | if ($currentPage > 1) { |
||
86 | $this->set('titleForLayout', __('page') . ' ' . $currentPage); |
||
87 | } |
||
88 | if ($currentPage === 1) { |
||
89 | if ($this->MarkAsRead->refresh()) { |
||
90 | return $this->redirect(['action' => 'index']); |
||
91 | } |
||
92 | $this->MarkAsRead->next(); |
||
93 | } |
||
94 | |||
95 | // @bogus |
||
96 | $this->request->getSession()->write('paginator.lastPage', $currentPage); |
||
97 | $this->set('showDisclaimer', true); |
||
98 | $this->set('showBottomNavigation', true); |
||
99 | $this->Slidetabs->show(); |
||
100 | |||
101 | $this->_setupCategoryChooser($this->CurrentUser); |
||
102 | |||
103 | /** @var AutoReloadComponent */ |
||
104 | $autoReload = $this->loadComponent('AutoReload'); |
||
105 | $autoReload->after($this->CurrentUser); |
||
106 | |||
107 | Stopwatch::stop('Entries->index()'); |
||
108 | } |
||
109 | |||
110 | /** |
||
111 | * Mix view |
||
112 | * |
||
113 | * @param string $tid thread-ID |
||
114 | * @return void|Response |
||
115 | * @throws NotFoundException |
||
116 | */ |
||
117 | public function mix($tid) |
||
118 | { |
||
119 | $tid = (int)$tid; |
||
120 | if ($tid <= 0) { |
||
121 | throw new BadRequestException(); |
||
122 | } |
||
123 | |||
124 | try { |
||
125 | $postings = $this->Entries->postingsForThread($tid, true, $this->CurrentUser); |
||
126 | } catch (RecordNotFoundException $e) { |
||
127 | /// redirect sub-posting to mix view of thread |
||
128 | $actualTid = $this->Entries->getThreadId($tid); |
||
129 | |||
130 | return $this->redirect([$actualTid, '#' => $tid], 301); |
||
131 | } |
||
132 | |||
133 | // check if anonymous tries to access internal categories |
||
134 | $root = $postings; |
||
135 | if (!$this->CurrentUser->getCategories()->permission('read', $root->get('category'))) { |
||
136 | return $this->_requireAuth(); |
||
137 | } |
||
138 | |||
139 | $this->_setRootEntry($root); |
||
140 | $this->Title->setFromPosting($root, __('view.type.mix')); |
||
141 | |||
142 | $this->set('showBottomNavigation', true); |
||
143 | $this->set('entries', $postings); |
||
144 | |||
145 | $this->_showAnsweringPanel(); |
||
146 | |||
147 | $this->Threads->incrementViewsForThread($root, $this->CurrentUser); |
||
148 | $this->MarkAsRead->thread($postings); |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * load front page force all entries mark-as-read |
||
153 | * |
||
154 | * @return void |
||
155 | */ |
||
156 | public function update() |
||
157 | { |
||
158 | $this->autoRender = false; |
||
159 | $this->CurrentUser->getLastRefresh()->set(); |
||
160 | $this->redirect('/entries/index'); |
||
161 | } |
||
162 | |||
163 | /** |
||
164 | * Outputs raw markup of an posting $id |
||
165 | * |
||
166 | * @param string $id posting-ID |
||
167 | * @return void |
||
168 | */ |
||
169 | public function source($id = null) |
||
170 | { |
||
171 | $this->viewBuilder()->enableAutoLayout(false); |
||
172 | $this->view($id); |
||
173 | } |
||
174 | |||
175 | /** |
||
176 | * View posting. |
||
177 | * |
||
178 | * @param string $id posting-ID |
||
179 | * @return \Cake\Http\Response|void |
||
180 | */ |
||
181 | public function view(string $id) |
||
182 | { |
||
183 | $id = (int)$id; |
||
184 | Stopwatch::start('Entries->view()'); |
||
185 | |||
186 | $entry = $this->Entries->get($id); |
||
187 | $posting = $entry->toPosting()->withCurrentUser($this->CurrentUser); |
||
188 | |||
189 | if (!$this->CurrentUser->getCategories()->permission('read', $posting->get('category'))) { |
||
190 | return $this->_requireAuth(); |
||
191 | } |
||
192 | |||
193 | $this->set('entry', $posting); |
||
194 | $this->Threads->incrementViewsForPosting($posting, $this->CurrentUser); |
||
195 | $this->_setRootEntry($posting); |
||
196 | $this->_showAnsweringPanel(); |
||
197 | |||
198 | $this->MarkAsRead->posting($posting); |
||
199 | |||
200 | // inline open |
||
201 | if ($this->request->is('ajax')) { |
||
202 | return $this->render('/Element/entry/view_posting'); |
||
203 | } |
||
204 | |||
205 | // full page request |
||
206 | $this->set( |
||
207 | 'tree', |
||
208 | $this->Entries->postingsForThread($posting->get('tid'), false, $this->CurrentUser) |
||
209 | ); |
||
210 | $this->Title->setFromPosting($posting); |
||
211 | |||
212 | Stopwatch::stop('Entries->view()'); |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * Add new posting. |
||
217 | * |
||
218 | * @return void|\Cake\Http\Response |
||
219 | */ |
||
220 | public function add() |
||
221 | { |
||
222 | $titleForPage = __('Write a New Posting'); |
||
223 | $this->set(compact('titleForPage')); |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Edit posting |
||
228 | * |
||
229 | * @param string $id posting-ID |
||
230 | * @return void|\Cake\Http\Response |
||
231 | * @throws NotFoundException |
||
232 | * @throws BadRequestException |
||
233 | */ |
||
234 | public function edit(string $id) |
||
235 | { |
||
236 | $id = (int)$id; |
||
237 | $entry = $this->Entries->get($id); |
||
238 | $posting = $entry->toPosting()->withCurrentUser($this->CurrentUser); |
||
239 | |||
240 | if (!$posting->isEditingAllowed()) { |
||
241 | throw new SaitoForbiddenException( |
||
242 | 'Access to posting in EntriesController:edit() forbidden.', |
||
243 | ['CurrentUser' => $this->CurrentUser] |
||
244 | ); |
||
245 | } |
||
246 | |||
247 | // show editing form |
||
248 | if (!$posting->isEditingAsUserAllowed()) { |
||
249 | $this->Flash->set( |
||
250 | __('notice_you_are_editing_as_mod'), |
||
251 | ['element' => 'warning'] |
||
252 | ); |
||
253 | } |
||
254 | |||
255 | $this->set(compact('posting')); |
||
256 | |||
257 | // set headers |
||
258 | $this->set( |
||
259 | 'headerSubnavLeftTitle', |
||
260 | __('back_to_posting_from_linkname', $posting->get('name')) |
||
261 | ); |
||
262 | $this->set('headerSubnavLeftUrl', ['action' => 'view', $id]); |
||
263 | $this->set('form_title', __('edit_linkname')); |
||
264 | $this->render('/Entries/add'); |
||
265 | } |
||
266 | |||
267 | /** |
||
268 | * Get thread-line to insert after an inline-answer |
||
269 | * |
||
270 | * @param string $id posting-ID |
||
271 | * @return void|\Cake\Http\Response |
||
272 | */ |
||
273 | public function threadLine($id = null) |
||
274 | { |
||
275 | $posting = $this->Entries->get($id)->toPosting()->withCurrentUser($this->CurrentUser); |
||
276 | if (!$this->CurrentUser->getCategories()->permission('read', $posting->get('category'))) { |
||
277 | return $this->_requireAuth(); |
||
278 | } |
||
279 | |||
280 | $this->set('entrySub', $posting); |
||
281 | // ajax requests so far are always answers |
||
282 | $this->response = $this->response->withType('json'); |
||
283 | $this->set('level', '1'); |
||
284 | } |
||
285 | |||
286 | /** |
||
287 | * Delete posting |
||
288 | * |
||
289 | * @param string $id posting-ID |
||
290 | * @return void |
||
291 | * @throws NotFoundException |
||
292 | * @throws MethodNotAllowedException |
||
293 | */ |
||
294 | public function delete(string $id) |
||
295 | { |
||
296 | //$this->request->allowMethod(['post', 'delete']); |
||
297 | $id = (int)$id; |
||
298 | if (!$id) { |
||
299 | throw new NotFoundException(); |
||
300 | } |
||
301 | /* @var Entry $posting */ |
||
302 | $posting = $this->Entries->get($id); |
||
303 | |||
304 | $action = $posting->isRoot() ? 'thread' : 'answer'; |
||
305 | $allowed = $this->CurrentUser->getCategories() |
||
306 | ->permission($action, $posting->get('category_id')); |
||
307 | if (!$allowed) { |
||
308 | throw new SaitoForbiddenException(); |
||
309 | } |
||
310 | |||
311 | $success = $this->Entries->deletePosting($id); |
||
312 | |||
313 | if ($success) { |
||
314 | $flashType = 'success'; |
||
315 | if ($posting->isRoot()) { |
||
316 | $message = __('delete_tree_success'); |
||
317 | $redirect = '/'; |
||
318 | } else { |
||
319 | $message = __('delete_subtree_success'); |
||
320 | $redirect = '/entries/view/' . $posting->get('pid'); |
||
321 | } |
||
322 | } else { |
||
323 | $flashType = 'error'; |
||
324 | $message = __('delete_tree_error'); |
||
325 | $redirect = $this->referer(); |
||
326 | } |
||
327 | $this->Flash->set($message, ['element' => $flashType]); |
||
328 | $this->redirect($redirect); |
||
329 | } |
||
330 | |||
331 | /** |
||
332 | * Empty function for benchmarking |
||
333 | * |
||
334 | * @return void |
||
335 | */ |
||
336 | public function e() |
||
337 | { |
||
338 | Stopwatch::start('Entries->e()'); |
||
339 | Stopwatch::stop('Entries->e()'); |
||
340 | } |
||
341 | |||
342 | /** |
||
343 | * Marks sub-entry $id as solution to its current root-entry |
||
344 | * |
||
345 | * @param string $id posting-ID |
||
346 | * @return void |
||
347 | * @throws BadRequestException |
||
348 | */ |
||
349 | public function solve($id) |
||
350 | { |
||
351 | $this->autoRender = false; |
||
352 | try { |
||
353 | $posting = $this->Entries->get($id); |
||
354 | |||
355 | if (empty($posting)) { |
||
356 | throw new \InvalidArgumentException('Posting to mark solved not found.'); |
||
357 | } |
||
358 | |||
359 | $rootId = $posting->get('tid'); |
||
360 | $rootPosting = $this->Entries->get($rootId); |
||
361 | |||
362 | $allowed = $this->CurrentUser->permission( |
||
363 | 'saito.core.posting.solves.set', |
||
364 | (new ResourceAI())->onRole($rootPosting->get('user')->getRole())->onOwner($rootPosting->get('user_id')) |
||
365 | ); |
||
366 | if (!$allowed) { |
||
367 | throw new SaitoForbiddenException( |
||
368 | sprintf('Attempt to mark posting %s as solution.', $posting->get('id')), |
||
369 | ['CurrentUser' => $this->CurrentUser] |
||
370 | ); |
||
371 | } |
||
372 | |||
373 | $value = $posting->get('solves') ? 0 : $rootPosting->get('tid'); |
||
374 | $success = $this->Entries->updateEntry($posting, ['solves' => $value]); |
||
375 | |||
376 | if (!$success) { |
||
377 | throw new BadRequestException(); |
||
378 | } |
||
379 | } catch (\Exception $e) { |
||
380 | throw new BadRequestException(); |
||
381 | } |
||
382 | } |
||
383 | |||
384 | /** |
||
385 | * Merge threads. |
||
386 | * |
||
387 | * @param string $sourceId posting-ID of thread to be merged |
||
388 | * @return void |
||
389 | * @throws NotFoundException |
||
390 | * @td put into admin entries controller |
||
391 | */ |
||
392 | public function merge(string $sourceId = null) |
||
393 | { |
||
394 | $sourceId = (int)$sourceId; |
||
395 | if (empty($sourceId)) { |
||
396 | throw new NotFoundException(); |
||
397 | } |
||
398 | |||
399 | /* @var Entry */ |
||
400 | $entry = $this->Entries->findById($sourceId)->first(); |
||
401 | |||
402 | if (!$entry || !$entry->isRoot()) { |
||
403 | throw new NotFoundException(); |
||
404 | } |
||
405 | |||
406 | // perform move operation |
||
407 | $targetId = $this->request->getData('targetId'); |
||
408 | if (!empty($targetId)) { |
||
409 | if ($this->Entries->threadMerge($sourceId, $targetId)) { |
||
410 | $this->redirect('/entries/view/' . $sourceId); |
||
411 | |||
412 | return; |
||
413 | } else { |
||
414 | $this->Flash->set(__('Error'), ['element' => 'error']); |
||
415 | } |
||
416 | } |
||
417 | |||
418 | $this->viewBuilder()->setLayout('Admin.admin'); |
||
419 | $this->set('posting', $entry); |
||
420 | } |
||
421 | |||
422 | /** |
||
423 | * Toggle posting property via ajax request. |
||
424 | * |
||
425 | * @param string $id posting-ID |
||
426 | * @param string $toggle property |
||
427 | * |
||
428 | * @return \Cake\Http\Response |
||
429 | */ |
||
430 | public function ajaxToggle($id = null, $toggle = null) |
||
431 | { |
||
432 | $allowed = ['fixed', 'locked']; |
||
433 | if ( |
||
434 | !$id |
||
435 | || !$toggle |
||
436 | || !$this->request->is('ajax') |
||
437 | || !in_array($toggle, $allowed) |
||
438 | ) { |
||
439 | throw new BadRequestException(); |
||
440 | } |
||
441 | |||
442 | $posting = $this->Entries->get($id); |
||
443 | $data = ['id' => (int)$id, $toggle => !$posting->get($toggle)]; |
||
444 | $this->Posting->update($posting, $data, $this->CurrentUser); |
||
445 | |||
446 | $this->response = $this->response->withType('json'); |
||
447 | $this->response = $this->response->withStringBody(json_encode('OK')); |
||
448 | |||
449 | return $this->response; |
||
450 | } |
||
451 | |||
452 | /** |
||
453 | * {@inheritDoc} |
||
454 | */ |
||
455 | public function beforeFilter(Event $event) |
||
471 | } |
||
472 | |||
473 | /** |
||
474 | * set view vars for category chooser |
||
475 | * |
||
476 | * @param CurrentUserInterface $User CurrentUser |
||
477 | * @return void |
||
478 | */ |
||
479 | protected function _setupCategoryChooser(CurrentUserInterface $User) |
||
480 | { |
||
481 | if (!$User->isLoggedIn()) { |
||
482 | return; |
||
483 | } |
||
484 | $globalActivation = Configure::read( |
||
485 | 'Saito.Settings.category_chooser_global' |
||
486 | ); |
||
487 | if (!$globalActivation) { |
||
488 | if ( |
||
489 | !Configure::read( |
||
490 | 'Saito.Settings.category_chooser_user_override' |
||
491 | ) |
||
492 | ) { |
||
493 | return; |
||
494 | } |
||
495 | if (!$User->get('user_category_override')) { |
||
496 | return; |
||
497 | } |
||
498 | } |
||
499 | |||
500 | $this->set( |
||
501 | 'categoryChooserChecked', |
||
502 | $User->getCategories()->getCustom('read') |
||
503 | ); |
||
504 | switch ($User->getCategories()->getType()) { |
||
505 | case 'single': |
||
506 | $title = $User->get('user_category_active'); |
||
507 | break; |
||
508 | case 'custom': |
||
509 | $title = __('Custom'); |
||
510 | break; |
||
511 | default: |
||
512 | $title = __('All Categories'); |
||
513 | } |
||
514 | $this->set('categoryChooserTitleId', $title); |
||
515 | $this->set( |
||
516 | 'categoryChooser', |
||
517 | $User->getCategories()->getAll('read', 'select') |
||
518 | ); |
||
519 | } |
||
520 | |||
521 | /** |
||
522 | * Decide if an answering panel is show when rendering a posting |
||
523 | * |
||
524 | * @return void |
||
525 | */ |
||
526 | protected function _showAnsweringPanel() |
||
527 | { |
||
528 | $showAnsweringPanel = false; |
||
529 | |||
530 | if ($this->CurrentUser->isLoggedIn()) { |
||
531 | // Only logged in users see the answering buttons if they … |
||
532 | if ( |
||
533 | // … directly on entries/view but not inline |
||
534 | ($this->request->getParam('action') === 'view' && !$this->request->is('ajax')) |
||
535 | // … directly in entries/mix |
||
536 | || $this->request->getParam('action') === 'mix' |
||
537 | // … inline viewing … on entries/index. |
||
538 | || ($this->Referer->wasController('entries') |
||
539 | && $this->Referer->wasAction('index')) |
||
540 | ) { |
||
541 | $showAnsweringPanel = true; |
||
542 | } |
||
543 | } |
||
544 | $this->set('showAnsweringPanel', $showAnsweringPanel); |
||
545 | } |
||
546 | |||
547 | /** |
||
548 | * makes root posting of $posting avaiable in view |
||
549 | * |
||
550 | * @param BasicPostingInterface $posting posting for root entry |
||
551 | * @return void |
||
552 | */ |
||
553 | protected function _setRootEntry(BasicPostingInterface $posting): void |
||
567 | } |
||
568 | } |
||
569 |
This trait has been deprecated. The supplier of the trait has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the trait will be removed and what other trait to use instead.