em = Database::getManager(); $this->owner = api_get_user_entity(api_get_user_id()); $this->course = api_get_course_entity(api_get_course_int_id()); $this->session = api_get_session_entity(api_get_session_id()); $cidreq = api_get_cidreq(); $this->baseUrl = api_get_self().'?'.($cidreq ? $cidreq.'&' : ''); } /** * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException */ public function addCategory() { global $interbreadcrumb; Display::addFlash( Display::return_message(get_lang('PortfolioCategoryFieldHelp'), 'info') ); $form = new FormValidator('add_category', 'post', "{$this->baseUrl}&action=add_category"); if (api_get_configuration_value('save_titles_as_html')) { $form->addHtmlEditor('title', get_lang('Title'), true, false, ['ToolbarSet' => 'TitleAsHtml']); } else { $form->addText('title', get_lang('Title')); $form->applyFilter('title', 'trim'); } $form->addHtmlEditor('description', get_lang('Description'), false, false, ['ToolbarSet' => 'Minimal']); $form->addButtonCreate(get_lang('Create')); if ($form->validate()) { $values = $form->exportValues(); $category = new PortfolioCategory(); $category ->setTitle($values['title']) ->setDescription($values['description']) ->setUser($this->owner); $this->em->persist($category); $this->em->flush(); Display::addFlash( Display::return_message(get_lang('CategoryAdded'), 'success') ); header("Location: {$this->baseUrl}"); exit; } $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 ); $content = $form->returnForm(); $this->renderView($content, get_lang('AddCategory'), $actions); } /** * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException * @throws \Exception */ public function editCategory(PortfolioCategory $category) { global $interbreadcrumb; if (!$this->categoryBelongToOwner($category)) { api_not_allowed(true); } Display::addFlash( Display::return_message(get_lang('PortfolioCategoryFieldHelp'), 'info') ); $form = new FormValidator( 'edit_category', 'post', $this->baseUrl."action=edit_category&id={$category->getId()}" ); if (api_get_configuration_value('save_titles_as_html')) { $form->addHtmlEditor('title', get_lang('Title'), true, false, ['ToolbarSet' => 'TitleAsHtml']); } else { $form->addText('title', get_lang('Title')); $form->applyFilter('title', 'trim'); } $form->addHtmlEditor('description', get_lang('Description'), false, false, ['ToolbarSet' => 'Minimal']); $form->addButtonUpdate(get_lang('Update')); $form->setDefaults( [ 'title' => $category->getTitle(), 'description' => $category->getDescription(), ] ); if ($form->validate()) { $values = $form->exportValues(); $category ->setTitle($values['title']) ->setDescription($values['description']); $this->em->persist($category); $this->em->flush(); Display::addFlash( Display::return_message(get_lang('Updated'), 'success') ); header("Location: $this->baseUrl"); exit; } $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 ); $content = $form->returnForm(); $this->renderView($content, get_lang('EditCategory'), $actions); } /** * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException */ public function showHideCategory(PortfolioCategory $category) { if (!$this->categoryBelongToOwner($category)) { api_not_allowed(true); } $category->setIsVisible(!$category->isVisible()); $this->em->persist($category); $this->em->flush(); Display::addFlash( Display::return_message(get_lang('VisibilityChanged'), 'success') ); header("Location: $this->baseUrl"); exit; } /** * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException */ public function deleteCategory(PortfolioCategory $category) { if (!$this->categoryBelongToOwner($category)) { api_not_allowed(true); } $this->em->remove($category); $this->em->flush(); Display::addFlash( Display::return_message(get_lang('CategoryDeleted'), 'success') ); header("Location: $this->baseUrl"); exit; } /** * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException * @throws \Doctrine\ORM\TransactionRequiredException * @throws \Exception */ public function addItem() { global $interbreadcrumb; $categories = $this->em ->getRepository('ChamiloCoreBundle:PortfolioCategory') ->findBy(['user' => $this->owner]); $form = new FormValidator('add_portfolio', 'post', $this->baseUrl.'action=add_item'); if (api_get_configuration_value('save_titles_as_html')) { $form->addHtmlEditor('title', get_lang('Title'), true, false, ['ToolbarSet' => 'TitleAsHtml']); } else { $form->addText('title', get_lang('Title')); $form->applyFilter('title', 'trim'); } $form->addHtmlEditor('content', get_lang('Content'), true, false, ['ToolbarSet' => 'NotebookStudent']); $form->addSelectFromCollection( 'category', [get_lang('Category'), get_lang('PortfolioCategoryFieldHelp')], $categories, [], true ); $extraField = new ExtraField('portfolio'); $extra = $extraField->addElements($form); $this->addAttachmentsFieldToForm($form); $form->addButtonCreate(get_lang('Create')); if ($form->validate()) { $values = $form->exportValues(); $currentTime = new DateTime( api_get_utc_datetime(), new DateTimeZone('UTC') ); $portfolio = new Portfolio(); $portfolio ->setTitle($values['title']) ->setContent($values['content']) ->setUser($this->owner) ->setCourse($this->course) ->setSession($this->session) ->setCategory( $this->em->find('ChamiloCoreBundle:PortfolioCategory', $values['category']) ) ->setCreationDate($currentTime) ->setUpdateDate($currentTime); $this->em->persist($portfolio); $this->em->flush(); $values['item_id'] = $portfolio->getId(); $extraFieldValue = new ExtraFieldValue('portfolio'); $extraFieldValue->saveFieldValues($values); $this->processAttachments( $form, $portfolio->getUser(), $portfolio->getId(), PortfolioAttachment::TYPE_ITEM ); $hook = HookPortfolioItemAdded::create(); $hook->setEventData(['portfolio' => $portfolio]); $hook->notifyItemAdded(); if (1 == api_get_course_setting('email_alert_teachers_new_post')) { if ($this->session) { $messageCourseTitle = "{$this->course->getTitle()} ({$this->session->getName()})"; $teachers = SessionManager::getCoachesByCourseSession( $this->session->getId(), $this->course->getId() ); $userIdListToSend = array_values($teachers); } else { $messageCourseTitle = $this->course->getTitle(); $teachers = CourseManager::get_teacher_list_from_course_code($this->course->getCode()); $userIdListToSend = array_keys($teachers); } $messageSubject = sprintf(get_lang('PortfolioAlertNewPostSubject'), $messageCourseTitle); foreach ($userIdListToSend as $userIdToSend) { $messageContent = sprintf( get_lang('PortfolioAlertNewPostContent'), $this->owner->getCompleteName(), $messageCourseTitle, $this->baseUrl.http_build_query(['action' => 'view', 'id' => $portfolio->getId()]) ); MessageManager::send_message_simple($userIdToSend, $messageSubject, $messageContent, 0, false, false, [], false); } } Display::addFlash( Display::return_message(get_lang('PortfolioItemAdded'), 'success') ); header("Location: $this->baseUrl"); exit; } $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 ); $content = $form->returnForm(); $this->renderView( $content."", get_lang('AddPortfolioItem'), $actions ); } /** * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException * @throws \Doctrine\ORM\TransactionRequiredException * @throws \Exception */ public function editItem(Portfolio $item) { global $interbreadcrumb; if (!$this->itemBelongToOwner($item)) { api_not_allowed(true); } $categories = $this->em ->getRepository('ChamiloCoreBundle:PortfolioCategory') ->findBy(['user' => $this->owner]); $form = new FormValidator('edit_portfolio', 'post', $this->baseUrl."action=edit_item&id={$item->getId()}"); if (api_get_configuration_value('save_titles_as_html')) { $form->addHtmlEditor('title', get_lang('Title'), true, false, ['ToolbarSet' => 'TitleAsHtml']); } else { $form->addText('title', get_lang('Title')); $form->applyFilter('title', 'trim'); } if ($item->getOrigin()) { if (Portfolio::TYPE_ITEM === $item->getOriginType()) { $origin = $this->em->find(Portfolio::class, $item->getOrigin()); $form->addLabel( sprintf(get_lang('PortfolioItemFromXUser'), $origin->getUser()->getCompleteName()), Display::panel( Security::remove_XSS($origin->getContent()) ) ); } elseif (Portfolio::TYPE_COMMENT === $item->getOriginType()) { $origin = $this->em->find(PortfolioComment::class, $item->getOrigin()); $form->addLabel( sprintf(get_lang('PortfolioCommentFromXUser'), $origin->getAuthor()->getCompleteName()), Display::panel( Security::remove_XSS($origin->getContent()) ) ); } } $form->addHtmlEditor('content', get_lang('Content'), true, false, ['ToolbarSet' => 'NotebookStudent']); $form->addSelectFromCollection( 'category', [get_lang('Category'), get_lang('PortfolioCategoryFieldHelp')], $categories, [], true ); $extraField = new ExtraField('portfolio'); $extra = $extraField->addElements($form, $item->getId()); $attachmentList = $this->generateAttachmentList($item, false); if (!empty($attachmentList)) { $form->addLabel(get_lang('AttachmentFiles'), $attachmentList); } $this->addAttachmentsFieldToForm($form); $form->addButtonUpdate(get_lang('Update')); $form->setDefaults( [ 'title' => $item->getTitle(), 'content' => $item->getContent(), 'category' => $item->getCategory() ? $item->getCategory()->getId() : '', ] ); if ($form->validate()) { $values = $form->exportValues(); $currentTime = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC')); $item ->setTitle($values['title']) ->setContent($values['content']) ->setUpdateDate($currentTime) ->setCategory( $this->em->find('ChamiloCoreBundle:PortfolioCategory', $values['category']) ); $values['item_id'] = $item->getId(); $extraFieldValue = new ExtraFieldValue('portfolio'); $extraFieldValue->saveFieldValues($values); $this->em->persist($item); $this->em->flush(); $this->processAttachments( $form, $item->getUser(), $item->getId(), PortfolioAttachment::TYPE_ITEM ); Display::addFlash( Display::return_message(get_lang('ItemUpdated'), 'success') ); header("Location: $this->baseUrl"); exit; } $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 ); $content = $form->returnForm(); $this->renderView( $content."", get_lang('EditPortfolioItem'), $actions ); } /** * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException */ public function showHideItem(Portfolio $item) { if (!$this->itemBelongToOwner($item)) { api_not_allowed(true); } $item->setIsVisible( !$item->isVisible() ); $this->em->persist($item); $this->em->flush(); Display::addFlash( Display::return_message(get_lang('VisibilityChanged'), 'success') ); header("Location: $this->baseUrl"); exit; } /** * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException */ public function deleteItem(Portfolio $item) { if (!$this->itemBelongToOwner($item)) { api_not_allowed(true); } $this->em->remove($item); $this->em->flush(); Display::addFlash( Display::return_message(get_lang('ItemDeleted'), 'success') ); header("Location: $this->baseUrl"); exit; } /** * @throws \Exception */ public function index(HttpRequest $httpRequest) { $listByUser = false; if ($httpRequest->query->has('user')) { $this->owner = api_get_user_entity($httpRequest->query->getInt('user')); if (empty($this->owner)) { api_not_allowed(true); } $listByUser = true; } $currentUserId = api_get_user_id(); $actions = []; if ($currentUserId == $this->owner->getId()) { $actions[] = Display::url( Display::return_icon('add.png', get_lang('Add'), [], ICON_SIZE_MEDIUM), $this->baseUrl.'action=add_item' ); $actions[] = Display::url( Display::return_icon('folder.png', get_lang('AddCategory'), [], ICON_SIZE_MEDIUM), $this->baseUrl.'action=add_category' ); $actions[] = Display::url( Display::return_icon('waiting_list.png', get_lang('PortfolioDetails'), [], ICON_SIZE_MEDIUM), $this->baseUrl.'action=details' ); } else { $actions[] = Display::url( Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), $this->baseUrl ); } $frmStudentList = null; $frmTagList = null; $categories = []; if ($this->course) { $frmTagList = $this->createFormTagFilter($listByUser); $frmStudentList = $this->createFormStudentFilter($listByUser); $frmStudentList->setDefaults(['user' => $this->owner->getId()]); } else { $categories = $this->getCategoriesForIndex($currentUserId); } $items = $this->getItemsForIndex($listByUser, $frmTagList); $template = new Template(null, false, false, false, false, false, false); $template->assign('user', $this->owner); $template->assign('course', $this->course); $template->assign('session', $this->session); $template->assign('portfolio', $categories); $template->assign('uncategorized_items', $items); $template->assign('frm_student_list', $this->course ? $frmStudentList->returnForm() : ''); $template->assign('frm_tag_list', $this->course ? $frmTagList->returnForm() : ''); $layout = $template->get_template('portfolio/list.html.twig'); $content = $template->fetch($layout); $this->renderView($content, get_lang('Portfolio'), $actions); } /** * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException * @throws \Doctrine\ORM\TransactionRequiredException */ 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' => '
'.PHP_EOL .$comment->getAuthor()->getCompleteName().PHP_EOL.''.$clockIcon.PHP_EOL .Display::dateToStringAgoAndLongDate($comment->getDate()).''.PHP_EOL; if ($comment->isImportant() && ($this->itemBelongToOwner($comment->getItem()) || $isAllowedToEdit) ) { $nodeHtml .= '' .get_lang('CommentMarkedAsImportant') .''.PHP_EOL; } $nodeHtml .= '
'.PHP_EOL .''.get_lang('Course').': '; if ($this->session) { $pdfContent .= $this->session->getName().' ('.$this->course->getTitle().')'; } else { $pdfContent .= $this->course->getTitle(); } $pdfContent .= '
'; } $items = $this->em ->getRepository(Portfolio::class) ->findItemsByUser($this->owner, $this->course, $this->session); $comments = $this->em ->getRepository(PortfolioComment::class) ->findCommentsByUser($this->owner, $this->course, $this->session); $itemsHtml = $this->getItemsInHtmlFormatted($items); $commentsHtml = $this->getCommentsInHtmlFormatted($comments); $pdfContent .= Display::page_subheader2(get_lang('PortfolioItems')); if (count($itemsHtml) > 0) { $pdfContent .= implode(PHP_EOL, $itemsHtml); } else { $pdfContent .= Display::return_message(get_lang('NoItemsInYourPortfolio'), 'warning'); } $pdfContent .= Display::page_subheader2(get_lang('PortfolioCommentsMade')); if (count($commentsHtml) > 0) { $pdfContent .= implode(PHP_EOL, $commentsHtml); } else { $pdfContent .= Display::return_message(get_lang('YouHaveNotCommented'), 'warning'); } $pdfName = $this->owner->getCompleteName() .($this->course ? '_'.$this->course->getCode() : '') .'_'.get_lang('Portfolio'); $pdf = new PDF(); $pdf->content_to_pdf( $pdfContent, null, $pdfName, $this->course ? $this->course->getCode() : null, 'D', false, null, false, true ); } public function exportZip(HttpRequest $httpRequest) { $isAllowedToFilterStudent = $this->course && api_is_allowed_to_edit(); if ($isAllowedToFilterStudent) { if ($httpRequest->query->has('user')) { $this->owner = api_get_user_entity($httpRequest->query->getInt('user')); if (empty($this->owner)) { api_not_allowed(true); } } } $itemsRepo = $this->em->getRepository(Portfolio::class); $commentsRepo = $this->em->getRepository(PortfolioComment::class); $attachmentsRepo = $this->em->getRepository(PortfolioAttachment::class); $items = $itemsRepo->findItemsByUser($this->owner, $this->course, $this->session); $comments = $commentsRepo->findCommentsByUser($this->owner, $this->course, $this->session); $itemsHtml = $this->getItemsInHtmlFormatted($items); $commentsHtml = $this->getCommentsInHtmlFormatted($comments); $sysArchivePath = api_get_path(SYS_ARCHIVE_PATH); $tempPortfolioDirectory = $sysArchivePath."portfolio/{$this->owner->getId()}"; $userDirectory = UserManager::getUserPathById($this->owner->getId(), 'system'); $attachmentsDirectory = $userDirectory.'portfolio_attachments/'; $tblItemsHeaders = []; $tblItemsHeaders[] = get_lang('Title'); $tblItemsHeaders[] = get_lang('CreationDate'); $tblItemsHeaders[] = get_lang('LastUpdate'); $tblItemsHeaders[] = get_lang('Category'); $tblItemsHeaders[] = get_lang('Category'); $tblItemsHeaders[] = get_lang('Score'); $tblItemsHeaders[] = get_lang('Course'); $tblItemsHeaders[] = get_lang('Session'); $tblItemsData = []; $tblCommentsHeaders = []; $tblCommentsHeaders[] = get_lang('Resume'); $tblCommentsHeaders[] = get_lang('Date'); $tblCommentsHeaders[] = get_lang('PortfolioItemTitle'); $tblCommentsHeaders[] = get_lang('Score'); $tblCommentsData = []; $filenames = []; $fs = new Filesystem(); /** * @var int $i * @var Portfolio $item */ foreach ($items as $i => $item) { $itemCategory = $item->getCategory(); $itemCourse = $item->getCourse(); $itemSession = $item->getSession(); $itemDirectory = $item->getCreationDate()->format('Y-m-d-H-i-s'); $itemFilename = sprintf('%s/items/%s/item.html', $tempPortfolioDirectory, $itemDirectory); $itemFileContent = $this->fixImagesSourcesToHtml($itemsHtml[$i]); $fs->dumpFile($itemFilename, $itemFileContent); $filenames[] = $itemFilename; $attachments = $attachmentsRepo->findFromItem($item); /** @var PortfolioAttachment $attachment */ foreach ($attachments as $attachment) { $attachmentFilename = sprintf( '%s/items/%s/attachments/%s', $tempPortfolioDirectory, $itemDirectory, $attachment->getFilename() ); $fs->copy( $attachmentsDirectory.$attachment->getPath(), $attachmentFilename ); $filenames[] = $attachmentFilename; } $tblItemsData[] = [ Display::url( Security::remove_XSS($item->getTitle()), sprintf('items/%s/item.html', $itemDirectory) ), api_convert_and_format_date($item->getCreationDate()), api_convert_and_format_date($item->getUpdateDate()), $itemCategory ? $itemCategory->getTitle() : null, $item->getComments()->count(), $item->getScore(), $itemCourse->getTitle(), $itemSession ? $itemSession->getName() : null, ]; } /** * @var int $i * @var PortfolioComment $comment */ foreach ($comments as $i => $comment) { $commentDirectory = $comment->getDate()->format('Y-m-d-H-i-s'); $commentFileContent = $this->fixImagesSourcesToHtml($commentsHtml[$i]); $commentFilename = sprintf('%s/comments/%s/comment.html', $tempPortfolioDirectory, $commentDirectory); $fs->dumpFile($commentFilename, $commentFileContent); $filenames[] = $commentFilename; $attachments = $attachmentsRepo->findFromComment($comment); /** @var PortfolioAttachment $attachment */ foreach ($attachments as $attachment) { $attachmentFilename = sprintf( '%s/comments/%s/attachments/%s', $tempPortfolioDirectory, $commentDirectory, $attachment->getFilename() ); $fs->copy( $attachmentsDirectory.$attachment->getPath(), $attachmentFilename ); $filenames[] = $attachmentFilename; } $tblCommentsData[] = [ Display::url( $comment->getExcerpt(), sprintf('comments/%s/comment.html', $commentDirectory) ), api_convert_and_format_date($comment->getDate()), Security::remove_XSS($comment->getItem()->getTitle()), $comment->getScore(), ]; } $tblItems = new HTML_Table(['class' => 'table table-hover table-striped table-bordered data_table']); $tblItems->setHeaders($tblItemsHeaders); $tblItems->setData($tblItemsData); $tblComments = new HTML_Table(['class' => 'table table-hover table-striped table-bordered data_table']); $tblComments->setHeaders($tblCommentsHeaders); $tblComments->setData($tblCommentsData); $itemFilename = sprintf('%s/index.html', $tempPortfolioDirectory); $filenames[] = $itemFilename; $fs->dumpFile( $itemFilename, $this->formatZipIndexFile($tblItems, $tblComments) ); $zipName = $this->owner->getCompleteName() .($this->course ? '_'.$this->course->getCode() : '') .'_'.get_lang('Portfolio'); $tempZipFile = $sysArchivePath."portfolio/$zipName.zip"; $zip = new PclZip($tempZipFile); foreach ($filenames as $filename) { $zip->add($filename, PCLZIP_OPT_REMOVE_PATH, $tempPortfolioDirectory); } DocumentManager::file_send_for_download($tempZipFile, true, "$zipName.zip"); $fs->remove($tempPortfolioDirectory); $fs->remove($tempZipFile); } public function qualifyItem(Portfolio $item) { global $interbreadcrumb; $em = Database::getManager(); $formAction = $this->baseUrl.http_build_query(['action' => 'qualify', 'item' => $item->getId()]); $form = new FormValidator('frm_qualify', 'post', $formAction); $form->addUserAvatar('user', get_lang('Author')); $form->addLabel(get_lang('Title'), $item->getTitle()); $itemContent = Security::remove_XSS( $this->generateItemContent($item) ); $form->addLabel(get_lang('Content'), $itemContent); $form->addNumeric( 'score', [get_lang('QualifyNumeric'), null, ' / '.api_get_course_setting('portfolio_max_score')] ); $form->addButtonSave(get_lang('QualifyThisPortfolioItem')); if ($form->validate()) { $values = $form->exportValues(); $item->setScore($values['score']); $em->persist($item); $em->flush(); Display::addFlash( Display::return_message(get_lang('PortfolioItemGraded'), 'success') ); header("Location: $formAction"); exit(); } $form->setDefaults( [ 'user' => $item->getUser(), 'score' => (float) $item->getScore(), ] ); $interbreadcrumb[] = [ 'name' => get_lang('Portfolio'), 'url' => $this->baseUrl, ]; $interbreadcrumb[] = [ 'name' => Security::remove_XSS($item->getTitle()), 'url' => $this->baseUrl.http_build_query(['action' => 'view', 'id' => $item->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('Qualify'), $actions); } public function qualifyComment(PortfolioComment $comment) { global $interbreadcrumb; $em = Database::getManager(); $item = $comment->getItem(); $commentPath = $em->getRepository(PortfolioComment::class)->getPath($comment); $template = new Template('', false, false, false, true, false, false); $template->assign('item', $item); $template->assign('comments_path', $commentPath); $commentContext = $template->fetch( $template->get_template('portfolio/comment_context.html.twig') ); $formAction = $this->baseUrl.http_build_query(['action' => 'qualify', 'comment' => $comment->getId()]); $form = new FormValidator('frm_qualify', 'post', $formAction); $form->addHtml($commentContext); $form->addUserAvatar('user', get_lang('Author')); $form->addLabel(get_lang('Comment'), $comment->getContent()); $form->addNumeric( 'score', [get_lang('QualifyNumeric'), null, '/ '.api_get_course_setting('portfolio_max_score')] ); $form->addButtonSave(get_lang('QualifyThisPortfolioComment')); if ($form->validate()) { $values = $form->exportValues(); $comment->setScore($values['score']); $em->persist($comment); $em->flush(); Display::addFlash( Display::return_message(get_lang('PortfolioCommentGraded'), 'success') ); header("Location: $formAction"); exit(); } $form->setDefaults( [ 'user' => $comment->getAuthor(), 'score' => (float) $comment->getScore(), ] ); $interbreadcrumb[] = [ 'name' => get_lang('Portfolio'), 'url' => $this->baseUrl, ]; $interbreadcrumb[] = [ 'name' => Security::remove_XSS($item->getTitle()), 'url' => $this->baseUrl.http_build_query(['action' => 'view', 'id' => $item->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('Qualify'), $actions); } public function downloadAttachment(HttpRequest $httpRequest) { $path = $httpRequest->query->get('file'); if (empty($path)) { api_not_allowed(true); } $em = Database::getManager(); $attachmentRepo = $em->getRepository(PortfolioAttachment::class); $attachment = $attachmentRepo->findOneByPath($path); if (empty($attachment)) { api_not_allowed(true); } $originOwnerId = 0; if (PortfolioAttachment::TYPE_ITEM === $attachment->getOriginType()) { $item = $em->find(Portfolio::class, $attachment->getOrigin()); $originOwnerId = $item->getUser()->getId(); } elseif (PortfolioAttachment::TYPE_COMMENT === $attachment->getOriginType()) { $comment = $em->find(PortfolioComment::class, $attachment->getOrigin()); $originOwnerId = $comment->getAuthor()->getId(); } else { api_not_allowed(true); } $userDirectory = UserManager::getUserPathById($originOwnerId, 'system'); $attachmentsDirectory = $userDirectory.'portfolio_attachments/'; $attachmentFilename = $attachmentsDirectory.$attachment->getPath(); if (!Security::check_abs_path($attachmentFilename, $attachmentsDirectory)) { api_not_allowed(true); } $downloaded = DocumentManager::file_send_for_download( $attachmentFilename, true, $attachment->getFilename() ); if (!$downloaded) { api_not_allowed(true); } } public function deleteAttachment(HttpRequest $httpRequest) { $currentUserId = api_get_user_id(); $path = $httpRequest->query->get('file'); if (empty($path)) { api_not_allowed(true); } $em = Database::getManager(); $fs = new Filesystem(); $attachmentRepo = $em->getRepository(PortfolioAttachment::class); $attachment = $attachmentRepo->findOneByPath($path); if (empty($attachment)) { api_not_allowed(true); } $originOwnerId = 0; $itemId = 0; if (PortfolioAttachment::TYPE_ITEM === $attachment->getOriginType()) { $item = $em->find(Portfolio::class, $attachment->getOrigin()); $originOwnerId = $item->getUser()->getId(); $itemId = $item->getId(); } elseif (PortfolioAttachment::TYPE_COMMENT === $attachment->getOriginType()) { $comment = $em->find(PortfolioComment::class, $attachment->getOrigin()); $originOwnerId = $comment->getAuthor()->getId(); $itemId = $comment->getItem()->getId(); } if ($currentUserId !== $originOwnerId) { api_not_allowed(true); } $em->remove($attachment); $em->flush(); $userDirectory = UserManager::getUserPathById($originOwnerId, 'system'); $attachmentsDirectory = $userDirectory.'portfolio_attachments/'; $attachmentFilename = $attachmentsDirectory.$attachment->getPath(); $fs->remove($attachmentFilename); if ($httpRequest->isXmlHttpRequest()) { echo Display::return_message(get_lang('AttachmentFileDeleteSuccess'), 'success'); } else { Display::addFlash( Display::return_message(get_lang('AttachmentFileDeleteSuccess'), 'success') ); header('Location: '.$this->baseUrl.http_build_query(['action' => 'view', 'id' => $itemId])); } exit; } /** * @param bool $showHeader */ 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); if ($showHeader) { $view->assign('header', $toolName); } $actionsStr = ''; if ($this->course) { $actionsStr .= Display::return_introduction_section(TOOL_PORTFOLIO); } if ($actions) { $actions = implode(PHP_EOL, $actions); $actionsStr .= Display::toolbarAction('portfolio-toolbar', [$actions]); } $view->assign('baseurl', $this->baseUrl); $view->assign('actions', $actionsStr); $view->assign('content', $content); $view->display_one_col_template(); } private function categoryBelongToOwner(PortfolioCategory $category): bool { if ($category->getUser()->getId() != $this->owner->getId()) { return false; } return true; } private function addAttachmentsFieldToForm(FormValidator $form) { $form->addButton('add_attachment', get_lang('AddAttachment'), 'plus'); $form->addHtml(' '); $script = "$(function () { var attachmentsTemplate = $('#container-attachments').html(); var \$btnAdd = $('[name=\"add_attachment\"]'); var \$reference = \$btnAdd.parents('.form-group'); \$btnAdd.on('click', function (e) { e.preventDefault(); $(attachmentsTemplate).insertBefore(\$reference); }); })"; $form->addHtml(""); } private function processAttachments( FormValidator $form, User $user, int $originId, int $originType ) { $em = Database::getManager(); $fs = new Filesystem(); $comments = $form->getSubmitValue('attachment_comment'); foreach ($_FILES['attachment_file']['error'] as $i => $attachmentFileError) { if ($attachmentFileError != UPLOAD_ERR_OK) { continue; } $_file = [ 'name' => $_FILES['attachment_file']['name'][$i], 'type' => $_FILES['attachment_file']['type'][$i], 'tmp_name' => $_FILES['attachment_file']['tmp_name'][$i], 'size' => $_FILES['attachment_file']['size'][$i], ]; if (empty($_file['type'])) { $_file['type'] = DocumentManager::file_get_mime_type($_file['name']); } $newFileName = add_ext_on_mime(stripslashes($_file['name']), $_file['type']); if (!filter_extension($newFileName)) { Display::addFlash(Display::return_message(get_lang('UplUnableToSaveFileFilteredExtension'), 'error')); continue; } $newFileName = uniqid(); $attachmentsDirectory = UserManager::getUserPathById($user->getId(), 'system').'portfolio_attachments/'; if (!$fs->exists($attachmentsDirectory)) { $fs->mkdir($attachmentsDirectory, api_get_permissions_for_new_directories()); } $attachmentFilename = $attachmentsDirectory.$newFileName; if (is_uploaded_file($_file['tmp_name'])) { $moved = move_uploaded_file($_file['tmp_name'], $attachmentFilename); if (!$moved) { Display::addFlash(Display::return_message(get_lang('UplUnableToSaveFile'), 'error')); continue; } } $attachment = new PortfolioAttachment(); $attachment ->setFilename($_file['name']) ->setComment($comments[$i]) ->setPath($newFileName) ->setOrigin($originId) ->setOriginType($originType) ->setSize($_file['size']); $em->persist($attachment); $em->flush(); } } private function itemBelongToOwner(Portfolio $item): bool { if ($item->getUser()->getId() != $this->owner->getId()) { return false; } return true; } private function createFormTagFilter(bool $listByUser = false): FormValidator { $extraField = new ExtraField('portfolio'); $tagFieldInfo = $extraField->get_handler_field_info_by_tags('tags'); $chbxTagOptions = array_map( function (array $tagOption) { return $tagOption['tag']; }, $tagFieldInfo['options'] ?? [] ); $frmTagList = new FormValidator( 'frm_tag_list', 'get', $this->baseUrl.($listByUser ? 'user='.$this->owner->getId() : ''), '', [], FormValidator::LAYOUT_BOX ); if (!empty($chbxTagOptions)) { $frmTagList->addCheckBoxGroup('tags', $tagFieldInfo['display_text'], $chbxTagOptions); } $frmTagList->addText('text', get_lang('Search'), false)->setIcon('search'); $frmTagList->applyFilter('text', 'trim'); $frmTagList->addHtml('$originContent" .'