public function get_unread_restriction_sql($forum, $userid = 0) { $userid = forum_utils::get_real_userid($userid); // See if they're already allowed to view all discussions if ($forum->can_view_hidden($userid)) { return ''; } // Otherwise restrict it return 'fpfirst.userid=' . $userid; }
/** * Return a list of groups the user belongs to that apply to this forum (same grouping) * @param int $userid * @param int $forumid * @return an array of group lists or an empty array */ function get_group_list($userid, $forumid) { global $CFG; //$courseid = forum::get_from_id($forumng->id, forum::CLONE_DIRECT)->get_course_id(); $sql_group = "\nSELECT\n g.id AS groupid\nFROM\n {$CFG->prefix}forumng f\n INNER JOIN {$CFG->prefix}course_modules cm on f.id = cm.instance \n INNER JOIN {$CFG->prefix}modules m on cm.module = m.id \n INNER JOIN {$CFG->prefix}groups_members gm ON gm.userid = {$userid} \n INNER JOIN {$CFG->prefix}groups g ON gm.groupid = g.id AND g.courseid = cm.course \n LEFT JOIN {$CFG->prefix}groupings_groups gg ON gg.groupid = g.id AND cm.groupingid = gg.groupingid \nWHERE\n f.id = {$forumid}\n AND m.name = 'forumng'\n AND (cm.groupingid = 0 or gg.id IS NOT NULL)"; $rs = forum_utils::get_recordset_sql($sql_group); $results = array(); while ($rec = rs_fetch_next_record($rs)) { $results[] = $rec->groupid; } rs_close($rs); return $results; }
function definition() { global $CFG, $USER; $mform = $this->_form; $course = $this->_customdata; // Query for supported forums $forums = forum_utils::get_convertible_forums($course); $forumoptions = array(); foreach ($forums as $forum) { $forumoptions[$forum->id] = $forum->name; } $mform->addElement('static', '', '', get_string('convert_info', 'forumng')); $select = $mform->addElement('select', 'forums', get_string('modulenameplural', 'forum'), $forumoptions); $select->setMultiple(true); $mform->addElement('checkbox', 'nodata', '', get_string('convert_nodata', 'forumng')); $mform->addElement('static', '', '', get_string('convert_warning', 'forumng')); $mform->addElement('checkbox', 'hide', '', get_string('convert_hide', 'forumng')); $this->add_action_buttons(true, get_string('convert_title', 'forumng')); $mform->addElement('hidden', 'course', $this->_customdata->id); }
if (!$courseid) { $courseid = $forum->get_course()->id; } redirect('index.php?id=' . $courseid); } if ($back == 'view') { redirect($forum->get_url(mod_forumng::PARAM_PLAIN)); } if ($back == 'discuss') { redirect('discuss.php?' . $discussion->get_link_params(mod_forumng::PARAM_PLAIN)); } // Not redirecting? OK, confirm if ($cmid || $discussionid) { $backurl = $forum->get_url(mod_forumng::PARAM_HTML); $out = $forum->init_page($pageurl, get_string($subscribe ? 'subscribeshort' : 'unsubscribeshort', 'forumng')); print $out->header(); print $out->notification($confirmtext, 'notifysuccess'); print $out->continue_button($backurl); print $out->footer(); } else { $backurl = $CFG->wwwroot . '/course/view.php?id=' . $courseid; $PAGE->set_url($pageurl); $PAGE->set_context(context_course::instance($courseid)); $PAGE->set_heading($COURSE->fullname); $PAGE->set_title($COURSE->shortname); $out = forum_utils::get_renderer(); print $out->header(); print $out->notification($confirmtext, 'notifysuccess'); print $out->continue_button($backurl); print $out->footer(); }
function definition() { global $CFG, $COURSE; $mform =& $this->_form; $coursecontext = get_context_instance(CONTEXT_COURSE, $COURSE->id); $forumng = $this->_instance ? get_record('forumng', 'id', $this->_instance) : null; $this->clone = $forumng ? $forumng->originalcmid : 0; // If this is a clone, don't show the normal form if ($this->clone) { $mform->addElement('hidden', 'name', $forumng->name); $mform->addElement('static', 'sharedthing', '', get_string('sharedinfo', 'forumng', $CFG->wwwroot . '/course/modedit.php?update=' . $this->clone . '&return=1')); $this->shared_definition_part($coursecontext); return; } $mform->addElement('header', 'general', get_string('general', 'form')); // Forum name $mform->addElement('text', 'name', get_string('forumname', 'forumng'), array('size' => '64')); if (!empty($CFG->formatstringstriptags)) { $mform->setType('name', PARAM_TEXT); } else { $mform->setType('name', PARAM_CLEAN); } $mform->addRule('name', null, 'required', null, 'client'); $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); // Forum types $types = forum_type::get_all(); $options = array(); foreach ($types as $type) { if ($type->is_user_selectable()) { $options[$type->get_id()] = $type->get_name(); } } $mform->addElement('select', 'type', get_string('forumtype', 'forumng'), $options); $mform->setHelpButton('type', array('forumtype', get_string('forumtype', 'forumng'), 'forumng')); $mform->setDefault('type', 'general'); $mform->addElement('htmleditor', 'intro', get_string('forumintro', 'forumng')); $mform->setType('intro', PARAM_RAW); $mform->setHelpButton('intro', array('writing', 'questions', 'richtext'), false, 'editorhelpbutton'); // Subscription option displays only if enabled at site level if ($CFG->forumng_subscription == -1) { $options = forum::get_subscription_options(); $mform->addElement('select', 'subscription', get_string('subscription', 'forumng'), $options); $mform->setDefault('subscription', forum::SUBSCRIPTION_PERMITTED); $mform->setHelpButton('subscription', array('subscription', get_string('subscription', 'forumng'), 'forumng')); } else { // Hidden element contains default value (not used anyhow) $mform->addElement('hidden', 'subscription', forum::SUBSCRIPTION_PERMITTED); } // Max size of attachments $choices = get_max_upload_sizes($CFG->maxbytes, $COURSE->maxbytes); $choices[-1] = get_string('uploadnotallowed'); $choices[0] = get_string('courseuploadlimit') . ' (' . display_size($COURSE->maxbytes) . ')'; $mform->addElement('select', 'attachmentmaxbytes', get_string('attachmentmaxbytes', 'forumng'), $choices); $mform->setHelpButton('attachmentmaxbytes', array('attachmentmaxbytes', get_string('attachmentmaxbytes', 'forumng'), 'forumng')); $mform->setDefault('attachmentmaxbytes', $CFG->forumng_attachmentmaxbytes); //Email address for reporting unacceptable post for this forum, default is blank $mform->addElement('text', 'reportingemail', get_string('reportingemail', 'forumng'), array('size' => 48)); $mform->setType('reportingemail', PARAM_NOTAGS); $mform->setHelpButton('reportingemail', array('reportingemail', get_string('reportingemail', 'forumng'), 'forumng')); // Atom/RSS feed on/off/discussions-only if ($CFG->enablerssfeeds && !empty($CFG->forumng_enablerssfeeds)) { if ($CFG->forumng_feedtype == -1 || $CFG->forumng_feeditems == -1) { $mform->addElement('header', '', get_string('feeds', 'forumng')); } if ($CFG->forumng_feedtype == -1) { $mform->addElement('select', 'feedtype', get_string('feedtype', 'forumng'), forum::get_feedtype_options()); $mform->setDefault('feedtype', forum::FEEDTYPE_ALL_POSTS); $mform->setHelpButton('feedtype', array('feedtype', get_string('feedtype', 'forumng'), 'forumng')); } // Atom/RSS feed item count if ($CFG->forumng_feeditems == -1) { $mform->addElement('select', 'feeditems', get_string('feeditems', 'forumng'), forum::get_feeditems_options()); $mform->setDefault('feeditems', 20); $mform->setHelpButton('feeditems', array('feeditems', get_string('feeditems', 'forumng'), 'forumng')); } } // Ratings header ///////////////// $mform->addElement('header', '', get_string('ratings', 'forumng')); $mform->addElement('checkbox', 'enableratings', get_string('enableratings', 'forumng')); $mform->setHelpButton('enableratings', array('enableratings', get_string('enableratings', 'forumng'), 'forumng')); // Scale $mform->addElement('modgrade', 'ratingscale', get_string('scale'), null, true); $mform->disabledIf('ratingscale', 'enableratings', 'notchecked'); $mform->setDefault('ratingscale', 5); // From/until times $mform->addElement('date_time_selector', 'ratingfrom', get_string('ratingfrom', 'forumng'), array('optional' => true)); $mform->disabledIf('ratingfrom', 'enableratings', 'notchecked'); $mform->addElement('date_time_selector', 'ratinguntil', get_string('ratinguntil', 'forumng'), array('optional' => true)); $mform->disabledIf('ratinguntil', 'enableratings', 'notchecked'); $mform->addElement('text', 'ratingthreshold', get_string('ratingthreshold', 'forumng')); $mform->setType('ratingthreshold', PARAM_INT); $mform->setDefault('ratingthreshold', 1); $mform->addRule('ratingthreshold', get_string('error_ratingthreshold', 'forumng'), 'regex', '/[1-9][0-9]*/', 'client'); $mform->setHelpButton('ratingthreshold', array('ratingthreshold', get_string('ratingthreshold', 'forumng'), 'forumng')); $mform->disabledIf('ratingthreshold', 'enableratings', 'notchecked'); // Grading $mform->addElement('select', 'grading', get_string('grade'), forum::get_grading_options()); $mform->setDefault('grading', forum::GRADING_NONE); $mform->setHelpButton('grading', array('grading', get_string('grade'), 'forumng')); $mform->disabledIf('grading', 'enableratings', 'notchecked'); // Blocking header ////////////////// $mform->addElement('header', '', get_string('limitposts', 'forumng')); // Post dates $mform->addElement('date_time_selector', 'postingfrom', get_string('postingfrom', 'forumng'), array('optional' => true)); $mform->addElement('date_time_selector', 'postinguntil', get_string('postinguntil', 'forumng'), array('optional' => true)); // User limits $limitgroup = array(); $limitgroup[] = $mform->createElement('checkbox', 'enablelimit', ''); $options = forum::get_max_posts_period_options(); $limitgroup[] = $mform->createElement('text', 'maxpostsblock', '', array('size' => 3)); $limitgroup[] = $mform->createElement('static', 'staticthing', '', ' ' . get_string('postsper', 'forumng') . ' '); $limitgroup[] = $mform->createElement('select', 'maxpostsperiod', '', $options); $mform->addGroup($limitgroup, 'limitgroup', get_string('enablelimit', 'forumng')); $mform->disabledIf('limitgroup[maxpostsblock]', 'limitgroup[enablelimit]'); $mform->disabledIf('limitgroup[maxpostsperiod]', 'limitgroup[enablelimit]'); $mform->setHelpButton('limitgroup', array('enablelimit', get_string('enablelimit', 'forumng'), 'forumng')); $mform->setType('limitgroup[maxpostsblock]', PARAM_INT); $mform->setDefault('limitgroup[maxpostsblock]', '10'); // Remove old discussion $options = array(); $options[0] = get_string('removeolddiscussionsdefault', 'forumng'); for ($i = 1; $i <= 36; $i++) { $options[$i * 2592000] = $i; } $mform->addElement('header', '', get_string('removeolddiscussions', 'forumng')); $mform->addElement('select', 'removeafter', get_string('removeolddiscussionsafter', 'forumng'), $options); $mform->setHelpButton('removeafter', array('removeolddiscussions', get_string('removeolddiscussions', 'forumng'), 'forumng')); $options = array(); $options[0] = get_string('deletepermanently', 'forumng'); $modinfo = get_fast_modinfo($COURSE); $targetforumid = $this->_instance ? $this->_instance : 0; // Add all in stances to drop down if the user can access them and it's not the same as the current forum if (array_key_exists('forumng', $modinfo->instances)) { foreach ($modinfo->instances['forumng'] as $info) { if ($info->uservisible && $targetforumid != $info->instance) { $options[$info->instance] = $info->name; } } } $mform->addElement('select', 'removeto', get_string('withremoveddiscussions', 'forumng'), $options); $mform->disabledIf('removeto', 'removeafter', 'eq', 0); $mform->setHelpButton('removeto', array('withremoveddiscussions', get_string('withremoveddiscussions', 'forumng'), 'forumng')); // Sharing options are advanced and for administrators only if ($CFG->forumng_enableadvanced && has_capability('moodle/site:config', $coursecontext)) { $mform->addElement('header', '', get_string('sharing', 'forumng')); $mform->addElement('advcheckbox', 'shared', get_string('shared', 'forumng')); $mform->setHelpButton('shared', array('shared', get_string('shared', 'forumng'), 'forumng')); // Only when creating a forum, you can choose to make it a clone if (!$this->_instance) { $sharegroup = array(); $sharegroup[] = $mform->createElement('checkbox', 'useshared', ''); $sharegroup[] = $mform->createElement('text', 'originalcmidnumber', ''); $mform->addGroup($sharegroup, 'usesharedgroup', get_string('useshared', 'forumng')); $mform->disabledIf('usesharedgroup[originalcmidnumber]', 'usesharedgroup[useshared]', 'notchecked'); $mform->setHelpButton('usesharedgroup', array('useshared', get_string('useshared', 'forumng'), 'forumng')); } } // Do definition that is shared with clone version of form $this->shared_definition_part($coursecontext); if (count(forum_utils::get_convertible_forums($COURSE)) > 0 && !$this->_instance) { $mform->addElement('static', '', '', '<div class="forumng-convertoffer">' . get_string('offerconvert', 'forumng', $CFG->wwwroot . '/mod/forumng/convert.php?course=' . $COURSE->id) . '</div>'); } }
private function mark_mailed($time) { global $CFG; $querychunk = $this->get_query_chunk($time, 'forumng_posts'); $before = microtime(true); mtrace('Marking processed posts: ', ''); forum_utils::update_with_subquery_grrr_mysql("\nUPDATE\n {$CFG->prefix}forumng_posts\nSET\n mailstate = " . $this->get_target_mail_state() . "\nWHERE\n id %'IN'%", "SELECT fp.id {$querychunk}"); mtrace(round(microtime(true) - $before, 1) . 's.'); unset_config($this->get_pending_flag_name(), 'forumng'); }
function make_students($courseid, $count) { print "<h3>Making {$count} students</h3><pre>"; $time = time(); forum_utils::start_transaction(); for ($i = 0; $i < $count; $i++) { make_student($courseid, $time . '_' . $i); print '.'; if (($i + 1) % 20 == 0) { print " (" . ($i + 1) . ")\n"; } flush(); } forum_utils::finish_transaction(); print "</pre>"; }
/** * Get search results. * @param object $course * @param string $author * @param int $daterangefrom * @param int $daterangeto * @param int $page * @param int $resultsperpage (FORUMNG_SEARCH_RESULTSPERPAGE used as constant) * @return object */ function forumng_get_results_for_all_forums($course, $author = null, $daterangefrom = 0, $daterangeto = 0, $page, $resultsperpage = FORUMNG_SEARCH_RESULTSPERPAGE) { $before = microtime(true); global $CFG, $USER; // Get all forums $modinfo = get_fast_modinfo($course); $visibleforums = array(); $accessallgroups = array(); foreach ($modinfo->cms as $cmid => $cm) { if ($cm->modname === 'forumng' && $cm->uservisible) { $visibleforums[$cm->instance] = $cm->groupmode; // Check access all groups for this forum, if they have it, add to list //$forum = forum::get_from_cmid($cm->id, 0); $forum = forum::get_from_id($cm->instance, 0); if ($forum->get_group_mode() == SEPARATEGROUPS) { if (has_capability('moodle/site:accessallgroups', $forum->get_context())) { $accessallgroups[] = $cm->instance; } } } } $forumids = array_keys($visibleforums); $separategroupsforumids = array_keys($visibleforums, SEPARATEGROUPS); $inforumids = forum_utils::in_or_equals($forumids); $inseparategroups = forum_utils::in_or_equals($separategroupsforumids); $inaccessallgroups = forum_utils::in_or_equals($accessallgroups); $where = "WHERE d.forumid {$inforumids}"; $where .= " AND (NOT (d.forumid {$inseparategroups})"; $where .= " OR d.forumid {$inaccessallgroups}"; $where .= " OR gm.id IS NOT NULL"; $where .= " OR d.groupid IS NULL)"; // Note: Even if you have capability to view the deleted or timed posts, // we don't show them for consistency with the full-text search. $currenttime = time(); $where .= " AND ({$currenttime} >= d.timestart OR d.timestart = 0)"; $where .= " AND ({$currenttime} < d.timeend OR d.timeend = 0)"; //exclude older post versions $where .= " AND p.oldversion=0 "; $where .= " AND d.deleted=0 AND p.deleted=0 "; if ($author) { $where .= forumng_get_author_sql($author); } if ($daterangefrom && !is_array($daterangefrom)) { $where .= " AND p.modified>={$daterangefrom}"; } if ($daterangeto && !is_array($daterangeto)) { $where .= " AND p.modified<={$daterangeto}"; } $sql = "SELECT p.modified, p.id, p.discussionid, gm.id AS useringroup, p.userid, p.parentpostid, \n p.subject AS title, p.message AS summary, u.username, u.firstname, \n u.lastname, d.forumid, d.groupid, d.postid AS discussionpostid\n FROM {$CFG->prefix}forumng_posts p\n INNER JOIN {$CFG->prefix}forumng_discussions d ON d.id = p.discussionid\n INNER JOIN {$CFG->prefix}user u ON p.userid = u.id\n LEFT JOIN {$CFG->prefix}groups_members gm ON gm.groupid = d.groupid AND gm.userid = {$USER->id}\n {$where}\n ORDER BY p.modified DESC, p.id ASC"; $results = new stdClass(); $results->success = 1; $results->numberofentries = 0; $results->done = 0; if (!($posts = get_records_sql($sql, $page, $resultsperpage))) { $posts = array(); } foreach ($posts as $post) { if (!$post->title) { // Ideally we would get the parent post that has a subject, but // this could involve a while loop that might make numeroous // queries, so instead, let's just use the discussion subject $post->title = get_string('re', 'forumng', get_field('forumng_posts', 'subject', 'id', $post->discussionpostid)); } $post->title = s(strip_tags($post->title)); $post->summary = s(strip_tags(shorten_text($post->summary, 250))); $post->url = $CFG->wwwroot . "/mod/forumng/discuss.php?d={$post->discussionid}#p{$post->id}"; } $results->results = $posts; $results->searchtime = microtime(true) - $before; $results->numberofentries = count($results->results); if (count($results->results) < $resultsperpage) { $results->done = 1; } elseif (!($extrapost = get_records_sql($sql, $page + $resultsperpage, 1))) { $results->done = 1; } return $results; }
/** * Displays the discussion page. * @param forum_discussion $discussion Discussion */ public function print_discussion_page($discussion) { $previousread = (int) $discussion->get_time_read(); // 'Read date' option (used when viewing all posts so that they keep // their read/unread colouring) $timeread = optional_param('timeread', 0, PARAM_INT); if ($timeread) { $discussion->pretend_time_read($timeread); $previousread = $timeread; } // 'Expand all' option (always chosen for non-JS browsers) $expandall = optional_param('expand', 0, PARAM_INT) || forum_utils::is_bad_browser(); // 'Expand all' option (always chosen for non-JS browsers) $collapseall = optional_param('collapse', 0, PARAM_INT); // Magic expand tracker (for use in JS only, never set server-side). // This tracks expanded posts, and makes the Back button 'work' in // the sense that it will expand these posts again. print '<form method="post" action="."><div>' . '<input type="hidden" id="expanded_posts" name="expanded_posts" ' . 'value="" /></div></form>'; // Get content for all posts in the discussion $options = array(); if ($expandall) { $options[forum_post::OPTION_CHILDREN_EXPANDED] = true; } if ($collapseall) { $options[forum_post::OPTION_CHILDREN_COLLAPSED] = true; } $content = $this->display_discussion($discussion, $options); // Some post display options use the read time to construct links // (usually for non-JS version) so that unread state is maintained. $options[forum_post::OPTION_READ_TIME] = $previousread; // Display expand all option if there are any 'Expand' links in content $fakedate = '&timeread=' . $previousread; print '<div id="forumng-expandall">'; $showexpandall = preg_match('~<a [^>]*href="discuss\\.php\\?d=[0-9]+[^"]*&expand=1#p[0-9]+">~', $content); $showcollapseall = preg_match('~<div class="forumng-post forumng-full.*<div class="forumng-post forumng-full~s', $content); if ($showexpandall) { print '<a href="' . $discussion->get_url(forum::PARAM_HTML) . '&expand=1' . $fakedate . '">' . get_string('expandall', 'forumng') . '</a>'; if ($showcollapseall) { print ' • '; } } if ($showcollapseall) { print '<a href="' . $discussion->get_url(forum::PARAM_HTML) . '&collapse=1' . $fakedate . '">' . get_string('collapseall', 'forumng') . '</a> '; } print '</div>'; // Display content print $content; // Print reply/edit forms for AJAX print $this->display_ajax_forms($discussion->get_forum()); // Link back to forum print $discussion->display_link_back_to_forum(); // Display discussion features (row of buttons) print $discussion->display_discussion_features(); // Display the subscription options to this disucssion if available print $discussion->display_subscribe_options(); // Atom/RSS links print $discussion->display_feed_links(); // Set read data [shouldn't this logic be somewhere else as it is not // part of display?] if (forum::mark_read_automatically()) { $discussion->mark_read(); } }
/** * This function handles all aspects of page processing and then calls * methods in $selector at the appropriate moments. * @param post_selector $selector Object that extends this base class */ static function go($selector) { $d = required_param('d', PARAM_INT); $cloneid = optional_param('clone', 0, PARAM_INT); $fromselect = optional_param('fromselect', 0, PARAM_INT); $all = optional_param('all', '', PARAM_RAW); $select = optional_param('select', '', PARAM_RAW); try { // Get basic objects $discussion = forum_discussion::get_from_id($d, $cloneid); if (optional_param('cancel', '', PARAM_RAW)) { // CALL TYPE 6 redirect('../../discuss.php?' . $discussion->get_link_params(forum::PARAM_PLAIN)); } $forum = $discussion->get_forum(); $cm = $forum->get_course_module(); $course = $forum->get_course(); $isform = optional_param('postselectform', 0, PARAM_INT); // Page name and permissions $pagename = $selector->get_page_name(); $buttonname = $selector->get_button_name(); $discussion->require_view(); $selector->require_capability($forum->get_context(), $discussion); if (!($fromselect || $isform || $all)) { // Either an initial request (non-JS) to display the 'dialog' box, // or a request to show the list of posts with checkboxes for // selection // Both types share same navigation $discussion->print_subpage_header($pagename); if (!$select) { // Show initial dialog print_box_start(); ?> <h2><?php print $buttonname; ?> </h2> <form action="<?php echo $_SERVER['PHP_SELF']; ?> " method="get"><div> <?php print $discussion->get_link_params(forum::PARAM_FORM); ?> <p><?php print_string('selectorall', 'forumng'); ?> </p> <div class="forumng-buttons"> <input type="submit" name="all" value="<?php print_string('discussion', 'forumng'); ?> " /> <input type="submit" name="select" value="<?php print_string('selectedposts', 'forumng'); ?> " /> </div> </div></form> <?php print_box_end(); } else { // Show list of posts to select ?> <div class="forumng-selectintro"> <p><?php print_string('selectintro', 'forumng'); ?> </p> </div> <form action="<?php echo $_SERVER['PHP_SELF']; ?> " method="post"><div> <?php print $discussion->get_link_params(forum::PARAM_FORM); ?> <input type="hidden" name="fromselect" value="1" /> <?php print $forum->get_type()->display_discussion($discussion, array(forum_post::OPTION_NO_COMMANDS => true, forum_post::OPTION_CHILDREN_EXPANDED => true, forum_post::OPTION_SELECTABLE => true)); ?> <div class="forumng-selectoutro"> <input type="submit" value="<?php print_string('confirmselection', 'forumng'); ?> " /> <input type="submit" name="cancel" value="<?php print_string('cancel'); ?> " /> </div> </div></form> <?php } // Display footer print_footer($course); } else { // Call types 3, 4, and 5 use the form (and may include list of postids) if ($all) { $postids = false; } else { $postids = array(); foreach ($_POST as $field => $value) { $matches = array(); if ((string) $value !== '0' && preg_match('~^selectp([0-9]+)$~', $field, $matches)) { $postids[] = $matches[1]; } } } // Get form to use $mform = $selector->get_form($discussion, $all, $postids); if (!$mform) { // Some options do not need a confirmation form; in that case, // just apply the action immediately. $selector->apply($discussion, $all, $postids, null); exit; } // Check cancel if ($mform->is_cancelled()) { redirect('../../discuss.php?' . $discussion->get_link_params(forum::PARAM_PLAIN)); } if ($fromform = $mform->get_data()) { // User submitted form to confirm process, which should now be // applied by selector. $selector->apply($discussion, $all, $postids, $fromform); exit; } else { $discussion->print_subpage_header($pagename); // User requested form either via JavaScript or the other way, and // either with all messages or the whole discussion. // Print form print $mform->display(); // Print optional content that goes after form print $selector->get_content_after_form($discussion, $all, $postids, $fromform); // Display footer print_footer($course); } } } catch (forum_exception $e) { forum_utils::handle_exception($e); } }
/** * Executes a database update in such a way that it will work in MySQL, * when the update uses a subquery that refers to the table being updated. * @param string $update Update query with the special string %'IN'% at the * point where the IN clause should go, i.e. replacing 'IN (SELECT id ...)' * @param string $inids Query that selects a column (which must be named * id), i.e. 'SELECT id ...' */ public static function update_with_subquery_grrr_mysql($update, $inids) { global $CFG; if (preg_match('~^mysql~', $CFG->dbtype)) { // MySQL is a PoS so the update can't directly run (you can't update // a table based on a subquery that refers to the table). Instead, // we do the same thing but with a separate update using an IN clause. // This might theoretically run into problems if you had a really huge // set of forums with frequent posts (so that the IN size exceeds // MySQL query limit) however the limits appear to be generous enough // that this is unlikely. $ids = array(); $rs = forum_utils::get_recordset_sql($inids); while ($rec = rs_fetch_next_record($rs)) { $ids[] = $rec->id; } rs_close($rs); if (count($ids) > 0) { $update = str_replace("%'IN'%", forum_utils::in_or_equals($ids), $update); forum_utils::execute_sql($update); } } else { // With a decent database we can do the update and query in one, // avoiding the need to transfer an ID list around. forum_utils::execute_sql(str_replace("%'IN'%", "IN ({$inids})", $update)); } }
/** * Gets URL for an Atom/RSS feed to this discussion. * @param int $feedformat FEEDFORMAT_xx constant * @param int $userid User ID or 0 for current * @return string URL for feed */ public function get_feed_url($feedformat, $userid = 0) { global $CFG; $userid = forum_utils::get_real_userid($userid); $groupid = $this->get_group_id(); return $CFG->wwwroot . '/mod/forumng/feed.php?' . $this->get_link_params(forum::PARAM_PLAIN) . '&user='******'&key=' . $this->get_forum()->get_feed_key($groupid, $userid) . '&format=' . ($feedformat == forum::FEEDFORMAT_RSS ? 'rss' : 'atom'); }
/** * Update the forumng_subscription table to incorporate the group subscription feature. * @param bool $moodleupdate If this is true, the function is running as part of the * moodle upgrade.php for Sep 2010 release. In this case, the database queries must * not be changed and other code must work the same way (avoid calls to functions * except Moodle standard ones) */ public function group_subscription_update($moodleupdate = false, $cmid = 0) { global $CFG; forum_utils::start_transaction(); if ($cmid) { //only update one forum $optionalquery = "AND cm.id = {$cmid}"; } else { $optionalquery = ''; } // Query get the distinct forums $sql_count = "\nSELECT\n COUNT(DISTINCT cm.id) AS totalnumberforum\nFROM \n {$CFG->prefix}forumng_subscriptions fs\n INNER JOIN {$CFG->prefix}course_modules cm on fs.forumid = cm.instance \n INNER JOIN {$CFG->prefix}modules m on cm.module = m.id \n INNER JOIN {$CFG->prefix}course c on c.id = cm.course \nWHERE \n discussionid IS NULL AND m.name='forumng' {$optionalquery}\n AND (CASE WHEN c.groupmodeforce=1 THEN c.groupmode ELSE cm.groupmode END ) = 1"; //Query lists all subscriptions to forums that have separate groups $sql_sub = "\nSELECT\n cm.id AS cmid, fs.id AS subid, fs.userid, fs.forumid, c.id AS courseid, cm.groupingid \nFROM\n {$CFG->prefix}forumng_subscriptions fs\n INNER JOIN {$CFG->prefix}course_modules cm on fs.forumid = cm.instance \n INNER JOIN {$CFG->prefix}modules m on cm.module = m.id \n INNER JOIN {$CFG->prefix}course c on c.id = cm.course \nWHERE \n discussionid IS NULL and m.name='forumng' {$optionalquery}\n AND (CASE WHEN c.groupmodeforce=1 THEN c.groupmode ELSE cm.groupmode END ) = 1 \nORDER BY cm.id, fs.id"; //Query lists all groups that the user belongs to from the above query $sql_group = "\nSELECT\n subs.subid, g.id AS groupid\nFROM\n ({$sql_sub}) subs \n INNER JOIN {$CFG->prefix}groups_members gm ON gm.userid = subs.userid \n INNER JOIN {$CFG->prefix}groups g ON gm.groupid = g.id AND g.courseid = subs.courseid \n LEFT JOIN {$CFG->prefix}groupings_groups gg ON gg.groupid = g.id AND subs.groupingid = gg.groupingid \nWHERE\n (subs.groupingid = 0 or gg.id IS NOT NULL)\nORDER BY\n subs.cmid, subs.subid"; $rs = forum_utils::get_recordset_sql($sql_group); $results = array(); while ($rec = rs_fetch_next_record($rs)) { if (!array_key_exists($rec->subid, $results)) { $results[$rec->subid] = array(); } $results[$rec->subid][] = $rec->groupid; } rs_close($rs); $rs = forum_utils::get_recordset_sql($sql_sub); $lastcmid = 0; $forumcount = 1; $totalforumcount = 0; $totalforumcount = count_records_sql($sql_count); while ($rec = rs_fetch_next_record($rs)) { if ($lastcmid != $rec->cmid) { if ($moodleupdate) { print "Updating the subscriptions {$forumcount}/{$totalforumcount} (current cmid:{$rec->cmid}) <br />"; } $context = get_context_instance(CONTEXT_MODULE, $rec->cmid); $aagusers = get_users_by_capability($context, 'moodle/site:accessallgroups', 'u.id'); $aagusers = $aagusers ? $aagusers : array(); $lastcmid = $rec->cmid; $forumcount++; } if (!array_key_exists($rec->userid, $aagusers)) { //Delete the whole forum subscription forum_utils::delete_records('forumng_subscriptions', 'id', $rec->subid); //check if the subid exists in the results array if (array_key_exists($rec->subid, $results)) { foreach ($results[$rec->subid] as $groupid) { $subrecord = new StdClass(); $subrecord->userid = $rec->userid; $subrecord->forumid = $rec->forumid; $subrecord->subscribed = 1; $subrecord->groupid = $groupid; forum_utils::insert_record('forumng_subscriptions', $subrecord); } } } } forum_utils::finish_transaction(); }
/** * Checks whether the user can edit the post, assuming that they can * view the discussion. * @param string &$whynot If returning false, set to the language string defining * reason for not being able to edit * @param int $userid User ID or 0 if current * @return bool True if user can edit this post */ function can_edit(&$whynot, $userid = 0) { global $CFG; $userid = forum_utils::get_real_userid($userid); $context = $this->get_forum()->get_context(); // Check if post is a special case if ($this->get_deleted() || $this->is_old_version() || $this->get_discussion()->is_deleted()) { $whynot = 'edit_notcurrentpost'; return false; } // Check if discussion is different group if (!$this->get_discussion()->can_write_to_group()) { $whynot = 'edit_wronggroup'; return false; } // Check if discussion is locked if ($this->get_discussion()->is_locked()) { $whynot = 'edit_locked'; return false; } // Check the 'edit any' capability $editanypost = has_capability('mod/forumng:editanypost', $context, $userid); if (!$editanypost) { // If they don't have edit any, they must have either the // 'start discussion' or 'reply post' capability (the same // one they needed to create the post in the first place) if ($this->is_root_post() && !has_capability('mod/forumng:startdiscussion', $context, $userid) && (!$this->is_root_post() && !has_capability('mod/forumng:replypost', $context, $userid))) { $whynot = 'edit_nopermission'; return false; } } // Check post belongs to specified user if ($this->get_user()->id != $userid && !$editanypost) { $whynot = 'edit_notyours'; return false; } // Check editing timeout if (time() > $this->get_edit_time_limit() && !$editanypost) { $whynot = 'edit_timeout'; return false; } // Check read-only dates if ($this->get_forum()->is_read_only($userid)) { $whynot = 'edit_readonly'; return false; } // OK! They're allowed to edit (whew) $whynot = ''; return true; }
function forumng_decode_content_links_caller($restore) { // Get all the items that might have links in, from the relevant new course try { global $CFG, $db; // 1. Intros if ($intros = get_records_select('forumng', 'course=' . $restore->course_id . ' AND intro IS NOT NULL', '', 'id, intro, name')) { foreach ($intros as $intro) { $newintro = $intro->intro; // Special behaviour hidden in intro $matches = array(); if (preg_match('~%%CMIDNUMBER:([^%]+)%%$~', $newintro, $matches)) { $newintro = substr($newintro, 0, -strlen($matches[0])); $idnumber = $matches[1]; $cm = forum::get_shared_cm_from_idnumber($idnumber); if ($cm) { set_field('forumng', 'originalcmid', $cm->id, 'id', $intro->id); } else { // The original forum cannot be found, so restore // this as not shared if (!defined('RESTORE_SILENTLY')) { $a = (object) array('name' => s($intro->name), 'idnumber' => s($idnumber)); print '<br />' . get_string('error_nosharedforum', 'forumng', $a) . '<br />'; } } } if (preg_match('~%%REMOVETHIS%%$~', $newintro)) { $newintro = substr($newintro, 0, -14); } $newintro = restore_decode_content_links_worker($newintro, $restore); if ($newintro != $intro->intro) { if (!set_field('forumng', 'intro', addslashes($newintro), 'id', $intro->id)) { throw new forum_exception("Failed to set intro for forum {$intro->id}: " . $db->ErrorMsg()); } } } } // 2. Post content $rs = get_recordset_sql("\nSELECT\n fp.id, fp.message, fp.format\nFROM\n {$CFG->prefix}forumng f\n INNER JOIN {$CFG->prefix}forumng_discussions d ON d.forumid = f.id\n INNER JOIN {$CFG->prefix}forumng_posts fp ON fp.discussionid = d.id\nWHERE\n f.course={$restore->course_id}\n"); if (!$rs) { throw new forum_exception("Failed to query for forum data: " . $db->ErrorMsg()); } while ($rec = rs_fetch_next_record($rs)) { $newcontent = restore_decode_content_links_worker($rec->message, $restore); if ($newcontent != $rec->message) { if (!set_field('forumng_posts', 'message', addslashes($newcontent), 'id', $rec->id)) { throw new forum_exception("Failed to update content {$ec->id}: " . $db->ErrorMsg()); } } } rs_close($rs); // 3. Update search data (note this is not actually do with content // links, but it has to be done here because we need a course-module // id. if (forum::search_installed()) { forumng_ousearch_update_all(false, $restore->course_id); } return true; } catch (Exception $e) { forum_utils::handle_backup_exception($e, 'restore'); return false; } }
/** * Displays a flagged item. * @param forum_post $post * @param bool $last * @return string HTML code for table row */ public function display_flagged_list_item($post, $last) { global $CFG; // Classes for Moodle table styles static $rownum = 0; $classes = ' r' . $rownum; $rownum = 1 - $rownum; if ($last) { $classes .= ' lastrow'; } $result = '<tr class="' . $classes . '">'; // Post cell $result .= '<td class="cell c0">'; // Get post URL $discussion = $post->get_discussion(); $link = '<a href="discuss.php?' . $discussion->get_link_params(forum::PARAM_HTML) . '#p' . $post->get_id() . '">'; // Get post summary $summary = self::get_post_summary($post->get_subject(), $post->get_message(), $post->get_format()); $result .= $link . $summary . '</a>'; $result .= '<small> ' . get_string('postby', 'forumng', $post->get_forum()->display_user_link($post->get_user())) . '</small>'; // Show flag icon. (Note: I tried to use before this so the // icon never ends up on a line of its own, but it does not work.) $result .= ' <form class="forumng-flag" action="flagpost.php" method="post"><div>' . '<input type="hidden" name="p" value="' . $post->get_id() . '" />' . '<input type="hidden" name="back" value="view" />' . '<input type="hidden" name="flag" value="0" />' . '<input type="image" title="' . get_string('clearflag', 'forumng') . '" src="' . $CFG->modpixpath . '/forumng/flag.on.png" alt="' . get_string('flagon', 'forumng') . '" /></div></form></td>'; // Discussion cell $result .= '<td class="cell c1"><a href="discuss.php?' . $discussion->get_link_params(forum::PARAM_HTML) . $discussion->get_id() . '">' . format_string($discussion->get_subject()) . '</a></td>'; // Date cell $result .= '<td class="cell c2 lastcol">' . forum_utils::display_date($post->get_created()) . '</td></tr>'; return $result; }
$cmid = required_param('id', PARAM_INT); $cloneid = optional_param('clone', 0, PARAM_INT); $file = required_param('file', PARAM_FILE); $playspaceid = optional_param('attachmentplayspace', 0, PARAM_SEQUENCE); $postid = optional_param('p', 0, PARAM_INT); try { // Security check if ($postid) { $post = forum_post::get_from_id($postid, $cloneid); $post->require_view(); } else { $forum = forum::get_from_cmid($cmid, $cloneid); $forum->require_view(forum::NO_GROUPS); } if (!$playspaceid) { $playspaceid = forum::create_attachment_playspace($postid ? $post : null); } // Delete the file (if not present, ignore) $files = forum::get_attachment_playspace_files($playspaceid, false); foreach ($files as $existing) { if (basename($existing) == $file) { forum_utils::unlink($existing); } } // Print out the playspace id in case they don't already have it header('Content-Type: text/plain'); print $playspaceid; } catch (forum_exception $e) { header('Content-Type: text/plain', true, 500); print $e->getMessage(); }
private function add_attachment($path, $courseid = 0, $forumid = 0, $draftid = 0) { // Check source file exists if (!file_exists($path)) { throw new forum_exception("Missing file {$path}"); } // Get folder if (isset($this)) { $folder = $this->get_attachment_folder(); } else { $folder = self::get_attachment_folder($courseid, $forumid, $draftid); } if (!check_dir_exists($folder, true, true)) { throw new forum_exception("Failed to create attachment folder {$folder}"); } // Check target path doesn't already exist. If it does, delete existing // file. $target = $folder . '/' . basename($path); if (file_exists($target)) { forum_utils::unlink($target); } // Move new file into place forum_utils::rename($path, $target); }
/** * Given a parent folder, deletes all folders within it (and their * contents) if they are older than 24 hours. * @param float $start Start time * @param string $folder Folder path */ private static function clear_old_folders($start, $folder) { // Threshold is 24 hours ago $threshold = time() - 24 * 3600; // Loop through all files in folder $killed = 0; $spared = 0; $handle = forum_utils::opendir($folder); while (($file = readdir($handle)) !== false) { if ($file == '.' || $file == '..') { continue; } // Check it's a folder $target = "{$folder}/{$file}"; if (!is_dir($target)) { continue; } // Check time if (filemtime($target) < $threshold) { // Older than threshold - delete if (!remove_dir($target)) { mtrace("\nError deleting folder: {$target}"); } $killed++; } else { $spared++; } } closedir($handle); mtrace("({$killed} deleted, {$spared} left): " . round(microtime(true) - $start, 1)); }
$forum = $discussion->get_forum(); $cm = $forum->get_course_module(); $course = $forum->get_course(); // Check permission for change $discussion->require_edit(); // Is this the actual delete? if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($delete) { $discussion->delete(); redirect($forum->get_url(forum::PARAM_PLAIN)); } else { $discussion->undelete(); redirect('../../discuss.php?' . $discussion->get_link_params(forum::PARAM_PLAIN)); } } // Confirm page. Work out navigation for header $pagename = get_string($delete ? 'deletediscussion' : 'undeletediscussion', 'forumng', $discussion->get_subject(false)); $navigation = array(); $navigation[] = array('name' => shorten_text(htmlspecialchars($discussion->get_subject())), 'link' => $discussion->get_url(), 'type' => 'forumng'); $navigation[] = array('name' => $pagename, 'type' => 'forumng'); $PAGEWILLCALLSKIPMAINDESTINATION = true; print_header_simple(format_string($forum->get_name()) . ': ' . $pagename, "", build_navigation($navigation, $cm), "", "", true, '', navmenu($course, $cm)); print skip_main_destination(); // Show confirm option $confirmstring = get_string($delete ? 'confirmdeletediscussion' : 'confirmundeletediscussion', 'forumng'); notice_yesno($confirmstring, 'delete.php', '../../discuss.php', array('d' => $discussion->get_id(), 'delete' => $delete, 'clone' => $cloneid), array('d' => $discussion->get_id(), 'clone' => $cloneid), 'post', 'get'); // Display footer print_footer($course); } catch (forum_exception $e) { forum_utils::handle_exception($e); }
function forumng_backup_post($xb, $post) { $xb->tag_start('POST'); // Post data $xb->tag_full('ID', $post->id); $xb->tag_full_notnull('PARENTPOSTID', $post->parentpostid); forumng_backup_userid($xb, $post->userid); $xb->tag_full('CREATED', $post->created); $xb->tag_full('MODIFIED', $post->modified); $xb->tag_full('DELETED', $post->deleted); forumng_backup_userid($xb, $post->deleteuserid, 'DELETEUSERID'); $xb->tag_full('MAILSTATE', $post->mailstate); $xb->tag_full('IMPORTANT', $post->important); $xb->tag_full('OLDVERSION', $post->oldversion); forumng_backup_userid($xb, $post->edituserid, 'EDITUSERID'); $xb->tag_full_notnull('SUBJECT', $post->subject); $xb->tag_full('MESSAGE', $post->message); $xb->tag_full('FORMAT', $post->format); $xb->tag_full('ATTACHMENTS', $post->attachments); // Ratings $xb->tag_start('RATINGS'); $rs = forum_utils::get_recordset('forumng_ratings', 'postid', $post->id); while ($rating = rs_fetch_next_record($rs)) { forumng_backup_rating($xb, $rating); } rs_close($rs); $xb->tag_end('RATINGS'); // Flags $xb->tag_start('FLAGS'); $rs = forum_utils::get_recordset('forumng_flags', 'postid', $post->id); while ($flag = rs_fetch_next_record($rs)) { forumng_backup_flag($xb, $flag); } rs_close($rs); $xb->tag_end('FLAGS'); $xb->tag_end('POST'); }