Passed
Push — portfolio ( 1f492e )
by Angel Fernando Quiroz
11:49
created

PortfolioController   F

Complexity

Total Complexity 70

Size/Duplication

Total Lines 981
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 544
dl 0
loc 981
rs 2.8
c 4
b 0
f 0
wmc 70

19 Methods

Rating   Name   Duplication   Size   Complexity  
A addCategory() 0 54 3
A __construct() 0 17 4
A showHideCategory() 0 17 2
A deleteCategory() 0 15 2
A addItem() 0 72 3
A editCategory() 0 66 4
A copyComment() 0 28 1
A copyItem() 0 28 1
B editItem() 0 91 8
B view() 0 120 5
B teacherCopyComment() 0 76 5
B teacherCopyItem() 0 76 5
A deleteItem() 0 15 2
A renderView() 0 29 5
A showHideItem() 0 19 2
A createCommentForm() 0 36 2
A categoryBelongToOwner() 0 7 2
A itemBelongToOwner() 0 15 6
B index() 0 85 8

How to fix   Complexity   

Complex Class

Complex classes like PortfolioController 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 PortfolioController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\Portfolio;
6
use Chamilo\CoreBundle\Entity\PortfolioCategory;
7
use Chamilo\CoreBundle\Entity\PortfolioComment;
8
9
/**
10
 * Class PortfolioController.
11
 */
12
class PortfolioController
13
{
14
    /**
15
     * @var string
16
     */
17
    public $baseUrl;
18
    /**
19
     * @var \Chamilo\CoreBundle\Entity\Course|null
20
     */
21
    private $course;
22
    /**
23
     * @var \Chamilo\CoreBundle\Entity\Session|null
24
     */
25
    private $session;
26
    /**
27
     * @var \Chamilo\UserBundle\Entity\User
28
     */
29
    private $owner;
30
    /**
31
     * @var int
32
     */
33
    private $currentUserId;
34
    /**
35
     * @var \Doctrine\ORM\EntityManager
36
     */
37
    private $em;
38
    /**
39
     * @var bool
40
     */
41
    private $allowEdit;
42
43
    /**
44
     * PortfolioController constructor.
45
     */
46
    public function __construct()
47
    {
48
        $this->em = Database::getManager();
49
50
        $this->currentUserId = api_get_user_id();
51
        $ownerId = isset($_GET['user']) ? (int) $_GET['user'] : $this->currentUserId;
52
        $this->owner = api_get_user_entity($ownerId);
53
        $this->course = api_get_course_entity(api_get_course_int_id());
54
        $this->session = api_get_session_entity(api_get_session_id());
55
56
        $cidreq = api_get_cidreq();
57
        $this->baseUrl = api_get_self().'?'.($cidreq ? $cidreq.'&' : '');
58
59
        $this->allowEdit = $this->currentUserId == $this->owner->getId();
60
61
        if (isset($_GET['preview'])) {
62
            $this->allowEdit = false;
63
        }
64
    }
65
66
    /**
67
     * @throws \Doctrine\ORM\ORMException
68
     * @throws \Doctrine\ORM\OptimisticLockException
69
     */
70
    public function addCategory()
71
    {
72
        global $interbreadcrumb;
73
74
        Display::addFlash(
75
            Display::return_message(get_lang('PortfolioCategoryFieldHelp'), 'info')
76
        );
77
78
        $form = new FormValidator('add_category', 'post', "{$this->baseUrl}&action=add_category");
79
80
        if (api_get_configuration_value('save_titles_as_html')) {
81
            $form->addHtmlEditor('title', get_lang('Title'), true, false, ['ToolbarSet' => 'TitleAsHtml']);
82
        } else {
83
            $form->addText('title', get_lang('Title'));
84
            $form->applyFilter('title', 'trim');
85
        }
86
87
        $form->addHtmlEditor('description', get_lang('Description'), false, false, ['ToolbarSet' => 'Minimal']);
88
        $form->addButtonCreate(get_lang('Create'));
89
90
        if ($form->validate()) {
91
            $values = $form->exportValues();
92
93
            $category = new PortfolioCategory();
94
            $category
95
                ->setTitle($values['title'])
96
                ->setDescription($values['description'])
97
                ->setUser($this->owner);
98
99
            $this->em->persist($category);
100
            $this->em->flush();
101
102
            Display::addFlash(
103
                Display::return_message(get_lang('CategoryAdded'), 'success')
104
            );
105
106
            header("Location: {$this->baseUrl}");
107
            exit;
108
        }
109
110
        $interbreadcrumb[] = [
111
            'name' => get_lang('Portfolio'),
112
            'url' => $this->baseUrl,
113
        ];
114
115
        $actions = [];
116
        $actions[] = Display::url(
117
            Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM),
118
            $this->baseUrl
119
        );
120
121
        $content = $form->returnForm();
122
123
        $this->renderView($content, get_lang('AddCategory'), $actions);
124
    }
125
126
    public function editCategory(PortfolioCategory $category)
127
    {
128
        global $interbreadcrumb;
129
130
        if (!$this->categoryBelongToOwner($category)) {
131
            api_not_allowed(true);
132
        }
133
134
        Display::addFlash(
135
            Display::return_message(get_lang('PortfolioCategoryFieldHelp'), 'info')
136
        );
137
138
        $form = new FormValidator(
139
            'edit_category',
140
            'post',
141
            $this->baseUrl."action=edit_category&id={$category->getId()}"
142
        );
143
144
        if (api_get_configuration_value('save_titles_as_html')) {
145
            $form->addHtmlEditor('title', get_lang('Title'), true, false, ['ToolbarSet' => 'TitleAsHtml']);
146
        } else {
147
            $form->addText('title', get_lang('Title'));
148
            $form->applyFilter('title', 'trim');
149
        }
150
151
        $form->addHtmlEditor('description', get_lang('Description'), false, false, ['ToolbarSet' => 'Minimal']);
152
        $form->addButtonUpdate(get_lang('Update'));
153
        $form->setDefaults(
154
            [
155
                'title' => $category->getTitle(),
156
                'description' => $category->getDescription(),
157
            ]
158
        );
159
160
        if ($form->validate()) {
161
            $values = $form->exportValues();
162
163
            $category
164
                ->setTitle($values['title'])
165
                ->setDescription($values['description']);
166
167
            $this->em->persist($category);
168
            $this->em->flush();
169
170
            Display::addFlash(
171
                Display::return_message(get_lang('Updated'), 'success')
172
            );
173
174
            header("Location: $this->baseUrl");
175
            exit;
176
        }
177
178
        $interbreadcrumb[] = [
179
            'name' => get_lang('Portfolio'),
180
            'url' => $this->baseUrl,
181
        ];
182
183
        $actions = [];
184
        $actions[] = Display::url(
185
            Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM),
186
            $this->baseUrl
187
        );
188
189
        $content = $form->returnForm();
190
191
        return $this->renderView($content, get_lang('EditCategory'), $actions);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->renderView($conte...itCategory'), $actions) targeting PortfolioController::renderView() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
192
    }
193
194
    public function showHideCategory(PortfolioCategory $category)
195
    {
196
        if (!$this->categoryBelongToOwner($category)) {
197
            api_not_allowed(true);
198
        }
199
200
        $category->setIsVisible(!$category->isVisible());
201
202
        $this->em->persist($category);
203
        $this->em->flush();
204
205
        Display::addFlash(
206
            Display::return_message(get_lang('VisibilityChanged'), 'success')
207
        );
208
209
        header("Location: $this->baseUrl");
210
        exit;
211
    }
212
213
    public function deleteCategory(PortfolioCategory $category)
214
    {
215
        if (!$this->categoryBelongToOwner($category)) {
216
            api_not_allowed(true);
217
        }
218
219
        $this->em->remove($category);
220
        $this->em->flush();
221
222
        Display::addFlash(
223
            Display::return_message(get_lang('CategoryDeleted'), 'success')
224
        );
225
226
        header("Location: $this->baseUrl");
227
        exit;
228
    }
229
230
    /**
231
     * @throws \Doctrine\ORM\ORMException
232
     * @throws \Doctrine\ORM\OptimisticLockException
233
     * @throws \Doctrine\ORM\TransactionRequiredException
234
     */
235
    public function addItem()
236
    {
237
        global $interbreadcrumb;
238
239
        $categories = $this->em
240
            ->getRepository('ChamiloCoreBundle:PortfolioCategory')
241
            ->findBy(['user' => $this->owner]);
242
243
        $form = new FormValidator('add_portfolio', 'post', $this->baseUrl.'action=add_item');
244
245
        if (api_get_configuration_value('save_titles_as_html')) {
246
            $form->addHtmlEditor('title', get_lang('Title'), true, false, ['ToolbarSet' => 'TitleAsHtml']);
247
        } else {
248
            $form->addText('title', get_lang('Title'));
249
            $form->applyFilter('title', 'trim');
250
        }
251
252
        $form->addHtmlEditor('content', get_lang('Content'), true, false, ['ToolbarSet' => 'NotebookStudent']);
253
        $form->addSelectFromCollection(
254
            'category',
255
            [get_lang('Category'), get_lang('PortfolioCategoryFieldHelp')],
256
            $categories,
257
            [],
258
            true
259
        );
260
        $form->addButtonCreate(get_lang('Create'));
261
262
        if ($form->validate()) {
263
            $values = $form->exportValues();
264
            $currentTime = new DateTime(
265
                api_get_utc_datetime(),
266
                new DateTimeZone('UTC')
267
            );
268
269
            $portfolio = new Portfolio();
270
            $portfolio
271
                ->setTitle($values['title'])
272
                ->setContent($values['content'])
273
                ->setUser($this->owner)
274
                ->setCourse($this->course)
275
                ->setSession($this->session)
276
                ->setCategory(
277
                    $this->em->find('ChamiloCoreBundle:PortfolioCategory', $values['category'])
278
                )
279
                ->setCreationDate($currentTime)
280
                ->setUpdateDate($currentTime);
281
282
            $this->em->persist($portfolio);
283
            $this->em->flush();
284
285
            Display::addFlash(
286
                Display::return_message(get_lang('PortfolioItemAdded'), 'success')
287
            );
288
289
            header("Location: $this->baseUrl");
290
            exit;
291
        }
292
293
        $interbreadcrumb[] = [
294
            'name' => get_lang('Portfolio'),
295
            'url' => $this->baseUrl,
296
        ];
297
298
        $actions = [];
299
        $actions[] = Display::url(
300
            Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM),
301
            $this->baseUrl
302
        );
303
304
        $content = $form->returnForm();
305
306
        $this->renderView($content, get_lang('AddPortfolioItem'), $actions);
307
    }
308
309
    /**
310
     * @throws \Doctrine\ORM\ORMException
311
     * @throws \Doctrine\ORM\OptimisticLockException
312
     * @throws \Doctrine\ORM\TransactionRequiredException
313
     */
314
    public function editItem(Portfolio $item)
315
    {
316
        global $interbreadcrumb;
317
318
        if (!$this->itemBelongToOwner($item)) {
319
            api_not_allowed(true);
320
        }
321
322
        $categories = $this->em
323
            ->getRepository('ChamiloCoreBundle:PortfolioCategory')
324
            ->findBy(['user' => $this->owner]);
325
326
        $form = new FormValidator('edit_portfolio', 'post', $this->baseUrl."action=edit_item&id={$item->getId()}");
327
328
        if (api_get_configuration_value('save_titles_as_html')) {
329
            $form->addHtmlEditor('title', get_lang('Title'), true, false, ['ToolbarSet' => 'TitleAsHtml']);
330
        } else {
331
            $form->addText('title', get_lang('Title'));
332
            $form->applyFilter('title', 'trim');
333
        }
334
335
        if ($item->getOrigin()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $item->getOrigin() of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. 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...
336
            if (Portfolio::TYPE_ITEM === $item->getOriginType()) {
337
                $origin = $this->em->find(Portfolio::class, $item->getOrigin());
338
339
                $form->addLabel(
340
                    sprintf(get_lang('PortfolioItemFromXUser'), $origin->getUser()->getCompleteName()),
341
                    Display::panel($origin->getContent())
342
                );
343
            } elseif (Portfolio::TYPE_COMMENT === $item->getOriginType()) {
344
                $origin = $this->em->find(PortfolioComment::class, $item->getOrigin());
345
346
                $form->addLabel(
347
                    sprintf(get_lang('PortfolioCommentFromXUser'), $origin->getAuthor()->getCompleteName()),
348
                    Display::panel($origin->getContent())
349
                );
350
            }
351
        }
352
353
        $form->addHtmlEditor('content', get_lang('Content'), true, false, ['ToolbarSet' => 'NotebookStudent']);
354
        $form->addSelectFromCollection(
355
            'category',
356
            [get_lang('Category'), get_lang('PortfolioCategoryFieldHelp')],
357
            $categories,
358
            [],
359
            true
360
        );
361
        $form->addButtonUpdate(get_lang('Update'));
362
        $form->setDefaults(
363
            [
364
                'title' => $item->getTitle(),
365
                'content' => $item->getContent(),
366
                'category' => $item->getCategory() ? $item->getCategory()->getId() : '',
367
            ]
368
        );
369
370
        if ($form->validate()) {
371
            $values = $form->exportValues();
372
            $currentTime = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
373
374
            $item
375
                ->setTitle($values['title'])
376
                ->setContent($values['content'])
377
                ->setUpdateDate($currentTime)
378
                ->setCategory(
379
                    $this->em->find('ChamiloCoreBundle:PortfolioCategory', $values['category'])
380
                );
381
382
            $this->em->persist($item);
383
            $this->em->flush();
384
385
            Display::addFlash(
386
                Display::return_message(get_lang('ItemUpdated'), 'success')
387
            );
388
389
            header("Location: $this->baseUrl");
390
            exit;
391
        }
392
393
        $interbreadcrumb[] = [
394
            'name' => get_lang('Portfolio'),
395
            'url' => $this->baseUrl,
396
        ];
397
        $actions = [];
398
        $actions[] = Display::url(
399
            Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM),
400
            $this->baseUrl
401
        );
402
        $content = $form->returnForm();
403
404
        $this->renderView($content, get_lang('EditPortfolioItem'), $actions);
405
    }
406
407
    /**
408
     * @throws \Doctrine\ORM\ORMException
409
     * @throws \Doctrine\ORM\OptimisticLockException
410
     */
411
    public function showHideItem(Portfolio $item)
412
    {
413
        if (!$this->itemBelongToOwner($item)) {
414
            api_not_allowed(true);
415
        }
416
417
        $item->setIsVisible(
418
            !$item->isVisible()
419
        );
420
421
        $this->em->persist($item);
422
        $this->em->flush();
423
424
        Display::addFlash(
425
            Display::return_message(get_lang('VisibilityChanged'), 'success')
426
        );
427
428
        header("Location: $this->baseUrl");
429
        exit;
430
    }
431
432
    /**
433
     * @throws \Doctrine\ORM\ORMException
434
     * @throws \Doctrine\ORM\OptimisticLockException
435
     */
436
    public function deleteItem(Portfolio $item)
437
    {
438
        if (!$this->itemBelongToOwner($item)) {
439
            api_not_allowed(true);
440
        }
441
442
        $this->em->remove($item);
443
        $this->em->flush();
444
445
        Display::addFlash(
446
            Display::return_message(get_lang('ItemDeleted'), 'success')
447
        );
448
449
        header("Location: $this->baseUrl");
450
        exit;
451
    }
452
453
    /**
454
     * @throws \Exception
455
     */
456
    public function index()
457
    {
458
        $actions = [];
459
460
        if ($this->currentUserId == $this->owner->getId()) {
461
            if ($this->allowEdit) {
462
                $actions[] = Display::url(
463
                    Display::return_icon('add.png', get_lang('Add'), [], ICON_SIZE_MEDIUM),
464
                    $this->baseUrl.'action=add_item'
465
                );
466
                $actions[] = Display::url(
467
                    Display::return_icon('folder.png', get_lang('AddCategory'), [], ICON_SIZE_MEDIUM),
468
                    $this->baseUrl.'action=add_category'
469
                );
470
                $actions[] = Display::url(
471
                    Display::return_icon('shared_setting.png', get_lang('Preview'), [], ICON_SIZE_MEDIUM),
472
                    $this->baseUrl.'preview=&user='.$this->owner->getId()
473
                );
474
            } else {
475
                $actions[] = Display::url(
476
                    Display::return_icon('shared_setting_na.png', get_lang('Preview'), [], ICON_SIZE_MEDIUM),
477
                    $this->baseUrl
478
                );
479
            }
480
        }
481
482
        $form = new FormValidator('a');
483
        $form->addUserAvatar('user', get_lang('User'), 'medium');
484
        $form->setDefaults(['user' => $this->owner]);
485
486
        $criteria = [];
487
488
        if (!$this->allowEdit) {
489
            $criteria['isVisible'] = true;
490
        }
491
492
        $categories = [];
493
494
        if (!$this->course) {
495
            $criteria['user'] = $this->owner;
496
497
            $categories = $this->em
498
                ->getRepository(PortfolioCategory::class)
499
                ->findBy($criteria);
500
        }
501
502
        if ($this->course) {
503
            unset($criteria['user']);
504
505
            $criteria['course'] = $this->course;
506
            $criteria['session'] = $this->session;
507
        } else {
508
            $criteria['user'] = $this->owner;
509
            $criteria['category'] = null;
510
        }
511
512
        $items = $this->em
513
            ->getRepository(Portfolio::class)
514
            ->findBy($criteria, ['creationDate' => 'DESC']);
515
516
        $items = array_filter(
517
            $items,
518
            function (Portfolio $item) {
519
                if ($this->currentUserId != $item->getUser()->getId()
520
                    && !$item->isVisible()
521
                ) {
522
                    return false;
523
                }
524
525
                return true;
526
            }
527
        );
528
529
        $template = new Template(null, false, false, false, false, false, false);
530
        $template->assign('user', $this->owner);
531
        $template->assign('course', $this->course);
532
        $template->assign('session', $this->session);
533
        $template->assign('allow_edit', $this->allowEdit);
534
        $template->assign('portfolio', $categories);
535
        $template->assign('uncategorized_items', $items);
536
537
        $layout = $template->get_template('portfolio/list.html.twig');
538
        $content = $template->fetch($layout);
539
540
        $this->renderView($content, get_lang('Portfolio'), $actions);
541
    }
542
543
    /**
544
     * @throws \Doctrine\ORM\ORMException
545
     * @throws \Doctrine\ORM\OptimisticLockException
546
     */
547
    public function view(Portfolio $item)
548
    {
549
        global $interbreadcrumb;
550
551
        $form = $this->createCommentForm($item);
552
553
        $commentsRepo = $this->em->getRepository(PortfolioComment::class);
554
555
        $query = $commentsRepo->createQueryBuilder('comment')
556
            ->where('comment.item = :item')
557
            ->orderBy('comment.root, comment.lft', 'ASC')
558
            ->setParameter('item', $item)
559
            ->getQuery();
560
561
        $clockIcon = Display::returnFontAwesomeIcon('clock-o', '', true);
562
563
        $commentsHtml = $commentsRepo->buildTree(
564
            $query->getArrayResult(),
565
            [
566
                'decorate' => true,
567
                'rootOpen' => '<ul class="media-list">',
568
                'rootClose' => '</ul>',
569
                'childOpen' => function ($node) use ($commentsRepo) {
570
                    /** @var PortfolioComment $comment */
571
                    $comment = $commentsRepo->find($node['id']);
572
                    $author = $comment->getAuthor();
573
574
                    $userPicture = UserManager::getUserPicture(
575
                        $comment->getAuthor()->getId(),
576
                        USER_IMAGE_SIZE_SMALL,
577
                        null,
578
                        [
579
                            'picture_uri' => $author->getPictureUri(),
580
                            'email' => $author->getEmail(),
581
                        ]
582
                    );
583
584
                    return '<li class="media">
585
                        <div class="media-left">
586
                            <img class="media-object thumbnail" src="'.$userPicture.'" alt="'.$author->getCompleteName().'">
587
                        </div>
588
                        <div class="media-body">';
589
                },
590
                'childClose' => '</div></li>',
591
                'nodeDecorator' => function ($node) use ($commentsRepo, $clockIcon) {
592
                    /** @var PortfolioComment $comment */
593
                    $comment = $commentsRepo->find($node['id']);
594
595
                    $commentActions = Display::url(
596
                        Display::return_icon('discuss.png', get_lang('ReplyToThisComment')),
597
                        '#',
598
                        [
599
                            'data-comment' => htmlspecialchars(
600
                                json_encode(['id' => $comment->getId()])
601
                            ),
602
                            'role' => 'button',
603
                            'class' => 'btn-reply-to',
604
                        ]
605
                    );
606
                    $commentActions .= PHP_EOL;
607
                    $commentActions .= Display::url(
608
                        Display::return_icon('copy.png', get_lang('CopyToMyPortfolio')),
609
                        $this->baseUrl.http_build_query(
610
                            [
611
                                'action' => 'copy',
612
                                'copy' => 'comment',
613
                                'id' => $comment->getId(),
614
                            ]
615
                        )
616
                    );
617
618
                    if (api_is_allowed_to_edit()) {
619
                        $commentActions .= Display::url(
620
                            Display::return_icon('copy.png', get_lang('CopyToStudentPortfolio')),
621
                            $this->baseUrl.http_build_query(
622
                                [
623
                                    'action' => 'teacher_copy',
624
                                    'copy' => 'comment',
625
                                    'id' => $comment->getId(),
626
                                ]
627
                            )
628
                        );
629
                    }
630
631
                    return '<p class="h4 media-heading">'.$comment->getAuthor()->getCompleteName().PHP_EOL.'<small>'
632
                        .$clockIcon.PHP_EOL.Display::dateToStringAgoAndLongDate($comment->getDate()).'</small>'
633
                        .'</p><div class="pull-right">'.$commentActions.'</div>'.$comment->getContent().PHP_EOL;
634
                },
635
            ]
636
        );
637
638
        $origin = null;
639
640
        if ($item->getOrigin() !== null) {
641
            if ($item->getOriginType() === Portfolio::TYPE_ITEM) {
642
                $origin = $this->em->find(Portfolio::class, $item->getOrigin());
643
            } elseif ($item->getOriginType() === Portfolio::TYPE_COMMENT) {
644
                $origin = $this->em->find(PortfolioComment::class, $item->getOrigin());
645
            }
646
        }
647
648
        $template = new Template(null, false, false, false, false, false, false);
649
        $template->assign('baseurl', $this->baseUrl);
650
        $template->assign('item', $item);
651
        $template->assign('origin', $origin);
652
        $template->assign('comments', $commentsHtml);
653
        $template->assign('form', $form);
654
655
        $layout = $template->get_template('portfolio/view.html.twig');
656
        $content = $template->fetch($layout);
657
658
        $interbreadcrumb[] = ['name' => get_lang('Portfolio'), 'url' => $this->baseUrl];
659
660
        $actions = [];
661
        $actions[] = Display::url(
662
            Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM),
663
            $this->baseUrl
664
        );
665
666
        $this->renderView($content, $item->getTitle(), $actions, false);
667
    }
668
669
    /**
670
     * @throws \Doctrine\ORM\ORMException
671
     * @throws \Doctrine\ORM\OptimisticLockException
672
     */
673
    public function copyItem(Portfolio $originItem)
674
    {
675
        $currentTime = api_get_utc_datetime(null, false, true);
676
677
        $portfolio = new Portfolio();
678
        $portfolio
679
            ->setIsVisible(false)
680
            ->setTitle(
681
                sprintf(get_lang('PortfolioItemFromXUser'), $originItem->getUser()->getCompleteName())
682
            )
683
            ->setContent('')
684
            ->setUser($this->owner)
685
            ->setOrigin($originItem->getId())
686
            ->setOriginType(Portfolio::TYPE_ITEM)
687
            ->setCourse($this->course)
688
            ->setSession($this->session)
689
            ->setCreationDate($currentTime)
690
            ->setUpdateDate($currentTime);
691
692
        $this->em->persist($portfolio);
693
        $this->em->flush();
694
695
        Display::addFlash(
696
            Display::return_message(get_lang('PortfolioItemAdded'), 'success')
697
        );
698
699
        header("Location: $this->baseUrl".http_build_query(['action' => 'edit_item', 'id' => $portfolio->getId()]));
700
        exit;
701
    }
702
703
    /**
704
     * @throws \Doctrine\ORM\ORMException
705
     * @throws \Doctrine\ORM\OptimisticLockException
706
     */
707
    public function copyComment(PortfolioComment $originComment)
708
    {
709
        $currentTime = api_get_utc_datetime(null, false, true);
710
711
        $portfolio = new Portfolio();
712
        $portfolio
713
            ->setIsVisible(false)
714
            ->setTitle(
715
                sprintf(get_lang('PortfolioCommentFromXUser'), $originComment->getAuthor()->getCompleteName())
716
            )
717
            ->setContent('')
718
            ->setUser($this->owner)
719
            ->setOrigin($originComment->getId())
720
            ->setOriginType(Portfolio::TYPE_COMMENT)
721
            ->setCourse($this->course)
722
            ->setSession($this->session)
723
            ->setCreationDate($currentTime)
724
            ->setUpdateDate($currentTime);
725
726
        $this->em->persist($portfolio);
727
        $this->em->flush();
728
729
        Display::addFlash(
730
            Display::return_message(get_lang('PortfolioItemAdded'), 'success')
731
        );
732
733
        header("Location: $this->baseUrl".http_build_query(['action' => 'edit_item', 'id' => $portfolio->getId()]));
734
        exit;
735
    }
736
737
    /**
738
     * @param bool $showHeader
739
     */
740
    private function renderView(string $content, string $toolName, array $actions = [], $showHeader = true)
741
    {
742
        global $this_section;
743
744
        $this_section = $this->course ? SECTION_COURSES : SECTION_SOCIAL;
745
746
        $view = new Template($toolName);
747
748
        if ($showHeader) {
749
            $view->assign('header', $toolName);
750
        }
751
752
        $actionsStr = '';
753
754
        if ($this->course) {
755
            $actionsStr .= Display::return_introduction_section(TOOL_PORTFOLIO);
0 ignored issues
show
Bug introduced by
Are you sure the usage of Display::return_introduc...section(TOOL_PORTFOLIO) targeting Display::return_introduction_section() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
756
        }
757
758
        if ($actions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $actions 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...
759
            $actions = implode(PHP_EOL, $actions);
760
761
            $actionsStr .= Display::toolbarAction('portfolio-toolbar', [$actions]);
762
        }
763
764
        $view->assign('baseurl', $this->baseUrl);
765
        $view->assign('actions', $actionsStr);
766
767
        $view->assign('content', $content);
768
        $view->display_one_col_template();
769
    }
770
771
    private function categoryBelongToOwner(PortfolioCategory $category): bool
772
    {
773
        if ($category->getUser()->getId() != $this->owner->getId()) {
774
            return false;
775
        }
776
777
        return true;
778
    }
779
780
    private function itemBelongToOwner(Portfolio $item): bool
781
    {
782
        if ($this->session && $item->getSession()->getId() != $this->session->getId()) {
783
            return false;
784
        }
785
786
        if ($this->course && $item->getCourse()->getId() != $this->course->getId()) {
787
            return false;
788
        }
789
790
        if ($item->getUser()->getId() != $this->owner->getId()) {
791
            return false;
792
        }
793
794
        return true;
795
    }
796
797
    /**
798
     * @throws \Doctrine\ORM\ORMException
799
     * @throws \Doctrine\ORM\OptimisticLockException
800
     */
801
    private function createCommentForm(Portfolio $item): string
802
    {
803
        $formAction = $this->baseUrl.http_build_query(['action' => 'view', 'id' => $item->getId()]);
804
805
        $form = new FormValidator('frm_comment', 'post', $formAction);
806
        $form->addHtmlEditor('content', get_lang('Comments'), true, false, ['ToolbarSet' => 'Minimal']);
807
        $form->addHidden('item', $item->getId());
808
        $form->addHidden('parent', 0);
809
        $form->applyFilter('content', 'trim');
810
        $form->addButtonSave(get_lang('Save'));
811
812
        if ($form->validate()) {
813
            $values = $form->exportValues();
814
815
            $parentComment = $this->em->find(PortfolioComment::class, $values['parent']);
816
817
            $comment = new PortfolioComment();
818
            $comment
819
                ->setAuthor($this->owner)
820
                ->setParent($parentComment)
821
                ->setContent($values['content'])
822
                ->setDate(api_get_utc_datetime(null, false, true))
823
                ->setItem($item);
824
825
            $this->em->persist($comment);
826
            $this->em->flush();
827
828
            Display::addFlash(
829
                Display::return_message(get_lang('CommentAdded'), 'success')
830
            );
831
832
            header("Location: $formAction");
833
            exit;
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
834
        }
835
836
        return $form->returnForm();
837
    }
838
839
    public function teacherCopyItem(Portfolio $originItem)
840
    {
841
        $actionParams = http_build_query(['action' => 'teacher_copy', 'copy' => 'item', 'id' => $originItem->getId()]);
842
843
        $form = new FormValidator('teacher_copy_portfolio', 'post', $this->baseUrl.$actionParams);
844
845
        if (api_get_configuration_value('save_titles_as_html')) {
846
            $form->addHtmlEditor('title', get_lang('Title'), true, false, ['ToolbarSet' => 'TitleAsHtml']);
847
        } else {
848
            $form->addText('title', get_lang('Title'));
849
            $form->applyFilter('title', 'trim');
850
        }
851
852
        $form->addLabel(
853
            sprintf(get_lang('PortfolioItemFromXUser'), $originItem->getUser()->getCompleteName()),
854
            Display::panel($originItem->getContent())
855
        );
856
        $form->addHtmlEditor('content', get_lang('Content'), true, false, ['ToolbarSet' => 'NotebookStudent']);
857
858
        $urlParams = http_build_query(
859
            [
860
                'a' => 'search_user_by_course',
861
                'course_id' => $this->course->getId(),
0 ignored issues
show
Bug introduced by
The method getId() does not exist on null. ( Ignorable by Annotation )

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

861
                'course_id' => $this->course->/** @scrutinizer ignore-call */ getId(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
862
                'session_id' => $this->session ? $this->session->getId() : 0,
863
            ]
864
        );
865
        $form->addSelectAjax(
866
            'students',
867
            get_lang('Students'),
868
            [],
869
            [
870
                'url' => api_get_path(WEB_AJAX_PATH)."course.ajax.php?$urlParams",
871
                'multiple' => true,
872
            ]
873
        );
874
        $form->addRule('students', get_lang('ThisFieldIsRequired'), 'required');
875
        $form->addButtonCreate(get_lang('Save'));
876
877
        $toolName = get_lang('CopyToStudentPortfolio');
878
        $content = $form->returnForm();
879
880
        if ($form->validate()) {
881
            $values = $form->exportValues();
882
883
            $currentTime = api_get_utc_datetime(null, false, true);
884
885
            foreach ($values['students'] as $studentId) {
886
                $owner = api_get_user_entity($studentId);
887
888
                $portfolio = new Portfolio();
889
                $portfolio
890
                    ->setIsVisible(false)
891
                    ->setTitle($values['title'])
892
                    ->setContent($values['content'])
893
                    ->setUser($owner)
894
                    ->setOrigin($originItem->getId())
895
                    ->setOriginType(Portfolio::TYPE_ITEM)
896
                    ->setCourse($this->course)
897
                    ->setSession($this->session)
898
                    ->setCreationDate($currentTime)
899
                    ->setUpdateDate($currentTime);
900
901
                $this->em->persist($portfolio);
902
            }
903
904
            $this->em->flush();
905
906
            Display::addFlash(
907
                Display::return_message(get_lang('PortfolioItemAddedToStudents'), 'success')
908
            );
909
910
            header("Location: $this->baseUrl");
911
            exit;
912
        }
913
914
        $this->renderView($content, $toolName);
915
    }
916
917
    public function teacherCopyComment(PortfolioComment $originComment)
918
    {
919
        $actionParams = http_build_query(['action' => 'teacher_copy', 'copy' => 'comment', 'id' => $originComment->getId()]);
920
921
        $form = new FormValidator('teacher_copy_portfolio', 'post', $this->baseUrl.$actionParams);
922
923
        if (api_get_configuration_value('save_titles_as_html')) {
924
            $form->addHtmlEditor('title', get_lang('Title'), true, false, ['ToolbarSet' => 'TitleAsHtml']);
925
        } else {
926
            $form->addText('title', get_lang('Title'));
927
            $form->applyFilter('title', 'trim');
928
        }
929
930
        $form->addLabel(
931
            sprintf(get_lang('PortfolioCommentFromXUser'), $originComment->getAuthor()->getCompleteName()),
932
            Display::panel($originComment->getContent())
933
        );
934
        $form->addHtmlEditor('content', get_lang('Content'), true, false, ['ToolbarSet' => 'NotebookStudent']);
935
936
        $urlParams = http_build_query(
937
            [
938
                'a' => 'search_user_by_course',
939
                'course_id' => $this->course->getId(),
940
                'session_id' => $this->session ? $this->session->getId() : 0,
941
            ]
942
        );
943
        $form->addSelectAjax(
944
            'students',
945
            get_lang('Students'),
946
            [],
947
            [
948
                'url' => api_get_path(WEB_AJAX_PATH)."course.ajax.php?$urlParams",
949
                'multiple' => true,
950
            ]
951
        );
952
        $form->addRule('students', get_lang('ThisFieldIsRequired'), 'required');
953
        $form->addButtonCreate(get_lang('Save'));
954
955
        $toolName = get_lang('CopyToStudentPortfolio');
956
        $content = $form->returnForm();
957
958
        if ($form->validate()) {
959
            $values = $form->exportValues();
960
961
            $currentTime = api_get_utc_datetime(null, false, true);
962
963
            foreach ($values['students'] as $studentId) {
964
                $owner = api_get_user_entity($studentId);
965
966
                $portfolio = new Portfolio();
967
                $portfolio
968
                    ->setIsVisible(false)
969
                    ->setTitle($values['title'])
970
                    ->setContent($values['content'])
971
                    ->setUser($owner)
972
                    ->setOrigin($originComment->getId())
973
                    ->setOriginType(Portfolio::TYPE_COMMENT)
974
                    ->setCourse($this->course)
975
                    ->setSession($this->session)
976
                    ->setCreationDate($currentTime)
977
                    ->setUpdateDate($currentTime);
978
979
                $this->em->persist($portfolio);
980
            }
981
982
            $this->em->flush();
983
984
            Display::addFlash(
985
                Display::return_message(get_lang('PortfolioItemAddedToStudents'), 'success')
986
            );
987
988
            header("Location: $this->baseUrl");
989
            exit;
990
        }
991
992
        $this->renderView($content, $toolName);
993
    }
994
}
995