From dcc7e3a65eb61bbee20abcfef699b876d0d4e443 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Tue, 5 Jan 2021 17:54:09 -0500 Subject: [PATCH] Portfolio: Allow comment in items - refs BT#18201 --- app/Resources/public/css/base.css | 20 ++ main/inc/lib/PortfolioController.php | 150 ++++++++++- main/install/configuration.dist.php | 5 + main/portfolio/index.php | 12 + .../default/portfolio/items.html.twig | 59 ++--- .../template/default/portfolio/view.html.twig | 103 ++++++++ src/Chamilo/CoreBundle/Entity/Portfolio.php | 18 ++ .../CoreBundle/Entity/PortfolioComment.php | 239 ++++++++++++++++++ 8 files changed, 575 insertions(+), 31 deletions(-) create mode 100644 main/template/default/portfolio/view.html.twig create mode 100644 src/Chamilo/CoreBundle/Entity/PortfolioComment.php diff --git a/app/Resources/public/css/base.css b/app/Resources/public/css/base.css index 95c7a13113..703a5b46d6 100644 --- a/app/Resources/public/css/base.css +++ b/app/Resources/public/css/base.css @@ -9485,6 +9485,25 @@ ul.dropdown-menu.inner > li > a { border-color: red; grid-column: span 2; } + +.portfolio-items.row .thumbnail .caption .h3, +.portfolio-item.thumbnail .caption .h3 { + margin-top: 0; +} + +.portfolio-items .thumbnail .portfolio-actions a > img, +.portfolio-item.thumbnail .portfolio-actions a > img { + display: inline; +} + +.fa-ul.list-inline { + margin-left: -15px; +} +.fa-ul.list-inline li { + margin-left: 45px; + padding: 0; +} + /* CSS Responsive */ @media (min-width: 1025px) and (max-width: 1200px) { .sidebar-scorm { @@ -10186,6 +10205,7 @@ ul.dropdown-menu.inner > li > a { clear: none; } + .portfolio-items.row .col-md-6:nth-child(2n+1), .col-md-3.course-tool:nth-child(4n+1) { clear: left; } diff --git a/main/inc/lib/PortfolioController.php b/main/inc/lib/PortfolioController.php index f1da18296d..b7f5f11275 100644 --- a/main/inc/lib/PortfolioController.php +++ b/main/inc/lib/PortfolioController.php @@ -4,6 +4,7 @@ use Chamilo\CoreBundle\Entity\Portfolio; use Chamilo\CoreBundle\Entity\PortfolioCategory; +use Chamilo\CoreBundle\Entity\PortfolioComment; /** * Class PortfolioController. @@ -126,15 +127,19 @@ class PortfolioController * @param string $content * @param string $toolName * @param array $actions + * @param bool $showHeader */ - private function renderView(string $content, string $toolName, array $actions = []) + private function renderView(string $content, string $toolName, array $actions = [], $showHeader = true) { global $this_section; $this_section = $this->course ? SECTION_COURSES : SECTION_SOCIAL; $view = new Template($toolName); - $view->assign('header', $toolName); + + if ($showHeader) { + $view->assign('header', $toolName); + } $actionsStr = ''; @@ -612,4 +617,145 @@ class PortfolioController $this->renderView($content, get_lang('Portfolio'), $actions); } + + /** + * @param \Chamilo\CoreBundle\Entity\Portfolio $item + * + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + */ + public function view(Portfolio $item) + { + global $interbreadcrumb; + + $form = $this->createCommentForm($item); + + $commentsRepo = $this->em->getRepository(PortfolioComment::class); + + $query = $commentsRepo->createQueryBuilder('comment') + ->where('comment.item = :item') + ->orderBy('comment.root, comment.lft', 'ASC') + ->setParameter('item', $item) + ->getQuery(); + + $clockIcon = Display::returnFontAwesomeIcon('clock-o', '', true); + + $commentsHtml = $commentsRepo->buildTree( + $query->getArrayResult(), + [ + 'decorate' => true, + 'rootOpen' => '', + 'childOpen' => function ($node) use ($commentsRepo) { + /** @var PortfolioComment $comment */ + $comment = $commentsRepo->find($node['id']); + $author = $comment->getAuthor(); + + $userPicture = UserManager::getUserPicture( + $comment->getAuthor()->getId(), + USER_IMAGE_SIZE_SMALL, + null, + [ + 'picture_uri' => $author->getPictureUri(), + 'email' => $author->getEmail(), + ] + ); + + return '
  • +
    + '.$author->getCompleteName().' +
    +
    '; + }, + 'childClose' => '
  • ', + 'nodeDecorator' => function ($node) use ($commentsRepo, $clockIcon) { + /** @var PortfolioComment $comment */ + $comment = $commentsRepo->find($node['id']); + + $commentActions = Display::toolbarButton( + get_lang('ReplyToThisComment'), + '#', + 'reply', + 'default', + [ + 'data-comment' => htmlspecialchars( + json_encode(['id' => $comment->getId()]) + ), + 'role' => 'button', + 'class' => 'btn-reply-to' + ], + false + ); + + return '

    '.$comment->getAuthor()->getCompleteName().PHP_EOL.'' + .$clockIcon.PHP_EOL.Display::dateToStringAgoAndLongDate($comment->getDate()).'' + .'

    '.$commentActions.'
    '.$comment->getContent().PHP_EOL; + }, + ] + ); + + $template = new Template(null, false, false, false, false, false, false); + $template->assign('item', $item); + $template->assign('comments', $commentsHtml); + $template->assign('form', $form); + + $layout = $template->get_template('portfolio/view.html.twig'); + $content = $template->fetch($layout); + + $interbreadcrumb[] = ['name' => get_lang('Portfolio'), 'url' => $this->baseUrl]; + + $actions = []; + $actions[] = Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + $this->baseUrl + ); + + $this->renderView($content, $item->getTitle(), $actions, false); + } + + /** + * @param \Chamilo\CoreBundle\Entity\Portfolio $item + * + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + * + * @return string + */ + private function createCommentForm(Portfolio $item): string + { + $formAction = $this->baseUrl.http_build_query(['action' => 'view', 'id' => $item->getId()]); + + $form = new FormValidator('frm_comment', 'post', $formAction); + $form->addHtmlEditor('content', get_lang('Comments'), true, false, ['ToolbarSet' => 'Minimal']); + $form->addHidden('item', $item->getId()); + $form->addHidden('parent', 0); + $form->applyFilter('content', 'trim'); + $form->addButtonSave(get_lang('Save')); + + if ($form->validate()) { + $values = $form->exportValues(); + + $parentComment = $this->em->find(PortfolioComment::class, $values['parent']); + + $comment = new PortfolioComment(); + $comment + ->setAuthor($this->owner) + ->setParent($parentComment) + ->setContent($values['content']) + ->setDate(api_get_utc_datetime(null, false, true)) + ->setItem($item); + + $this->em->persist($comment); + $this->em->flush(); + + Display::addFlash( + Display::return_message(get_lang('CommentAdded'), 'success') + ); + + header("Location: $formAction"); + exit; + } + + return $form->returnForm(); + } } diff --git a/main/install/configuration.dist.php b/main/install/configuration.dist.php index edbcc22f5f..14f626708c 100755 --- a/main/install/configuration.dist.php +++ b/main/install/configuration.dist.php @@ -920,6 +920,11 @@ ALTER TABLE portfolio ADD CONSTRAINT FK_A9ED1062613FECDF FOREIGN KEY (session_id ALTER TABLE portfolio ADD CONSTRAINT FK_A9ED106212469DE2 FOREIGN KEY (category_id) REFERENCES portfolio_category (id) ON DELETE SET NULL; ALTER TABLE portfolio_category ADD CONSTRAINT FK_7AC64359A76ED395 FOREIGN KEY (user_id) REFERENCES user (id); INSERT INTO settings_current(variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES('course_create_active_tools','portfolio','checkbox','Tools','true','CourseCreateActiveToolsTitle','CourseCreateActiveToolsComment',NULL,'Portfolio', 0); +CREATE TABLE portfolio_comment (id INT AUTO_INCREMENT NOT NULL, author_id INT NOT NULL, item_id INT NOT NULL, tree_root INT DEFAULT NULL, parent_id INT DEFAULT NULL, content LONGTEXT NOT NULL, date DATETIME NOT NULL, lft INT NOT NULL, lvl INT NOT NULL, rgt INT NOT NULL, INDEX IDX_C2C17DA2F675F31B (author_id), INDEX IDX_C2C17DA2126F525E (item_id), INDEX IDX_C2C17DA2A977936C (tree_root), INDEX IDX_C2C17DA2727ACA70 (parent_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +ALTER TABLE portfolio_comment ADD CONSTRAINT FK_C2C17DA2F675F31B FOREIGN KEY (author_id) REFERENCES user (id); +ALTER TABLE portfolio_comment ADD CONSTRAINT FK_C2C17DA2126F525E FOREIGN KEY (item_id) REFERENCES portfolio (id); +ALTER TABLE portfolio_comment ADD CONSTRAINT FK_C2C17DA2A977936C FOREIGN KEY (tree_root) REFERENCES portfolio_comment (id) ON DELETE CASCADE; +ALTER TABLE portfolio_comment ADD CONSTRAINT FK_C2C17DA2727ACA70 FOREIGN KEY (parent_id) REFERENCES portfolio_comment (id) ON DELETE CASCADE; */ // In 1.11.8, before enabling this feature, you also need to: // - edit src/Chamilo/CoreBundle/Entity/Portfolio.php and PortfolioCategory.php diff --git a/main/portfolio/index.php b/main/portfolio/index.php index 616c6eb9f4..ed8f892aad 100755 --- a/main/portfolio/index.php +++ b/main/portfolio/index.php @@ -104,6 +104,18 @@ switch ($action) { $controller->deleteItem($item); return; + case 'view': + $id = isset($_GET['id']) ? (int) $_GET['id'] : 0; + + /** @var Portfolio $item */ + $item = $em->find('ChamiloCoreBundle:Portfolio', $id); + + if (empty($item)) { + break; + } + + $controller->view($item); + return; case 'list': default: $controller->index(); diff --git a/main/template/default/portfolio/items.html.twig b/main/template/default/portfolio/items.html.twig index 208e3de33d..ca88205942 100644 --- a/main/template/default/portfolio/items.html.twig +++ b/main/template/default/portfolio/items.html.twig @@ -5,12 +5,34 @@ {% set delete_img = 'delete.png'|img(22, 'Delete'|get_lang) %} {% set baseurl = _p.web_self ~ '?' ~ (_p.web_cid_query ? _p.web_cid_query ~ '&' : '') %} -
    +
    {% for item in items %} -
    +
    -

    {{ item.title }}

    + {% if _u.id == item.user.id %} + + {% endif %} + +

    + {{ item.title }} +

      {% if _c is empty %} @@ -43,37 +65,16 @@ {{ 'UpdateDate'|get_lang ~ ': ' ~ item.updateDate|date_to_time_ago }} {% endif %} + +
    • + + {{ 'XComments'|get_lang|format(item.comments.count) }} +

    {{ item.content }} - - {% if _u.id == item.user.id %} -
    - -

    - - - {{ 'Edit'|get_lang }} - - {% if item.isVisible %} - - - {{ 'Invisible'|get_lang }} - - {% else %} - - - {{ 'Visible'|get_lang }} - - {% endif %} - - - {{ 'Delete'|get_lang }} - -

    - {% endif %}
    diff --git a/main/template/default/portfolio/view.html.twig b/main/template/default/portfolio/view.html.twig new file mode 100644 index 0000000000..b26c49e67e --- /dev/null +++ b/main/template/default/portfolio/view.html.twig @@ -0,0 +1,103 @@ +
    +
    + {% if _u.id == item.user.id %} + + {% endif %} + +

    {{ item.title }}

    + +
      + {% if _c is empty %} + {% if item.session %} +
    • + + {{ 'Course'|get_lang ~ ': ' ~ item.session.name ~ ' (' ~ item.course.title ~ ')' }} +
    • + {% elseif not item.session and item.course %} +
    • + + {{ 'Course'|get_lang ~ ': ' ~ item.course.title }} +
    • + {% endif %} + {% else %} +
    • + + {{ item.user.completeName }} +
    • + {% endif %} + +
    • + + {{ 'CreationDate'|get_lang ~ ': ' ~ item.creationDate|date_to_time_ago }} +
    • + + {% if item.creationDate != item.updateDate %} +
    • + + {{ 'UpdateDate'|get_lang ~ ': ' ~ item.updateDate|date_to_time_ago }} +
    • + {% endif %} + +
    • + + {{ 'XComments'|get_lang|format(item.comments.count) }} +
    • +
    + +
    + + {{ item.content }} +
    +
    + +
    + +
    {{ 'XComments'|get_lang|format(item.comments.count) }}
    + +{{ comments }} + +{{ form }} + + \ No newline at end of file diff --git a/src/Chamilo/CoreBundle/Entity/Portfolio.php b/src/Chamilo/CoreBundle/Entity/Portfolio.php index 3059c7b93f..f868304eba 100644 --- a/src/Chamilo/CoreBundle/Entity/Portfolio.php +++ b/src/Chamilo/CoreBundle/Entity/Portfolio.php @@ -4,6 +4,8 @@ namespace Chamilo\CoreBundle\Entity; use Chamilo\UserBundle\Entity\User; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; /** @@ -100,12 +102,20 @@ class Portfolio */ protected $category; + /** + * @var \Doctrine\Common\Collections\ArrayCollection + * + * @ORM\OneToMany(targetEntity="Chamilo\CoreBundle\Entity\PortfolioComment", mappedBy="item") + */ + private $comments; + /** * Portfolio constructor. */ public function __construct() { $this->category = new PortfolioCategory(); + $this->comments = new ArrayCollection(); } /** @@ -321,4 +331,12 @@ class Portfolio return $this; } + + /** + * @return \Doctrine\Common\Collections\Collection + */ + public function getComments(): Collection + { + return $this->comments; + } } diff --git a/src/Chamilo/CoreBundle/Entity/PortfolioComment.php b/src/Chamilo/CoreBundle/Entity/PortfolioComment.php new file mode 100644 index 0000000000..c98a60e586 --- /dev/null +++ b/src/Chamilo/CoreBundle/Entity/PortfolioComment.php @@ -0,0 +1,239 @@ +children = new ArrayCollection(); + } + + /** + * @return int + */ + public function getId(): int + { + return $this->id; + } + + /** + * @return \Chamilo\UserBundle\Entity\User + */ + public function getAuthor(): User + { + return $this->author; + } + + /** + * @param \Chamilo\UserBundle\Entity\User $author + * + * @return PortfolioComment + */ + public function setAuthor(User $author): PortfolioComment + { + $this->author = $author; + + return $this; + } + + /** + * @return \Chamilo\CoreBundle\Entity\Portfolio + */ + public function getItem(): Portfolio + { + return $this->item; + } + + /** + * @param \Chamilo\CoreBundle\Entity\Portfolio $item + * + * @return PortfolioComment + */ + public function setItem(Portfolio $item): PortfolioComment + { + $this->item = $item; + + return $this; + } + + /** + * @return string + */ + public function getContent(): string + { + return $this->content; + } + + /** + * @param string $content + * + * @return PortfolioComment + */ + public function setContent(string $content): PortfolioComment + { + $this->content = $content; + + return $this; + } + + /** + * @return \DateTime + */ + public function getDate(): DateTime + { + return $this->date; + } + + /** + * @param \DateTime $date + * + * @return PortfolioComment + */ + public function setDate(DateTime $date): PortfolioComment + { + $this->date = $date; + + return $this; + } + + /** + * @return \Chamilo\CoreBundle\Entity\PortfolioComment|null + */ + public function getParent(): ?PortfolioComment + { + return $this->parent; + } + + /** + * @param \Chamilo\CoreBundle\Entity\PortfolioComment|null $parent + * + * @return PortfolioComment + */ + public function setParent(?PortfolioComment $parent): PortfolioComment + { + $this->parent = $parent; + + return $this; + } + + /** + * @return \Doctrine\Common\Collections\ArrayCollection + */ + public function getChildren(): ArrayCollection + { + return $this->children; + } + + /** + * @param \Doctrine\Common\Collections\ArrayCollection $children + * + * @return PortfolioComment + */ + public function setChildren(ArrayCollection $children): PortfolioComment + { + $this->children = $children; + + return $this; + } +}