find($id); } switch ($action) { case 'add_forum': $formContent = forumForm(null, $lp_id); return $formContent; break; case 'edit_forum': $repo = Container::getForumRepository(); $resource = $repo->find($id); $formContent = forumForm($resource, $lp_id); return $formContent; break; case 'add_category': $formContent = getForumCategoryAddForm($lp_id); return $formContent; break; case 'edit_category': $repo = Container::getForumCategoryRepository(); $category = $repo->find($id); $formContent = editForumCategoryForm($category); return $formContent; break; case 'notify': if (0 != api_get_session_id() && false == api_is_allowed_to_session_edit(false, true) ) { api_not_allowed(); } $message = set_notification($content, $id); Display::addFlash(Display::return_message($message, 'confirm', false)); header('Location: '.$url); exit; break; case 'lock': case 'unlock': if (null !== $resource) { if ('lock' === $action) { $locked = 1; $message = get_lang('Locked: students can no longer post new messages in this forum category, forum or thread but they can still read the messages that were already posted'); } else { $locked = 0; $message = get_lang('Unlocked: learners can post new messages in this forum category, forum or thread'); } $resource->setLocked($locked); $repo->update($resource); Display::addFlash( Display::return_message($message, 'confirmation', false) ); } header('Location: '.$url); exit; break; case 'move': moveUpDown($content, $_REQUEST['direction'] ?? '', $id); header('Location: '.$url); exit; break; case 'move_thread': $message = move_thread_form(); return $message; break; case 'visible': case 'invisible': if (null !== $resource) { if ('visible' === $action) { $repo->setVisibilityPublished($resource); } else { $repo->setVisibilityPending($resource); } if ('visible' === $action) { handle_mail_cue($content, $id); } Display::addFlash( Display::return_message(get_lang('Updated'), 'confirmation', false) ); } header('Location: '.$url); exit; break; case 'delete_category': if ($resource) { $repo->delete($resource); Display::addFlash( Display::return_message(get_lang('Forum category deleted'), 'confirmation', false) ); } header('Location: '.$url); exit; break; case 'delete_forum': if ($resource) { $resource = Container::getForumRepository()->find($id); $repo->delete($resource); Display::addFlash(Display::return_message(get_lang('Forum deleted'), 'confirmation', false)); } header('Location: '.$url); exit; break; case 'delete_thread': $locked = api_resource_is_locked_by_gradebook($id, LINK_FORUM_THREAD); if ($resource && false === $locked) { $repo->delete($resource); SkillModel::deleteSkillsFromItem($id, ITEM_TYPE_FORUM_THREAD); $link_info = GradebookUtils::isResourceInCourseGradebook( api_get_course_id(), 5, $id, api_get_session_id() ); if (false !== $link_info) { $link_id = $link_info['id']; GradebookUtils::remove_resource_from_course_gradebook($link_id); } Display::addFlash(Display::return_message(get_lang('Thread deleted'), 'confirmation', false)); } header('Location: '.$url); exit; break; } } } /** * This function displays the form that is used to add a forum category * or validates the form input, depending on the context. * * @author Patrick Cool , Ghent University * @author Juan Carlos RaƱa Trabado (return to lp_id) * * @version may 2011, Chamilo 1.8.8 * @throws Exception */ function getForumCategoryAddForm(int $lp_id = null): string { $form = new FormValidator( 'forumcategory', 'post', 'index.php?'.api_get_cidreq().'&action=add_category' ); // hidden field if from learning path $form->addHidden('lp_id', $lp_id); $form->addHidden('action', 'add_category'); // Setting the form elements. $form->addHeader(get_lang('Add forum category')); $form->addText('forum_category_title', get_lang('Title'), true, ['autofocus']); $form->addHtmlEditor( 'forum_category_comment', get_lang('Description'), false, false, ['ToolbarSet' => 'Forum', 'Width' => '98%', 'Height' => '200'] ); $extraField = new ExtraField('forum_category'); $extraField->addElements( $form, null, [], //exclude false, // filter false, // tag as select [], //show only fields [], // order fields [] // extra data ); $form->addButtonCreate(get_lang('Create category'), 'SubmitForumCategory'); // Setting the rules. $form->addRule('forum_category_title', get_lang('Required field'), 'required'); // The validation or display if ($form->validate()) { $check = Security::check_token('post'); if ($check) { $values = $form->exportValues(); saveForumCategory($values); } Security::clear_token(); return ''; } else { $token = Security::get_token(); $form->addElement('hidden', 'sec_token'); $form->setConstants(['sec_token' => $token]); return $form->returnForm(); } } /** * Generates a forum creation or edition form */ function forumForm(CForum $forum = null, int $lp_id = null): string { $_course = api_get_course_info(); // The header for the form $form_title = get_lang('Add a forum'); $action = 'add_forum'; $id = 0; if ($forum) { $id = $forum->getIid(); $action = 'edit_forum'; $form_title = get_lang('Edit forum'); } $form = new FormValidator( 'forumcategory', 'post', 'index.php?'.api_get_cidreq().'&action='.$action.'&id='.$id ); $form->addHidden('action', $action); $form->addHeader($form_title); // We have a hidden field if we are editing. if ($forum) { $form->addHidden('forum_id', $id); } // hidden field if from learning path $form->addHidden('lp_id', $lp_id); // The title of the forum $form->addText('forum_title', get_lang('Title'), true, ['autofocus']); // The comment of the forum. $form->addHtmlEditor( 'forum_comment', get_lang('Description'), false, false, ['ToolbarSet' => 'Forum', 'Width' => '98%', 'Height' => '200'] ); // Dropdown list: Forum categories $forum_categories = get_forum_categories(); $forum_categories_titles = []; foreach ($forum_categories as $value) { $forum_categories_titles[$value->getIid()] = $value->getCatTitle(); } $form->addSelect( 'forum_category', get_lang('Create in category'), $forum_categories_titles ); $form->applyFilter('forum_category', 'html_filter'); if (COURSE_VISIBILITY_OPEN_WORLD == $_course['visibility']) { // This is for horizontal $group = []; $group[] = $form->createElement('radio', 'allow_anonymous', null, get_lang('Yes'), 1); $group[] = $form->createElement('radio', 'allow_anonymous', null, get_lang('No'), 0); $form->addGroup($group, 'allow_anonymous_group', get_lang('Allow anonymous posts?')); } $form->addButtonAdvancedSettings('advanced_params'); $form->addHtml(''); // The OK button if ($forum) { $form->addButtonUpdate(get_lang('Edit forum'), 'SubmitForum'); } else { $form->addButtonCreate(get_lang('Create forum'), 'SubmitForum'); } // setting the rules $form->addRule('forum_title', get_lang('Required field'), 'required'); $form->addRule('forum_category', get_lang('Required field'), 'required'); // Settings the defaults if (null === $forum) { $defaults['moderated']['moderated'] = 0; $defaults['allow_anonymous_group']['allow_anonymous'] = 0; $defaults['students_can_edit_group']['students_can_edit'] = 0; $defaults['approval_direct_group']['approval_direct'] = 0; $defaults['allow_attachments_group']['allow_attachments'] = 1; $defaults['allow_new_threads_group']['allow_new_threads'] = 1; $defaults['default_view_type_group']['default_view_type'] = api_get_setting('default_forum_view'); $defaults['public_private_group_forum_group']['public_private_group_forum'] = 'public'; if (isset($_GET['forumcategory'])) { $defaults['forum_category'] = Security::remove_XSS($_GET['forumcategory']); } } else { // the default values when editing = the data in the table $defaults['forum_id'] = $forum->getIid(); $defaults['forum_title'] = prepare4display($forum->getForumTitle()); $defaults['forum_comment'] = prepare4display($forum->getForumComment()); $defaults['start_time'] = api_get_local_time($forum->getStartTime()); $defaults['end_time'] = api_get_local_time($forum->getEndTime()); $defaults['moderated']['moderated'] = $forum->isModerated(); $defaults['forum_category'] = $forum->getForumCategory()->getIid(); $defaults['allow_anonymous_group']['allow_anonymous'] = $forum->getAllowAnonymous(); $defaults['students_can_edit_group']['students_can_edit'] = $forum->getAllowEdit(); $defaults['approval_direct_group']['approval_direct'] = $forum->getApprovalDirectPost(); $defaults['allow_attachments_group']['allow_attachments'] = $forum->getAllowAttachments(); $defaults['allow_new_threads_group']['allow_new_threads'] = $forum->getAllowNewThreads(); $defaults['default_view_type_group']['default_view_type'] = $forum->getDefaultView(); $defaults['public_private_group_forum_group']['public_private_group_forum'] = $forum->getForumGroupPublicPrivate(); $defaults['group_forum'] = $forum->getForumOfGroup(); } $form->setDefaults($defaults); // Validation or display if ($form->validate()) { $check = Security::check_token('post'); if ($check) { $values = $form->getSubmitValues(); $forumId = store_forum($values, [], true); if ($forumId) { // SkillModel::saveSkills($form, ITEM_TYPE_FORUM, $forumId); if (isset($values['forum_id'])) { Display::addFlash(Display::return_message(get_lang('The forum has been modified'), 'confirmation')); } else { Display::addFlash(Display::return_message(get_lang('The forum has been added'), 'confirmation')); } } $url = api_get_path(WEB_CODE_PATH).'forum/index.php?'.api_get_cidreq(); header('Location: '.$url); exit; } Security::clear_token(); } else { $token = Security::get_token(); $form->addElement('hidden', 'sec_token'); $form->setConstants(['sec_token' => $token]); return $form->returnForm(); } return ''; } /** * This function deletes the forum image if exists. * @author Julio Montoya * @version february 2006, dokeos 1.8 * @todo Implement * @throws Exception */ function deleteForumImage(CForum $forum): bool { throw new Exception('delete_forum_image'); return false; } /** * Generates an HTML form to edit the forum category * @throws Exception */ function editForumCategoryForm(CForumCategory $category): string { $categoryId = $category->getIid(); $form = new FormValidator( 'forumcategory', 'post', 'index.php?action=edit_category&'.api_get_cidreq().'&id='.$categoryId ); // Setting the form elements. $form->addElement('header', '', get_lang('Edit forumCategory')); $form->addElement('hidden', 'action', 'edit_category'); $form->addElement('hidden', 'forum_category_id'); $form->addElement('text', 'forum_category_title', get_lang('Title')); $form->addElement( 'html_editor', 'forum_category_comment', get_lang('Comment'), null, ['ToolbarSet' => 'Forum', 'Width' => '98%', 'Height' => '200'] ); $extraField = new ExtraField('forum_category'); $extraField->addElements( $form, $categoryId, [], //exclude false, // filter false, // tag as select [], //show only fields [], // order fields [] // extra data ); $form->addButtonUpdate(get_lang('Edit category'), 'SubmitEdit forumCategory'); // Setting the default values. $defaultvalues['forum_category_id'] = $categoryId; $defaultvalues['forum_category_title'] = $category->getCatTitle(); $defaultvalues['forum_category_comment'] = $category->getCatComment(); $form->setDefaults($defaultvalues); // Setting the rules. $form->addRule('forum_category_title', get_lang('Required field'), 'required'); // Validation or display if ($form->validate()) { $check = Security::check_token('post'); if ($check) { $values = $form->exportValues(); saveForumCategory($values); } Security::clear_token(); } else { $token = Security::get_token(); $form->addElement('hidden', 'sec_token'); $form->setConstants(['sec_token' => $token]); return $form->returnForm(); } return ''; } /** * This function stores the forum category in the database. * The new category is added to the end. * @throws Exception */ function saveForumCategory(array $values, array $courseInfo = [], bool $showMessage = true): CForumCategory { $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo; $course_id = $courseInfo['real_id']; $new_max = 1; $session_id = api_get_session_id(); $clean_cat_title = $values['forum_category_title']; $repo = Container::getForumCategoryRepository(); $message = ''; if (isset($values['forum_category_id'])) { /** @var CForumCategory $category */ $category = $repo->find($values['forum_category_id']); $category ->setCatComment($values['forum_category_comment'] ?? '') ->setCatTitle($values['forum_category_title']) ; $repo->update($category); $message = get_lang('The forum category has been modified'); $logInfo = [ 'tool' => TOOL_FORUM, 'action' => 'update-forumcategory', 'action_details' => 'forumcategory', 'info' => $clean_cat_title, ]; Event::registerLog($logInfo); $values['item_id'] = $values['forum_category_id']; } else { $course = api_get_course_entity($course_id); $session = api_get_session_entity($session_id); $category = new CForumCategory(); $category ->setCatTitle($clean_cat_title) ->setCatComment($values['forum_category_comment'] ?? '') ->setCatOrder($new_max) ->setParent($course) ->addCourseLink($course, $session) ; $repo->create($category); $last_id = $category->getIid(); if ($last_id > 0) { $message = get_lang('The forum category has been added'); } $logInfo = [ 'tool' => TOOL_FORUM, 'action' => 'new-forumcategory', 'action_details' => 'forumcategory', 'info' => $clean_cat_title, ]; Event::registerLog($logInfo); $values['item_id'] = $last_id; } $extraFieldValue = new ExtraFieldValue('forum_category'); $extraFieldValue->saveFieldValues($values); if ($showMessage) { Display::addFlash(Display::return_message($message, 'confirmation')); } return $category; } /** * Stores a new or edited forum in the database. * The new forum is added to the end of the others. * Returns an ID if the option 'returnId' was set to true, otherwise returns a confirmation message */ function store_forum(array $values, array $courseInfo = [], bool $returnId = false): string|int { $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo; $courseId = $courseInfo['real_id']; $session_id = api_get_session_id(); // Find the max forum_order for the given category. The new forum is added at the end => max cat_order + & if (null === $values['forum_category']) { $new_max = null; } else { /*$sql = "SELECT MAX(forum_order) as sort_max FROM $table_forums WHERE c_id = $courseId AND forum_category='".Database::escape_string($values['forum_category'])."'"; $result = Database::query($sql); $row = Database::fetch_array($result); $new_max = $row['sort_max'] + 1;*/ } $new_max = 0; // Forum images $has_attachment = false; $image_moved = true; if (!empty($_FILES['picture']['name'])) { $upload_ok = process_uploaded_file($_FILES['picture']); $has_attachment = true; } // Remove existing picture if it was requested. if (!empty($_POST['remove_picture'])) { //deleteForumImage($values['forum_id']); } $new_file_name = ''; if (isset($upload_ok)) { if ($has_attachment) { throw new Exception('$has_attachment'); /*$course_dir = $courseInfo['path'].'/upload/forum/images'; $sys_course_path = api_get_path(SYS_COURSE_PATH); $updir = $sys_course_path.$course_dir; // Try to add an extension to the file if it hasn't one. $new_file_name = add_ext_on_mime( Database::escape_string($_FILES['picture']['name']), $_FILES['picture']['type'] ); if (!filter_extension($new_file_name)) { //Display::addFlash(Display::return_message(get_lang('File upload failed: this file extension or file type is prohibited'), 'error')); $image_moved = false; } else { $file_extension = explode('.', $_FILES['picture']['name']); $file_extension = strtolower($file_extension[count($file_extension) - 1]); $new_file_name = uniqid('').'.'.$file_extension; $new_path = $updir.'/'.$new_file_name; $result = @move_uploaded_file($_FILES['picture']['tmp_name'], $new_path); // Storing the attachments if any if ($result) { $image_moved = true; } }*/ } } $repo = Container::getForumRepository(); if (!isset($values['forum_id'])) { $forum = new CForum(); $forum->setForumOrder($new_max ?? null); } else { /** @var CForum $forum */ $forum = $repo->find($values['forum_id']); } $forumCategory = null; if (!empty($values['forum_category'])) { $repoForumCategory = Container::getForumCategoryRepository(); $forumCategory = $repoForumCategory->find($values['forum_category']); } $lpId = $values['lp_id'] ?? 0; $lpRepo = Container::getLpRepository(); $lp = null; if (!empty($lpId)) { /** @var CLp $lp */ $lp = $lpRepo->find($lpId); } $forum ->setForumTitle($values['forum_title']) ->setForumComment($values['forum_comment'] ?? '') ->setForumCategory($forumCategory) ->setAllowAnonymous($values['allow_anonymous_group']['allow_anonymous'] ?? 0) ->setAllowEdit($values['students_can_edit_group']['students_can_edit'] ?? 0) ->setApprovalDirectPost((string) ($values['approval_direct_group']['approval_direct'] ?? '0')) ->setAllowAttachments($values['allow_attachments_group']['allow_attachments'] ?? 0) ->setAllowNewThreads((int) ($values['allow_new_threads_group']['allow_new_threads'] ?? 0)) ->setDefaultView($values['default_view_type_group']['default_view_type'] ?? '') ->setForumOfGroup((string) ($values['group_forum'] ?? '')) ->setForumGroupPublicPrivate($values['public_private_group_forum_group']['public_private_group_forum'] ?? '') ->setModerated((bool) ($values['moderated']['moderated'] ?? false)) ->setStartTime(!empty($values['start_time']) ? api_get_utc_datetime($values['start_time'], true, true) : null) ->setEndTime(!empty($values['end_time']) ? api_get_utc_datetime($values['end_time'], true, true) : null) ->setLp($lp) ; $course = api_get_course_entity($courseId); $session = api_get_session_entity($session_id); if (isset($values['forum_id'])) { // Update. $repo->update($forum); // Move groups from one group to another if (isset($values['group_forum']) && false) { $forumData = get_forums($values['forum_id']); $currentGroupId = $forumData['forum_of_group']; } $return_message = get_lang('The forum has been modified'); $forumId = $forum->getIid(); $logInfo = [ 'tool' => TOOL_FORUM, 'tool_id' => $values['forum_id'], 'action' => 'update-forum', 'action_details' => 'forum', 'info' => $values['forum_title'], ]; Event::registerLog($logInfo); } else { $forum ->setParent($forumCategory) ->addCourseLink($course, $session); $repo->create($forum); $forumId = $forum->getIid(); if ($forumId > 0) { $courseCode = $courseInfo['code']; $subscribe = (int) api_get_course_setting('subscribe_users_to_forum_notifications'); $status = STUDENT; if (!empty($session_id)) { $status = 0; } if (1 === $subscribe) { $userList = CourseManager::get_user_list_from_course_code( $courseCode, $session_id, null, null, $status ); foreach ($userList as $userInfo) { set_notification('forum', $forumId, false, $userInfo, $courseInfo); } } $logInfo = [ 'tool' => TOOL_FORUM, 'tool_id' => $forumId, 'action' => 'new-forum', 'action_details' => 'forum', 'info' => $values['forum_title'], ]; Event::registerLog($logInfo); } $return_message = get_lang('The forum has been added'); } if ($returnId) { return $forumId; } return $return_message; } function deletePost(CForumPost $post): void { $table_threads = Database::get_course_table(TABLE_FORUM_THREAD); $em = Database::getManager(); $em ->createQuery(' UPDATE ChamiloCourseBundle:CForumPost p SET p.postParent = :parent_of_deleted_post WHERE p.postParent = :post AND p.thread = :thread_of_deleted_post AND p.forum = :forum_of_deleted_post ') ->execute([ 'parent_of_deleted_post' => $post->getPostParent(), 'post' => $post->getIid(), 'thread_of_deleted_post' => $post->getThread() ? $post->getThread()->getIid() : 0, 'forum_of_deleted_post' => $post->getForum(), ]); $attachments = $post->getAttachments(); if (!empty($attachments)) { foreach ($attachments as $attachment) { $em->remove($attachment); } } $em->remove($post); $em->flush(); $lastPostOfTheTread = getLastPostOfThread($post->getThread()->getIid()); if (!empty($lastPostOfTheTread)) { // Decreasing the number of replies for this thread and also changing the last post information. $sql = "UPDATE $table_threads SET thread_replies = thread_replies - 1, thread_last_post = ".$lastPostOfTheTread['iid'].", thread_date = '".$lastPostOfTheTread['post_date']."' WHERE iid = ".$post->getThread()->getIid(); Database::query($sql); Display::addFlash(Display::return_message(get_lang('Post has been deleted'))); } else { // We deleted the very last post of the thread, so we need to delete the thread as well. $sql = "DELETE FROM $table_threads WHERE iid = ".$post->getThread()->getIid(); Database::query($sql); Display::addFlash(Display::return_message(get_lang('Thread deleted'))); } } /** * This function gets the all information of the last (=most recent) post of the thread * This can be done by sorting the posts that have the field threadId=$threadId and sort them by post_date. * @author Patrick Cool , Ghent University * @version february 2006, dokeos 1.8 */ function getLastPostOfThread(int $threadId): array { $post = Container::getForumPostRepository()->findOneBy(['thread' => $threadId], ['postDate' => 'DESC']); if (null === $post) { return []; } return [ 'iid' => $post->getIid(), 'post_date' => $post->getPostDate()->format('Y-m-d H:i:s'), ]; } /** * @param string $content Type of content forum category, forum, thread, post * @param int $id the id of the content we want to make invisible * @param int $current_visibility_status what is the current status of the visibility (0 = invisible, 1 = visible) * @param array $additional_url_parameters * * @return string HTML */ function returnVisibleInvisibleIcon( string $content, int $id, int $current_visibility_status, array $additional_url_parameters = [] ): string { $html = ''; if (1 == $current_visibility_status) { $html .= ''. Display::return_icon('visible.png', get_lang('MakeInvisible'), [], ICON_SIZE_SMALL).''; } if (0 == $current_visibility_status) { $html .= ''. Display::return_icon('invisible.png', get_lang('Make Visible'), [], ICON_SIZE_SMALL).''; } return $html; } /** * Returns an HTML string with the appropriate icon * @param string $content Type of content forum category, forum, thread, post * @param int $id the id of the content we want to make invisible * @param int $current_lock_status what is the current status of the visibility (0 = unlocked, 1 = locked) * @param array $additional_url_parameters */ function returnLockUnlockIcon( string $content, int $id, int $current_lock_status, array $additional_url_parameters = [] ): string { $html = ''; //check if the forum is blocked due if ('thread' === $content) { if (api_resource_is_locked_by_gradebook($id, LINK_FORUM_THREAD)) { return $html.Display::return_icon( 'lock_na.png', get_lang( 'This option is not available because this activity is contained by an assessment, which is currently locked. To unlock the assessment, ask your platform administrator.' ), [], ICON_SIZE_SMALL ); } } if ('1' == $current_lock_status) { $html .= ''. Display::return_icon('lock.png', get_lang('Unlock'), [], ICON_SIZE_SMALL).''; } if ('0' == $current_lock_status) { $html .= ''. Display::return_icon('unlock.png', get_lang('Lock'), [], ICON_SIZE_SMALL).''; } return $html; } /** * This function takes care of the display of the up and down icon. * * @param string $content what is it that we want to make (in)visible: forum category, forum, thread, post * @param int $id is the id of the item we want to display the icons for * @param array $list is an array of all the items. All items in this list should have * an up and down icon except for the first (no up icon) and the last (no down icon) * The key of this $list array is the id of the item. * * @return string HTML */ function returnUpDownIcon(string $content, int $id, array $list): string { $total_items = count($list); $position = 0; $internal_counter = 0; $forumCategory = isset($_GET['forumcategory']) ? Security::remove_XSS($_GET['forumcategory']) : null; if (!empty($list)) { foreach ($list as $item) { $internal_counter++; if ($id == $item->getIid()) { $position = $internal_counter; } } } if ($position > 1) { $return_value = ''. Display::return_icon('up.png', get_lang('Move up'), [], ICON_SIZE_SMALL).''; } else { $return_value = Display::url( Display::return_icon('up_na.png', '-', [], ICON_SIZE_SMALL), 'javascript:void(0)' ); } if ($position < $total_items) { $return_value .= ''. Display::return_icon('down.png', get_lang('Move down'), [], ICON_SIZE_SMALL).''; } else { $return_value = Display::url( Display::return_icon('down_na.png', '-', [], ICON_SIZE_SMALL), 'javascript:void(0)' ); } return $return_value; } /** * This function moves a forum or a forum category up or down. * * @param string $content is it that we want to make (in)visible: forum category, forum, thread, post * @param string $direction we want to move it up or down * @param int $id id of the content we want to make invisible * * @return string language variable * * @todo consider removing the table_item_property calls here but this can * prevent unwanted side effects when a forum does not have an entry in * the item_property table but does have one in the forum table. * * @author Patrick Cool , Ghent University * * @version february 2006, dokeos 1.8 */ function moveUpDown(string $content, string $direction, int $id): string { $table_categories = Database::get_course_table(TABLE_FORUM_CATEGORY); $table_forums = Database::get_course_table(TABLE_FORUM); $course_id = api_get_course_int_id(); // Determine which field holds the sort order. if ('forumcategory' === $content) { $table = $table_categories; $sort_column = 'cat_order'; $id_column = 'cat_id'; $sort_column = 'cat_order'; } elseif ('forum' === $content) { $table = $table_forums; $sort_column = 'forum_order'; $id_column = 'forum_id'; $sort_column = 'forum_order'; // We also need the forum_category of this forum. $sql = "SELECT forum_category FROM $table_forums WHERE forum_id = ".$id; $result = Database::query($sql); $row = Database::fetch_array($result); $forum_category = $row['forum_category']; } else { return ''; } // Determine the need for sorting ascending or descending order. if ('down' === $direction) { $sort_direction = 'ASC'; } elseif ('up' === $direction) { $sort_direction = 'DESC'; } else { return ''; } // The SQL statement if ('forumcategory' === $content) { $sql = "SELECT * FROM $table_categories forum_categories WHERE forum_categories.c_id = $course_id ORDER BY forum_categories.cat_order $sort_direction"; } if ('forum' === $content) { $sql = "SELECT * FROM $table WHERE c_id = $course_id AND forum_category = '".Database::escape_string($forum_category)."' ORDER BY forum_order $sort_direction"; } // Finding the items that need to be switched. $result = Database::query($sql); $found = false; $next_sort = ''; $this_sort = ''; while ($row = Database::fetch_array($result, 'ASSOC')) { if ($found) { $next_id = $row[$id_column]; $next_sort = $row[$sort_column]; $found = false; } if ($id == $row[$id_column]) { $this_id = $id; $this_sort = $row[$sort_column]; $found = true; } } if ('forum' === $content && $next_sort) { $repo = Container::getForumRepository(); /** @var CForum $forum */ $forum = $repo->find($id); $forum->setForumOrder($next_sort); $repo->update($forum); Display::addFlash(Display::return_message(get_lang('Updated'))); } else { if ($next_sort) { $repo = Container::getForumCategoryRepository(); /** @var CForumCategory $forum */ $category = $repo->find($id); if ($category) { $category->setCatOrder($next_sort); $repo->update($category); Display::addFlash(Display::return_message(get_lang('Updated'))); } } } return ''; } /** * Retrieve all the information off the forum categories (or one specific) for the current course. * The categories are sorted according to their sorting order (cat_order. * * @param int $courseId Optional. The course ID * @param int $sessionId Optional. The session ID * * @return CForumCategory[] */ function get_forum_categories(int $courseId = 0, int $sessionId = 0): Array { $repo = Container::getForumCategoryRepository(); $course = api_get_course_entity($courseId); $session = api_get_session_entity($sessionId); $qb = $repo->getResourcesByCourse($course, $session, null, $course->getResourceNode()); return $qb->getQuery()->getResult(); } /** * This function retrieves all the fora in a given forum category. * * @param int $categoryId the id of the forum category * @param int $courseId Optional. The course ID * * @return CForum[] containing all the information about the forums (regardless of their category) * * @author Patrick Cool , Ghent University * * @version february 2006, dokeos 1.8 */ function get_forums_in_category(int $categoryId, int $courseId = 0) { $repo = Container::getForumRepository(); $course = api_get_course_entity($courseId); $qb = $repo->getResourcesByCourse($course, null); $qb ->andWhere('resource.forumCategory = :catId') ->setParameter('catId', $categoryId) ->orderBy('resource.forumOrder') ; return $qb->getQuery()->getResult(); } /** * Retrieve all the forums (regardless of their category) or of only one. * The forums are sorted according to the forum_order. * Since it does not take the forum category into account there probably * will be two or more forums that have forum_order=1, ... * * @param bool $includeGroupsForum * @param int $sessionId * * @return CForum[] */ function get_forums( int $courseId = null, int $sessionId = 0 ) { $repo = Container::getForumRepository(); $courseId = empty($courseId) ? api_get_course_int_id() : $courseId; $course = api_get_course_entity($courseId); $session = api_get_session_entity($sessionId); $qb = $repo->getResourcesByCourse($course, $session); /*$qb->andWhere('resource.forumCategory = :catId') ->setParameter('catId', $cat_id); */ return $qb->getQuery()->getResult(); } /** * Returns the given forum ID's forum instance */ function getForum( int $forumId = null ): CForum|bool { if (!empty($forumId)) { $repo = Container::getForumRepository(); $qb = $repo->find($forumId); return $qb->getQuery()->getResult(); } return false; } /** * Retrieve all the threads of a given forum. * * @param int|null $courseId Optional If is null then it is considered the current course * @param int|null $sessionId Optional. If is null then it is considered the current session * * @return CForumThread[] */ function get_threads(int $forumId, int $courseId = null, int $sessionId = null): Array { $repo = Container::getForumThreadRepository(); $courseId = empty($courseId) ? api_get_course_int_id() : $courseId; $course = api_get_course_entity($courseId); $session = api_get_session_entity($sessionId); $qb = $repo->getResourcesByCourse($course, $session); $qb->andWhere('resource.forum = :forum')->setParameter('forum', $forumId); return $qb->getQuery()->getResult(); } /** * Get a thread by Id and course id. * * @param int $threadId the thread Id * * @return array containing all the information about the thread */ function getThreadInfo(int $threadId): Array { $repo = Database::getManager()->getRepository(CForumThread::class); /** @var CForumThread $forumThread */ $forumThread = $repo->findOneBy(['iid' => $threadId]); $thread = []; if ($forumThread) { $thread['iid'] = $forumThread->getIid(); $thread['threadId'] = $forumThread->getIid(); $thread['threadTitle'] = $forumThread->getThreadTitle(); $thread['forumId'] = $forumThread->getForum() ? $forumThread->getForum()->getIid() : 0; //$thread['sessionId'] = $forumThread->getSessionId(); $thread['threadSticky'] = $forumThread->getThreadSticky(); $thread['locked'] = $forumThread->getLocked(); $thread['threadTitleQualify'] = $forumThread->getThreadTitleQualify(); $thread['threadQualifyMax'] = $forumThread->getThreadQualifyMax(); $thread['threadCloseDate'] = $forumThread->getThreadCloseDate(); $thread['threadWeight'] = $forumThread->getThreadWeight(); $thread['threadPeerQualify'] = $forumThread->isThreadPeerQualify(); } return $thread; } /** * Retrieve all posts of a given thread. * * @param int $threadId The thread ID * @param string $orderDirection Optional. The direction for sort the posts * @param bool $recursive Optional. If the list is recursive * @param int $postId Optional. The post ID for recursive list * @param int $depth Optional. The depth to indicate the indent * * @todo move to a repository * * @return array containing all the information about the posts of a given thread */ function getPosts( CForum $forum, int $threadId, string $orderDirection = 'ASC', bool $recursive = false, int $postId = null, int $depth = -1 ): Array { $em = Database::getManager(); if (api_is_allowed_to_edit(false, true)) { $visibleCriteria = Criteria::expr()->neq('visible', 2); } else { $visibleCriteria = Criteria::expr()->eq('visible', 1); } $criteria = Criteria::create(); $criteria ->where(Criteria::expr()->eq('thread', $threadId)) //->andWhere(Criteria::expr()->eq('cId', $forum->getCId())) ->andWhere($visibleCriteria) ; $groupId = api_get_group_id(); $filterModerated = true; if (empty($groupId)) { if (api_is_allowed_to_edit()) { $filterModerated = false; } } else { $groupEntity = api_get_group_entity($groupId); if (GroupManager::isTutorOfGroup(api_get_user_id(), $groupEntity) || api_is_allowed_to_edit(false, true) ) { $filterModerated = false; } } if ($recursive) { $criteria->andWhere(Criteria::expr()->eq('postParent', $postId)); } $qb = $em->getRepository(CForumPost::class)->createQueryBuilder('p'); $qb->select('p') ->addCriteria($criteria) ->addOrderBy('p.iid', $orderDirection); if ($filterModerated && 1 == $forum->isModerated()) { if (!api_is_allowed_to_edit(false, true)) { $userId = api_get_user_id(); $qb->andWhere( 'p.status = 1 OR (p.status = '.CForumPost::STATUS_WAITING_MODERATION." AND p.posterId = $userId) OR (p.status = ".CForumPost::STATUS_REJECTED." AND p.posterId = $userId) OR (p.status IS NULL AND p.posterId = $userId) " ); } } $posts = $qb->getQuery()->getResult(); $depth++; $list = []; /** @var CForumPost $post */ foreach ($posts as $post) { $postInfo = [ 'iid' => $post->getIid(), 'post_id' => $post->getIid(), 'post_title' => $post->getPostTitle(), 'post_text' => $post->getPostText(), 'threadId' => $post->getThread() ? $post->getThread()->getIid() : 0, 'forum_id' => $post->getForum()->getIid(), //'poster_id' => $post->getPosterId(), //'poster_name' => $post->getPosterName(), 'post_date' => $post->getPostDate(), 'post_notification' => $post->getPostNotification(), 'post_parent_id' => $post->getPostParent() ? $post->getPostParent()->getIid() : 0, 'visible' => $post->getVisible(), 'status' => $post->getStatus(), 'indent_cnt' => $depth, 'entity' => $post, ]; $list[] = $postInfo; if (!$recursive) { continue; } $list = array_merge( $list, getPosts( $forum, $threadId, $orderDirection, $recursive, $post->getIid(), $depth ) ); } return $list; } /** * This function retrieves forum thread users details. * * @return Doctrine\DBAL\Driver\Statement|null array Array of type * ([user_id=>w,lastname=>x,firstname=>y,threadId=>z],[]) * * @author Christian Fasanando , * * @todo this function needs to be improved * * @version October 2008, dokeos 1.8 */ function get_thread_users_details(int $thread_id) { $t_posts = Database::get_course_table(TABLE_FORUM_POST); $t_users = Database::get_main_table(TABLE_MAIN_USER); $t_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER); $t_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER); $course_id = api_get_course_int_id(); $is_western_name_order = api_is_western_name_order(); if ($is_western_name_order) { $orderby = 'ORDER BY user.firstname, user.lastname '; } else { $orderby = 'ORDER BY user.lastname, user.firstname'; } $session = api_get_session_entity(); if ($session) { $generalCoachesId = $session->getGeneralCoaches()->map(fn(User $coach) => $coach->getId())->getValues(); $sessionAdminsId = $session->getSessionAdmins()->map(fn(User $admin) => $admin->getId())->getValues(); $coachesId = array_merge($generalCoachesId, $sessionAdminsId); $user_to_avoid = implode(', ', $coachesId); //not showing coaches $sql = "SELECT DISTINCT user.id, user.lastname, user.firstname, threadId FROM $t_posts p, $t_users user, $t_session_rel_user session_rel_user_rel_course WHERE p.poster_id = user.id AND user.id = session_rel_user_rel_course.user_id AND session_rel_user_rel_course.status = ".SessionEntity::STUDENT." AND session_rel_user_rel_course.user_id NOT IN ($user_to_avoid) AND p.threadId = $thread_id AND session_id = ".api_get_session_id()." AND p.c_id = $course_id AND session_rel_user_rel_course.c_id = $course_id $orderby "; } else { $sql = "SELECT DISTINCT user.id, user.lastname, user.firstname, threadId FROM $t_posts p, $t_users user, $t_course_user course_user WHERE p.poster_id = user.id AND user.id = course_user.user_id AND course_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH." AND p.threadId = $thread_id AND course_user.status != '1' AND p.c_id = $course_id AND course_user.c_id = $course_id $orderby"; } return Database::query($sql); } /** * This function retrieves forum thread users qualify. * * @return Doctrine\DBAL\Driver\Statement|null Array of type ([user_id=>w,lastname=>x,firstname=>y,threadId=>z],[]) * @author Jhon Hinojosa * @todo this function needs to be improved */ function get_thread_users_qualify(int $thread_id) { $t_posts = Database::get_course_table(TABLE_FORUM_POST); $t_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY); $t_users = Database::get_main_table(TABLE_MAIN_USER); $t_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER); $t_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER); $course_id = api_get_course_int_id(); $sessionId = api_get_session_id(); $is_western_name_order = api_is_western_name_order(); if ($is_western_name_order) { $orderby = 'ORDER BY user.firstname, user.lastname '; } else { $orderby = 'ORDER BY user.lastname, user.firstname'; } $session = api_get_session_entity(); if ($session) { $generalCoachesId = $session->getGeneralCoaches()->map(fn(User $coach) => $coach->getId())->getValues(); $sessionAdminsId = $session->getSessionAdmins()->map(fn(User $admin) => $admin->getId())->getValues(); $coachesId = array_merge($generalCoachesId, $sessionAdminsId); $user_to_avoid = implode(', ', $coachesId); //not showing coaches $sql = "SELECT DISTINCT post.poster_id, user.lastname, user.firstname, post.threadId,user.id,qualify.qualify FROM $t_posts post , $t_users user, $t_session_rel_user scu, $t_qualify qualify WHERE poster_id = user.id AND post.poster_id = qualify.user_id AND user.id = scu.user_id AND scu.status = ".SessionEntity::STUDENT." AND scu.user_id NOT IN ($user_to_avoid) AND qualify.threadId = $thread_id AND post.threadId = $thread_id AND scu.session_id = $sessionId AND scu.c_id = $course_id AND qualify.c_id = $course_id AND post.c_id = $course_id $orderby "; } else { $sql = "SELECT DISTINCT post.poster_id, user.lastname, user.firstname, post.threadId,user.id,qualify.qualify FROM $t_posts post, $t_qualify qualify, $t_users user, $t_course_user course_user WHERE post.poster_id = user.id AND post.poster_id = qualify.user_id AND user.id = course_user.user_id AND course_user.relation_type<>".COURSE_RELATION_TYPE_RRHH." AND qualify.threadId = $thread_id AND post.threadId = $thread_id AND course_user.status not in('1') AND course_user.c_id = $course_id AND qualify.c_id = $course_id AND post.c_id = $course_id $orderby "; } return Database::query($sql); } /** * This function retrieves forum thread users not qualify. * * @param int $threadId Thread ID * @param string Course DB name (optional) * * @return Doctrine\DBAL\Driver\Statement|null Array of type ([user_id=>w,lastname=>x,firstname=>y,threadId=>z],[]) * * @author Jhon Hinojosa, * * @version oct 2008, dokeos 1.8 */ function get_thread_users_not_qualify($thread_id) { $t_posts = Database::get_course_table(TABLE_FORUM_POST); $t_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY); $t_users = Database::get_main_table(TABLE_MAIN_USER); $t_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER); $t_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER); $is_western_name_order = api_is_western_name_order(); if ($is_western_name_order) { $orderby = 'ORDER BY user.firstname, user.lastname '; } else { $orderby = 'ORDER BY user.lastname, user.firstname'; } $course_id = api_get_course_int_id(); $sql1 = "SELECT user_id FROM $t_qualify WHERE threadId = '".$thread_id."'"; $result1 = Database::query($sql1); $cad = ''; while ($row = Database::fetch_array($result1)) { $cad .= $row['user_id'].','; } if ('' == $cad) { $cad = '0'; } else { $cad = substr($cad, 0, strlen($cad) - 1); } $session = api_get_session_entity(); if ($session) { $generalCoachesId = $session->getGeneralCoaches()->map(fn(User $coach) => $coach->getId())->getValues(); $sessionAdminsId = $session->getSessionAdmins()->map(fn(User $admin) => $admin->getId())->getValues(); $coachesId = array_merge($generalCoachesId, $sessionAdminsId); $user_to_avoid = implode(', ', $coachesId); //not showing coaches $sql = "SELECT DISTINCT user.id, user.lastname, user.firstname, post.threadId FROM $t_posts post , $t_users user, $t_session_rel_user session_rel_user_rel_course WHERE poster_id = user.id AND user.id NOT IN (".$cad.") AND user.id = session_rel_user_rel_course.user_id AND session_rel_user_rel_course.status = ".SessionEntity::STUDENT." AND session_rel_user_rel_course.user_id NOT IN ($user_to_avoid) AND post.threadId = ".(int) $thread_id.' AND session_id = '.api_get_session_id()." AND session_rel_user_rel_course.c_id = $course_id AND post.c_id = $course_id $orderby "; } else { $sql = "SELECT DISTINCT user.id, user.lastname, user.firstname, post.threadId FROM $t_posts post, $t_users user,$t_course_user course_user WHERE post.poster_id = user.id AND user.id NOT IN (".$cad.') AND user.id = course_user.user_id AND course_user.relation_type<>'.COURSE_RELATION_TYPE_RRHH.' AND post.threadId = '.(int) $thread_id." AND course_user.status not in('1') AND course_user.c_id = $course_id AND post.c_id = $course_id $orderby"; } return Database::query($sql); } /** * This function counts the number of forums inside a given category. * * @param int $cat_id the id of the forum category * * @todo an additional parameter that takes the visibility into account. For instance $countinvisible=0 would return * the number of visible forums, $countinvisible=1 would return the number of visible and invisible forums * * @return int the number of forums inside the given category * * @author Patrick Cool , Ghent University * * @version february 2006, dokeos 1.8 */ function count_number_of_forums_in_category($cat_id) { $table_forums = Database::get_course_table(TABLE_FORUM); $course_id = api_get_course_int_id(); $cat_id = (int) $cat_id; $sql = "SELECT count(*) AS number_of_forums FROM $table_forums WHERE forum_category = $cat_id"; $result = Database::query($sql); $row = Database::fetch_array($result); return $row['number_of_forums']; } /** * This function update a thread. * * @param array $values - The form Values */ function updateThread($values) { if (!api_is_allowed_to_edit()) { return ''; } $logInfo = [ 'tool' => TOOL_FORUM, 'tool_id' => $values['forum_id'], 'tool_id_detail' => $values['threadId'], 'action' => 'edit-thread', 'action_details' => 'thread', 'info' => $values['thread_title'], ]; Event::registerLog($logInfo); $threadTable = Database::get_course_table(TABLE_FORUM_THREAD); $courseId = api_get_course_int_id(); $courseCode = api_get_course_id(); $sessionId = api_get_session_id(); // Simple update + set gradebook values to null $params = [ 'thread_title' => $values['thread_title'], 'thread_sticky' => $values['thread_sticky'] ?? 0, ]; $where = ['iid = ?' => [$values['threadId']]]; Database::update($threadTable, $params, $where); $id = $values['threadId']; $linkInfo = GradebookUtils::isResourceInCourseGradebook( $courseCode, LINK_FORUM_THREAD, $id, $sessionId ); $gradebookLink = null; $em = Database::getManager(); if (!empty($linkInfo) && isset($linkInfo['id'])) { $gradebookLink = $em->getRepository(GradebookLink::class)->find($linkInfo['id']); } // values 1 or 0 $check = isset($values['thread_qualify_gradebook']) ? $values['thread_qualify_gradebook'] : false; if ($check) { $title = Security::remove_XSS(stripslashes($values['calification_notebook_title'])); $value = isset($values['numeric_calification']) ? (int) ($values['numeric_calification']) : 0; $weight = isset($values['weight_calification']) ? (float) ($values['weight_calification']) : 0; $description = ''; // Update title $params = [ 'thread_title_qualify' => $values['calification_notebook_title'], 'thread_qualify_max' => api_float_val($values['numeric_calification']), 'thread_weight' => api_float_val($values['weight_calification']), 'thread_peer_qualify' => $values['thread_peer_qualify'], ]; $where = ['iid = ?' => [$values['threadId']]]; Database::update($threadTable, $params, $where); if (!$linkInfo) { GradebookUtils::add_resource_to_course_gradebook( $values['category_id'], $courseCode, LINK_FORUM_THREAD, $id, $title, $weight, $value, $description, 1, $sessionId ); } else { if ($gradebookLink) { $gradebookLink->setWeight($weight); $em->persist($gradebookLink); $em->flush(); } } } else { $params = [ 'thread_title_qualify' => '', 'thread_qualify_max' => 0, 'thread_weight' => 0, 'thread_peer_qualify' => 0, ]; $where = ['iid = ?' => [$values['threadId']]]; Database::update($threadTable, $params, $where); if (!empty($linkInfo)) { if ($gradebookLink) { $em->remove($gradebookLink); $em->flush(); } } } $message = get_lang('The post has been modified').'
'; Display::addFlash(Display::return_message($message, 'confirmation', false)); } function saveThread( CForum $forum, array $values, array $courseInfo = [], $showMessage = true, $userId = 0, $sessionId = 0 ): ?CForumThread { $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo; $userId = $userId ?: api_get_user_id(); $course_id = $courseInfo['real_id']; $courseCode = $courseInfo['code']; $sessionId = $sessionId ?: api_get_session_id(); $table_threads = Database::get_course_table(TABLE_FORUM_THREAD); $post_date = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC')); $visible = true; if ('1' == $forum->getApprovalDirectPost() && !api_is_allowed_to_edit(null, true)) { $visible = false; // The post has not been approved yet. } $clean_post_title = $values['post_title']; $user = api_get_user_entity(api_get_user_id()); $course = api_get_course_entity($course_id); $session = api_get_session_entity($sessionId); // We first store an entry in the forum_thread table because the threadId is used in the forum_post table. $thread = new CForumThread(); $thread ->setThreadTitle($clean_post_title) ->setForum($forum) ->setUser($user) ->setThreadDate($post_date) ->setThreadSticky((bool) ($values['thread_sticky'] ?? false)) ->setThreadTitleQualify($values['calification_notebook_title'] ?? '') ->setThreadQualifyMax(api_float_val($values['numeric_calification'] ?? 0)) ->setThreadWeight(api_float_val($values['weight_calification'] ?? 0)) ->setThreadPeerQualify(isset($values['thread_peer_qualify']) ? (bool) $values['thread_peer_qualify'] : false) ->setParent($forum) ->addCourseLink($course, $session) ; $em = Database::getManager(); $itemId = isset($values['lp_item_id']) ? (int) $values['lp_item_id'] : 0; if (!empty($itemId)) { $item = $em->getRepository(CLpItem::class)->find($itemId); $thread->setItem($item); } $repo = Container::getForumThreadRepository(); $repo->create($thread); if (!$thread->getIid()) { return null; } // Add option gradebook qualify. if (isset($values['thread_qualify_gradebook']) && 1 == $values['thread_qualify_gradebook'] ) { // Add function gradebook. $resourcename = stripslashes($values['calification_notebook_title']); GradebookUtils::add_resource_to_course_gradebook( $values['category_id'], $courseCode, 5, $thread->getIid(), $resourcename, $values['weight_calification'], $values['numeric_calification'], '', 0, $sessionId ); } $logInfo = [ 'tool' => TOOL_FORUM, 'tool_id' => $values['forum_id'], 'tool_id_detail' => $thread->getIid(), 'action' => 'new-thread', 'info' => $clean_post_title, ]; Event::registerLog($logInfo); // We now store the content in the table_post table. $post = new CForumPost(); $post ->setPostTitle($clean_post_title) ->setPostText($values['post_text']) ->setThread($thread) ->setForum($forum) ->setUser(api_get_user_entity($userId)) ->setPostDate($post_date) ->setPostNotification(isset($values['post_notification']) ? (bool) $values['post_notification'] : false) ->setVisible($visible) ->setStatus(CForumPost::STATUS_VALIDATED) ->setParent($thread) ->addCourseLink($course, $session) ; if ($forum->isModerated()) { $post->setStatus( api_is_course_admin() ? CForumPost::STATUS_VALIDATED : CForumPost::STATUS_WAITING_MODERATION ); } $repo = Container::getForumPostRepository(); $repo->create($post); $thread->setThreadLastPost($post); $em = Database::getManager(); $em->persist($thread); $em->flush(); $postId = $post->getIid(); $logInfo = [ 'tool' => TOOL_FORUM, 'tool_id' => $values['forum_id'], 'tool_id_detail' => $thread->getIid(), 'action' => 'new-post', 'info' => $clean_post_title, ]; Event::registerLog($logInfo); // Now we have to update the thread table to fill the thread_last_post // field (so that we know when the thread has been updated for the last time). $sql = "UPDATE $table_threads SET thread_last_post = '".$postId."' WHERE iid = '".$thread->getIid()."'"; Database::query($sql); $message = ''; if ($showMessage) { Display::addFlash(Display::return_message(get_lang('The new thread has been added'), 'success', false)); } // Overwrite default message. if ($forum->isModerated() && !api_is_allowed_to_edit(null, true) ) { if ($showMessage) { Display::addFlash(Display::return_message(get_lang('Your message has to be approved before people can view it.'), 'success', false)); } } add_forum_attachment_file( null, $post ); if ('1' == $forum->getApprovalDirectPost() && !api_is_allowed_to_edit(null, true) ) { $message .= get_lang('Your message has to be approved before people can view it.').'
'; $message .= get_lang('You can now return to the'). ' '. get_lang('Forum').'
'; } else { $message .= get_lang('You can now return to the'). ' '. get_lang('Forum').'
'; $message .= get_lang('You can now return to the'). ' '. get_lang('Message').''; } $reply_info['new_post_id'] = $postId; $my_post_notification = isset($values['post_notification']) ? $values['post_notification'] : null; if (1 == $my_post_notification) { set_notification('thread', $thread->getIid(), true); } send_notification_mails( $forum, $thread, $reply_info ); Session::erase('formelements'); Session::erase('origin'); Session::erase('breadcrumbs'); Session::erase('addedresource'); Session::erase('addedresourceid'); if ($showMessage) { Display::addFlash(Display::return_message($message, 'success', false)); } return $thread; } /** * This function displays the form that is used to add a post. This can be a new thread or a reply. * * @param string $action * is the parameter that determines if we are * 2. replythread: Replying to a thread ($action = replythread) => I-frame with the complete * thread (if enabled) * 3. replymessage: Replying to a message ($action =replymessage) => I-frame with the * complete thread (if enabled) * (I first thought to put and I-frame with the message only) * 4. quote: Quoting a message ($action= quotemessage) => I-frame with the complete thread * (if enabled). The message will be in the reply. (I first thought not to put an I-frame * here) * @param array $form_values * @param bool $showPreview * * @return FormValidator */ function show_add_post_form(CForum $forum, CForumThread $thread, CForumPost $post = null, $action, $form_values, $showPreview = true) { $_user = api_get_user_info(); $action = isset($action) ? Security::remove_XSS($action) : ''; $threadId = $thread->getIid(); $forumId = $forum->getIid(); $giveRevision = isset($_GET['give_revision']) && 1 == $_GET['give_revision']; $postId = $post ? $post->getIid() : 0; $url = api_get_self().'?'.http_build_query( [ 'action' => $action, 'forum' => $forumId, 'thread' => $threadId, 'post' => $postId, ] ).'&'.api_get_cidreq(); $form = new FormValidator( 'thread', 'post', $url ); $form->setConstants(['forum' => '5']); // Setting the form elements. $form->addElement('hidden', 'forum_id', $forumId); $form->addElement('hidden', 'threadId', $threadId); $form->addElement('hidden', 'action', $action); // If anonymous posts are allowed we also display a form to allow the user to put his name or username in. if (1 == $forum->getAllowAnonymous() && !isset($_user['user_id'])) { $form->addElement('text', 'poster_name', get_lang('Name')); $form->applyFilter('poster_name', 'html_filter'); } $form->addElement('text', 'post_title', get_lang('Title')); $form->addHtmlEditor( 'post_text', get_lang('Text'), true, false, api_is_allowed_to_edit(null, true) ? [ 'ToolbarSet' => 'Forum', 'Width' => '100%', 'Height' => '300', ] : [ 'ToolbarSet' => 'ForumStudent', 'Width' => '100%', 'Height' => '300', 'UserStatus' => 'student', ] ); $form->addRule('post_text', get_lang('Required field'), 'required'); if (in_array($action, ['replythread', 'replymessage', 'quote'])) { $extraFields = new ExtraField('forum_post'); $extraFields->addElements( $form, null, [], //exclude false, // filter false, // tag as select ['ask_for_revision'], //show only fields [], // order fields [] // extra data); ); } $iframe = null; if ($showPreview) { if ('newthread' !== $action && !empty($threadId)) { $iframe = ''; } if (!empty($iframe)) { $form->addElement('label', get_lang('Thread'), $iframe); } } if (in_array($action, ['quote', 'replymessage'])) { $form->addFile('user_upload[]', get_lang('Attachment')); $form->addButton( 'add_attachment', get_lang('Add attachment'), 'paperclip', 'default', 'default', null, ['id' => 'reply-add-attachment'] ); } else { $form->addFile('user_upload', get_lang('Attachment')); } if ($giveRevision) { $hide = ('true' === api_get_setting('forum.hide_forum_post_revision_language')); $form->addHidden('give_revision', 1); if (false === $hide) { $extraField = new ExtraField('forum_post'); $extraField->addElements( $form, null, [], //exclude false, // filter false, // tag as select ['revision_language'], //show only fields [], // order fields [] // extra data ); } else { $form->addHidden('extra_revision_language', 1); } } // Setting the class and text of the form title and submit button. if ('quote' === $action) { $form->addButtonCreate(get_lang('Quote this message'), 'SubmitPost'); } elseif ('replythread' === $action) { $form->addButtonCreate(get_lang('Reply to this thread'), 'SubmitPost'); } elseif ('replymessage' === $action) { $form->addButtonCreate(get_lang('Reply to this message'), 'SubmitPost'); } $defaults['thread_peer_qualify'] = 0; if (!empty($form_values)) { $defaults['post_title'] = prepare4display($form_values['post_title']); $defaults['post_text'] = prepare4display($form_values['post_text']); $defaults['post_notification'] = (int) $form_values['post_notification']; $defaults['thread_sticky'] = (int) $form_values['thread_sticky']; $defaults['thread_peer_qualify'] = (int) $form_values['thread_peer_qualify']; } // If we are quoting a message we have to retrieve the information of the post we are quoting so that // we can add this as default to the textarea. // We also need to put the parent_id of the post in a hidden form when if (('quote' === $action || 'replymessage' === $action || $giveRevision) && !empty($postId)) { // we are quoting or replying to a message (<> reply to a thread !!!) $form->addHidden('post_parent_id', $post->getIid()); // If we are replying or are quoting then we display a default title. $posterName = UserManager::formatUserFullName($post->getUser()); $defaults['post_title'] = get_lang('Re:').api_html_entity_decode($post->getPostTitle(), ENT_QUOTES); // When we are quoting a message then we have to put that message into the wysiwyg editor. // Note: The style has to be hardcoded here because using class="quote" didn't work. if ('quote' === $action) { $defaults['post_text'] = '
 
'. get_lang('Quoting').' '.$posterName.':
'. prepare4display($post->getPostText()).'
 
 
'; } if ($giveRevision) { $defaults['post_text'] = prepare4display($post->getPostText()); } } $form->setDefaults(isset($defaults) ? $defaults : []); // The course admin can make a thread sticky (=appears with special icon and always on top). $form->addRule('post_title', get_lang('Required field'), 'required'); if (1 == $forum->getAllowAnonymous() && !isset($_user['user_id'])) { $form->addRule( 'poster_name', get_lang('Required field'), 'required' ); } // Validation or display if ($form->validate()) { $check = Security::check_token('post'); if ($check) { $values = $form->getSubmitValues(); if (isset($values['thread_qualify_gradebook']) && '1' == $values['thread_qualify_gradebook'] && empty($values['weight_calification']) ) { Display::addFlash( Display::return_message( get_lang('You must assign a score to this activity').' '.get_lang('Back').'', 'error', false ) ); return false; } $postId = 0; $threadId = 0; switch ($action) { case 'quote': case 'replythread': case 'replymessage': $postId = store_reply($forum, $thread, $values); break; } if ($postId) { if (isset($values['give_revision']) && 1 == $values['give_revision']) { $extraFieldValues = new ExtraFieldValue('forum_post'); $revisionLanguage = isset($values['extra_revision_language']) ? $values['extra_revision_language'] : ''; $params = [ 'item_id' => $postId, 'extra_revision_language' => $revisionLanguage, ]; $extraFieldValues->saveFieldValues( $params, false, false, ['revision_language'] ); } if (in_array($action, ['replythread', 'replymessage', 'quote'])) { $extraFieldValues = new ExtraFieldValue('forum_post'); $params = [ 'item_id' => $postId, 'extra_ask_for_revision' => isset($values['extra_ask_for_revision']) ? $values['extra_ask_for_revision'] : '', ]; $extraFieldValues->saveFieldValues( $params, false, false, ['ask_for_revision'] ); } } $url = api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.api_get_cidreq().'&'.http_build_query( [ 'forum' => $forumId, 'thread' => $thread->getIid(), ] ); Security::clear_token(); header('Location: '.$url); exit; } } else { $token = Security::get_token(); $form->addElement('hidden', 'sec_token'); $form->setConstants(['sec_token' => $token]); // Delete from $_SESSION forum attachment from other posts // and keep only attachments for new post //clearAttachedFiles(FORUM_NEW_POST); // Get forum attachment ajax table to add it to form $attachmentAjaxTable = getAttachmentsAjaxTable(0, $forum->getIid()); $ajaxHtml = $attachmentAjaxTable; $form->addElement('html', $ajaxHtml); return $form; } } function newThread(CForum $forum, $form_values = '', $showPreview = true) { $_user = api_get_user_info(); $forumId = $forum->getIid(); $my_post = isset($_GET['post']) ? (int) $_GET['post'] : ''; $giveRevision = isset($_GET['give_revision']) && 1 == $_GET['give_revision']; $action = 'new_thread'; $url = api_get_self().'?'.http_build_query( [ 'action' => $action, 'forum' => $forumId, 'post' => $my_post, ] ).'&'.api_get_cidreq(); $form = new FormValidator( 'thread', 'post', $url ); // Setting the form elements. $form->addElement('hidden', 'forum_id', $forumId); $form->addElement('hidden', 'threadId', 0); $form->addElement('hidden', 'action', $action); // If anonymous posts are allowed we also display a form to allow the user to put his name or username in. if (1 == $forum->getAllowAnonymous() && !isset($_user['user_id'])) { $form->addElement('text', 'poster_name', get_lang('Name')); $form->applyFilter('poster_name', 'html_filter'); } $form->addElement('text', 'post_title', get_lang('Title')); $form->addHtmlEditor( 'post_text', get_lang('Text'), true, false, api_is_allowed_to_edit(null, true) ? [ 'ToolbarSet' => 'Forum', 'Width' => '100%', 'Height' => '300', ] : [ 'ToolbarSet' => 'ForumStudent', 'Width' => '100%', 'Height' => '300', 'UserStatus' => 'student', ] ); $form->addRule('post_text', get_lang('Required field'), 'required'); $extraFields = new ExtraField('forum_post'); $extraFields->addElements( $form, null, [], //exclude false, // filter false, // tag as select ['ask_for_revision'], //show only fields [], // order fields [] // extra data); ); if (Gradebook::is_active() && (api_is_course_admin() || api_is_session_general_coach() || api_is_course_tutor()) ) { $form->addElement('advanced_settings', 'advanced_params', get_lang('Advanced settings')); $form->addElement('html', ''); } SkillModel::addSkillsToForm($form, ITEM_TYPE_FORUM_THREAD, 0); $form->addElement('checkbox', 'thread_sticky', '', get_lang('This is a sticky message (appears always on top and has a special sticky icon)')); $form->addFile('user_upload', get_lang('Attachment')); if ($giveRevision) { $hide = ('true' === api_get_setting('forum.hide_forum_post_revision_language')); $form->addHidden('give_revision', 1); if (false === $hide) { $extraField = new ExtraField('forum_post'); $extraField->addElements( $form, null, [], //exclude false, // filter false, // tag as select ['revision_language'], //show only fields [], // order fields [] // extra data ); } else { $form->addHidden('extra_revision_language', 1); } } $form->addButtonCreate(get_lang('Create thread'), 'SubmitPost'); $defaults['thread_peer_qualify'] = 0; if (!empty($form_values)) { $defaults['post_title'] = prepare4display($form_values['post_title']); $defaults['post_text'] = prepare4display($form_values['post_text']); $defaults['post_notification'] = (int) $form_values['post_notification']; $defaults['thread_sticky'] = (int) $form_values['thread_sticky']; $defaults['thread_peer_qualify'] = (int) $form_values['thread_peer_qualify']; } $form->setDefaults(isset($defaults) ? $defaults : []); // The course admin can make a thread sticky (=appears with special icon and always on top). $form->addRule('post_title', get_lang('Required field'), 'required'); if (1 == $forum->getAllowAnonymous() && !isset($_user['user_id'])) { $form->addRule( 'poster_name', get_lang('Required field'), 'required' ); } // Validation or display if ($form->validate()) { $check = Security::check_token('post'); if ($check) { $values = $form->getSubmitValues(); if (isset($values['thread_qualify_gradebook']) && '1' == $values['thread_qualify_gradebook'] && empty($values['weight_calification']) ) { Display::addFlash( Display::return_message( get_lang('You must assign a score to this activity').' '.get_lang('Back').'', 'error', false ) ); return false; } $newThread = saveThread($forum, $values); if ($newThread) { SkillModel::saveSkills($form, ITEM_TYPE_FORUM_THREAD, $newThread->getIid()); $post = $newThread->getThreadLastPost(); if ($post) { $postId = $post->getIid(); if (isset($values['give_revision']) && 1 == $values['give_revision']) { $extraFieldValues = new ExtraFieldValue('forum_post'); $revisionLanguage = isset($values['extra_revision_language']) ? $values['extra_revision_language'] : ''; $params = [ 'item_id' => $postId, 'extra_revision_language' => $revisionLanguage, ]; $extraFieldValues->saveFieldValues( $params, false, false, ['revision_language'] ); } $extraFieldValues = new ExtraFieldValue('forum_post'); $params = [ 'item_id' => $postId, 'extra_ask_for_revision' => $values['extra_ask_for_revision'] ?? '', ]; $extraFieldValues->saveFieldValues( $params, false, false, ['ask_for_revision'] ); } } $url = api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.api_get_cidreq().'&'.http_build_query( [ 'forum' => $forumId, 'thread' => $newThread->getIid(), ] ); Security::clear_token(); header('Location: '.$url); exit; } } else { $token = Security::get_token(); $form->addElement('hidden', 'sec_token'); $form->setConstants(['sec_token' => $token]); // Delete from $_SESSION forum attachment from other posts // and keep only attachments for new post //clearAttachedFiles(FORUM_NEW_POST); // Get forum attachment ajax table to add it to form $attachmentAjaxTable = getAttachmentsAjaxTable(0, $forum->getIid()); $ajaxHtml = $attachmentAjaxTable; $form->addElement('html', $ajaxHtml); return $form; } } /** * @param CForumThread $threadInfo * @param int $user_id * @param int $thread_id * @param int $thread_qualify * @param int $qualify_time * @param int $session_id * * @return string * * @author Isaac Flores , U.N.A.S University * * @version October 2008, dokeos 1.8.6 */ function saveThreadScore( CForumThread $threadEntity, $user_id, $thread_id, $thread_qualify, $qualify_time, $session_id ) { $table_threads_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY); $course_id = api_get_course_int_id(); $session_id = (int) $session_id; $thread_id = (int) $thread_id; $user_id = (int) $user_id; $thread_qualify = (float) $thread_qualify; $currentUserId = api_get_user_id(); $qualify_time = Database::escape_string($qualify_time); $max = $threadEntity->getThreadQualifyMax(); if ($thread_qualify <= $max) { if ($threadEntity->isThreadPeerQualify()) { $sql = "SELECT COUNT(*) FROM $table_threads_qualify WHERE user_id = $user_id AND qualify_user_id = $currentUserId AND threadId = ".$thread_id; } else { $sql = "SELECT COUNT(*) FROM $table_threads_qualify WHERE user_id = $user_id AND threadId = ".$thread_id; } $result = Database::query($sql); $row = Database::fetch_array($result); if (0 == $row[0]) { $sql = "INSERT INTO $table_threads_qualify (c_id, user_id, threadId,qualify,qualify_user_id,qualify_time,session_id) VALUES (".$course_id.", '".$user_id."','".$thread_id."',".$thread_qualify.", '".$currentUserId."','".$qualify_time."','".$session_id."')"; Database::query($sql); return 'insert'; } else { saveThreadScoreHistory( '1', $course_id, $user_id, $thread_id ); // Update $sql = "UPDATE $table_threads_qualify SET qualify = '".$thread_qualify."', qualify_time = '".$qualify_time."' WHERE user_id=".$user_id.' AND threadId='.$thread_id." AND qualify_user_id = $currentUserId "; Database::query($sql); return 'update'; } } return ''; } /** * This function shows qualify. * * @param string $option contains the information of option to run * @param int $user_id contains the information the current user id * @param int $thread_id contains the information the current thread id * * @return int qualify * $option=1 obtained the qualification of the current thread * * @author Isaac Flores , U.N.A.S University * * @version October 2008, dokeos 1.8.6 */ function showQualify($option, $user_id, $thread_id) { $table_threads_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY); $table_threads = Database::get_course_table(TABLE_FORUM_THREAD); $course_id = api_get_course_int_id(); $user_id = (int) $user_id; $thread_id = (int) $thread_id; if (empty($user_id) || empty($thread_id)) { return 0; } $sql = ''; switch ($option) { case 1: $sql = "SELECT qualify FROM $table_threads_qualify WHERE c_id = $course_id AND user_id=".$user_id.' AND threadId='.$thread_id; break; case 2: $sql = "SELECT thread_qualify_max FROM $table_threads WHERE c_id = $course_id AND iid=".$thread_id; break; } if (!empty($sql)) { $rs = Database::query($sql); $row = Database::fetch_array($rs); if ($row) { return $row[0]; } } return 0; } /** * This function gets qualify historical. * * @param int $user_id contains the information the current user id * @param int $thread_id contains the information the current thread id * @param bool $opt contains the information of option to run * * @return array * * @author Christian Fasanando , * @author Isaac Flores , * * @version October 2008, dokeos 1.8.6 */ function getThreadScoreHistory($user_id, $thread_id, $opt) { $user_id = (int) $user_id; $thread_id = (int) $thread_id; $table_threads_qualify_log = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY_LOG); $course_id = api_get_course_int_id(); if ('false' == $opt) { $sql = "SELECT * FROM $table_threads_qualify_log WHERE c_id = $course_id AND threadId='".$thread_id."' AND user_id='".$user_id."' ORDER BY qualify_time"; } else { $sql = "SELECT * FROM $table_threads_qualify_log WHERE c_id = $course_id AND threadId='".$thread_id."' AND user_id='".$user_id."' ORDER BY qualify_time DESC"; } $rs = Database::query($sql); $log = []; while ($row = Database::fetch_array($rs, 'ASSOC')) { $log[] = $row; } return $log; } /** * This function stores qualify historical. * * @param bool contains the information of option to run * @param string contains the information the current course id * @param int contains the information the current forum id * @param int contains the information the current user id * @param int contains the information the current thread id * @param int contains the information the current qualify * @param string $option * @param int $course_id * @param int $user_id * @param int $thread_id * * @author Isaac Flores , U.N.A.S University * * @version October 2008, dokeos 1.8.6 */ function saveThreadScoreHistory( $option, $course_id, $user_id, $thread_id ) { $table_threads_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY); $table_threads_qualify_log = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY_LOG); $thread_id = (int) $thread_id; $course_id = (int) $course_id; $user_id = (int) $user_id; $qualify_user_id = api_get_user_id(); if (1 == $option) { // Extract information of thread_qualify. $sql = "SELECT qualify, qualify_time FROM $table_threads_qualify WHERE c_id = $course_id AND user_id = ".$user_id.' AND threadId = '.$thread_id." AND qualify_user_id = $qualify_user_id "; $rs = Database::query($sql); $row = Database::fetch_array($rs); // Insert thread_historical. $sql = "INSERT INTO $table_threads_qualify_log (c_id, user_id, threadId, qualify, qualify_user_id,qualify_time,session_id) VALUES(".$course_id.", '".$user_id."','".$thread_id."',".(float) $row[0].", '".$qualify_user_id."','".$row[1]."','')"; Database::query($sql); } } /** * This function shows current thread qualify . * * @param int $threadId * @param int $sessionId * @param int $userId * * @return array or null if is empty * * @author Isaac Flores , U.N.A.S University * * @version December 2008, dokeos 1.8.6 */ function current_qualify_of_thread($threadId, $sessionId, $userId) { $table_threads_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY); $course_id = api_get_course_int_id(); $currentUserId = api_get_user_id(); $sessionId = (int) $sessionId; $threadId = (int) $threadId; $sql = "SELECT qualify FROM $table_threads_qualify WHERE c_id = $course_id AND threadId = $threadId AND session_id = $sessionId AND qualify_user_id = $currentUserId AND user_id = $userId "; $res = Database::query($sql); $row = Database::fetch_array($res, 'ASSOC'); if ($row) { return $row['qualify']; } return 0; } /** * This function stores a reply in the forum_post table. * It also updates the forum_threads table (thread_replies +1 , thread_last_post, thread_date). * * @param array $values * @param int $courseId Optional * @param int $userId Optional * * @return int post id */ function store_reply(CForum $forum, CForumThread $thread, $values, $courseId = 0, $userId = 0) { $courseId = !empty($courseId) ? $courseId : api_get_course_int_id(); $post_date = api_get_utc_datetime(); $userId = $userId ?: api_get_user_id(); if (1 == $forum->getAllowAnonymous()) { if (api_is_anonymous() && empty($userId)) { $userId = api_get_anonymous_id(); } } if (empty($userId)) { return false; } $visible = 1; if ('1' == $forum->getApprovalDirectPost() && !api_is_allowed_to_edit(null, true) ) { $visible = 0; } $upload_ok = 1; $new_post_id = 0; if ($upload_ok) { $course = api_get_course_entity($courseId); $session = api_get_session_entity(); $repo = Container::getForumPostRepository(); $post = new CForumPost(); $text = empty($values['post_text']) ? '' : $values['post_text']; $post ->setPostTitle($values['post_title']) ->setPostText($text) ->setThread($thread) ->setForum($forum) ->setUser(api_get_user_entity($userId)) ->setPostNotification(isset($values['post_notification']) ? (bool) $values['post_notification'] : false) ->setVisible($visible) ->setPostDate(api_get_utc_datetime(null, false, true)) ->setParent($thread) ->addCourseLink($course, $session) ; if (isset($values['post_parent_id']) && !empty($values['post_parent_id'])) { $parent = $repo->find($values['post_parent_id']); $post->setPostParent($parent); } $repo->create($post); $new_post_id = $post->getIid(); if ($new_post_id) { $values['new_post_id'] = $new_post_id; $message = get_lang('The reply has been added'); if (!empty($_POST['file_ids']) && is_array($_POST['file_ids'])) { foreach ($_POST['file_ids'] as $key => $id) { editAttachedFile( [ 'comment' => $_POST['file_comments'][$key], 'post_id' => $new_post_id, ], $id ); } } // Update the thread. updateThreadInfo($values['threadId'], $new_post_id, $post_date); if ('1' == $forum->getApprovalDirectPost() && !api_is_allowed_to_edit(null, true) ) { $message .= '
'.get_lang('Your message has to be approved before people can view it.').'
'; } if ($forum->isModerated() && !api_is_allowed_to_edit(null, true) ) { $message .= '
'.get_lang('Your message has to be approved before people can view it.').'
'; } // Setting the notification correctly. $my_post_notification = isset($values['post_notification']) ? $values['post_notification'] : null; if (1 == $my_post_notification) { set_notification('thread', $values['threadId'], true); } send_notification_mails( $forum, $thread, $values ); add_forum_attachment_file('', $post); $logInfo = [ 'tool' => TOOL_FORUM, 'tool_id' => $values['forum_id'], 'tool_id_detail' => $values['threadId'], 'action' => 'new-post', 'action_details' => $values['action'], 'info' => $values['post_title'], ]; Event::registerLog($logInfo); } Session::erase('formelements'); Session::erase('origin'); Session::erase('breadcrumbs'); Session::erase('addedresource'); Session::erase('addedresourceid'); Display::addFlash(Display::return_message($message, 'confirmation', false)); } else { Display::addFlash( Display::return_message( get_lang('No file was uploaded.').' '.get_lang('Please select a file before pressing the upload button.'), 'error' ) ); } return $new_post_id; } /** * This function displays the form that is used to edit a post. This can be a new thread or a reply. * * @param CForumPost $post contains all the information about the current post * @param CForumThread $thread contains all the information about the current thread * @param CForum $forum contains all info about the current forum (to check if attachments are allowed) * @param array $form_values contains the default values to fill the form * * @author Patrick Cool , Ghent University * * @version february 2006, dokeos 1.8 */ function show_edit_post_form( $post, $thread, $forum, $form_values, $id_attach = 0 ) { // Initialize the object. $form = new FormValidator( 'edit_post', 'post', api_get_self().'?'.api_get_cidreq().'&forum='.(int) ($_GET['forum']).'&thread='.(int) ($_GET['thread']).'&post='.(int) ($_GET['post']) ); $form->addElement('header', get_lang('Edit a post')); // Setting the form elements. $form->addElement('hidden', 'post_id', $post->getIid()); $form->addElement('hidden', 'threadId', $thread->getIid()); $form->addElement('hidden', 'id_attach', $id_attach); if (null === $post->getPostParent()) { $form->addElement('hidden', 'is_first_post_of_thread', '1'); } $form->addElement('text', 'post_title', get_lang('Title')); $form->applyFilter('post_title', 'html_filter'); $form->addElement( 'html_editor', 'post_text', get_lang('Text'), null, api_is_allowed_to_edit(null, true) ? [ 'ToolbarSet' => 'Forum', 'Width' => '100%', 'Height' => '400', ] : [ 'ToolbarSet' => 'ForumStudent', 'Width' => '100%', 'Height' => '400', 'UserStatus' => 'student', ] ); $form->addRule('post_text', get_lang('Required field'), 'required'); $extraFields = new ExtraField('forum_post'); $extraFields->addElements($form, $post->getIid()); $form->addButtonAdvancedSettings('advanced_params'); $form->addElement('html', ''); $form->addFile('user_upload[]', get_lang('Attachment')); $form->addButton( 'add_attachment', get_lang('Add attachment'), 'paperclip', 'default', 'default', null, ['id' => 'reply-add-attachment'] ); $form->addButtonUpdate(get_lang('Edit'), 'SubmitPost'); // Setting the default values for the form elements. $defaults['post_title'] = $post->getPostTitle(); $defaults['post_text'] = $post->getPostText(); if (1 == $post->getPostNotification()) { $defaults['post_notification'] = true; } if (!empty($form_values)) { $defaults['post_notification'] = Security::remove_XSS($form_values['post_notification']); $defaults['thread_sticky'] = Security::remove_XSS($form_values['thread_sticky']); } $form->setDefaults($defaults); // The course admin can make a thread sticky (=appears with special icon and always on top). $form->addRule('post_title', get_lang('Required field'), 'required'); // Validation or display if ($form->validate()) { $values = $form->exportValues(); $values['item_id'] = $post->getIid(); $extraFieldValues = new ExtraFieldValue('forum_post'); $extraFieldValues->saveFieldValues($values); store_edit_post($forum, $values); } else { // Delete from $_SESSION forum attachment from other posts clearAttachedFiles($post->getIid()); // Get forum attachment ajax table to add it to form $fileData = getAttachmentsAjaxTable($post->getIid(), $forum->getIid()); $form->addElement('html', $fileData); $form->display(); } } /** * This function stores the edit of a post in the forum_post table. * * @author Patrick Cool , Ghent University * * @version february 2006, dokeos 1.8 */ function store_edit_post(CForum $forum, $values) { $logInfo = [ 'tool' => TOOL_FORUM, 'tool_id' => $_GET['forum'], 'tool_id_detail' => $values['threadId'], 'action' => 'edit-post', 'action_details' => 'post', 'info' => $values['post_title'], ]; Event::registerLog($logInfo); $threadTable = Database::get_course_table(TABLE_FORUM_THREAD); //check if this post is the first of the thread // First we check if the change affects the thread and if so we commit // the changes (sticky and post_title=thread_title are relevant). $posts = getPosts($forum, $values['threadId']); $first_post = null; if (!empty($posts) && count($posts) > 0 && isset($posts[0])) { $first_post = $posts[0]; } if (!empty($first_post) && $first_post['post_id'] == $values['post_id']) { // Simple edit $params = [ 'thread_title' => $values['post_title'], 'thread_sticky' => isset($values['thread_sticky']) ? $values['thread_sticky'] : 0, ]; $where = ['iid = ?' => [$values['threadId']]]; Database::update($threadTable, $params, $where); } $status = ''; $updateStatus = false; if ($forum->isModerated()) { if (api_is_allowed_to_edit(null, true)) { $status = $values['status']['status']; $updateStatus = true; } else { $status = CForumPost::STATUS_WAITING_MODERATION; $updateStatus = true; } } $postId = $values['post_id']; $repo = Container::getForumPostRepository(); /** @var CForumPost $post */ $post = $repo->find($postId); if ($post) { $post ->setPostTitle($values['post_title']) ->setPostText($values['post_text']) ->setPostNotification(isset($values['post_notification'])) ; if ($updateStatus) { $post->setStatus($status); } $repo->update($post); } // Update attached files if (!empty($_POST['file_ids']) && is_array($_POST['file_ids'])) { foreach ($_POST['file_ids'] as $key => $id) { editAttachedFile( [ 'comment' => $_POST['file_comments'][$key], 'post_id' => $values['post_id'], ], $id ); } } if (!empty($values['remove_attach'])) { throw new Exception('remove_attach'); } if (empty($values['id_attach'])) { add_forum_attachment_file( isset($values['file_comment']) ? $values['file_comment'] : null, $post ); } $message = get_lang('The post has been modified').'
'; $message .= get_lang('You can now return to the'). ' '. get_lang('Forum').'
'; $message .= get_lang('You can now return to the'). ' '. get_lang('Message').''; Session::erase('formelements'); Session::erase('origin'); Session::erase('breadcrumbs'); Session::erase('addedresource'); Session::erase('addedresourceid'); echo Display::return_message($message, 'confirmation', false); } function displayUserLink(User $user) { return ''. Security::remove_XSS(UserManager::formatUserFullName($user)).''; } function displayUserImage(User $user) { $url = Container::getIllustrationRepository()->getIllustrationUrl($user, '', ICON_SIZE_BIG); return '
'; } /** * The relies counter gets increased every time somebody replies to the thread. * * @author Patrick Cool , Ghent University * * @version february 2006, dokeos 1.8 * * @param int $threadId * @param string $lastPostId * @param string $post_date */ function updateThreadInfo($threadId, $lastPostId, $post_date) { $table_threads = Database::get_course_table(TABLE_FORUM_THREAD); $threadId = (int) $threadId; $lastPostId = (int) $lastPostId; $sql = "UPDATE $table_threads SET thread_replies = thread_replies+1, thread_last_post = '".$lastPostId."', thread_date = '".Database::escape_string($post_date)."' WHERE iid ='".$threadId."'"; // this needs to be cleaned first Database::query($sql); } function approvePost(CForumPost $post, $action) { if ('invisible' === $action) { $visibility = 0; } if ('visible' === $action) { $visibility = 1; handle_mail_cue('post', $post->getIid()); } $post->setVisible($visibility); Database::getManager()->persist($post); Database::getManager()->flush(); Display::addFlash(Display::return_message(get_lang('The visibility has been changed'))); } /** * This function retrieves all the unapproved messages for a given forum * This is needed to display the icon that there are unapproved messages in that thread (only the courseadmin can see * this). * * @param int $forum_id forum where we want to know the unapproved messages of * * @return array returns * * @author Patrick Cool , Ghent University * * @version february 2006, dokeos 1.8 */ function get_unaproved_messages($forum_id) { $table_posts = Database::get_course_table(TABLE_FORUM_POST); $course_id = api_get_course_int_id(); $return_array = []; $sql = "SELECT DISTINCT threadId FROM $table_posts WHERE c_id = $course_id AND forum_id='".Database::escape_string($forum_id)."' AND visible='0' "; $result = Database::query($sql); while ($row = Database::fetch_array($result)) { $return_array[] = $row['threadId']; } return $return_array; } /** * This function sends the notification mails to everybody who stated that they wanted to be informed when a new post * was added to a given thread. */ function send_notification_mails(CForum $forum, CForumThread $thread, $reply_info) { $courseEntity = api_get_course_entity(); $courseId = $courseEntity->getId(); $sessionId = api_get_session_id(); $sessionEntity = api_get_session_entity($sessionId); $table = Database::get_course_table(TABLE_FORUM_MAIL_QUEUE); // First we need to check if // 1. the forum category is visible // 2. the forum is visible // 3. the thread is visible // 4. the reply is visible (=when there is) $current_forum_category = null; if ($forum->getForumCategory()) { $current_forum_category = $forum->getForumCategory(); } $send_mails = false; if ($thread->isVisible($courseEntity, $sessionEntity) && $forum->isVisible($courseEntity, $sessionEntity) && ($current_forum_category && $forum->getForumCategory()->isVisible($courseEntity, $sessionEntity)) && '1' != $forum->getApprovalDirectPost() ) { $send_mails = true; } // The forum category, the forum, the thread and the reply are visible to the user if ($send_mails && !empty($forum)) { $postId = isset($reply_info['new_post_id']) ? $reply_info['new_post_id'] : 0; send_notifications($forum, $thread, $postId); } else { $table_notification = Database::get_course_table(TABLE_FORUM_NOTIFICATION); if ($forum) { $sql = "SELECT * FROM $table_notification WHERE c_id = ".$courseId." AND ( forum_id = '".$forum->getIid()."' OR threadId = '".$thread->getIid()."' ) "; $result = Database::query($sql); $user_id = api_get_user_id(); while ($row = Database::fetch_array($result)) { $sql = "INSERT INTO $table (c_id, threadId, post_id, user_id) VALUES (".$courseId.", '".$thread->getIid()."', '".(int) ($reply_info['new_post_id'])."', '$user_id' )"; Database::query($sql); } } } } /** * This function is called whenever something is made visible because there might * be new posts and the user might have indicated that (s)he wanted to be * informed about the new posts by mail. * * @param string $content Content type (post, thread, forum, forum_category) * @param int $id Item DB ID of the corresponding content type * * @return string language variable * * @author Patrick Cool , Ghent University * * @version february 2006, dokeos 1.8 */ function handle_mail_cue($content, $id) { $table_mailcue = Database::get_course_table(TABLE_FORUM_MAIL_QUEUE); $table_forums = Database::get_course_table(TABLE_FORUM); $table_threads = Database::get_course_table(TABLE_FORUM_THREAD); $table_posts = Database::get_course_table(TABLE_FORUM_POST); $table_users = Database::get_main_table(TABLE_MAIN_USER); $course_id = api_get_course_int_id(); $id = (int) $id; /* If the post is made visible we only have to send mails to the people who indicated that they wanted to be informed for that thread.*/ if ('post' === $content) { // Getting the information about the post (need the threadId). /** @var CForumPost $post */ $post = Container::getForumPostRepository()->find($id); $thread_id = $post->getThread()->getIid(); // Sending the mail to all the users that wanted to be informed for replies on this thread. $sql = "SELECT users.firstname, users.lastname, users.id as user_id, users.email FROM $table_mailcue mailcue, $table_posts posts, $table_users users WHERE posts.c_id = $course_id AND mailcue.c_id = $course_id AND posts.threadId = $thread_id AND posts.post_notification = '1' AND mailcue.threadId = $thread_id AND users.id = posts.poster_id AND users.active = 1 GROUP BY users.email"; $result = Database::query($sql); $forum = Container::getForumRepository()->find($post->getForum()->getIid()); while ($row = Database::fetch_array($result)) { send_mail($row, $forum, $post->getThread(), $post); } } elseif ('thread' === $content) { // Sending the mail to all the users that wanted to be informed for replies on this thread. $sql = "SELECT users.firstname, users.lastname, users.id as user_id, users.email, posts.forum_id FROM $table_mailcue mailcue, $table_posts posts, $table_users users WHERE posts.c_id = $course_id AND mailcue.c_id = $course_id AND posts.threadId = $id AND posts.post_notification = '1' AND mailcue.threadId = $id AND users.id = posts.poster_id AND users.active = 1 GROUP BY users.email"; $result = Database::query($sql); while ($row = Database::fetch_array($result)) { $forum = Container::getForumRepository()->find($row['forum_id']); $thread = Container::getForumThreadRepository()->find($id); send_mail($row, $forum, $thread); } // Deleting the relevant entries from the mailcue. $sql = "DELETE FROM $table_mailcue WHERE c_id = $course_id AND threadId = $id"; Database::query($sql); } elseif ('forum' === $content) { $sql = "SELECT iid FROM $table_threads WHERE c_id = $course_id AND forum_id = $id"; $result = Database::query($sql); while ($row = Database::fetch_array($result)) { handle_mail_cue('thread', $row['iid']); } } elseif ('forum_category' === $content) { $sql = "SELECT iid FROM $table_forums WHERE c_id = $course_id AND forum_category = $id"; $result = Database::query($sql); while ($row = Database::fetch_array($result)) { handle_mail_cue('forum', $row['iid']); } } else { return get_lang('Error'); } } /** * This function sends the mails for the mail notification. */ function send_mail($userInfo, CForum $forum, CForumThread $thread, CForumPost $postInfo = null) { if (empty($userInfo) || empty($forum) || empty($thread)) { return false; } $_course = api_get_course_info(); $user_id = api_get_user_id(); $forumId = $forum->getIid(); $threadId = $thread->getIid(); $thread_link = api_get_path(WEB_CODE_PATH). 'forum/viewthread.php?'.api_get_cidreq().'&forum='.$forumId.'&thread='.$threadId; $email_body = get_lang('Dear').' '. api_get_person_name($userInfo['firstname'], $userInfo['lastname'], null, PERSON_NAME_EMAIL_ADDRESS).",
\n\r"; $email_body .= get_lang('New Post in the forum'). ': '.$forum->getForumTitle().' - '.$thread->getThreadTitle()."
\n"; $courseId = (int) api_get_setting('forum.global_forums_course_id'); $subject = get_lang('New Post in the forum').' - '. $_course['official_code'].': '.$forum->getForumTitle().' - '.$thread->getThreadTitle()."
\n"; $courseInfoTitle = get_lang('Course').': '.$_course['name'].' - ['.$_course['official_code']."] -
\n"; if (!empty($courseId) && $_course['real_id'] == $courseId) { $subject = get_lang('New Post in the forum').': '. $forum->getForumTitle().' - '.$thread->getThreadTitle()."
\n"; $courseInfoTitle = "
\n"; } $email_body .= $courseInfoTitle; if (!empty($postInfo)) { $text = cut(strip_tags($postInfo->getPostText()), 100); if (!empty($text)) { $email_body .= get_lang('Message').":
\n "; $email_body .= $text; $email_body .= "

\n"; } } $email_body .= get_lang('You stated that you wanted to be informed by e-mail whenever somebody replies on the thread')."
\n"; if (!empty($thread_link)) { $email_body .= get_lang('The thread can be found here').' :
'.$thread_link."\n"; } if ($userInfo['user_id'] != $user_id) { MessageManager::send_message( $userInfo['user_id'], $subject, $email_body, [], [], null, null, null, null, $user_id ); } } /** * This function displays the form for moving a thread to a different (already existing) forum. * * @author Patrick Cool , Ghent University * * @version february 2006, dokeos 1.8 */ function move_thread_form() { $form = new FormValidator( 'movepost', 'post', api_get_self().'?forum='.(int) ($_GET['forum']).'&thread='.(int) ($_GET['thread']).'&action='.Security::remove_XSS($_GET['action']).'&'.api_get_cidreq() ); $form->addHeader(get_lang('Move Thread')); // Invisible form: the threadId $form->addHidden('threadId', (int) ($_GET['thread'])); $forum_categories = get_forum_categories(); $htmlcontent = '
*'.get_lang('Move to').'
'; $htmlcontent .= ''; $htmlcontent .= '
'; $form->addElement('html', $htmlcontent); // The OK button $form->addButtonSave(get_lang('Move Thread'), 'SubmitForum'); // Validation or display if ($form->validate()) { $values = $form->exportValues(); if (isset($_POST['forum'])) { store_move_thread($values); Display::addFlash(Display::return_message(get_lang('Moved'))); } } else { return $form->returnForm(); } } /** * This function displays the form for moving a post message to a different (already existing) or a new thread. * * @author Patrick Cool , Ghent University * * @version february 2006, dokeos 1.8 */ function move_post_form() { $form = new FormValidator( 'movepost', 'post', api_get_self().'?'.api_get_cidreq().'&forum='.(int) ($_GET['forum']).'&thread='.(int) ($_GET['thread']).'&post='.Security::remove_XSS($_GET['post']).'&action='.Security::remove_XSS($_GET['action']).'&post='.Security::remove_XSS($_GET['post']) ); // The header for the form $form->addElement('header', '', get_lang('Move post')); // Invisible form: the post_id $form->addElement('hidden', 'post_id', (int) ($_GET['post'])); // Dropdown list: Threads of this forum $threads = get_threads($_GET['forum']); $threads_list[0] = get_lang('A new thread'); foreach ($threads as $thread) { $threads_list[$thread->getIid()] = $thread->getThreadTitle(); } $form->addSelect('thread', get_lang('Move toThread'), $threads_list); $form->applyFilter('thread', 'html_filter'); // The OK button $form->addButtonSave(get_lang('Move post'), 'submit'); // Setting the rules $form->addRule('thread', get_lang('Required field'), 'required'); return $form; } /** * @param array $values * * @return string HTML language variable * * @author Patrick Cool , Ghent University * * @version february 2006, dokeos 1.8 */ function store_move_post($values) { $_course = api_get_course_info(); $course_id = api_get_course_int_id(); $table_forums = Database::get_course_table(TABLE_FORUM); $table_threads = Database::get_course_table(TABLE_FORUM_THREAD); $table_posts = Database::get_course_table(TABLE_FORUM_POST); $user = api_get_user_entity(api_get_user_id()); $course = api_get_course_entity($course_id); $session = api_get_session_entity(); if ('0' == $values['thread']) { $repoPost = Container::getForumPostRepository(); /** @var CForumPost $post */ $post = $repoPost->find($values['post_id']); $forumId = $post->getForum()->getIid(); $threadId = $post->getThread()->getIid(); $thread = new CForumThread(); $thread ->setThreadTitle($post->getPostTitle()) ->setForum($post->getForum()) ->setUser($post->getUser()) ->setThreadLastPost($post) ->setThreadDate($post->getPostDate()) ->setParent($post->getForum()) ->addCourseLink( $user, $course, $session ); $repo = Container::getForumThreadRepository(); $repo->create($thread); $new_thread_id = $thread->getIid(); // Moving the post to the newly created thread. $sql = "UPDATE $table_posts SET threadId='".$new_thread_id."', post_parent_id = NULL WHERE c_id = $course_id AND iid ='".(int) ($values['post_id'])."'"; Database::query($sql); // Resetting the parent_id of the thread to 0 for all those who had this moved post as parent. $sql = "UPDATE $table_posts SET post_parent_id = NULL WHERE c_id = $course_id AND post_parent_id='".(int) ($values['post_id'])."'"; Database::query($sql); // Updating updating the number of threads in the forum. $sql = "UPDATE $table_forums SET forum_threads=forum_threads+1 WHERE c_id = $course_id AND iid ='".$forumId."'"; Database::query($sql); // Resetting the last post of the old thread and decreasing the number of replies and the thread. $sql = "SELECT * FROM $table_posts WHERE c_id = $course_id AND threadId='".$threadId."' ORDER BY iid DESC"; $result = Database::query($sql); $row = Database::fetch_array($result); $sql = "UPDATE $table_threads SET thread_last_post='".$row['iid']."', thread_replies=thread_replies-1 WHERE c_id = $course_id AND iid ='".$threadId."'"; Database::query($sql); } else { // Moving to the chosen thread. $sql = 'SELECT threadId FROM '.$table_posts." WHERE c_id = $course_id AND iid = '".$values['post_id']."' "; $result = Database::query($sql); $row = Database::fetch_array($result); $original_thread_id = $row['threadId']; $sql = 'SELECT thread_last_post FROM '.$table_threads." WHERE c_id = $course_id AND iid = '".$original_thread_id."' "; $result = Database::query($sql); $row = Database::fetch_array($result); $thread_is_last_post = $row['thread_last_post']; // If is this thread, update the thread_last_post with the last one. if ($thread_is_last_post == $values['post_id']) { $sql = 'SELECT iid as post_id FROM '.$table_posts." WHERE c_id = $course_id AND threadId = '".$original_thread_id."' AND iid <> '".$values['post_id']."' ORDER BY post_date DESC LIMIT 1"; $result = Database::query($sql); $row = Database::fetch_array($result); $thread_new_last_post = $row['post_id']; $sql = 'UPDATE '.$table_threads." SET thread_last_post = '".$thread_new_last_post."' WHERE c_id = $course_id AND iid = '".$original_thread_id."' "; Database::query($sql); } $sql = "UPDATE $table_threads SET thread_replies=thread_replies-1 WHERE c_id = $course_id AND iid ='".$original_thread_id."'"; Database::query($sql); // moving to the chosen thread $sql = "UPDATE $table_posts SET threadId='".(int) ($_POST['thread'])."', post_parent_id = NULL WHERE c_id = $course_id AND iid ='".(int) ($values['post_id'])."'"; Database::query($sql); // resetting the parent_id of the thread to 0 for all those who had this moved post as parent $sql = "UPDATE $table_posts SET post_parent_id = NULL WHERE c_id = $course_id AND post_parent_id='".(int) ($values['post_id'])."'"; Database::query($sql); $sql = "UPDATE $table_threads SET thread_replies=thread_replies+1 WHERE c_id = $course_id AND iid ='".(int) ($_POST['thread'])."'"; Database::query($sql); } return get_lang('Thread moved'); } /** * @param array $values * * @return string HTML language variable * * @author Patrick Cool , Ghent University * * @version february 2006, dokeos 1.8 */ function store_move_thread($values) { $table_threads = Database::get_course_table(TABLE_FORUM_THREAD); $table_posts = Database::get_course_table(TABLE_FORUM_POST); $courseId = api_get_course_int_id(); $sessionId = api_get_session_id(); $forumId = (int) ($_POST['forum']); $threadId = (int) ($_POST['threadId']); //$forumInfo = get_forums($forumId); // Change the thread table: Setting the forum_id to the new forum. $sql = "UPDATE $table_threads SET forum_id = $forumId WHERE c_id = $courseId AND iid = $threadId"; Database::query($sql); // Changing all the posts of the thread: setting the forum_id to the new forum. $sql = "UPDATE $table_posts SET forum_id = $forumId WHERE c_id = $courseId AND threadId= $threadId"; Database::query($sql); // Fix group id, if forum is moved to a different group /*if (!empty($forumInfo['to_group_id'])) { $groupId = $forumInfo['to_group_id']; $item = api_get_item_property_info( $courseId, TABLE_FORUM_THREAD, $threadId, $sessionId, $groupId ); $table = Database::get_course_table(TABLE_ITEM_PROPERTY); $sessionCondition = api_get_session_condition($sessionId); if (!empty($item)) { if ($item['to_group_id'] != $groupId) { $sql = "UPDATE $table SET to_group_id = $groupId WHERE tool = '".TABLE_FORUM_THREAD."' AND c_id = $courseId AND ref = ".$item['ref']." $sessionCondition "; Database::query($sql); } } else { $sql = "UPDATE $table SET to_group_id = $groupId WHERE tool = '".TABLE_FORUM_THREAD."' AND c_id = $courseId AND ref = ".$threadId." $sessionCondition "; Database::query($sql); } }*/ return get_lang('Thread moved'); } /** * Prepares a string for displaying by highlighting the search results inside, if any. * * @param string $input the input string * * @return string the same string with highlighted hits inside * * @author Patrick Cool , Ghent University, February 2006 - the initial version. * @author Ivan Tcholakov, March 2011 - adaptation for Chamilo LMS. */ function prepare4display($input) { static $highlightcolors = ['yellow', '#33CC33', '#3399CC', '#9999FF', '#33CC33']; static $search; if (!isset($search)) { if (isset($_POST['search_term'])) { $search = $_POST['search_term']; // No html at all. } elseif (isset($_GET['search'])) { $search = $_GET['search']; } else { $search = ''; } } if (!empty($search)) { if (strstr($search, '+')) { $search_terms = explode('+', $search); } else { $search_terms[] = trim($search); } $counter = 0; foreach ($search_terms as $key => $search_term) { $input = api_preg_replace( '/'.preg_quote(trim($search_term), '/').'/i', '$0', $input ); $counter++; } } // TODO: Security should be implemented outside this function. // Change this to COURSEMANAGERLOWSECURITY or COURSEMANAGER to lower filtering and allow more styles // (see comments of Security::remove_XSS() method to learn about other levels). return Security::remove_XSS($input, STUDENT, true); } /** * Display the search form for the forum and display the search results. * * @author Patrick Cool , Ghent University, Belgium * * @version march 2008, dokeos 1.8.5 */ function forum_search() { $form = new FormValidator( 'forumsearch', 'post', 'forumsearch.php?'.api_get_cidreq() ); // Setting the form elements. $form->addElement('header', '', get_lang('Search in the Forum')); $form->addElement('text', 'search_term', get_lang('Search term'), ['autofocus']); $form->applyFilter('search_term', 'html_filter'); $form->addElement('static', 'search_information', '', get_lang('Search in the ForumInformation')); $form->addButtonSearch(get_lang('Search')); // Setting the rules. $form->addRule('search_term', get_lang('Required field'), 'required'); $form->addRule('search_term', get_lang('Too short'), 'minlength', 3); // Validation or display. if ($form->validate()) { $values = $form->exportValues(); $form->setDefaults($values); $form->display(); // Display the search results. display_forum_search_results($values['search_term']); } else { $form->display(); } } /** * Display the search results. * * @param string $search_term * * @author Patrick Cool , Ghent University, Belgium * * @version march 2008, dokeos 1.8.5 */ function display_forum_search_results($search_term) { /*$table_threads = Database::get_course_table(TABLE_FORUM_THREAD); $table_posts = Database::get_course_table(TABLE_FORUM_POST); $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY); $session_id = api_get_session_id(); $course_id = api_get_course_int_id(); // Defining the search strings as an array. if (strstr($search_term, '+')) { $search_terms = explode('+', $search_term); } else { $search_terms[] = $search_term; } // Search restriction. foreach ($search_terms as $value) { $search_restriction[] = " ( posts.post_title LIKE '%".Database::escape_string(trim($value))."%' OR posts.post_text LIKE '%".Database::escape_string(trim($value))."%' )"; } $sessionCondition = api_get_session_condition( $session_id, true, false, 'item_property.session_id' ); $sql = "SELECT posts.* FROM $table_posts posts INNER JOIN $table_threads threads ON (posts.threadId = threads.threadId AND posts.c_id = threads.c_id) INNER JOIN $table_item_property item_property ON (item_property.ref = threads.threadId AND item_property.c_id = threads.c_id) WHERE posts.c_id = $course_id AND item_property.c_id = $course_id AND item_property.visibility = 1 $sessionCondition AND posts.visible = 1 AND item_property.tool = '".TOOL_FORUM_THREAD."' AND ".implode(' AND ', $search_restriction).' GROUP BY posts.post_id'; // Getting all the information of the forum categories. $forum_categories_list = get_forum_categories(); // Getting all the information of the forums. $forum_list = get_forums(); $result = Database::query($sql); $search_results = []; while ($row = Database::fetch_array($result, 'ASSOC')) { $forumId = $row['forum_id']; $forumData = get_forums($forumId); $category = isset($forum_categories_list[$forumData['forum_category']]) ? $forum_categories_list[$forumData['forum_category']] : null; $display_result = false; // We only show it when // 1. forum category is visible // 2. forum is visible // 3. thread is visible (to do) // 4. post is visible if (!api_is_allowed_to_edit(null, true)) { if (!empty($category)) { if ('1' == $category['visibility'] && '1' == $forumData['visibility']) { $display_result = true; } } else { if ('1' == $forumData['visible']) { $display_result = true; } } } else { $display_result = true; } if ($display_result) { $categoryName = !empty($category) ? $category['cat_title'] : ''; $search_results_item = '
  • '. prepare4display($categoryName).' > '; $search_results_item .= ''. prepare4display($forum_list[$row['forum_id']]['forum_title']).' > '; $search_results_item .= ''. prepare4display($row['post_title']).''; $search_results_item .= '
    '; if (api_strlen($row['post_title']) > 200) { $search_results_item .= prepare4display(api_substr(strip_tags($row['post_title']), 0, 200)).'...'; } else { $search_results_item .= prepare4display($row['post_title']); } $search_results_item .= '
  • '; $search_results[] = $search_results_item; } } echo ''.count($search_results).' '.get_lang('Search in the ForumResults').''; echo '
      '; if ($search_results) { echo implode($search_results); } echo '
    ';*/ } /** * Return the link to the forum search page. * * @author Patrick Cool , Ghent University, Belgium * * @version April 2008, dokeos 1.8.5 */ function search_link() { // @todo implement search return ''; $return = ''; $origin = api_get_origin(); if ('learnpath' != $origin) { $return = ' '; $return .= Display::return_icon('search.png', get_lang('Search'), '', ICON_SIZE_MEDIUM).''; if (!empty($_GET['search'])) { $return .= ': '.Security::remove_XSS($_GET['search']).' '; $url = api_get_self().'?'; $url_parameter = []; foreach ($_GET as $key => $value) { if ('search' != $key) { $url_parameter[] = Security::remove_XSS($key).'='.Security::remove_XSS($value); } } $url .= implode('&', $url_parameter); $return .= ''.Display::return_icon('delete.gif', get_lang('Clean search results')).''; } } return $return; } /** * This function adds an attachment file into a forum. * * @param string $file_comment a comment about file * @param CForumPost $post from forum_post table * * @return false|null */ function add_forum_attachment_file($file_comment, CForumPost $post) { $request = Container::getRequest(); if (false === $request->files->has('user_upload')) { return false; } $file = $request->files->get('user_upload'); if (empty($file)) { return false; } $files = []; if ($file instanceof UploadedFile) { $files[] = $file; } else { $files = $file; } /** @var UploadedFile $attachment */ foreach ($files as $file) { $valid = process_uploaded_file($file); if (!$valid) { continue; } // Try to add an extension to the file if it hasn't one. $new_file_name = add_ext_on_mime( stripslashes($file->getPathname()), $file->getType() ); // User's file name $file_name = $file->getClientOriginalName(); if (!filter_extension($new_file_name)) { Display::addFlash( Display::return_message( get_lang('File upload failed: this file extension or file type is prohibited'), 'error' ) ); return; } $safe_file_comment = Database::escape_string($file_comment); $user = api_get_user_entity(api_get_user_id()); $course = api_get_course_entity(api_get_course_int_id()); $session = api_get_session_entity(api_get_session_id()); $attachment = (new CForumAttachment()) ->setCId(api_get_course_int_id()) ->setComment($safe_file_comment) ->setFilename($file_name) ->setPath($file_name) ->setPost($post) ->setSize($file->getSize()) ->setParent($post) ->addCourseLink( $course, $session ); $repo = Container::getForumAttachmentRepository(); $repo->create($attachment); $repo->addFile($attachment, $file); $repo->update($attachment); } } /** * This function edits an attachment file into a forum. * * @param string $file_comment a comment about file * @param int $post_id * @param int $id_attach attachment file Id */ function edit_forum_attachment_file($file_comment, $post_id, $id_attach) { throw new Exception('edit_forum_attachment_file'); /* $_course = api_get_course_info(); $table_forum_attachment = Database::get_course_table(TABLE_FORUM_ATTACHMENT); $course_id = api_get_course_int_id(); $filesData = []; if (!is_array($_FILES['user_upload']['name'])) { $filesData[] = $_FILES['user_upload']; } else { $fileCount = count($_FILES['user_upload']['name']); $fileKeys = array_keys($_FILES['user_upload']); for ($i = 0; $i < $fileCount; $i++) { foreach ($fileKeys as $key) { $filesData[$i][$key] = $_FILES['user_upload'][$key][$i]; } } } foreach ($filesData as $attachment) { if (empty($attachment['name'])) { continue; } $upload_ok = process_uploaded_file($attachment); if (!$upload_ok) { continue; } $course_dir = $_course['path'].'/upload/forum'; $sys_course_path = api_get_path(SYS_COURSE_PATH); $updir = $sys_course_path.$course_dir; // Try to add an extension to the file if it hasn't one. $new_file_name = add_ext_on_mime(stripslashes($attachment['name']), $attachment['type']); // User's file name $file_name = $attachment['name']; if (!filter_extension($new_file_name)) { Display::addFlash( Display::return_message( get_lang('File upload failed: this file extension or file type is prohibited'), 'error' ) ); } else { $new_file_name = uniqid(''); $new_path = $updir.'/'.$new_file_name; $result = @move_uploaded_file($attachment['tmp_name'], $new_path); $safe_file_comment = Database::escape_string($file_comment); $safe_file_name = Database::escape_string($file_name); $safe_new_file_name = Database::escape_string($new_file_name); $safe_post_id = (int) $post_id; $safe_id_attach = (int) $id_attach; // Storing the attachments if any. if ($result) { $sql = "UPDATE $table_forum_attachment SET filename = '$safe_file_name', comment = '$safe_file_comment', path = '$safe_new_file_name', post_id = '$safe_post_id', size ='".$attachment['size']."' WHERE c_id = $course_id AND id = '$safe_id_attach'"; Database::query($sql); api_item_property_update( $_course, TOOL_FORUM_ATTACH, $safe_id_attach, 'ForumAttachmentUpdated', api_get_user_id() ); } } }*/ } /** * Show a list with all the attachments according to the post's id. * * @param int $postId * * @return array with the post info * * @author Julio Montoya * * @version avril 2008, dokeos 1.8.5 */ function get_attachment($postId) { $table = Database::get_course_table(TABLE_FORUM_ATTACHMENT); $course_id = api_get_course_int_id(); $row = []; $postId = (int) $postId; if (empty($postId)) { return []; } $sql = "SELECT iid, path, filename, comment FROM $table WHERE c_id = $course_id AND post_id = $postId"; $result = Database::query($sql); if (0 != Database::num_rows($result)) { $row = Database::fetch_array($result); } return $row; } /** * Delete the all the attachments from the DB and the file according to the post's id or attach id(optional). * * @param int $postId * @param int $attachmentId * * @return bool */ function delete_attachment($postId, $attachmentId) { $repo = Container::getForumPostRepository(); $em = Database::getManager(); /** @var CForumPost $post */ $post = $repo->find($postId); if ($post) { $repoAttachment = Container::getForumAttachmentRepository(); $attachment = $repoAttachment->find($attachmentId); if ($attachment) { $post->removeAttachment($attachment); $em->remove($attachment); } $repo->update($post); Display::addFlash(Display::return_message(get_lang('The attached file has been deleted'), 'confirmation')); } return true; } /** * This function gets all the forum information of the all the forum of the group. * * @param array $groupInfo the id of the group we need the fora of (see forum.forum_of_group) * * @return CForum[] * * @todo this is basically the same code as the get_forums function. Consider merging the two. */ function get_forums_of_group(CGroup $group) { $course = api_get_course_entity(); $session = api_get_session_entity(); $repo = Container::getForumRepository(); $qb = $repo->getResourcesByCourse($course, $session, $group); return $qb->getQuery()->getResult(); } /** * This function stores which users have to be notified of which forums or threads. * * @param string $content does the user want to be notified about a forum or about a thread * @param int $id the id of the forum or thread * @param bool $addOnly * @param array $userInfo * @param array $courseInfo * * @return string language variable * * @author Patrick Cool , Ghent University, Belgium * @author Julio Montoya * * @since May 2008 v1.8.5 */ function set_notification($content, $id, $addOnly = false, $userInfo = [], $courseInfo = []) { $userInfo = empty($userInfo) ? api_get_user_info() : $userInfo; $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo; $id = (int) $id; if (empty($userInfo) || empty($courseInfo) || empty($id) || empty($content)) { return false; } // Database table definition $table_notification = Database::get_course_table(TABLE_FORUM_NOTIFICATION); $course_id = $courseInfo['real_id']; // Which database field do we have to store the id in? $field = 'threadId'; if ('forum' === $content) { $field = 'forum_id'; } $userId = $userInfo['user_id']; // First we check if the notification is already set for this. $sql = "SELECT * FROM $table_notification WHERE c_id = $course_id AND $field = $id AND user_id = $userId "; $result = Database::query($sql); $total = Database::num_rows($result); // If the user did not indicate that (s)he wanted to be notified already // then we store the notification request (to prevent double notification requests). if ($total <= 0) { $notification = new CForumNotification(); $notification ->setCId($course_id) ->setUserId($userId) ; if ('forum' === $content) { $notification->setForumId($id); } else { $notification->setThreadId($id); } $em = Database::getManager(); $em->persist($notification); $em->flush(); Session::erase('forum_notification'); getNotificationsPerUser(0, true); return get_lang('You will be notified of new posts by e-mail.'); } else { if (!$addOnly) { $sql = "DELETE FROM $table_notification WHERE c_id = $course_id AND $field = $id AND user_id = $userId "; Database::query($sql); Session::erase('forum_notification'); getNotificationsPerUser(0, true); return get_lang('You will no longer be notified of new posts by email'); } } } /** * This function retrieves all the email adresses of the users who wanted to be notified * about a new post in a certain forum or thread. * * @param string $content does the user want to be notified about a forum or about a thread * @param int $id the id of the forum or thread * * @return array returns * * @author Patrick Cool , Ghent University, Belgium * @author Julio Montoya * * @version May 2008, dokeos 1.8.5 * * @since May 2008, dokeos 1.8.5 */ function get_notifications($content, $id) { // Database table definition $table_users = Database::get_main_table(TABLE_MAIN_USER); $table_notification = Database::get_course_table(TABLE_FORUM_NOTIFICATION); $course_id = api_get_course_int_id(); // Which database field contains the notification? $field = 'threadId'; if ('forum' === $content) { $field = 'forum_id'; } $id = (int) $id; $sql = "SELECT user.id as user_id, user.firstname, user.lastname, user.email, user.id user FROM $table_users user, $table_notification notification WHERE notification.c_id = $course_id AND user.active = 1 AND user.id = notification.user_id AND notification.$field = $id "; $result = Database::query($sql); $return = []; while ($row = Database::fetch_array($result)) { $return['user'.$row['user_id']] = ['email' => $row['email'], 'user_id' => $row['user_id']]; } return $return; } /** * Get all the users who need to receive a notification of a new post (those subscribed to * the forum or the thread). * * @param int $post_id the id of the post * * @return false|null * * @author Patrick Cool , Ghent University, Belgium * * @version May 2008, dokeos 1.8.5 * * @since May 2008, dokeos 1.8.5 */ function send_notifications(CForum $forum, CForumThread $thread, $post_id = 0) { if (!$forum) { return false; } // Users who subscribed to the forum $users_to_be_notified_by_forum = get_notifications('forum', $forum->getIid()); // User who subscribed to the thread $users_to_be_notified_by_thread = []; if (!$thread) { $users_to_be_notified_by_thread = get_notifications('thread', $thread->getIid()); } $postInfo = null; if (!empty($post_id)) { $postInfo = Container::getForumPostRepository()->find($post_id); } // Merging the two $users_to_be_notified = array_merge($users_to_be_notified_by_forum, $users_to_be_notified_by_thread); if (is_array($users_to_be_notified)) { foreach ($users_to_be_notified as $value) { $userInfo = api_get_user_info($value['user_id']); send_mail($userInfo, $forum, $thread, $postInfo); } } } /** * Get all the notification subscriptions of the user * = which forums and which threads does the user wants to be informed of when a new * post is added to this thread. * * @param int $user_id the user_id of a user (default = 0 => the current user) * @param bool $force force get the notification subscriptions (even if the information is already in the session * * @return array * * @author Patrick Cool , Ghent University, Belgium * * @version May 2008, dokeos 1.8.5 * * @since May 2008, dokeos 1.8.5 */ function getNotificationsPerUser($user_id = 0, $force = false, $course_id = 0) { // Database table definition $table_notification = Database::get_course_table(TABLE_FORUM_NOTIFICATION); $course_id = empty($course_id) ? api_get_course_int_id() : (int) $course_id; if (empty($course_id) || -1 == $course_id) { return null; } $user_id = empty($user_id) ? api_get_user_id() : (int) $user_id; if (!isset($_SESSION['forum_notification']) || $_SESSION['forum_notification']['course'] != $course_id || true == $force ) { $_SESSION['forum_notification']['course'] = $course_id; $sql = "SELECT * FROM $table_notification WHERE c_id = $course_id AND user_id='".$user_id."'"; $result = Database::query($sql); while ($row = Database::fetch_array($result)) { if (null !== $row['forum_id']) { $_SESSION['forum_notification']['forum'][] = $row['forum_id']; } if (null !== $row['threadId']) { $_SESSION['forum_notification']['thread'][] = $row['threadId']; } } } } /** * This function counts the number of post inside a thread. * * @param int $thread_id * * @return int the number of post inside a thread * * @author Jhon Hinojosa , * * @version octubre 2008, dokeos 1.8 */ function count_number_of_post_in_thread($thread_id) { $table_posts = Database::get_course_table(TABLE_FORUM_POST); $course_id = api_get_course_int_id(); if (empty($course_id)) { return 0; } $sql = "SELECT count(*) count FROM $table_posts WHERE c_id = $course_id AND threadId='".(int) $thread_id."' "; $result = Database::query($sql); $count = 0; if (Database::num_rows($result) > 0) { $row = Database::fetch_array($result); $count = $row['count']; } return $count; } /** * This function counts the number of post inside a thread user. * * @param int $thread_id * @param int $user_id * * @return int the number of post inside a thread user */ function count_number_of_post_for_user_thread($thread_id, $user_id) { $table_posts = Database::get_course_table(TABLE_FORUM_POST); $sql = "SELECT count(iid) as count FROM $table_posts WHERE threadId=".(int) $thread_id.' AND poster_id = '.(int) $user_id.' AND visible = 1 '; $result = Database::query($sql); $count = 0; if (Database::num_rows($result) > 0) { $count = Database::fetch_array($result); $count = $count['count']; } return $count; } /** * This function retrieves information of statistical. * * @param int $thread_id * @param int $user_id * @param int $course_id * * @return array the information of statistical * * @author Jhon Hinojosa , * * @version oct 2008, dokeos 1.8 */ function get_statistical_information($thread_id, $user_id, $course_id) { $result = []; $courseInfo = api_get_course_info_by_id($course_id); $result['user_course'] = CourseManager::get_users_count_in_course($courseInfo['code']); $result['post'] = count_number_of_post_in_thread($thread_id); $result['user_post'] = count_number_of_post_for_user_thread($thread_id, $user_id); return $result; } /** * This function return the posts inside a thread from a given user. * * @param int $thread_id * @param int $user_id * * @return array posts inside a thread * * @author Jhon Hinojosa , * * @version oct 2008, dokeos 1.8 */ function get_thread_user_post(Course $course, $thread_id, $user_id) { $table_posts = Database::get_course_table(TABLE_FORUM_POST); $table_users = Database::get_main_table(TABLE_MAIN_USER); $thread_id = (int) $thread_id; $user_id = (int) $user_id; $course_id = $course->getId(); if (empty($course_id)) { $course_id = api_get_course_int_id(); } $sql = "SELECT *, user.id as user_id FROM $table_posts posts LEFT JOIN $table_users user ON posts.poster_id = user.id WHERE posts.c_id = $course_id AND posts.threadId='$thread_id' AND posts.poster_id='$user_id' ORDER BY posts.iid ASC"; $result = Database::query($sql); $post_list = []; while ($row = Database::fetch_array($result)) { $row['status'] = '1'; $post_list[] = $row; $sql = "SELECT * FROM $table_posts posts LEFT JOIN $table_users users ON (posts.poster_id=users.id) WHERE posts.c_id = $course_id AND posts.threadId='$thread_id' AND posts.post_parent_id='".$row['iid']."' ORDER BY posts.iid ASC"; $result2 = Database::query($sql); while ($row2 = Database::fetch_array($result2)) { $row2['status'] = '0'; $post_list[] = $row2; } } return $post_list; } /** * This function get the name of an thread by id. * * @param int $thread_id * * @return string * * @author Christian Fasanando * @author Julio Montoya Adding security */ function get_name_thread_by_id($thread_id) { $t_forum_thread = Database::get_course_table(TABLE_FORUM_THREAD); $course_id = api_get_course_int_id(); $sql = "SELECT thread_title FROM $t_forum_thread WHERE c_id = $course_id AND iid = '".(int) $thread_id."' "; $result = Database::query($sql); $row = Database::fetch_array($result); return $row[0]; } /** * This function gets all the post written by an user. * * @param int $user_id * @param int $courseId * * @return string */ function get_all_post_from_user(int $user_id, int $courseId): string { $j = 0; $forums = get_forums($courseId); krsort($forums); $forum_results = ''; foreach ($forums as $forum) { $forumId = $forum->getIid(); /*if (0 == $forum['visibility']) { continue; }*/ if ($j <= 4) { $threads = get_threads($forumId); if ($threads) { $i = 0; $hand_forums = ''; $post_counter = 0; foreach ($threads as $thread) { /*if (0 == $thread['visibility']) { continue; }*/ if ($i <= 4) { $post_list = get_thread_user_post_limit( $courseId, $thread->getIid(), $user_id, 1 ); $post_counter = count($post_list); if (is_array($post_list) && count($post_list) > 0) { $hand_forums .= '
    '; $hand_forums .= Display::return_icon( 'thread.png', get_lang('Thread'), '', ICON_SIZE_MEDIUM ); $hand_forums .= ' '.Security::remove_XSS($thread->getThreadTitle(), STUDENT); $hand_forums .= '
    '; foreach ($post_list as $posts) { $hand_forums .= '
    '; $hand_forums .= ''.Security::remove_XSS($posts['post_title'], STUDENT).''; $hand_forums .= '
    '; $hand_forums .= Security::remove_XSS($posts['post_text'], STUDENT); $hand_forums .= '
    '; $hand_forums .= '
    '; } } } $i++; } $forum_results .= '
    '; $forum_results .= '

    '; $forum_results .= '
    '. Display::return_icon('forum.gif', get_lang('Forum')).' '.Security::remove_XSS($forum->getForumTitle(), STUDENT). '
    '; $forum_results .= '
    '; if ($post_counter > 0) { $forum_results .= $hand_forums; } $forum_results .= '
    '; } $j++; } } return $forum_results; } /** * @param int $thread_id * @param int $user_id * @param int $limit * * @return array */ function get_thread_user_post_limit($courseId, $thread_id, $user_id, $limit = 10) { $table_posts = Database::get_course_table(TABLE_FORUM_POST); $table_users = Database::get_main_table(TABLE_MAIN_USER); $courseId = (int) $courseId; $limit = (int) $limit; $sql = "SELECT * FROM $table_posts posts LEFT JOIN $table_users users ON posts.poster_id=users.id WHERE posts.c_id = $courseId AND posts.threadId='".Database::escape_string($thread_id)."' AND posts.poster_id='".Database::escape_string($user_id)."' ORDER BY posts.post_id DESC LIMIT $limit "; $result = Database::query($sql); $post_list = []; while ($row = Database::fetch_array($result)) { $row['status'] = '1'; $post_list[] = $row; } return $post_list; } /** * @param string $userId * @param array $courseInfo * @param int $sessionId * * @return array */ function getForumCreatedByUser($userId, $courseInfo, $sessionId) { if (empty($userId) || empty($courseInfo)) { return []; } $courseId = $courseInfo['real_id']; $repo = Container::getForumRepository(); $courseEntity = api_get_course_entity($courseId); $sessionEntity = api_get_session_entity($sessionId); $qb = $repo->getResourcesByCourse($courseEntity, $sessionEntity); $qb->andWhere('node.creator = :creator'); $qb->setParameter('creator', $userId); $items = $qb->getQuery()->getResult(); $forumList = []; if (!empty($items)) { /** @var CForum $forum */ foreach ($items as $forum) { $forumList[] = [ $forum->getForumTitle(), api_get_local_time($forum->getResourceNode()->getCreatedAt()), api_get_local_time($forum->getResourceNode()->getUpdatedAt()), ]; } } return $forumList; } /** * This function builds an array of all the posts in a given thread * where the key of the array is the post_id * It also adds an element children to the array which itself is an array * that contains all the id's of the first-level children. * * @return array containing all the information on the posts of a thread * * @author Patrick Cool , Ghent University */ function calculate_children($rows) { $sorted_rows = [0 => []]; if (!empty($rows)) { foreach ($rows as $row) { $rows_with_children[$row['post_id']] = $row; $rows_with_children[$row['post_parent_id']]['children'][] = $row['post_id']; } $rows = $rows_with_children; forumRecursiveSort($rows, $sorted_rows); unset($sorted_rows[0]); } return $sorted_rows; } /** * @param $rows * @param $threads * @param int $seed * @param int $indent */ function forumRecursiveSort($rows, &$threads, $seed = 0, $indent = 0) { if ($seed > 0) { $threads[$rows[$seed]['post_id']] = $rows[$seed]; $threads[$rows[$seed]['post_id']]['indent_cnt'] = $indent; $indent++; } if (isset($rows[$seed]['children'])) { foreach ($rows[$seed]['children'] as $child) { forumRecursiveSort($rows, $threads, $child, $indent); } } } /** * Update forum attachment data, used to update comment and post ID. * * @param array $array (field => value) to update forum attachment row * @param int $id ID to find row to update * @param int $courseId course ID to find row to update * * @return int number of affected rows */ function editAttachedFile($array, $id, $courseId = null) { // Init variables $setString = ''; $id = (int) $id; $courseId = (int) $courseId; if (empty($courseId)) { // $courseId can be null, use api method $courseId = api_get_course_int_id(); } /* * Check if Attachment ID and Course ID are greater than zero * and array of field values is not empty */ if ($id > 0 && $courseId > 0 && !empty($array) && is_array($array)) { foreach ($array as $key => &$item) { $item = Database::escape_string($item); $setString .= $key.' = "'.$item.'", '; } // Delete last comma $setString = substr($setString, 0, strlen($setString) - 2); $forumAttachmentTable = Database::get_course_table(TABLE_FORUM_ATTACHMENT); $sql = "UPDATE $forumAttachmentTable SET $setString WHERE c_id = $courseId AND iid = $id"; $result = Database::query($sql); if (false !== $result) { $affectedRows = Database::affected_rows($result); if ($affectedRows > 0) { /* * If exist in $_SESSION variable, then delete them from it * because they would be deprecated */ if (!empty($_SESSION['forum']['upload_file'][$courseId][$id])) { unset($_SESSION['forum']['upload_file'][$courseId][$id]); } } return $affectedRows; } } return 0; } /** * Return a table where the attachments will be set. * * @param int $postId Forum Post ID * * @return string The Forum Attachments Ajax Table */ function getAttachmentsAjaxTable($postId = 0) { $postId = (int) $postId; $courseId = api_get_course_int_id(); $attachIds = getAttachmentIdsByPostId($postId, $courseId); $fileDataContent = ''; // Update comment to show if form did not pass validation if (!empty($_REQUEST['file_ids']) && is_array($_REQUEST['file_ids'])) { // 'file_ids is the name from forum attachment ajax form foreach ($_REQUEST['file_ids'] as $key => $attachId) { if (!empty($_SESSION['forum']['upload_file'][$courseId][$attachId]) && is_array($_SESSION['forum']['upload_file'][$courseId][$attachId]) ) { // If exist forum attachment then update into $_SESSION data $_SESSION['forum']['upload_file'][$courseId][$attachId]['comment'] = $_POST['file_comments'][$key]; } } } // Get data to fill into attachment files table if (!empty($_SESSION['forum']['upload_file'][$courseId]) && is_array($_SESSION['forum']['upload_file'][$courseId]) ) { $uploadedFiles = $_SESSION['forum']['upload_file'][$courseId]; foreach ($uploadedFiles as $k => $uploadedFile) { if (!empty($uploadedFile) && in_array($uploadedFile['id'], $attachIds)) { // Buil html table including an input with attachmentID $fileDataContent .= ''.$uploadedFile['name'].''.$uploadedFile['size'].' '.$uploadedFile['result']. ' '. $uploadedFile['delete'].''. ''.''; } else { /* * If attachment data is empty, then delete it from $_SESSION * because could generate and empty row into html table */ unset($_SESSION['forum']['upload_file'][$courseId][$k]); } } } $style = empty($fileDataContent) ? 'display: none;' : ''; // Forum attachment Ajax table return '
    '.$fileDataContent.'
    '.get_lang('Filename').' '.get_lang('Size').' '.get_lang('Status').' '.get_lang('Comment').' '.get_lang('Delete').'
    '; } /** * Return an array of prepared attachment data to build forum attachment table * Also, save this array into $_SESSION to do available the attachment data. * * @param int $forumId * @param int $threadId * @param int $postId * @param int $attachId * @param int $courseId * * @return array */ function getAttachedFiles( $forumId, $threadId, $postId = 0, $attachId = 0, $courseId = 0 ) { $forumId = (int) $forumId; $courseId = (int) $courseId; $attachId = (int) $attachId; $postId = (int) $postId; $threadId = (int) $threadId; if (empty($courseId)) { // $courseId can be null, use api method $courseId = api_get_course_int_id(); } if (empty($forumId)) { if (!empty($_REQUEST['forum'])) { $forumId = (int) $_REQUEST['forum']; } else { // if forum ID is empty, cannot generate delete url return []; } } // Check if exist at least one of them to filter forum attachment select query if (empty($postId) && empty($attachId)) { return []; } if (empty($postId)) { $filter = "AND iid = $attachId"; } elseif (empty($attachId)) { $filter = "AND post_id = $postId"; } else { $filter = "AND post_id = $postId AND iid = $attachId"; } $forumAttachmentTable = Database::get_course_table(TABLE_FORUM_ATTACHMENT); $sql = "SELECT iid FROM $forumAttachmentTable WHERE c_id = $courseId $filter"; $result = Database::query($sql); $json = []; if (Database::num_rows($result) > 0) { $repo = Container::getForumAttachmentRepository(); while ($row = Database::fetch_array($result, 'ASSOC')) { /** @var CForumAttachment $attachment */ $attachment = $repo->find($row['iid']); $downloadUrl = $repo->getResourceFileDownloadUrl($attachment); // name contains an URL to download attachment file and its filename $json['name'] = Display::url( api_htmlentities($attachment->getFilename()), $downloadUrl, ['target' => '_blank', 'class' => 'attachFilename'] ); $json['id'] = $row['iid']; $json['comment'] = $attachment->getComment(); // Format file size $json['size'] = format_file_size($attachment->getSize()); // Check if $row is consistent if ($attachment) { // Set result as success and bring delete URL $json['result'] = Display::return_icon('accept.png', get_lang('Uploaded.')); $url = api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.api_get_cidreq().'&action=delete_attach&forum='.$forumId.'&thread='.$threadId.'&id_attach='.$row['iid']; $json['delete'] = Display::url( Display::return_icon('delete.png', get_lang('Delete'), [], ICON_SIZE_SMALL), $url, ['class' => 'deleteLink'] ); } else { // If not, set an exclamation result $json['result'] = Display::return_icon('exclamation.png', get_lang('Error')); } // Store array data into $_SESSION $_SESSION['forum']['upload_file'][$courseId][$json['id']] = $json; } } return $json; } /** * Clear forum attachment data stored in $_SESSION, * If is not defined post, it will clear all forum attachment data from course. * * @param int $postId -1 : Clear all attachments from course stored in $_SESSION * 0 : Clear attachments from course, except from temporal post "0" * but without delete them from file system and database * Other values : Clear attachments from course except specified post * and delete them from file system and database * @param int $courseId : Course ID, if it is null, will use api_get_course_int_id() * * @return array */ function clearAttachedFiles($postId = 0, $courseId = 0) { // Init variables $courseId = (int) $courseId; $postId = (int) $postId; $array = []; if (empty($courseId)) { // $courseId can be null, use api method $courseId = api_get_course_int_id(); } if (-1 === $postId) { // If post ID is -1 then delete course's attachment data from $_SESSION if (!empty($_SESSION['forum']['upload_file'][$courseId])) { $array = array_keys($_SESSION['forum']['upload_file'][$courseId]); unset($_SESSION['forum']['upload_file'][$courseId]); } } else { $attachIds = getAttachmentIdsByPostId($postId, $courseId); if (!empty($_SESSION['forum']['upload_file'][$courseId]) && is_array($_SESSION['forum']['upload_file'][$courseId])) { foreach ($_SESSION['forum']['upload_file'][$courseId] as $attachId => $attach) { if (!in_array($attachId, $attachIds)) { // If attach ID is not into specified post, delete attachment // Save deleted attachment ID $array[] = $attachId; if (0 !== $postId) { // Post 0 is temporal, delete them from file system and DB delete_attachment(0, $attachId); } // Delete attachment data from $_SESSION unset($_SESSION['forum']['upload_file'][$courseId][$attachId]); } } } } return $array; } /** * Returns an array of forum attachment ids into a course and forum post. * * @param int $postId * @param int $courseId * * @return array */ function getAttachmentIdsByPostId($postId, $courseId = 0) { $array = []; $courseId = (int) $courseId; $postId = (int) $postId; if (empty($courseId)) { // $courseId can be null, use api method $courseId = api_get_course_int_id(); } if ($courseId > 0) { $forumAttachmentTable = Database::get_course_table(TABLE_FORUM_ATTACHMENT); $sql = "SELECT iid FROM $forumAttachmentTable WHERE c_id = $courseId AND post_id = $postId"; $result = Database::query($sql); if (false !== $result && Database::num_rows($result) > 0) { while ($row = Database::fetch_array($result, 'ASSOC')) { $array[] = $row['iid']; } } } return $array; } function getPostStatus(CForum $forum, array $row, bool $addWrapper = true): string { $statusIcon = ''; if ($forum->isModerated()) { if ($addWrapper) { $statusIcon = '

    '; } $row['status'] = empty($row['status']) ? 2 : $row['status']; $addUrl = false; $showStatus = false; if (api_is_allowed_to_edit(false, true)) { $addUrl = true; } else { if ($row['user_id'] == api_get_user_id()) { $showStatus = true; } } $label = ''; $icon = ''; $buttonType = ''; switch ($row['status']) { case CForumPost::STATUS_VALIDATED: $label = get_lang('Validated'); $icon = 'check-circle'; $buttonType = 'success'; break; case CForumPost::STATUS_WAITING_MODERATION: $label = get_lang('Waiting for moderation'); $icon = 'alert'; $buttonType = 'warning'; break; case CForumPost::STATUS_REJECTED: $label = get_lang('Rejected'); $icon = 'minus-circle'; $buttonType = 'danger'; break; } if ($addUrl) { $statusIcon .= Display::toolbarButton( $label.' ', 'javascript:void(0)', $icon, $buttonType, ['class' => 'change_post_status'] ); } else { if ($showStatus) { $statusIcon .= Display::label( Display::getMdiIcon($icon).$label, $buttonType ); } } if ($addWrapper) { $statusIcon .= ''; } } return $statusIcon; } /** * @param CForum $forum * @param int $threadId * @param int $status */ function getCountPostsWithStatus($status, $forum, $threadId = null) { $em = Database::getManager(); $criteria = Criteria::create(); $criteria ->where(Criteria::expr()->eq('status', $status)) //->andWhere(Criteria::expr()->eq('cId', $forum->getCId())) ->andWhere(Criteria::expr()->eq('visible', 1)) ; if (!empty($threadId)) { $criteria->andWhere(Criteria::expr()->eq('thread', $threadId)); } $qb = $em->getRepository(CForumPost::class)->createQueryBuilder('p'); $qb->select('count(p.iid)') ->addCriteria($criteria); return $qb->getQuery()->getSingleScalarResult(); } /** * @param CForum $forum * @param CForumPost $post * * @return bool */ function postIsEditableByStudent($forum, $post) { if (api_is_platform_admin() || api_is_allowed_to_edit()) { return true; } if (1 == $forum->isModerated()) { if (null === $post->getStatus()) { return true; } else { return in_array( $post->getStatus(), [ CForumPost::STATUS_WAITING_MODERATION, CForumPost::STATUS_REJECTED, ] ); } } return true; } /** * @return bool */ function savePostRevision(CForumPost $post) { $userId = api_get_user_id(); if ($post->getUser()->getId() != $userId) { return false; } $status = (int) !postNeedsRevision($post); $extraFieldValue = new ExtraFieldValue('forum_post'); $params = [ 'item_id' => $post->getIid(), 'extra_ask_for_revision' => ['extra_ask_for_revision' => $status], ]; if (empty($status)) { unset($params['extra_ask_for_revision']); } $extraFieldValue->saveFieldValues( $params, true, false, ['ask_for_revision'] ); } /** * @param int $postId * * @return string */ function getPostRevision($postId) { $extraFieldValue = new ExtraFieldValue('forum_post'); $value = $extraFieldValue->get_values_by_handler_and_field_variable( $postId, 'revision_language' ); $revision = ''; if ($value && isset($value['value'])) { $revision = $value['value']; } return $revision; } function postNeedsRevision(CForumPost $post): bool { $postId = $post->getIid(); $extraFieldValue = new ExtraFieldValue('forum_post'); $value = $extraFieldValue->get_values_by_handler_and_field_variable( $postId, 'ask_for_revision' ); $hasRevision = false; if ($value && isset($value['value'])) { return 1 == $value['value']; } return $hasRevision; } /** * Generates an HTML button to ask for a review */ function getAskRevisionButton(CForumPost $post, CForumThread $threadInfo): string { if ('false' === api_get_setting('forum.allow_forum_post_revisions')) { return ''; } $postId = $post->getIid(); $status = 'btn--plain'; if (postNeedsRevision($post)) { $status = 'btn--success'; } return Display::url( get_lang('Ask for a revision'), api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'. api_get_cidreq().'&action=ask_revision&post_id='.$postId.'&forum='.$threadInfo->getForum()->getIid().'&thread='.$threadInfo->getIid(), ['class' => "btn $status", 'title' => get_lang('Ask for a revision')] ); } /** * Generates an HTML button to give a review */ function getGiveRevisionButton(int $postId, CForumThread $threadInfo): string { return Display::toolbarButton( get_lang('Give revision'), api_get_path(WEB_CODE_PATH).'forum/reply.php?'.api_get_cidreq().'&'.http_build_query( [ 'forum' => $threadInfo->getForum()->getIid(), 'thread' => $threadInfo->getIid(), 'post' => $postId, 'action' => 'replymessage', 'give_revision' => 1, ] ), 'reply', 'primary', ['id' => "reply-to-post-{$postId}"] ); } /** * Generates an HTML button to report a post as offensive */ function getReportButton(int $postId, CForumThread $threadInfo): string { return Display::url( Display::getMdiIcon('flag'), api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'. api_get_cidreq().'&action=report&post_id='.$postId. '&forum='.$threadInfo->getForum()->getIid().'&thread='.$threadInfo->getIid(), ['class' => 'btn btn--danger', 'title' => get_lang('Report')] ); } /** * @return bool */ function reportAvailable() { $extraFieldValue = new ExtraFieldValue('course'); $value = $extraFieldValue->get_values_by_handler_and_field_variable( api_get_course_int_id(), 'allow_forum_report_button' ); $allowReport = false; if ($value && isset($value['value']) && 1 == $value['value']) { $allowReport = true; } return $allowReport; } /** * @return array */ function getReportRecipients() { $extraFieldValue = new ExtraFieldValue('course'); $value = $extraFieldValue->get_values_by_handler_and_field_variable( api_get_course_int_id(), 'forum_report_recipients' ); $users = []; if ($value && isset($value['value'])) { $usersType = explode(';', $value['value']); foreach ($usersType as $type) { switch ($type) { case 'teachers': $teachers = CourseManager::get_teacher_list_from_course_code(api_get_course_id()); if (!empty($teachers)) { $users = array_merge($users, array_column($teachers, 'user_id')); } break; case 'admins': $admins = UserManager::get_all_administrators(); if (!empty($admins)) { $users = array_merge($users, array_column($admins, 'user_id')); } break; case 'community_managers': $managers = api_get_configuration_value('community_managers_user_list'); if (!empty($managers) && isset($managers['users'])) { $users = array_merge($users, $managers['users']); } break; } } $users = array_unique(array_filter($users)); } return $users; } /** * Sends an e-mail to all users from getReportRecipients() (users with extra field 'forum_report_recipients') */ function reportPost(CForumPost $post, CForum $forumInfo, CForumThread $threadInfo): bool { if (!reportAvailable()) { return false; } if (empty($forumInfo) || empty($threadInfo)) { return false; } $postId = $post->getIid(); $currentUser = api_get_user_info(); $users = getReportRecipients(); if (!empty($users)) { $url = api_get_path(WEB_CODE_PATH). 'forum/viewthread.php?forum='.$forumInfo->getIid().'&thread='.$threadInfo->getIid().'&'.api_get_cidreq().'&post_id='.$postId.'#post_id_'.$postId; $postLink = Display::url( $post->getPostTitle(), $url ); $subject = get_lang('Post reported'); $content = sprintf( get_lang('User %s has reported the message %s in the forum %s'), $currentUser['complete_name'], $postLink, $forumInfo->getForumTitle() ); foreach ($users as $userId) { MessageManager::send_message_simple($userId, $subject, $content); } } return true; }