/** * Performs a multi-byte safe substr() operation based on number of characters. * Position is counted from the beginning of str. First character's position is * 0. Second character position is 1, and so on. * * @param string $str The string to extract the substring from. * @param int $start If start is non-negative, the returned string will * start at the start'th position in string, counting * from zero. For instance, in the string 'abcdef', * the character at position 0 is 'a', the character * at position 2 is 'c', and so forth. * @param int $length Maximum number of characters to use from str. If * omitted or NULL is passed, extract all characters * to the end of the string. * @param string $encoding The encoding parameter is the character encoding. * If it is omitted, the internal character encoding * value will be used. * * @return string The portion of str specified by the start and length parameters. */ function mb_substr($str, $start, $length = null, $encoding = null) { if ($encoding === null) { $encoding = mb_internal_encoding(); } return \core_text::substr($str, $start, $length, $encoding); }
/** * Determine output for the name column. * * @param stdClass $recording The recording row being worked on. * @return string The output to display. */ public function col_name($recording) { if (\core_text::strlen($recording->name) > 60) { return \core_text::substr($recording->name, 0, 55) . '…'; } return $recording->name; }
/** * Sets user course sorting preference in course_overview block * * @param array $sortorder list of course ids */ function block_course_overview_update_myorder($sortorder) { $value = implode(',', $sortorder); if (core_text::strlen($value) > 1333) { // The value won't fit into the user preference. Remove courses in the end of the list (mostly likely user won't even notice). $value = preg_replace('/,[\\d]*$/', '', core_text::substr($value, 0, 1334)); } set_user_preference('course_overview_course_sortorder', $value); }
/** * @uses LABEL_MAX_NAME_LENGTH * @param object $label * @return string */ function get_label_name($label) { $name = strip_tags(format_string($label->intro, true)); if (core_text::strlen($name) > LABEL_MAX_NAME_LENGTH) { $name = core_text::substr($name, 0, LABEL_MAX_NAME_LENGTH) . "..."; } if (empty($name)) { // arbitrary name $name = get_string('modulename', 'label'); } return $name; }
/** * @uses COURSECLASSROOM_MAX_NAME_LENGTH * @param object $trainingevent * @return string */ function get_trainingevent_name($trainingevent) { $name = strip_tags(format_string($trainingevent->name, true)); if (core_text::strlen($name) > COURSECLASSROOM_MAX_NAME_LENGTH) { $name = core_text::substr($name, 0, COURSECLASSROOM_MAX_NAME_LENGTH) . "..."; } if (empty($name)) { // Arbitrary name. $name = get_string('modulename', 'trainingevent'); } return $name; }
/** * Processes the message and sends a notification via airnotifier * * @param stdClass $eventdata the event data submitted by the message sender plus $eventdata->savedmessageid * @return true if ok, false if error */ public function send_message($eventdata) { global $CFG; require_once $CFG->libdir . '/filelib.php'; if (!empty($CFG->noemailever)) { // Hidden setting for development sites, set in config.php if needed. debugging('$CFG->noemailever active, no airnotifier message sent.', DEBUG_MINIMAL); return true; } // Skip any messaging suspended and deleted users. if ($eventdata->userto->auth === 'nologin' or $eventdata->userto->suspended or $eventdata->userto->deleted) { return true; } // Site id, to map with Moodle Mobile stored sites. $siteid = md5($CFG->wwwroot . $eventdata->userto->username); // Mandatory notification data that need to be sent in the payload. They have variable length. // We need to take them in consideration to calculate the maximum message size. $notificationdata = array("site" => $siteid, "type" => $eventdata->component . '_' . $eventdata->name, "device" => "xxxxxxxxxx", "notif" => "x", "userfrom" => !empty($eventdata->userfrom) ? fullname($eventdata->userfrom) : ''); // Calculate the size of the message knowing Apple payload must be lower than 256 bytes. // Airnotifier using few bytes of the payload, we must limit our message to even less characters. $maxmsgsize = 205 - core_text::strlen(json_encode($notificationdata)); $message = s($eventdata->smallmessage); // If the message size is too big make it shorter. if (core_text::strlen($message) >= $maxmsgsize) { // Cut the message to the maximum possible size. -4 for the the ending 3 dots (...). $message = core_text::substr($message, 0, $maxmsgsize - 4); // We need to check when the message is "escaped" then the message is not too long. $encodedmsgsize = core_text::strlen(json_encode($message)); if ($encodedmsgsize > $maxmsgsize) { $totalescapedchar = $encodedmsgsize - core_text::strlen($message); // Cut the message to the maximum possible size (taking the escaped character in account). $message = core_text::substr($message, 0, $maxmsgsize - 4 - $totalescapedchar); } $message = $message . '...'; } // We are sending to message to all devices. $airnotifiermanager = new message_airnotifier_manager(); $devicetokens = $airnotifiermanager->get_user_devices($CFG->airnotifiermobileappname, $eventdata->userto->id); foreach ($devicetokens as $devicetoken) { if (!$devicetoken->enable) { continue; } // Sending the message to the device. $serverurl = $CFG->airnotifierurl . ':' . $CFG->airnotifierport . '/notification/'; $header = array('Accept: application/json', 'X-AN-APP-NAME: ' . $CFG->airnotifierappname, 'X-AN-APP-KEY: ' . $CFG->airnotifieraccesskey); $curl = new curl(); $curl->setHeader($header); $params = array('alert' => $message, 'date' => !empty($eventdata->timecreated) ? $eventdata->timecreated : time(), 'site' => $siteid, 'type' => $eventdata->component . '_' . $eventdata->name, 'userfrom' => !empty($eventdata->userfrom) ? fullname($eventdata->userfrom) : '', 'device' => $devicetoken->platform, 'notif' => !empty($eventdata->notification) ? '1' : '0', 'token' => $devicetoken->pushid); $resp = $curl->post($serverurl, $params); } return true; }
/** * Set the title of the worksheet inside a spreadsheet * * For some formats this will be ignored. * * @param string $title */ public function set_sheettitle($title) { if (!$title) { return; } // Replace any characters in the name that ODS cannot cope with. $title = strtr(trim($title, "'"), '[]*/\\?:', ' '); // Shorten the title if necessary. $title = \core_text::substr($title, 0, 31); // After the substr, we might now have a single quote on the end. $title = trim($title, "'"); $this->sheettitle = $title; }
function validation($data, $files) { $errors = array(); foreach ($data as $key => $value) { if ($value == 'null') { if (core_text::substr($key, 0, 2) == 'of') { $errors[$key] = get_string('overallfairnessrequired', 'mod_emarking'); } else { $errors[$key] = get_string('expectationrealityrequired', 'mod_emarking'); } } } return $errors; }
public function save_usage($preferredbehaviour, $attempt, $qas, $quizlayout) { global $OUTPUT; $missing = array(); $layout = explode(',', $attempt->layout); $questionkeys = array_combine(array_values($layout), array_keys($layout)); $this->set_quba_preferred_behaviour($attempt->uniqueid, $preferredbehaviour); $i = 0; foreach (explode(',', $quizlayout) as $questionid) { if ($questionid == 0) { continue; } $i++; if (!array_key_exists($questionid, $qas)) { $missing[] = $questionid; $layout[$questionkeys[$questionid]] = $questionid; continue; } $qa = $qas[$questionid]; $qa->questionusageid = $attempt->uniqueid; $qa->slot = $i; if (core_text::strlen($qa->questionsummary) > question_bank::MAX_SUMMARY_LENGTH) { // It seems some people write very long quesions! MDL-30760 $qa->questionsummary = core_text::substr($qa->questionsummary, 0, question_bank::MAX_SUMMARY_LENGTH - 3) . '...'; } $this->insert_record('question_attempts', $qa); $layout[$questionkeys[$questionid]] = $qa->slot; foreach ($qa->steps as $step) { $step->questionattemptid = $qa->id; $this->insert_record('question_attempt_steps', $step); foreach ($step->data as $name => $value) { $datum = new stdClass(); $datum->attemptstepid = $step->id; $datum->name = $name; $datum->value = $value; $this->insert_record('question_attempt_step_data', $datum, false); } } } $this->set_quiz_attempt_layout($attempt->uniqueid, implode(',', $layout)); if ($missing) { $message = "Question sessions for questions " . implode(', ', $missing) . " were missing when upgrading question usage {$attempt->uniqueid}."; echo $OUTPUT->notification($message); } }
public function validation($data, $files) { global $DB, $CFG; $errors = parent::validation($data, $files); $instance = $this->instance; if ($instance->password !== '') { if ($data['guestpassword'] !== $instance->password) { $plugin = enrol_get_plugin('guest'); if ($plugin->get_config('showhint')) { $hint = core_text::substr($instance->password, 0, 1); $errors['guestpassword'] = get_string('passwordinvalidhint', 'enrol_guest', $hint); } else { $errors['guestpassword'] = get_string('passwordinvalid', 'enrol_guest'); } } } return $errors; }
function rfc2445_fold($string) { if (core_text::strlen($string, 'utf-8') <= RFC2445_FOLDED_LINE_LENGTH) { return $string; } $retval = ''; $i = 0; $len_count = 0; //multi-byte string, get the correct length $section_len = core_text::strlen($string, 'utf-8'); while ($len_count < $section_len) { //get the current portion of the line $section = core_text::substr($string, $i * RFC2445_FOLDED_LINE_LENGTH, RFC2445_FOLDED_LINE_LENGTH, 'utf-8'); //increment the length we've processed by the length of the new portion $len_count += core_text::strlen($section, 'utf-8'); /* Add the portion to the return value, terminating with CRLF.HTAB As per RFC 2445, CRLF.HTAB will be replaced by the processor of the data */ $retval .= $section . RFC2445_CRLF . substr(RFC2445_WSP, 0, 1); $i++; } return $retval; }
protected function bootstrap_process_message($message) { global $DB; $messagecontent = new stdClass(); if ($message->notification) { $messagecontent->text = get_string('unreadnewnotification', 'message'); } else { if ($message->fullmessageformat == FORMAT_HTML) { $message->smallmessage = html_to_text($message->smallmessage); } if (core_text::strlen($message->smallmessage) > 15) { $messagecontent->text = core_text::substr($message->smallmessage, 0, 15) . '...'; } else { $messagecontent->text = $message->smallmessage; } } if (time() - $message->timecreated <= 3600 * 3) { $messagecontent->date = format_time(time() - $message->timecreated); } else { $messagecontent->date = userdate($message->timecreated, get_string('strftimetime', 'langconfig')); } $messagecontent->from = $DB->get_record('user', array('id' => $message->useridfrom)); return $messagecontent; }
public function test_replace_all_text() { $DB = $this->tdb; $dbman = $DB->get_manager(); if (!$DB->replace_all_text_supported()) { $this->markTestSkipped($DB->get_name() . ' does not support replacing of texts'); } $table = $this->get_test_table(); $tablename = $table->getName(); $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('name', XMLDB_TYPE_CHAR, '20', null, null); $table->add_field('intro', XMLDB_TYPE_TEXT, 'big', null, null); $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); $dbman->create_table($table); $id1 = (string) $DB->insert_record($tablename, array('name' => null, 'intro' => null)); $id2 = (string) $DB->insert_record($tablename, array('name' => '', 'intro' => '')); $id3 = (string) $DB->insert_record($tablename, array('name' => 'xxyy', 'intro' => 'vvzz')); $id4 = (string) $DB->insert_record($tablename, array('name' => 'aa bb aa bb', 'intro' => 'cc dd cc aa')); $id5 = (string) $DB->insert_record($tablename, array('name' => 'kkllll', 'intro' => 'kkllll')); $expected = $DB->get_records($tablename, array(), 'id ASC'); $columns = $DB->get_columns($tablename); $DB->replace_all_text($tablename, $columns['name'], 'aa', 'o'); $result = $DB->get_records($tablename, array(), 'id ASC'); $expected[$id4]->name = 'o bb o bb'; $this->assertEquals($expected, $result); $DB->replace_all_text($tablename, $columns['intro'], 'aa', 'o'); $result = $DB->get_records($tablename, array(), 'id ASC'); $expected[$id4]->intro = 'cc dd cc o'; $this->assertEquals($expected, $result); $DB->replace_all_text($tablename, $columns['name'], '_', '*'); $DB->replace_all_text($tablename, $columns['name'], '?', '*'); $DB->replace_all_text($tablename, $columns['name'], '%', '*'); $DB->replace_all_text($tablename, $columns['intro'], '_', '*'); $DB->replace_all_text($tablename, $columns['intro'], '?', '*'); $DB->replace_all_text($tablename, $columns['intro'], '%', '*'); $result = $DB->get_records($tablename, array(), 'id ASC'); $this->assertEquals($expected, $result); $long = '1234567890123456789'; $DB->replace_all_text($tablename, $columns['name'], 'kk', $long); $result = $DB->get_records($tablename, array(), 'id ASC'); $expected[$id5]->name = core_text::substr($long . 'llll', 0, 20); $this->assertEquals($expected, $result); $DB->replace_all_text($tablename, $columns['intro'], 'kk', $long); $result = $DB->get_records($tablename, array(), 'id ASC'); $expected[$id5]->intro = $long . 'llll'; $this->assertEquals($expected, $result); }
/** * Prints an individual user box * * @deprecated since 3.1 * @param user_object $user (contains the following fields: id, firstname, lastname and picture) * @param bool $return if true return html string * @return string|null a HTML string or null if this function does the output */ function tag_print_user_box($user, $return = false) { global $CFG, $OUTPUT; debugging('Function tag_print_user_box() is deprecated without replacement. ' . 'See core_user_renderer for similar code.', DEBUG_DEVELOPER); $usercontext = context_user::instance($user->id); $profilelink = ''; if ($usercontext and has_capability('moodle/user:viewdetails', $usercontext) || has_coursecontact_role($user->id)) { $profilelink = $CFG->wwwroot . '/user/view.php?id=' . $user->id; } $output = $OUTPUT->box_start('user-box', 'user' . $user->id); $fullname = fullname($user); $alt = ''; if (!empty($profilelink)) { $output .= '<a href="' . $profilelink . '">'; $alt = $fullname; } $output .= $OUTPUT->user_picture($user, array('size' => 100)); $output .= '<br />'; if (!empty($profilelink)) { $output .= '</a>'; } //truncate name if it's too big if (core_text::strlen($fullname) > 26) { $fullname = core_text::substr($fullname, 0, 26) . '...'; } $output .= '<strong>' . $fullname . '</strong>'; $output .= $OUTPUT->box_end(); if ($return) { return $output; } else { echo $output; } }
/** * Given a record in the {blog_external} table, checks the blog's URL * for new entries not yet copied into Moodle. * Also attempts to identify and remove deleted blog entries * * @param object $externalblog * @return boolean False if the Feed is invalid */ function blog_sync_external_entries($externalblog) { global $CFG, $DB; require_once $CFG->libdir . '/simplepie/moodle_simplepie.php'; $rss = new moodle_simplepie(); $rssfile = $rss->registry->create('File', array($externalblog->url)); $filetest = $rss->registry->create('Locator', array($rssfile)); if (!$filetest->is_feed($rssfile)) { $externalblog->failedlastsync = 1; $DB->update_record('blog_external', $externalblog); return false; } else { if (!empty($externalblog->failedlastsync)) { $externalblog->failedlastsync = 0; $DB->update_record('blog_external', $externalblog); } } $rss->set_feed_url($externalblog->url); $rss->init(); if (empty($rss->data)) { return null; } //used to identify blog posts that have been deleted from the source feed $oldesttimestamp = null; $uniquehashes = array(); foreach ($rss->get_items() as $entry) { // If filtertags are defined, use them to filter the entries by RSS category if (!empty($externalblog->filtertags)) { $containsfiltertag = false; $categories = $entry->get_categories(); $filtertags = explode(',', $externalblog->filtertags); $filtertags = array_map('trim', $filtertags); $filtertags = array_map('strtolower', $filtertags); foreach ($categories as $category) { if (in_array(trim(strtolower($category->term)), $filtertags)) { $containsfiltertag = true; } } if (!$containsfiltertag) { continue; } } $uniquehashes[] = $entry->get_permalink(); $newentry = new stdClass(); $newentry->userid = $externalblog->userid; $newentry->module = 'blog_external'; $newentry->content = $externalblog->id; $newentry->uniquehash = $entry->get_permalink(); $newentry->publishstate = 'site'; $newentry->format = FORMAT_HTML; // Clean subject of html, just in case $newentry->subject = clean_param($entry->get_title(), PARAM_TEXT); // Observe 128 max chars in DB // TODO: +1 to raise this to 255 if (core_text::strlen($newentry->subject) > 128) { $newentry->subject = core_text::substr($newentry->subject, 0, 125) . '...'; } $newentry->summary = $entry->get_description(); //used to decide whether to insert or update //uses enty permalink plus creation date if available $existingpostconditions = array('uniquehash' => $entry->get_permalink()); //our DB doesnt allow null creation or modified timestamps so check the external blog supplied one $entrydate = $entry->get_date('U'); if (!empty($entrydate)) { $existingpostconditions['created'] = $entrydate; } //the post ID or false if post not found in DB $postid = $DB->get_field('post', 'id', $existingpostconditions); $timestamp = null; if (empty($entrydate)) { $timestamp = time(); } else { $timestamp = $entrydate; } //only set created if its a new post so we retain the original creation timestamp if the post is edited if ($postid === false) { $newentry->created = $timestamp; } $newentry->lastmodified = $timestamp; if (empty($oldesttimestamp) || $timestamp < $oldesttimestamp) { //found an older post $oldesttimestamp = $timestamp; } if (core_text::strlen($newentry->uniquehash) > 255) { // The URL for this item is too long for the field. Rather than add // the entry without the link we will skip straight over it. // RSS spec says recommended length 500, we use 255. debugging('External blog entry skipped because of oversized URL', DEBUG_DEVELOPER); continue; } if ($postid === false) { $id = $DB->insert_record('post', $newentry); // Set tags if ($tags = tag_get_tags_array('blog_external', $externalblog->id)) { tag_set('post', $id, $tags); } } else { $newentry->id = $postid; $DB->update_record('post', $newentry); } } // Look at the posts we have in the database to check if any of them have been deleted from the feed. // Only checking posts within the time frame returned by the rss feed. Older items may have been deleted or // may just not be returned anymore. We can't tell the difference so we leave older posts alone. $sql = "SELECT id, uniquehash\n FROM {post}\n WHERE module = 'blog_external'\n AND " . $DB->sql_compare_text('content') . " = " . $DB->sql_compare_text(':blogid') . "\n AND created > :ts"; $dbposts = $DB->get_records_sql($sql, array('blogid' => $externalblog->id, 'ts' => $oldesttimestamp)); $todelete = array(); foreach ($dbposts as $dbpost) { if (!in_array($dbpost->uniquehash, $uniquehashes)) { $todelete[] = $dbpost->id; } } $DB->delete_records_list('post', 'id', $todelete); $DB->update_record('blog_external', array('id' => $externalblog->id, 'timefetched' => time())); }
/** * Legacy add_to_log() code. * * @param int $courseid The course id * @param string $module The module name e.g. forum, journal, resource, course, user etc * @param string $action 'view', 'update', 'add' or 'delete', possibly followed by another word to clarify. * @param string $url The file and parameters used to see the results of the action * @param string $info Additional description information * @param int $cm The course_module->id if there is one * @param int|\stdClass $user If log regards $user other than $USER */ public function legacy_add_to_log($courseid, $module, $action, $url, $info, $cm, $user) { // Note that this function intentionally does not follow the normal Moodle DB access idioms. // This is for a good reason: it is the most frequently used DB update function, // so it has been optimised for speed. global $DB, $CFG, $USER; if (!$this->is_logging()) { return; } if ($cm === '' || is_null($cm)) { // Postgres won't translate empty string to its default. $cm = 0; } if ($user) { $userid = $user; } else { if (\core\session\manager::is_loggedinas()) { // Don't log. return; } $userid = empty($USER->id) ? '0' : $USER->id; } if (isset($CFG->logguests) and !$CFG->logguests) { if (!$userid or isguestuser($userid)) { return; } } $remoteaddr = getremoteaddr(); $timenow = time(); if (!empty($url)) { // Could break doing html_entity_decode on an empty var. $url = html_entity_decode($url, ENT_QUOTES, 'UTF-8'); } else { $url = ''; } // Restrict length of log lines to the space actually available in the // database so that it doesn't cause a DB error. Log a warning so that // developers can avoid doing things which are likely to cause this on a // routine basis. if (\core_text::strlen($action) > 40) { $action = \core_text::substr($action, 0, 37) . '...'; debugging('Warning: logged very long action', DEBUG_DEVELOPER); } if (!empty($info) && \core_text::strlen($info) > 255) { $info = \core_text::substr($info, 0, 252) . '...'; debugging('Warning: logged very long info', DEBUG_DEVELOPER); } // If the 100 field size is changed, also need to alter print_log in course/lib.php. if (!empty($url) && \core_text::strlen($url) > 100) { $url = \core_text::substr($url, 0, 97) . '...'; debugging('Warning: logged very long URL', DEBUG_DEVELOPER); } if (defined('MDL_PERFDB')) { global $PERF; $PERF->logwrites++; } $log = array('time' => $timenow, 'userid' => $userid, 'course' => $courseid, 'ip' => $remoteaddr, 'module' => $module, 'cmid' => $cm, 'action' => $action, 'url' => $url, 'info' => $info); try { $DB->insert_record_raw('log', $log, false); } catch (\dml_exception $e) { debugging('Error: Could not insert a new entry to the Moodle log. ' . $e->errorcode, DEBUG_ALL); // MDL-11893, alert $CFG->supportemail if insert into log failed. if ($CFG->supportemail and empty($CFG->noemailever)) { // Function email_to_user is not usable because email_to_user tries to write to the logs table, // and this will get caught in an infinite loop, if disk is full. $site = get_site(); $subject = 'Insert into log failed at your moodle site ' . $site->fullname; $message = "Insert into log table failed at " . date('l dS \\of F Y h:i:s A') . ".\n It is possible that your disk is full.\n\n"; $message .= "The failed query parameters are:\n\n" . var_export($log, true); $lasttime = get_config('admin', 'lastloginserterrormail'); if (empty($lasttime) || time() - $lasttime > 60 * 60 * 24) { // Limit to 1 email per day. // Using email directly rather than messaging as they may not be able to log in to access a message. mail($CFG->supportemail, $subject, $message); set_config('lastloginserterrormail', time(), 'admin'); } } } }
/** * Given some text (which may contain HTML) and an ideal length, * this function truncates the text neatly on a word boundary if possible * * @category string * @param string $text text to be shortened * @param int $ideal ideal string length * @param boolean $exact if false, $text will not be cut mid-word * @param string $ending The string to append if the passed string is truncated * @return string $truncate shortened string */ function shorten_text($text, $ideal = 30, $exact = false, $ending = '...') { // If the plain text is shorter than the maximum length, return the whole text. if (core_text::strlen(preg_replace('/<.*?>/', '', $text)) <= $ideal) { return $text; } // Splits on HTML tags. Each open/close/empty tag will be the first thing // and only tag in its 'line'. preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER); $totallength = core_text::strlen($ending); $truncate = ''; // This array stores information about open and close tags and their position // in the truncated string. Each item in the array is an object with fields // ->open (true if open), ->tag (tag name in lower case), and ->pos // (byte position in truncated text). $tagdetails = array(); foreach ($lines as $linematchings) { // If there is any html-tag in this line, handle it and add it (uncounted) to the output. if (!empty($linematchings[1])) { // If it's an "empty element" with or without xhtml-conform closing slash (f.e. <br/>). if (!preg_match('/^<(\\s*.+?\\/\\s*|\\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\\s.+?)?)>$/is', $linematchings[1])) { if (preg_match('/^<\\s*\\/([^\\s]+?)\\s*>$/s', $linematchings[1], $tagmatchings)) { // Record closing tag. $tagdetails[] = (object) array('open' => false, 'tag' => core_text::strtolower($tagmatchings[1]), 'pos' => core_text::strlen($truncate)); } else { if (preg_match('/^<\\s*([^\\s>!]+).*?>$/s', $linematchings[1], $tagmatchings)) { // Record opening tag. $tagdetails[] = (object) array('open' => true, 'tag' => core_text::strtolower($tagmatchings[1]), 'pos' => core_text::strlen($truncate)); } else { if (preg_match('/^<!--\\[if\\s.*?\\]>$/s', $linematchings[1], $tagmatchings)) { $tagdetails[] = (object) array('open' => true, 'tag' => core_text::strtolower('if'), 'pos' => core_text::strlen($truncate)); } else { if (preg_match('/^<!--<!\\[endif\\]-->$/s', $linematchings[1], $tagmatchings)) { $tagdetails[] = (object) array('open' => false, 'tag' => core_text::strtolower('if'), 'pos' => core_text::strlen($truncate)); } } } } } // Add html-tag to $truncate'd text. $truncate .= $linematchings[1]; } // Calculate the length of the plain text part of the line; handle entities as one character. $contentlength = core_text::strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $linematchings[2])); if ($totallength + $contentlength > $ideal) { // The number of characters which are left. $left = $ideal - $totallength; $entitieslength = 0; // Search for html entities. if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $linematchings[2], $entities, PREG_OFFSET_CAPTURE)) { // Calculate the real length of all entities in the legal range. foreach ($entities[0] as $entity) { if ($entity[1] + 1 - $entitieslength <= $left) { $left--; $entitieslength += core_text::strlen($entity[0]); } else { // No more characters left. break; } } } $breakpos = $left + $entitieslength; // If the words shouldn't be cut in the middle... if (!$exact) { // Search the last occurence of a space. for (; $breakpos > 0; $breakpos--) { if ($char = core_text::substr($linematchings[2], $breakpos, 1)) { if ($char === '.' or $char === ' ') { $breakpos += 1; break; } else { if (strlen($char) > 2) { // Chinese/Japanese/Korean text can be truncated at any UTF-8 character boundary. $breakpos += 1; break; } } } } } if ($breakpos == 0) { // This deals with the test_shorten_text_no_spaces case. $breakpos = $left + $entitieslength; } else { if ($breakpos > $left + $entitieslength) { // This deals with the previous for loop breaking on the first char. $breakpos = $left + $entitieslength; } } $truncate .= core_text::substr($linematchings[2], 0, $breakpos); // Maximum length is reached, so get off the loop. break; } else { $truncate .= $linematchings[2]; $totallength += $contentlength; } // If the maximum length is reached, get off the loop. if ($totallength >= $ideal) { break; } } // Add the defined ending to the text. $truncate .= $ending; // Now calculate the list of open html tags based on the truncate position. $opentags = array(); foreach ($tagdetails as $taginfo) { if ($taginfo->open) { // Add tag to the beginning of $opentags list. array_unshift($opentags, $taginfo->tag); } else { // Can have multiple exact same open tags, close the last one. $pos = array_search($taginfo->tag, array_reverse($opentags, true)); if ($pos !== false) { unset($opentags[$pos]); } } } // Close all unclosed html-tags. foreach ($opentags as $tag) { if ($tag === 'if') { $truncate .= '<!--<![endif]-->'; } else { $truncate .= '</' . $tag . '>'; } } return $truncate; }
public function survey_copy($owner) { global $DB; // Clear the sid, clear the creation date, change the name, and clear the status. $survey = clone $this->survey; unset($survey->id); $survey->owner = $owner; // Make sure that the survey name is not larger than the field size (CONTRIB-2999). Leave room for extra chars. $survey->name = core_text::substr($survey->name, 0, 64 - 10); $survey->name .= '_copy'; $survey->status = 0; // Check for 'name' conflict, and resolve. $i = 0; $name = $survey->name; while ($DB->count_records('questionnaire_survey', array('name' => $name)) > 0) { $name = $survey->name . ++$i; } if ($i) { $survey->name .= $i; } // Create new survey. if (!($newsid = $DB->insert_record('questionnaire_survey', $survey))) { return false; } // Make copies of all the questions. $pos = 1; // Skip logic: some changes needed here for dependencies down below. $qidarray = array(); $cidarray = array(); foreach ($this->questions as $question) { // Fix some fields first. $oldid = $question->id; unset($question->id); $question->survey_id = $newsid; $question->position = $pos++; // Copy question to new survey. if (!($newqid = $DB->insert_record('questionnaire_question', $question))) { return false; } $qidarray[$oldid] = $newqid; foreach ($question->choices as $key => $choice) { $oldcid = $key; unset($choice->id); $choice->question_id = $newqid; if (!($newcid = $DB->insert_record('questionnaire_quest_choice', $choice))) { return false; } $cidarray[$oldcid] = $newcid; } } // Skip logic: now we need to set the new values for dependencies. if ($newquestions = $DB->get_records('questionnaire_question', array('survey_id' => $newsid), 'id')) { foreach ($newquestions as $question) { if ($question->dependquestion != 0) { $dependqtypeid = $this->questions[$question->dependquestion]->type_id; $record = new stdClass(); $record->id = $question->id; $record->dependquestion = $qidarray[$question->dependquestion]; if ($dependqtypeid != 1) { $record->dependchoice = $cidarray[$question->dependchoice]; } $DB->update_record('questionnaire_question', $record); } } } return $newsid; }
/** * Tests the static sub string method. */ public function test_substr() { $str = "Žluťoučký koníček"; $this->assertSame($str, core_text::substr($str, 0)); $this->assertSame('luťoučký koníček', core_text::substr($str, 1)); $this->assertSame('luť', core_text::substr($str, 1, 3)); $this->assertSame($str, core_text::substr($str, 0, 100)); $this->assertSame('če', core_text::substr($str, -3, 2)); $iso2 = pack("H*", "ae6c75bb6f75e86bfd206b6f6eede8656b"); $this->assertSame(core_text::convert('luť', 'utf-8', 'iso-8859-2'), core_text::substr($iso2, 1, 3, 'iso-8859-2')); $this->assertSame(core_text::convert($str, 'utf-8', 'iso-8859-2'), core_text::substr($iso2, 0, 100, 'iso-8859-2')); $this->assertSame(core_text::convert('če', 'utf-8', 'iso-8859-2'), core_text::substr($iso2, -3, 2, 'iso-8859-2')); $win = pack("H*", "8e6c759d6f75e86bfd206b6f6eede8656b"); $this->assertSame(core_text::convert('luť', 'utf-8', 'cp1250'), core_text::substr($win, 1, 3, 'cp1250')); $this->assertSame(core_text::convert($str, 'utf-8', 'cp1250'), core_text::substr($win, 0, 100, 'cp1250')); $this->assertSame(core_text::convert('če', 'utf-8', 'cp1250'), core_text::substr($win, -3, 2, 'cp1250')); $str = pack("H*", "b8c0b8ecc0dfc4ea"); // EUC-JP $s = pack("H*", "b8ec"); // EUC-JP $this->assertSame($s, core_text::substr($str, 1, 1, 'EUC-JP')); $str = pack("H*", "1b24423840386c405f446a1b2842"); // ISO-2022-JP $s = pack("H*", "1b2442386c1b2842"); // ISO-2022-JP $this->assertSame($s, core_text::substr($str, 1, 1, 'ISO-2022-JP')); $str = pack("H*", "8cbe8cea90dd92e8"); // SHIFT-JIS $s = pack("H*", "8cea"); // SHIFT-JIS $this->assertSame($s, core_text::substr($str, 1, 1, 'SHIFT-JIS')); $str = pack("H*", "bcf2cce5d6d0cec4"); // GB2312 $s = pack("H*", "cce5"); // GB2312 $this->assertSame($s, core_text::substr($str, 1, 1, 'GB2312')); $str = pack("H*", "bcf2cce5d6d0cec4"); // GB18030 $s = pack("H*", "cce5"); // GB18030 $this->assertSame($s, core_text::substr($str, 1, 1, 'GB18030')); }
/** * This function takes some text and replaces about half of the characters * with HTML entity equivalents. Return string is obviously longer. * * @param string $plaintext The text to be obfuscated * @return string The obfuscated text */ function obfuscate_text($plaintext) { $i = 0; $length = core_text::strlen($plaintext); $obfuscated = ''; $prevobfuscated = false; while ($i < $length) { $char = core_text::substr($plaintext, $i, 1); $ord = core_text::utf8ord($char); $numerical = $ord >= ord('0') && $ord <= ord('9'); if ($prevobfuscated and $numerical) { $obfuscated .= '&#' . $ord . ';'; } else { if (rand(0, 2)) { $obfuscated .= '&#' . $ord . ';'; $prevobfuscated = true; } else { $obfuscated .= $char; $prevobfuscated = false; } } $i++; } return $obfuscated; }
/** * Print a select box allowing the user to choose to view new messages, course participants etc. * * Called by message_print_contact_selector() * @param int $viewing What page is the user viewing ie MESSAGE_VIEW_UNREAD_MESSAGES, MESSAGE_VIEW_RECENT_CONVERSATIONS etc * @param array $courses array of course objects. The courses the user is enrolled in. * @param array $coursecontexts array of course contexts. Keyed on course id. * @param int $countunreadtotal how many unread messages does the user have? * @param int $countblocked how many users has the current user blocked? * @param stdClass $user1 The user whose messages we are viewing. * @param string $strunreadmessages a preconstructed message about the number of unread messages the user has * @return void */ function message_print_usergroup_selector($viewing, $courses, $coursecontexts, $countunreadtotal, $countblocked, $strunreadmessages, $user1 = null) { global $PAGE; $options = array(); if ($countunreadtotal>0) { //if there are unread messages $options[MESSAGE_VIEW_UNREAD_MESSAGES] = $strunreadmessages; } $str = get_string('contacts', 'message'); $options[MESSAGE_VIEW_CONTACTS] = $str; $options[MESSAGE_VIEW_RECENT_CONVERSATIONS] = get_string('mostrecentconversations', 'message'); $options[MESSAGE_VIEW_RECENT_NOTIFICATIONS] = get_string('mostrecentnotifications', 'message'); if (!empty($courses)) { $courses_options = array(); foreach($courses as $course) { if (has_capability('moodle/course:viewparticipants', $coursecontexts[$course->id])) { //Not using short_text() as we want the end of the course name. Not the beginning. $shortname = format_string($course->shortname, true, array('context' => $coursecontexts[$course->id])); if (core_text::strlen($shortname) > MESSAGE_MAX_COURSE_NAME_LENGTH) { $courses_options[MESSAGE_VIEW_COURSE.$course->id] = '...'.core_text::substr($shortname, -MESSAGE_MAX_COURSE_NAME_LENGTH); } else { $courses_options[MESSAGE_VIEW_COURSE.$course->id] = $shortname; } } } if (!empty($courses_options)) { $options[] = array(get_string('courses') => $courses_options); } } if ($countblocked>0) { $str = get_string('blockedusers','message', $countblocked); $options[MESSAGE_VIEW_BLOCKED] = $str; } $select = new single_select($PAGE->url, 'viewing', $options, $viewing, false); $select->set_label(get_string('messagenavigation', 'message')); $renderer = $PAGE->get_renderer('core'); echo $renderer->render($select); }
/** * Prints the page list tab content * * */ private function print_page_list_content() { global $OUTPUT; $page = $this->page; if ($page->timerendered + WIKI_REFRESH_CACHE_TIME < time()) { $fresh = wiki_refresh_cachedcontent($page); $page = $fresh['page']; } $pages = wiki_get_page_list($this->subwiki->id); $stdaux = new stdClass(); $strspecial = get_string('special', 'wiki'); foreach ($pages as $page) { // We need to format the title here to account for any filtering $letter = format_string($page->title, true, array('context' => $this->modcontext)); $letter = core_text::substr($letter, 0, 1); if (preg_match('/^[a-zA-Z]$/', $letter)) { $letter = core_text::strtoupper($letter); $stdaux->{$letter}[] = wiki_parser_link($page); } else { $stdaux->{$strspecial}[] = wiki_parser_link($page); } } $table = new html_table(); $table->head = array(get_string('pagelist', 'wiki') . $OUTPUT->help_icon('pagelist', 'wiki')); $table->attributes['class'] = 'wiki_editor generalbox'; $table->align = array('center'); foreach ($stdaux as $key => $elem) { $table->data[] = array($key); foreach ($elem as $e) { $table->data[] = array(html_writer::link($e['url'], format_string($e['content'], true, array('context' => $this->modcontext)))); } } echo html_writer::table($table); }
/** * given a course object with shortname & fullname, this function will * truncate the the number of chars allowed and add ... if it was too long */ function course_format_name($course, $max = 100) { $context = context_course::instance($course->id); $shortname = format_string($course->shortname, true, array('context' => $context)); $fullname = format_string($course->fullname, true, array('context' => context_course::instance($course->id))); $str = $shortname . ': ' . $fullname; if (core_text::strlen($str) <= $max) { return $str; } else { return core_text::substr($str, 0, $max - 3) . '...'; } }
/** * Adds exception information to Moodle log. * @param Exception $e Exception */ public static function log_exception(Exception $e) { global $DB; $info = ''; // Default courseid (may override later on) global $COURSE; $courseid = isset($COURSE->id) ? $COURSE->id : 0; $cmid = 0; // These are numeric params so OK to include; I made this list from // all the params in editpost.php where this is initially implemented foreach (array('clone', 'id', 'd', 'p', 'ajax', 'draft', 'group', 'replyto', 'lock') as $param) { if ($val = optional_param($param, 0, PARAM_INT)) { $info .= $param . '=' . $val . ','; // Guess courseid from param if (!$cmid) { if ($param === 'clone' || $param === 'id') { $cmid = $val; } else { if ($param === 'd') { $cmid = $DB->get_field_sql('SELECT cm.id FROM {forumng_discussions} fd ' . 'JOIN {forumng} f ON f.id = fd.forumngid ' . 'JOIN {modules} m ON m.name = ? ' . 'JOIN {course_modules} cm ON cm.instance = f.id ' . 'AND cm.module = m.id ' . 'WHERE fd.id = ?', array('forumng', $val), IGNORE_MISSING); } else { if ($param === 'p' || $param === 'replyto') { $cmid = $DB->get_field_sql('SELECT cm.id FROM {forumng_posts} fp ' . 'JOIN {forumng_discussions} fd ON fd.id = fp.discussionid ' . 'JOIN {forumng} f ON f.id = fd.forumngid ' . 'JOIN {modules} m ON m.name = ? ' . 'JOIN {course_modules} cm ON cm.instance = f.id ' . 'AND cm.module = m.id ' . 'WHERE fp.id = ?', array('forumng', $val), IGNORE_MISSING); } } } } } } if ($cmid) { $courseid = $DB->get_field('course_modules', 'course', array('id' => $cmid), IGNORE_MISSING); } // Remove final , $info = preg_replace('~\\,$~', '', $info); // Trace begins with | sign $info .= '|'; global $CFG; // Annoyingly the trace array does not include the 'first' location $firsttrace = array('file' => $e->getFile(), 'line' => $e->getLine()); $trace = array_merge(array($firsttrace), $e->getTrace()); $file = ''; foreach ($trace as $line) { // To reduce the number of required characters, remove the location // prefix and .php, and remove mod/forumng/ too. $file = str_replace($CFG->dirroot . '/', '', $line['file']); $file = str_replace('mod/forumng/', '', $file); $file = str_replace('.php', '', $file); $info .= $file . ':' . $line['line'] . ','; } // Remove final , $info = preg_replace('~\\,$~', '', $info); // Finally let's add the exception message $info .= '|' . $e->getMessage(); // Cut off (using textlib in case message contains UTF-8) if (core_text::strlen($info) > 255) { // Use first part + ellipsis $info = core_text::substr($info, 0, 254) . html_entity_decode('…', ENT_QUOTES, 'UTF-8'); } // Add entry to Moodle log (using root file in action) $cmid = $cmid ? $cmid : 0; $courseid = $courseid ? $courseid : 0; $params = array('context' => context_module::instance($cmid), 'other' => array('info' => $info, 'url' => $_SERVER['REQUEST_URI'])); switch ($file) { case 'editpost': $event = \mod_forumng\event\post_updated_failed::create($params); break; } $event->trigger(); }
/** * Self enrol the current user in the given course. * * @param int $courseid id of course * @param string $password enrolment key * @param int $instanceid instance id of self enrolment plugin * @return array of warnings and status result * @since Moodle 3.0 * @throws moodle_exception */ public static function enrol_user($courseid, $password = '', $instanceid = 0) { global $CFG; require_once $CFG->libdir . '/enrollib.php'; $params = self::validate_parameters(self::enrol_user_parameters(), array('courseid' => $courseid, 'password' => $password, 'instanceid' => $instanceid)); $warnings = array(); $course = get_course($params['courseid']); $context = context_course::instance($course->id); self::validate_context(context_system::instance()); if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) { throw new moodle_exception('coursehidden'); } // Retrieve the self enrolment plugin. $enrol = enrol_get_plugin('self'); if (empty($enrol)) { throw new moodle_exception('canntenrol', 'enrol_self'); } // We can expect multiple self-enrolment instances. $instances = array(); $enrolinstances = enrol_get_instances($course->id, true); foreach ($enrolinstances as $courseenrolinstance) { if ($courseenrolinstance->enrol == "self") { // Instance specified. if (!empty($params['instanceid'])) { if ($courseenrolinstance->id == $params['instanceid']) { $instances[] = $courseenrolinstance; break; } } else { $instances[] = $courseenrolinstance; } } } if (empty($instances)) { throw new moodle_exception('canntenrol', 'enrol_self'); } // Try to enrol the user in the instance/s. $enrolled = false; foreach ($instances as $instance) { $enrolstatus = $enrol->can_self_enrol($instance); if ($enrolstatus === true) { if ($instance->password and $params['password'] !== $instance->password) { // Check if we are using group enrolment keys. if ($instance->customint1) { require_once $CFG->dirroot . "/enrol/self/locallib.php"; if (!enrol_self_check_group_enrolment_key($course->id, $params['password'])) { $warnings[] = array('item' => 'instance', 'itemid' => $instance->id, 'warningcode' => '2', 'message' => get_string('passwordinvalid', 'enrol_self')); continue; } } else { if ($enrol->get_config('showhint')) { $hint = core_text::substr($instance->password, 0, 1); $warnings[] = array('item' => 'instance', 'itemid' => $instance->id, 'warningcode' => '3', 'message' => s(get_string('passwordinvalidhint', 'enrol_self', $hint))); continue; } else { $warnings[] = array('item' => 'instance', 'itemid' => $instance->id, 'warningcode' => '4', 'message' => get_string('passwordinvalid', 'enrol_self')); continue; } } } // Do the enrolment. $data = array('enrolpassword' => $params['password']); $enrol->enrol_self($instance, (object) $data); $enrolled = true; break; } else { $warnings[] = array('item' => 'instance', 'itemid' => $instance->id, 'warningcode' => '1', 'message' => $enrolstatus); } } $result = array(); $result['status'] = $enrolled; $result['warnings'] = $warnings; return $result; }
/** * This function trims any given text and returns it with some dots at the end * * @param string $text * @param string $limit * * @return string */ function wiki_trim_string($text, $limit = 25) { if (core_text::strlen($text) > $limit) { $text = core_text::substr($text, 0, $limit) . '...'; } return $text; }
public function validation($data, $files) { global $DB, $CFG; $errors = parent::validation($data, $files); $instance = $this->instance; if ($this->toomany) { $errors['notice'] = get_string('error'); return $errors; } if ($instance->password) { if ($data['enrolpassword'] !== $instance->password) { if ($instance->customint1) { $groups = $DB->get_records('groups', array('courseid' => $instance->courseid), 'id ASC', 'id, enrolmentkey'); $found = false; foreach ($groups as $group) { if (empty($group->enrolmentkey)) { continue; } if ($group->enrolmentkey === $data['enrolpassword']) { $found = true; break; } } if (!$found) { // We can not hint because there are probably multiple passwords. $errors['enrolpassword'] = get_string('passwordinvalid', 'enrol_boleto'); } } else { $plugin = enrol_get_plugin('boleto'); if ($plugin->get_config('showhint')) { $hint = core_text::substr($instance->password, 0, 1); $errors['enrolpassword'] = get_string('passwordinvalidhint', 'enrol_boleto', $hint); } else { $errors['enrolpassword'] = get_string('passwordinvalid', 'enrol_boleto'); } } } } return $errors; }
/** * If new messages are waiting for the current user, then insert * JavaScript to pop up the messaging window into the page * * @return void */ function message_popup_window() { global $USER, $DB, $PAGE, $CFG; if (!$PAGE->get_popup_notification_allowed() || empty($CFG->messaging)) { return; } if (!isloggedin() || isguestuser()) { return; } if (!isset($USER->message_lastpopup)) { $USER->message_lastpopup = 0; } else { if ($USER->message_lastpopup > time() - 120) { // Don't run the query to check whether to display a popup if its been run in the last 2 minutes. return; } } // A quick query to check whether the user has new messages. $messagecount = $DB->count_records('message', array('useridto' => $USER->id)); if ($messagecount < 1) { return; } // Got unread messages so now do another query that joins with the user table. $namefields = get_all_user_name_fields(true, 'u'); $messagesql = "SELECT m.id, m.smallmessage, m.fullmessageformat, m.notification, {$namefields}\n FROM {message} m\n JOIN {message_working} mw ON m.id=mw.unreadmessageid\n JOIN {message_processors} p ON mw.processorid=p.id\n JOIN {user} u ON m.useridfrom=u.id\n WHERE m.useridto = :userid\n AND p.name='popup'"; // If the user was last notified over an hour ago we can re-notify them of old messages // so don't worry about when the new message was sent. $lastnotifiedlongago = $USER->message_lastpopup < time() - 3600; if (!$lastnotifiedlongago) { $messagesql .= 'AND m.timecreated > :lastpopuptime'; } $messageusers = $DB->get_records_sql($messagesql, array('userid' => $USER->id, 'lastpopuptime' => $USER->message_lastpopup)); // If we have new messages to notify the user about. if (!empty($messageusers)) { $strmessages = ''; if (count($messageusers) > 1) { $strmessages = get_string('unreadnewmessages', 'message', count($messageusers)); } else { $messageusers = reset($messageusers); // Show who the message is from if its not a notification. if (!$messageusers->notification) { $strmessages = get_string('unreadnewmessage', 'message', fullname($messageusers)); } // Try to display the small version of the message. $smallmessage = null; if (!empty($messageusers->smallmessage)) { // Display the first 200 chars of the message in the popup. $smallmessage = null; if (core_text::strlen($messageusers->smallmessage) > 200) { $smallmessage = core_text::substr($messageusers->smallmessage, 0, 200) . '...'; } else { $smallmessage = $messageusers->smallmessage; } // Prevent html symbols being displayed. if ($messageusers->fullmessageformat == FORMAT_HTML) { $smallmessage = html_to_text($smallmessage); } else { $smallmessage = s($smallmessage); } } else { if ($messageusers->notification) { // Its a notification with no smallmessage so just say they have a notification. $smallmessage = get_string('unreadnewnotification', 'message'); } } if (!empty($smallmessage)) { $strmessages .= '<div id="usermessage">' . s($smallmessage) . '</div>'; } } $strgomessage = get_string('gotomessages', 'message'); $strstaymessage = get_string('ignore', 'admin'); $notificationsound = null; $beep = get_user_preferences('message_beepnewmessage', ''); if (!empty($beep)) { // Browsers will work down this list until they find something they support. $sourcetags = html_writer::empty_tag('source', array('src' => $CFG->wwwroot . '/message/bell.wav', 'type' => 'audio/wav')); $sourcetags .= html_writer::empty_tag('source', array('src' => $CFG->wwwroot . '/message/bell.ogg', 'type' => 'audio/ogg')); $sourcetags .= html_writer::empty_tag('source', array('src' => $CFG->wwwroot . '/message/bell.mp3', 'type' => 'audio/mpeg')); $sourcetags .= html_writer::empty_tag('embed', array('src' => $CFG->wwwroot . '/message/bell.wav', 'autostart' => 'true', 'hidden' => 'true')); $notificationsound = html_writer::tag('audio', $sourcetags, array('preload' => 'auto', 'autoplay' => 'autoplay')); } $url = $CFG->wwwroot . '/message/index.php'; $content = html_writer::start_tag('div', array('id' => 'newmessageoverlay', 'class' => 'mdl-align')) . html_writer::start_tag('div', array('id' => 'newmessagetext')) . $strmessages . html_writer::end_tag('div') . $notificationsound . html_writer::start_tag('div', array('id' => 'newmessagelinks')) . html_writer::link($url, $strgomessage, array('id' => 'notificationyes')) . ' ' . html_writer::link('', $strstaymessage, array('id' => 'notificationno')) . html_writer::end_tag('div'); html_writer::end_tag('div'); $PAGE->requires->js_init_call('M.core_message.init_notification', array('', $content, $url)); $USER->message_lastpopup = time(); } }
/** * Create a shorten filename * * @param string $str filename * @param int $maxlength max file name length * @return string short filename */ public function get_short_filename($str, $maxlength) { if (core_text::strlen($str) >= $maxlength) { return trim(core_text::substr($str, 0, $maxlength)) . '...'; } else { return $str; } }
/** * Store an entire {@link question_attempt} in the database, * including all the question_attempt_steps that comprise it. * @param question_attempt $qa the question attempt to store. * @param context $context the context of the owning question_usage_by_activity. */ public function insert_question_attempt(question_attempt $qa, $context) { $record = new stdClass(); $record->questionusageid = $qa->get_usage_id(); $record->slot = $qa->get_slot(); $record->behaviour = $qa->get_behaviour_name(); $record->questionid = $qa->get_question()->id; $record->variant = $qa->get_variant(); $record->maxmark = $qa->get_max_mark(); $record->minfraction = $qa->get_min_fraction(); $record->maxfraction = $qa->get_max_fraction(); $record->flagged = $qa->is_flagged(); $record->questionsummary = $qa->get_question_summary(); if (core_text::strlen($record->questionsummary) > question_bank::MAX_SUMMARY_LENGTH) { // It seems some people write very long quesions! MDL-30760 $record->questionsummary = core_text::substr($record->questionsummary, 0, question_bank::MAX_SUMMARY_LENGTH - 3) . '...'; } $record->rightanswer = $qa->get_right_answer_summary(); $record->responsesummary = $qa->get_response_summary(); $record->timemodified = time(); $record->id = $this->db->insert_record('question_attempts', $record); $qa->set_database_id($record->id); foreach ($qa->get_step_iterator() as $seq => $step) { $this->insert_question_attempt_step($step, $record->id, $seq, $context); } }