From 90d9d4696c97e92d9d7117a8ed40c716a3062c15 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Wed, 8 Feb 2023 01:42:33 -0500 Subject: [PATCH] Portfolio: Allow advanced selection of who can view the comments - refs BT#18201 --- main/inc/lib/PortfolioController.php | 201 +++++++++++++++++- main/install/configuration.dist.php | 4 +- main/portfolio/index.php | 11 + .../default/portfolio/items.html.twig | 4 +- .../template/default/portfolio/view.html.twig | 2 +- src/Chamilo/CoreBundle/Entity/Portfolio.php | 8 +- .../CoreBundle/Entity/PortfolioComment.php | 24 +++ 7 files changed, 245 insertions(+), 9 deletions(-) diff --git a/main/inc/lib/PortfolioController.php b/main/inc/lib/PortfolioController.php index d3cdb2627d..fb5d9f461d 100644 --- a/main/inc/lib/PortfolioController.php +++ b/main/inc/lib/PortfolioController.php @@ -1172,16 +1172,48 @@ class PortfolioController $commentsRepo = $this->em->getRepository(PortfolioComment::class); - $query = $commentsRepo->createQueryBuilder('comment') - ->where('comment.item = :item') + $commentsQueryBuilder = $commentsRepo->createQueryBuilder('comment'); + $commentsQueryBuilder->where('comment.item = :item'); + + if ($this->advancedSharingEnabled) { + $commentsQueryBuilder + ->leftJoin( + CItemProperty::class, + 'cip', + Join::WITH, + "cip.ref = comment.id + AND cip.tool = :cip_tool + AND cip.course = :course + AND cip.lasteditType = 'visible' + AND cip.toUser = :current_user" + ) + ->andWhere( + sprintf( + 'comment.visibility = %d + OR ( + comment.visibility = %d AND cip IS NOT NULL OR comment.author = :current_user + )', + PortfolioComment::VISIBILITY_VISIBLE, + PortfolioComment::VISIBILITY_PER_USER + ) + ) + ->setParameter('cip_tool', TOOL_PORTFOLIO_COMMENT) + ->setParameter('current_user', $this->owner->getId()) + ->setParameter('course', $item->getCourse()) + ; + } + + $comments = $commentsQueryBuilder ->orderBy('comment.root, comment.lft', 'ASC') ->setParameter('item', $item) - ->getQuery(); + ->getQuery() + ->getArrayResult() + ; $clockIcon = Display::returnFontAwesomeIcon('clock-o', '', true); $commentsHtml = $commentsRepo->buildTree( - $query->getArrayResult(), + $comments, [ 'decorate' => true, 'rootOpen' => '
', @@ -1296,6 +1328,13 @@ class PortfolioController } if ($this->commentBelongsToOwner($comment)) { + if ($this->advancedSharingEnabled) { + $commentActions[] = Display::url( + Display::return_icon('visible.png', get_lang('ChooseRecipients')), + $this->baseUrl.http_build_query(['action' => 'comment_visiblity_choose', 'id' => $comment->getId()]) + ); + } + $commentActions[] = Display::url( Display::return_icon('edit.png', get_lang('Edit')), $this->baseUrl.http_build_query(['action' => 'edit_comment', 'id' => $comment->getId()]) @@ -1335,6 +1374,7 @@ class PortfolioController $template->assign('baseurl', $this->baseUrl); $template->assign('item', $item); $template->assign('item_content', $this->generateItemContent($item)); + $template->assign('count_comments', count($comments)); $template->assign('comments', $commentsHtml); $template->assign('form', $form); $template->assign('attachment_list', $this->generateAttachmentList($item)); @@ -3182,6 +3222,131 @@ class PortfolioController ); } + public function commentVisibilityChooser(PortfolioComment $comment) + { + global $interbreadcrumb; + + if (!$this->commentBelongsToOwner($comment)) { + api_not_allowed(true); + } + + $em = Database::getManager(); + $tblItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY); + + $courseId = $this->course->getId(); + $sessionId = $this->session ? $this->session->getId() : 0; + $item = $comment->getItem(); + + $formAction = $this->baseUrl.http_build_query(['action' => 'comment_visiblity_choose', 'id' => $comment->getId()]); + + $form = new FormValidator('visibility', 'post', $formAction); + CourseManager::addUserGroupMultiSelect($form, ['USER:'.$this->owner->getId()]); + $form->addLabel( + '', + Display::return_message( + get_lang('OnlySelectedUsersWillSeeTheContent') + .'
'.get_lang('LeaveEmptyToEnableTheContentForEveryone'), + 'info', + false + ) + ); + $form->addButtonSave(get_lang('Save')); + + if ($form->validate()) { + $values = $form->exportValues(); + $recipients = CourseManager::separateUsersGroups($values['users'])['users']; + $courseInfo = api_get_course_info_by_id($courseId); + + Database::delete( + $tblItemProperty, + [ + 'c_id = ? ' => [$courseId], + 'AND tool = ? AND ref = ? ' => [TOOL_PORTFOLIO_COMMENT, $comment->getId()], + 'AND lastedit_type = ? ' => ['visible'], + ] + ); + + foreach ($recipients as $userId) { + api_item_property_update( + $courseInfo, + TOOL_PORTFOLIO_COMMENT, + $comment->getId(), + 'visible', + api_get_user_id(), + [], + $userId, + '', + '', + $sessionId + ); + } + + if (empty($recipients)) { + $comment->setVisibility(PortfolioComment::VISIBILITY_VISIBLE); + } else { + $comment->setVisibility(PortfolioComment::VISIBILITY_PER_USER); + } + + $em->flush(); + + Display::addFlash( + Display::return_message(get_lang('VisibilityChanged'), 'success') + ); + + header("Location: $formAction"); + exit; + } + + $result = Database::select( + 'to_user_id', + $tblItemProperty, + [ + 'where' => [ + 'c_id = ? ' => [$courseId], + 'AND tool = ? AND ref = ? ' => [TOOL_PORTFOLIO_COMMENT, $comment->getId()], + 'AND to_user_id IS NOT NULL ' => [], + ], + ] + ); + + $recipients = array_map( + function (array $itemProperty): string { + return 'USER:'.$itemProperty['to_user_id']; + }, + $result + ); + + $form->setDefaults(['users' => $recipients]); + $form->protect(); + + $interbreadcrumb[] = [ + 'name' => get_lang('Portfolio'), + 'url' => $this->baseUrl, + ]; + $interbreadcrumb[] = [ + 'name' => $item->getExcerpt(40), + 'url' => $this->baseUrl.http_build_query(['action' => 'view', 'id' => $item->getId()]), + ]; + $interbreadcrumb[] = [ + 'name' => $comment->getExcerpt(40), + 'url' => $this->baseUrl + .http_build_query(['action' => 'view', 'id' => $item->getId()]) + .'#comment-'.$comment->getId(), + ]; + + $actions = []; + $actions[] = Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + $this->baseUrl.http_build_query(['action' => 'view', 'id' => $item->getId()]) + ); + + $this->renderView( + $form->returnForm(), + get_lang('ChooseRecipients'), + $actions + ); + } + private function isAllowed(): bool { $isSubscribedInCourse = false; @@ -4179,6 +4344,34 @@ class PortfolioController ; } + if ($this->advancedSharingEnabled) { + $queryBuilder + ->leftJoin( + CItemProperty::class, + 'cip', + Join::WITH, + "cip.ref = c.id + AND cip.tool = :cip_tool + AND cip.course = :course + AND cip.lasteditType = 'visible' + AND cip.toUser = :current_user" + ) + ->andWhere( + sprintf( + 'c.visibility = %d + OR ( + c.visibility = %d AND cip IS NOT NULL OR c.author = :current_user + )', + PortfolioComment::VISIBILITY_VISIBLE, + PortfolioComment::VISIBILITY_PER_USER + ) + ) + ->setParameter('cip_tool', TOOL_PORTFOLIO_COMMENT) + ->setParameter('current_user', $this->owner->getId()) + ->setParameter('course', $this->course) + ; + } + $queryBuilder->orderBy('c.date', 'DESC'); return $queryBuilder->getQuery()->getResult(); diff --git a/main/install/configuration.dist.php b/main/install/configuration.dist.php index b216ac250b..c50fa3022c 100644 --- a/main/install/configuration.dist.php +++ b/main/install/configuration.dist.php @@ -1087,7 +1087,9 @@ ALTER TABLE portfolio_rel_tag ADD CONSTRAINT FK_DB734472613FECDF FOREIGN KEY (se // and follow the instructions about the @ORM\Entity() line // - launch composer install to rebuild the autoload.php //$_configuration['allow_portfolio_tool'] = false; -// Allow advanced selection of who can view the posts. +// Allow advanced selection of who can view the posts and comments. It requires DB changes: +// ALTER TABLE portfolio_comment ADD visibility SMALLINT DEFAULT 1 NOT NULL; +// Then add the "@" symbol to the CPortfolioComment::$visibility property in the ORM\Column() line. //$_configuration['portfolio_advanced_sharing'] = false; // Enable best score column in gradebook. Previously called disable_gradebook_stats diff --git a/main/portfolio/index.php b/main/portfolio/index.php index c4bbcf08c4..4d681d5888 100755 --- a/main/portfolio/index.php +++ b/main/portfolio/index.php @@ -156,6 +156,17 @@ switch ($action) { $controller->itemVisibilityChooser($item); break; + case 'comment_visiblity_choose': + $id = $httpRequest->query->getInt('id'); + + $comment = $em->find(PortfolioComment::class, $id); + + if (empty($comment)) { + break; + } + + $controller->commentVisibilityChooser($comment); + break; case 'delete_item': $id = $httpRequest->query->getInt('id'); diff --git a/main/template/default/portfolio/items.html.twig b/main/template/default/portfolio/items.html.twig index 15d8c75b50..2fe874dd2c 100644 --- a/main/template/default/portfolio/items.html.twig +++ b/main/template/default/portfolio/items.html.twig @@ -9,7 +9,7 @@
{% for item in items %} {% set item_url = baseurl ~ {'action':'view', 'id':item.id}|url_encode %} - {% set comments = item.lastComments %} + {% set comments = item.lastComments(3, is_advanced_sharing_enabled) %}
@@ -117,7 +117,7 @@ {% if comments|length > 0 %}

- {{ 'XComments'|get_lang|format(item.comments.count) }} + {{ 'XComments'|get_lang|format(comments|length) }} · {{ 'AddNewComment'|get_lang }} diff --git a/main/template/default/portfolio/view.html.twig b/main/template/default/portfolio/view.html.twig index 3f52974ab3..93cd033515 100644 --- a/main/template/default/portfolio/view.html.twig +++ b/main/template/default/portfolio/view.html.twig @@ -58,7 +58,7 @@

- {{ 'XComments'|get_lang|format(item.comments.count) }} + {{ 'XComments'|get_lang|format(count_comments) }}

{{ comments }} diff --git a/src/Chamilo/CoreBundle/Entity/Portfolio.php b/src/Chamilo/CoreBundle/Entity/Portfolio.php index 04b2d2027b..bce121320b 100644 --- a/src/Chamilo/CoreBundle/Entity/Portfolio.php +++ b/src/Chamilo/CoreBundle/Entity/Portfolio.php @@ -376,13 +376,19 @@ class Portfolio return $this->comments; } - public function getLastComments(int $number = 3): Collection + public function getLastComments(int $number = 3, bool $avoidPerUserVisibility = false): Collection { $criteria = Criteria::create(); $criteria ->orderBy(['date' => 'DESC']) ->setMaxResults($number); + if ($avoidPerUserVisibility) { + $criteria->where( + Criteria::expr()->neq('visibility', PortfolioComment::VISIBILITY_PER_USER) + ); + } + return $this->comments->matching($criteria); } diff --git a/src/Chamilo/CoreBundle/Entity/PortfolioComment.php b/src/Chamilo/CoreBundle/Entity/PortfolioComment.php index 331284dfe8..8ebdd71309 100644 --- a/src/Chamilo/CoreBundle/Entity/PortfolioComment.php +++ b/src/Chamilo/CoreBundle/Entity/PortfolioComment.php @@ -22,6 +22,17 @@ use Gedmo\Mapping\Annotation as Gedmo; */ class PortfolioComment { + public const VISIBILITY_VISIBLE = 1; + public const VISIBILITY_PER_USER = 2; + + /** + * @var int + * + * Add @ to the next line if portfolio_advanced_sharing config setting is true + * ORM\Column(name="visibility", type="smallint", options={"default": 1}) + */ + protected $visibility = 1; + /** * @var int * @@ -128,6 +139,7 @@ class PortfolioComment { $this->isImportant = false; $this->children = new ArrayCollection(); + $this->visibility = 1; } public function getId(): int @@ -268,4 +280,16 @@ class PortfolioComment return $this; } + + public function getVisibility(): int + { + return $this->visibility; + } + + public function setVisibility(int $visibility): PortfolioComment + { + $this->visibility = $visibility; + + return $this; + } }