Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b19d180a86 | |||
| 93b943b322 | |||
| 8e3b262bb7 | |||
| f0b9026133 | |||
| b779dc805e | |||
| e74867d06c | |||
| 41feb40e7e | |||
| 7696a8d9e3 | |||
| c44b6e79fa | |||
| 81d5b794d0 | |||
| 8137f98b1c | |||
| 9103f6ca8a | |||
| e6973df683 | |||
| 7d7ca07870 | |||
| 8398fbe8b6 | |||
| 7c362df750 | |||
| 550b956e23 | |||
| 59dbf60906 | |||
| f6f7a788ac | |||
| b6c0d8fc2a | |||
| b7bbe9fd94 | |||
| f6da281a83 | |||
| f72830b4f0 | |||
| ecd1bce400 | |||
| 6c0e979eda | |||
| 3ff27109ec | |||
| f06f4603b8 | |||
| 7bed262b8f | |||
| e9ba7022fc | |||
| 60aab30175 | |||
| 85e9ee2175 | |||
| 10426ccf9d | |||
| babe95c9dc | |||
| 9a15bdc97f | |||
| 86f0bb4591 | |||
| b0d7351cff | |||
| ad5761237a | |||
| d98544a729 | |||
| 1dd71c502a | |||
| fae9236939 | |||
| 36a7b9f234 | |||
| 878d69b794 | |||
| 2032491730 | |||
| 7b2fc2b81e | |||
| c8fe4d8b88 | |||
| 567c0bb51d | |||
| fe8a827cb0 |
+1
-4
@@ -45,12 +45,9 @@ matrix:
|
||||
fast_finish: true
|
||||
|
||||
include:
|
||||
# Run grunt/npm install on lowest supported npm version
|
||||
- php: 7
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='0.10'
|
||||
# Run grunt/npm install on highest version ('node' is an alias for the latest node.js version.)
|
||||
- php: 7
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='node'
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='lts/carbon'
|
||||
|
||||
exclude:
|
||||
# MySQL - it's just too slow.
|
||||
|
||||
@@ -32,7 +32,9 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
|
||||
$ADMIN->add('development', $temp);
|
||||
|
||||
// "Profiling" settingpage (conditionally if the 'xhprof' extension is available only).
|
||||
$xhprofenabled = extension_loaded('xhprof') || extension_loaded('tideways');
|
||||
$xhprofenabled = extension_loaded('tideways_xhprof');
|
||||
$xhprofenabled = $xhprofenabled || extension_loaded('tideways');
|
||||
$xhprofenabled = $xhprofenabled || extension_loaded('xhprof');
|
||||
$temp = new admin_settingpage('profiling', new lang_string('profiling', 'admin'), 'moodle/site:config', !$xhprofenabled);
|
||||
// Main profiling switch.
|
||||
$temp->add(new admin_setting_configcheckbox('profilingenabled', new lang_string('profilingenabled', 'admin'), new lang_string('profilingenabled_help', 'admin'), false));
|
||||
|
||||
@@ -25,8 +25,12 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
// profiling tool, added to development
|
||||
if ((extension_loaded('xhprof') || extension_loaded('tideways')) && (!empty($CFG->profilingenabled) || !empty($CFG->earlyprofilingenabled))) {
|
||||
// Profiling tool, added to development.
|
||||
$hasextension = extension_loaded('tideways_xhprof');
|
||||
$hasextension = $hasextension || extension_loaded('tideways');
|
||||
$hasextension = $hasextension || extension_loaded('xhprof');
|
||||
$isenabled = !empty($CFG->profilingenabled) || !empty($CFG->earlyprofilingenabled);
|
||||
if ($hasextension && $isenabled) {
|
||||
$ADMIN->add('development', new admin_externalpage('toolprofiling', get_string('pluginname', 'tool_profiling'),
|
||||
"$CFG->wwwroot/$CFG->admin/tool/profiling/index.php", 'moodle/site:config'));
|
||||
}
|
||||
|
||||
+45
-26
@@ -50,6 +50,12 @@ class block_edit_form extends moodleform {
|
||||
*/
|
||||
public $page;
|
||||
|
||||
/**
|
||||
* Defaults set in set_data() that need to be returned in get_data() if form elements were not created
|
||||
* @var array
|
||||
*/
|
||||
protected $defaults = [];
|
||||
|
||||
function __construct($actionurl, $block, $page) {
|
||||
global $CFG;
|
||||
$this->block = $block;
|
||||
@@ -92,9 +98,6 @@ class block_edit_form extends moodleform {
|
||||
}
|
||||
|
||||
$parentcontext = context::instance_by_id($this->block->instance->parentcontextid);
|
||||
$mform->addElement('hidden', 'bui_parentcontextid', $parentcontext->id);
|
||||
$mform->setType('bui_parentcontextid', PARAM_INT);
|
||||
|
||||
$mform->addElement('static', 'bui_homecontext', get_string('createdat', 'block'), $parentcontext->get_context_name());
|
||||
$mform->addHelpButton('bui_homecontext', 'createdat', 'block');
|
||||
|
||||
@@ -109,21 +112,9 @@ class block_edit_form extends moodleform {
|
||||
// implies one (and only one) harcoded page-type that will be set later
|
||||
// when processing the form data at {@link block_manager::process_url_edit()}
|
||||
|
||||
// There are some conditions to check related to contexts
|
||||
$ctxconditions = $this->page->context->contextlevel == CONTEXT_COURSE &&
|
||||
$this->page->context->instanceid == get_site()->id;
|
||||
// And also some pagetype conditions
|
||||
$pageconditions = isset($bits[0]) && isset($bits[1]) && $bits[0] == 'site' && $bits[1] == 'index';
|
||||
// So now we can be 100% sure if edition is happening at frontpage
|
||||
$editingatfrontpage = $ctxconditions && $pageconditions;
|
||||
|
||||
// Let the form to know about that, can be useful later
|
||||
$mform->addElement('hidden', 'bui_editingatfrontpage', (int)$editingatfrontpage);
|
||||
$mform->setType('bui_editingatfrontpage', PARAM_INT);
|
||||
|
||||
// Front page, show the page-contexts element and set $pagetypelist to 'any page' (*)
|
||||
// as unique option. Processign the form will do any change if needed
|
||||
if ($editingatfrontpage) {
|
||||
if ($this->is_editing_the_frontpage()) {
|
||||
$contextoptions = array();
|
||||
$contextoptions[BUI_CONTEXTS_FRONTPAGE_ONLY] = get_string('showonfrontpageonly', 'block');
|
||||
$contextoptions[BUI_CONTEXTS_FRONTPAGE_SUBS] = get_string('showonfrontpageandsubs', 'block');
|
||||
@@ -135,16 +126,13 @@ class block_edit_form extends moodleform {
|
||||
// Any other system context block, hide the page-contexts element,
|
||||
// it's always system-wide BUI_CONTEXTS_ENTIRE_SITE
|
||||
} else if ($parentcontext->contextlevel == CONTEXT_SYSTEM) {
|
||||
$mform->addElement('hidden', 'bui_contexts', BUI_CONTEXTS_ENTIRE_SITE);
|
||||
|
||||
} else if ($parentcontext->contextlevel == CONTEXT_COURSE) {
|
||||
// 0 means display on current context only, not child contexts
|
||||
// but if course managers select mod-* as pagetype patterns, block system will overwrite this option
|
||||
// to 1 (display on current context and child contexts)
|
||||
$mform->addElement('hidden', 'bui_contexts', BUI_CONTEXTS_CURRENT);
|
||||
} else if ($parentcontext->contextlevel == CONTEXT_MODULE or $parentcontext->contextlevel == CONTEXT_USER) {
|
||||
// module context doesn't have child contexts, so display in current context only
|
||||
$mform->addElement('hidden', 'bui_contexts', BUI_CONTEXTS_CURRENT);
|
||||
} else {
|
||||
$parentcontextname = $parentcontext->get_context_name();
|
||||
$contextoptions[BUI_CONTEXTS_CURRENT] = get_string('showoncontextonly', 'block', $parentcontextname);
|
||||
@@ -181,8 +169,6 @@ class block_edit_form extends moodleform {
|
||||
} else {
|
||||
$values = array_keys($pagetypelist);
|
||||
$value = array_pop($values);
|
||||
$mform->addElement('hidden', 'bui_pagetypepattern', $value);
|
||||
$mform->setType('bui_pagetypepattern', PARAM_RAW);
|
||||
// Now we are really hiding a lot (both page-contexts and page-type-patterns),
|
||||
// specially in some systemcontext pages having only one option (my/user...)
|
||||
// so, until it's decided if we are going to add the 'bring-back' pattern to
|
||||
@@ -191,7 +177,7 @@ class block_edit_form extends moodleform {
|
||||
// TODO: Revisit this once MDL-30574 has been decided and implemented, although
|
||||
// perhaps it's not bad to always show this statically when only one pattern is
|
||||
// available.
|
||||
if (!$editingatfrontpage) {
|
||||
if (!$this->is_editing_the_frontpage()) {
|
||||
// Try to beautify it
|
||||
$strvalue = $value;
|
||||
$strkey = 'page-'.str_replace('*', 'x', $strvalue);
|
||||
@@ -205,10 +191,7 @@ class block_edit_form extends moodleform {
|
||||
}
|
||||
|
||||
if ($this->page->subpage) {
|
||||
if ($parentcontext->contextlevel == CONTEXT_USER) {
|
||||
$mform->addElement('hidden', 'bui_subpagepattern', '%@NULL@%');
|
||||
$mform->setType('bui_subpagepattern', PARAM_RAW);
|
||||
} else {
|
||||
if ($parentcontext->contextlevel != CONTEXT_USER) {
|
||||
$subpageoptions = array(
|
||||
'%@NULL@%' => get_string('anypagematchingtheabove', 'block'),
|
||||
$this->page->subpage => get_string('thisspecificpage', 'block', $this->page->subpage),
|
||||
@@ -252,6 +235,19 @@ class block_edit_form extends moodleform {
|
||||
$this->add_action_buttons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user is editing a frontpage.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_editing_the_frontpage() {
|
||||
// There are some conditions to check related to contexts.
|
||||
$ctxconditions = $this->page->context->contextlevel == CONTEXT_COURSE &&
|
||||
$this->page->context->instanceid == get_site()->id;
|
||||
$issiteindex = (strpos($this->page->pagetype, 'site-index') === 0);
|
||||
// So now we can be 100% sure if edition is happening at frontpage.
|
||||
return ($ctxconditions && $issiteindex);
|
||||
}
|
||||
|
||||
function set_data($defaults) {
|
||||
// Prefix bui_ on all the core field names.
|
||||
$blockfields = array('showinsubcontexts', 'pagetypepattern', 'subpagepattern', 'parentcontextid',
|
||||
@@ -281,6 +277,13 @@ class block_edit_form extends moodleform {
|
||||
$defaults->bui_contexts = $defaults->bui_showinsubcontexts;
|
||||
}
|
||||
|
||||
// Some fields may not be editable, remember the values here so we can return them in get_data().
|
||||
$this->defaults = [
|
||||
'bui_parentcontextid' => $defaults->bui_parentcontextid,
|
||||
'bui_contexts' => $defaults->bui_contexts,
|
||||
'bui_pagetypepattern' => $defaults->bui_pagetypepattern,
|
||||
'bui_subpagepattern' => $defaults->bui_subpagepattern,
|
||||
];
|
||||
parent::set_data($defaults);
|
||||
}
|
||||
|
||||
@@ -291,4 +294,20 @@ class block_edit_form extends moodleform {
|
||||
protected function specific_definition($mform) {
|
||||
// By default, do nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* Return submitted data if properly submitted or returns NULL if validation fails or
|
||||
* if there is no submitted data.
|
||||
*
|
||||
* @return object submitted data; NULL if not valid or not submitted or cancelled
|
||||
*/
|
||||
public function get_data() {
|
||||
if ($data = parent::get_data()) {
|
||||
// Blocklib expects 'bui_editingatfrontpage' property to be returned from this form.
|
||||
$data->bui_editingatfrontpage = $this->is_editing_the_frontpage();
|
||||
// Some fields are non-editable and we need to populate them with the values from set_data().
|
||||
return (object)((array)$data + $this->defaults);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1524,9 +1524,7 @@ class core_course_external extends external_api {
|
||||
break;
|
||||
|
||||
case 'visible':
|
||||
if (has_capability('moodle/category:manage', $context)
|
||||
or has_capability('moodle/category:viewhiddencategories',
|
||||
context_system::instance())) {
|
||||
if (has_capability('moodle/category:viewhiddencategories', $context)) {
|
||||
$value = clean_param($crit['value'], PARAM_INT);
|
||||
} else {
|
||||
throw new moodle_exception('criteriaerror',
|
||||
@@ -1637,9 +1635,7 @@ class core_course_external extends external_api {
|
||||
if (!isset($excludedcats[$category->id])) {
|
||||
|
||||
// Final check to see if the category is visible to the user.
|
||||
if ($category->visible
|
||||
or has_capability('moodle/category:viewhiddencategories', context_system::instance())
|
||||
or has_capability('moodle/category:manage', $context)) {
|
||||
if ($category->visible or has_capability('moodle/category:viewhiddencategories', $context)) {
|
||||
|
||||
$categoryinfo = array();
|
||||
$categoryinfo['id'] = $category->id;
|
||||
|
||||
@@ -201,6 +201,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
// Set the required capabilities by the external function.
|
||||
$context = context_system::instance();
|
||||
$roleid = $this->assignUserCapability('moodle/category:manage', $context->id);
|
||||
$this->assignUserCapability('moodle/category:viewhiddencategories', $context->id, $roleid);
|
||||
|
||||
// Retrieve category1 + sub-categories except not visible ones
|
||||
$categories = core_course_external::get_categories(array(
|
||||
@@ -259,10 +260,10 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
|
||||
$this->assertEquals($DB->count_records('course_categories'), count($categories));
|
||||
|
||||
$this->unassignUserCapability('moodle/category:manage', $context->id, $roleid);
|
||||
$this->unassignUserCapability('moodle/category:viewhiddencategories', $context->id, $roleid);
|
||||
|
||||
// Ensure maxdepthcategory is 2 and retrieve all categories without category:manage capability. It should retrieve all
|
||||
// visible categories as well.
|
||||
// Ensure maxdepthcategory is 2 and retrieve all categories without category:viewhiddencategories capability.
|
||||
// It should retrieve all visible categories as well.
|
||||
set_config('maxcategorydepth', 2);
|
||||
$categories = core_course_external::get_categories();
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ if (!empty($CFG->defaulthomepage) && ($CFG->defaulthomepage == HOMEPAGE_MY) && o
|
||||
$urlparams['redirect'] = 0;
|
||||
}
|
||||
$PAGE->set_url('/', $urlparams);
|
||||
$PAGE->set_course($SITE);
|
||||
$PAGE->set_pagelayout('frontpage');
|
||||
$PAGE->set_other_editing_capability('moodle/course:update');
|
||||
$PAGE->set_other_editing_capability('moodle/course:manageactivities');
|
||||
@@ -47,11 +46,7 @@ $PAGE->set_other_editing_capability('moodle/course:activityvisibility');
|
||||
// Prevent caching of this page to stop confusion when changing page after making AJAX changes.
|
||||
$PAGE->set_cacheable(false);
|
||||
|
||||
if ($CFG->forcelogin) {
|
||||
require_login();
|
||||
} else {
|
||||
user_accesstime_log();
|
||||
}
|
||||
require_course_login($SITE);
|
||||
|
||||
$hassiteconfig = has_capability('moodle/site:config', context_system::instance());
|
||||
|
||||
|
||||
@@ -38,3 +38,6 @@ $string['clitypevalue'] = 'Tiepe waarde';
|
||||
$string['clitypevaluedefault'] = 'Tiepe waarde, druk Enter om die standaard waarde te gebruik ({$a})';
|
||||
$string['cliunknowoption'] = 'Ontkende opsies: {$ a} Gebruik asseblief - help opsie.';
|
||||
$string['cliyesnoprompt'] = 'Tik y (beteken ja) of n (beteken nee)';
|
||||
$string['environmentrequireinstall'] = 'moet geïnstalleer en geaktiveer wees';
|
||||
$string['environmentrequireversion'] = 'weergawe {$a->needed} word benodig en jy het tans {$a->current}';
|
||||
$string['upgradekeyset'] = 'Opgradering sleutel (los skoon om nie te stel nie)';
|
||||
|
||||
@@ -33,7 +33,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
$string['clianswerno'] = 'No';
|
||||
$string['cliansweryes'] = 'Sí';
|
||||
$string['cliincorrectvalueerror'] = 'Error, valor incorrecte "{$a->value}" per a "{$a->option}"';
|
||||
$string['cliincorrectvalueretry'] = 'Valor incorrecte, si us plau, torneu-ho a provar.';
|
||||
$string['cliincorrectvalueretry'] = 'Valor incorrecte; torneu-ho a provar.';
|
||||
$string['clitypevalue'] = 'Valor de tipus';
|
||||
$string['clitypevaluedefault'] = 'valor de tipus, premeu la tecla de retorn (<em>Enter</em>) per fer servir un valor per defecte ({$a})';
|
||||
$string['cliunknowoption'] = 'Opcions invàlides:
|
||||
|
||||
@@ -52,15 +52,15 @@ $string['installation'] = 'Instal·lació';
|
||||
$string['langdownloaderror'] = 'Dissortadament l\'idioma "{$a}" no es pot baixar. La instal·lació prosseguirà en anglès.';
|
||||
$string['memorylimithelp'] = '<p>El límit de memòria del PHP del vostre servidor actualment està definit en {$a}.</p>
|
||||
|
||||
<p>Això pot causar que Moodle tingui problemes de memòria més endavant, especialment si teniu molts mòduls habilitats i/o molts usuaris.</p>
|
||||
<p>Això pot causar que Moodle tingui problemes de memòria més endavant, especialment, si teniu molts mòduls habilitats i/o molts usuaris.</p>
|
||||
|
||||
<p>És recomanable que configureu el PHP amb un límit superior, com ara 40 MB, sempre que sigui possible. Hi ha diverses maneres de fer això:</p>
|
||||
<ol>
|
||||
<li>Si podeu, recompileu el PHP amb <i>--enable-memory-limit</i>. Això permetrà que Moodle defineixi el límit de memòria per si mateix.</li>
|
||||
<li>Si teniu accés al fitxer php.ini, podeu canviar el paràmetre <b>memory_limit</b> a 40 MB. Si no hi teniu accés podeu demanar al vostre administrador que ho faci ell.</li>
|
||||
<li>En alguns servidors PHP podeu crear un fitxer .htaccess dins del directori de Moodle amb aquesta línia:
|
||||
<li>Si teniu accés al fitxer php.ini, podeu canviar el paràmetre <b>memory_limit</b> a 40 MB. Si no hi teniu accés, podeu demanar al vostre administrador que us ho faci.</li>
|
||||
<li>En alguns servidors PHP podeu crear un fitxer .htaccess dins el directori de Moodle amb aquesta línia:
|
||||
<p><blockquote>php_value memory_limit 40M</blockquote></p>
|
||||
<p>Tanmateix, en alguns servidors això farà que no funcioni <b>cap</b> pàgina PHP (es visualitzaran errors) en el qual cas hauríeu de suprimir el fitxer .htaccess.</p></li>
|
||||
<p>Tanmateix, en alguns servidors això farà que no funcioni <b>cap</b> pàgina PHP (es visualitzaran errors); en aquest cas, hauríeu de suprimir el fitxer .htaccess.</p></li>
|
||||
</ol>';
|
||||
$string['paths'] = 'Camins';
|
||||
$string['pathserrcreatedataroot'] = 'L\'instal·lador no pot crear el directori de dades ({$a->dataroot}).';
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
*
|
||||
* Do not edit this file manually! It contains just a subset of strings
|
||||
* needed during the very first steps of installation. This file was
|
||||
* generated automatically by export-installer.php (which is part of AMOS
|
||||
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
|
||||
* list of strings defined in /install/stringnames.txt.
|
||||
*
|
||||
* @package installer
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['parentlanguage'] = 'de';
|
||||
$string['thisdirection'] = 'ltr';
|
||||
$string['thislanguage'] = 'Deutsch - Schweiz';
|
||||
@@ -30,6 +30,9 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['cannotcreatedboninstall'] = '<p>Αδυναμία δημιουργίας βάσης δεδομένων.</p>
|
||||
<p>Η βάση δεδομένων που προσδιορίστηκε δεν υπάρχει και ο χρήστης που δόθηκε δεν έχει δικαίωμα να δημιουργήσει την βάση δεδομένων.</p>
|
||||
<p>Ο διαχειριστής του ιστοτόπου πρέπει να επαληθεύσει την ρύθμιση της βάσης δεδομένων.</p>';
|
||||
$string['cannotcreatelangdir'] = 'Δε δημιουργήθηκε φάκελος γλώσσας.';
|
||||
$string['cannotcreatetempdir'] = 'Δε δημιουργήθηκε φάκελος temp.';
|
||||
$string['cannotdownloadcomponents'] = 'Δεν κατέβηκαν τα τμήματα.';
|
||||
|
||||
@@ -30,5 +30,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['parentlanguage'] = '';
|
||||
$string['thisdirection'] = 'ltr';
|
||||
$string['thislanguage'] = 'Euskara';
|
||||
|
||||
@@ -32,3 +32,4 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['language'] = 'Tungumál';
|
||||
$string['next'] = 'Næsta';
|
||||
$string['previous'] = 'Undanfarna';
|
||||
|
||||
@@ -59,11 +59,8 @@ $string['pathsroparentdataroot'] = 'A felettes könyvtár ({$a->parent}) nem ír
|
||||
$string['pathssubadmindir'] = 'Egy pár webes gazdagép esetén az /admin speciális URL pl. a vezérlőpanel eléréséhez. Ez ütközik a Moodle admin oldalainak standard helyével. Javítás: a telepítésben nevezze át a rendszergazda könyvtárát, az új nevet pedig írja be ide. Például: <em>moodleadmin</em>. Ezzel helyrehozhatók a Moodle rendszergazdai ugrópontjai.';
|
||||
$string['pathssubdataroot'] = 'Szüksége van egy helyre, ahol a Moodle mentheti a feltöltött állományokat. Ez a könyvtár a webszerver felhasználója (általában \'nobody\' vagy \'apache\') számára legyen mind olvasható, MIND ÍRHATÓ. Ha nem létezik, a telepítő megpróbálja létrehozni.';
|
||||
$string['pathssubdirroot'] = 'Teljes útvonal a Moodle telepítéséhez. ';
|
||||
$string['pathssubwwwroot'] = 'A Moodle elérésére használandó teljes webcím. A Moodle egyszerre több
|
||||
címről nem érhető el. Ha portálja több címet használ, a jelen cím kivételével az összeshez állandó
|
||||
átirányítást kell beállítania. Ha portálja mind intranetről, mind az internetről elérhető, a nyilvános
|
||||
címet itt adja meg, a DNS-t pedig úgy állítsa be, hogy az intranetről a
|
||||
nyilvános cím is elérhető legyen. Ha a cím hibás, módosítsa böngészőjében az URL-t, hogy a telepítés egy másik értékkel induljon újra.';
|
||||
$string['pathssubwwwroot'] = 'A Moodle elérésére használandó teljes webcím. A Moodle egyszerre több címről nem érhető el. Ha portálja több címet használ, a jelen cím kivételével az összeshez állandó
|
||||
átirányítást kell beállítania. Ha portálja mind intranetről, mind az internetről elérhető, a nyilvános címet itt adja meg, a DNS-t pedig úgy állítsa be, hogy az intranetről a nyilvános cím is elérhető legyen. Ha a cím hibás, módosítsa böngészőjében a webcímet, hogy a telepítés egy másik értékkel induljon újra.';
|
||||
$string['pathsunsecuredataroot'] = 'Az adatok gyökérkönyvtára nem biztonságos.';
|
||||
$string['pathswrongadmindir'] = 'Nem létezik az admin könyvtár.';
|
||||
$string['phpextension'] = '{$a} PHP-bővítmény';
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['language'] = 'Bahasa';
|
||||
$string['moodlelogo'] = 'Logo Moodle';
|
||||
$string['next'] = 'Selanjutnya';
|
||||
$string['previous'] = 'Sebelumnya';
|
||||
$string['reload'] = 'Muat ulang';
|
||||
|
||||
@@ -36,8 +36,8 @@ $string['cannotcreatetempdir'] = 'Não é possível criar a pasta de ficheiros t
|
||||
$string['cannotdownloadcomponents'] = 'Não é possível descarregar os componentes.';
|
||||
$string['cannotdownloadzipfile'] = 'Não é possível descarregar o ficheiro ZIP.';
|
||||
$string['cannotfindcomponent'] = 'Não é possível encontrar o componente';
|
||||
$string['cannotsavemd5file'] = 'Não é possível gravar o ficheiro md5';
|
||||
$string['cannotsavezipfile'] = 'Não é possível gravar o ficheiro ZIP';
|
||||
$string['cannotsavemd5file'] = 'Não é possível guardar o ficheiro md5';
|
||||
$string['cannotsavezipfile'] = 'Não é possível guardar o ficheiro ZIP';
|
||||
$string['cannotunzipfile'] = 'Não é possível descompactar o ficheiro ZIP';
|
||||
$string['componentisuptodate'] = 'O componente está atualizado.';
|
||||
$string['dmlexceptiononinstall'] = '<p>Ocorreu um erro de base de dados [{$a->errorcode}].<br />{$a->debuginfo}</p>';
|
||||
|
||||
@@ -34,15 +34,18 @@ $string['admindirname'] = 'Adresár pre správu (admin)';
|
||||
$string['availablelangs'] = 'Dostupné jazykové balíčky';
|
||||
$string['chooselanguagehead'] = 'Vyberte jazyk';
|
||||
$string['chooselanguagesub'] = 'Zvoľte si jazyk pre inštaláciu. Tento jazyk bude tiež použitý ako východzí jazyk portálu, ale môže byť neskôr zmenený.';
|
||||
$string['clialreadyinstalled'] = 'Súbor config.php už existuje. Použite admin/cli/upgrade.php ak chcete aktualizovať váš portál.';
|
||||
$string['clialreadyconfigured'] = 'Konfiguračný súbor config.php už existuje. Spusťte admin/cli/install_database.php, ak chcete inštalovať Moodle pre tento portál.';
|
||||
$string['clialreadyinstalled'] = 'Súbor config.php už existuje. Použite admin/cli/install_database.php ak chcete aktualizovať váš portál.';
|
||||
$string['cliinstallheader'] = 'Moodle {$a} inštalačný program z príkazového riadku';
|
||||
$string['databasehost'] = 'Databázový server';
|
||||
$string['databasename'] = 'Názov databázy';
|
||||
$string['databasetypehead'] = 'Vyberte ovládač pre databázu';
|
||||
$string['dataroot'] = 'Adresár pre údaje';
|
||||
$string['datarootpermission'] = 'Prístupové práva k údajovému adresáru';
|
||||
$string['dbprefix'] = 'Predpona tabuliek';
|
||||
$string['dirroot'] = 'Adresár Moodle';
|
||||
$string['environmenthead'] = 'Kontrola programového prostredia...';
|
||||
$string['environmentsub2'] = 'Každé vydanie Moodle vyžaduje určitú minimálnu verziu PHP a niekoľko povinných rozšírení PHP. Plná kontrola prostredia sa vykonáva pred každou inštaláciou a aktualizáciou. Prosím, kontaktujte správcu servera, ak neviete, ako nainštalovať novú verziu, alebo povoliť rozšírenia PHP.';
|
||||
$string['errorsinenvironment'] = 'Kontrola programového prostredia zlyhala!';
|
||||
$string['installation'] = 'Inštalácia';
|
||||
$string['langdownloaderror'] = 'Bohužiaľ, jazyk "{$a}" sa nepodarilo nainštalovať. Inštalácia bude pokračovať v angličtine.';
|
||||
@@ -62,8 +65,9 @@ $string['pathserrcreatedataroot'] = 'Inštalátor nemôže vytvoriť dátový ad
|
||||
$string['pathshead'] = 'Vytvoriť cesty';
|
||||
$string['pathsrodataroot'] = 'Kmeňový adresár nie je zapisovateľný';
|
||||
$string['pathsroparentdataroot'] = 'Nadriadený adresár ({$a->parent}) nie je zapisovateľný. Inštalátor nemôže vytvoriť dátový adresár ({$a->dataroot}). ';
|
||||
$string['pathssubdataroot'] = 'Potrebujete adresár, kam Moodle bude ukladať nahrané súbory. Adresár by mal povoliť čítanie a zapisovanie údajov používateľom web serveru, ale nesmie byť prístupný priamo cez web rozhranie. Pokiaľ ešte neexistuje, inštalácia sa pokúsi o jeho vytvorenie. ';
|
||||
$string['pathssubdirroot'] = 'Plná cesta adresára moodle inštalácie';
|
||||
$string['pathssubdataroot'] = '<p>Potrebujete adresár, kam Moodle bude ukladať nahrané súbory.</p>
|
||||
<p>Adresár by mal povoliť čítanie a zapisovanie údajov používateľom web serveru (webový server býva väčšinou spustený pod používateľom "www-data" alebo "apache"), ale nesmie byť prístupný priamo cez web rozhranie. Pokiaľ ešte neexistuje, inštalácia sa pokúsi o jeho vytvorenie. </p>';
|
||||
$string['pathssubdirroot'] = '<p>Absolútna cesta adresára Moodle inštalácie.</p>';
|
||||
$string['pathsunsecuredataroot'] = 'Umiestnenie dátového adresára nie je bezpečné';
|
||||
$string['pathswrongadmindir'] = 'Administrátorský adresár neexistuje';
|
||||
$string['phpextension'] = 'Rozšírenie PHP {$a}';
|
||||
@@ -73,7 +77,7 @@ $string['phpversionhelp'] = '<p>Moodle si vyžaduje verziu PHP aspoň 4.3.0 ale
|
||||
<p>Musíte aktualizovať PHP alebo sa presunúť na hostiteľský počítač s novšou verziou PHP!<br />(V prípade 5.0.X môžete tiež prejsť na verziu 4.4.x)</p>';
|
||||
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
|
||||
$string['welcomep20'] = 'Podarilo so vám úspešne nainštalovať a spustiť balíček <strong>{$a->packname} {$a->packversion}</strong>. Gratulujeme!';
|
||||
$string['welcomep30'] = '<strong>{$a->installername}</strong> obsahuje aplikáciu k vytvoreniu prostredia, v ktorom bude prevádzkovaný váš <strong>Moodle</strong>. Menovite sa jedná o:';
|
||||
$string['welcomep30'] = 'Táto verzia <strong>{$a->installername}</strong> obsahuje aplikáciu k vytvoreniu prostredia, v ktorom bude prevádzkovaný váš <strong>Moodle</strong>. Menovite sa jedná o:';
|
||||
$string['welcomep40'] = 'Balíček tiež obsahuje <strong>Moodle vo verzii {$a->moodlerelease} ({$a->moodleversion})</strong>.';
|
||||
$string['welcomep50'] = 'Použitie všetkých aplikácií v tomto balíčku je viazané ich príslušnými licenciami. Kompletný balíček <strong>{$a->installername}</strong> je software s <a href="http://www.opensource.org/docs/definition_plain.html"> otvoreným kódom (open source)</a> a je šírený pod licenciou <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>.';
|
||||
$string['welcomep60'] = 'Nasledujúce stránky vás povedú v nekoľkých jednoduchých krokoch nastavením <strong>Moodle</strong> na vašom počítači. Môžete prijať východzie nastavenie, alebo si ich upraviť podľa svojich potrieb.';
|
||||
|
||||
@@ -42,3 +42,4 @@ $string['cliunknowoption'] = 'Unrecognised ఎంపికలు:
|
||||
$string['cliyesnoprompt'] = 'y (అంటే ఔనని) గానీ, n (అంటే వద్దని) గానీ నొక్కండి';
|
||||
$string['environmentrequireinstall'] = 'స్థాపించి, చేతనం చెయ్యడం తప్పనిసరి';
|
||||
$string['environmentrequireversion'] = '{$a->needed} వెర్షను తప్పనిసరి. మీరేమో {$a->current} వెర్షను వాడుతున్నారు';
|
||||
$string['upgradekeyset'] = 'అప్గ్రేడ్ కీ (ఇది సెట్ చేయకుండా ఖాళీగా వదలండి)';
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['language'] = 'భాష';
|
||||
$string['moodlelogo'] = 'మూడ్లె లోగో';
|
||||
$string['next'] = 'తరువాతి';
|
||||
$string['previous'] = 'గత';
|
||||
$string['reload'] = 'మళ్ళీ లోడు చెయ్యి';
|
||||
|
||||
@@ -30,5 +30,15 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['clianswerno'] = 'n';
|
||||
$string['cliansweryes'] = 'y';
|
||||
$string['cliincorrectvalueerror'] = 'Error, maling halagang "{$a->value}" para sa "{$a->option}"';
|
||||
$string['cliincorrectvalueretry'] = 'Maling halaga, ulitin po';
|
||||
$string['clitypevalue'] = 'iteklado ang halaga';
|
||||
$string['clitypevaluedefault'] = 'iteklado ang halaga, pindutin ang Enter para magamit ang default na halaga ({$a})';
|
||||
$string['cliunknowoption'] = 'Di-kilalang opsiyon:
|
||||
{$a}
|
||||
Gamit po ang --help na opsiyon';
|
||||
$string['cliyesnoprompt'] = 'iteklado ang y (ibig sabihin ay yes/oo) o n (ibig sabihin ay no/hindi)';
|
||||
$string['environmentrequireinstall'] = 'ay kinakailangang maluklok/mabuhay';
|
||||
$string['environmentrequireversion'] = 'ang bersiyon {$a->needed} ay kinakailangan at ang pinatatakbo mo ay {$a->current}';
|
||||
|
||||
@@ -31,5 +31,7 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['language'] = 'Wikà';
|
||||
$string['moodlelogo'] = 'Logo ng Moodle';
|
||||
$string['next'] = 'Susunod';
|
||||
$string['previous'] = 'Nakaraan';
|
||||
$string['reload'] = 'I-reload';
|
||||
|
||||
@@ -25,29 +25,37 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once("{$CFG->libdir}/filelib.php");
|
||||
require_once("{$CFG->dirroot}/iplookup/lib.php");
|
||||
|
||||
|
||||
/**
|
||||
* GeoIp data file parsing test.
|
||||
*/
|
||||
class core_iplookup_geoip_testcase extends advanced_testcase {
|
||||
|
||||
public function setUp() {
|
||||
global $CFG;
|
||||
require_once("$CFG->libdir/filelib.php");
|
||||
require_once("$CFG->dirroot/iplookup/lib.php");
|
||||
|
||||
if (!PHPUNIT_LONGTEST) {
|
||||
// this may take a long time
|
||||
// These tests are intensive and required downloads.
|
||||
$this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
|
||||
}
|
||||
|
||||
$this->resetAfterTest();
|
||||
}
|
||||
|
||||
// let's store the file somewhere
|
||||
|
||||
/**
|
||||
* Setup the GeoIP2File system.
|
||||
*/
|
||||
public function setup_geoip2file() {
|
||||
global $CFG;
|
||||
|
||||
// Store the file somewhere where it won't be wiped out..
|
||||
$gzfile = "$CFG->dataroot/phpunit/geoip/GeoLiteCity.dat.gz";
|
||||
check_dir_exists(dirname($gzfile));
|
||||
if (file_exists($gzfile) and (filemtime($gzfile) < time() - 60*60*24*30)) {
|
||||
// delete file if older than 1 month
|
||||
// Delete file if older than 1 month.
|
||||
unlink($gzfile);
|
||||
}
|
||||
|
||||
@@ -72,17 +80,40 @@ class core_iplookup_geoip_testcase extends advanced_testcase {
|
||||
$CFG->geoipfile = $geoipfile;
|
||||
}
|
||||
|
||||
public function test_ipv4() {
|
||||
/**
|
||||
* Test the format of data returned in the iplookup_find_location function.
|
||||
*
|
||||
* @dataProvider ip_provider
|
||||
* @param string $ip The IP to test
|
||||
*/
|
||||
public function test_ip($ip) {
|
||||
$this->setup_geoip2file();
|
||||
|
||||
$result = iplookup_find_location('192.30.255.112');
|
||||
// Note: The results we get from the iplookup tests are beyond our control.
|
||||
// We used to check a specific IP to a known location, but these have become less reliable and change too
|
||||
// frequently to be used for testing.
|
||||
|
||||
$this->assertEquals('array', gettype($result));
|
||||
$this->assertEquals('San Francisco', $result['city']);
|
||||
$this->assertEquals(-122.3933, $result['longitude'], 'Coordinates are out of accepted tolerance', 0.01);
|
||||
$this->assertEquals(37.7697, $result['latitude'], 'Coordinates are out of accepted tolerance', 0.01);
|
||||
$result = iplookup_find_location($ip);
|
||||
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertInternalType('float', $result['latitude']);
|
||||
$this->assertInternalType('float', $result['longitude']);
|
||||
$this->assertInternalType('string', $result['city']);
|
||||
$this->assertInternalType('string', $result['country']);
|
||||
$this->assertInternalType('array', $result['title']);
|
||||
$this->assertInternalType('string', $result['title'][0]);
|
||||
$this->assertInternalType('string', $result['title'][1]);
|
||||
$this->assertNull($result['error']);
|
||||
$this->assertEquals('array', gettype($result['title']));
|
||||
$this->assertEquals('San Francisco', $result['title'][0]);
|
||||
$this->assertEquals('United States', $result['title'][1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for IP lookup test.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function ip_provider() {
|
||||
return [
|
||||
'IPv4: github.com' => ['192.30.255.112'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -63,6 +63,11 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str'
|
||||
|
||||
// Set cell 'time modified' to 'now' when any of the element is updated in this row.
|
||||
$('body').on('updated', '[data-inplaceeditable]', function(e) {
|
||||
str.get_string('selecttag', 'core_tag', e.ajaxreturn.value)
|
||||
.then(function(s) {
|
||||
return $('label[for="tagselect' + e.ajaxreturn.itemid + '"]').html(s);
|
||||
})
|
||||
.fail(notification.exception);
|
||||
str.get_string('now').done(function(s) {
|
||||
$(e.target).closest('tr').find('td.col-timemodified').html(s);
|
||||
});
|
||||
|
||||
@@ -2962,6 +2962,10 @@ function require_course_login($courseorid, $autologinguest = true, $cm = null, $
|
||||
// Always login for hidden activities.
|
||||
require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
|
||||
|
||||
} else if (isloggedin() && !isguestuser()) {
|
||||
// User is already logged in. Make sure the login is complete (user is fully setup, policies agreed).
|
||||
require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
|
||||
|
||||
} else if ($issite) {
|
||||
// Login for SITE not required.
|
||||
// We still need to instatiate PAGE vars properly so that things that rely on it like navigation function correctly.
|
||||
|
||||
@@ -117,9 +117,9 @@ class portfolio_exporter {
|
||||
* @param portfolio_caller_base $caller portfolio caller (passed by reference)
|
||||
* @param string $callercomponent the name of the callercomponent
|
||||
*/
|
||||
public function __construct(&$instance, &$caller, $callercomponent) {
|
||||
$this->instance =& $instance;
|
||||
$this->caller =& $caller;
|
||||
public function __construct($instance, portfolio_caller_base $caller, $callercomponent) {
|
||||
$this->instance = $instance;
|
||||
$this->caller = $caller;
|
||||
if ($instance) {
|
||||
$this->instancefile = 'portfolio/' . $instance->get('plugin') . '/lib.php';
|
||||
$this->instance->set('exporter', $this);
|
||||
|
||||
@@ -1357,8 +1357,11 @@ function portfolio_include_callback_file($component, $class = null) {
|
||||
throw new portfolio_button_exception('nocallbackfile', 'portfolio', '', $component);
|
||||
}
|
||||
|
||||
if (!is_null($class) && !class_exists($class)) {
|
||||
throw new portfolio_button_exception('nocallbackclass', 'portfolio', '', $class);
|
||||
if (!is_null($class)) {
|
||||
// If class is specified, check it exists and extends portfolio_caller_base.
|
||||
if (!class_exists($class) || !is_subclass_of($class, 'portfolio_caller_base')) {
|
||||
throw new portfolio_button_exception('nocallbackclass', 'portfolio', '', $class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,21 @@ function profiling_is_saved($value = null) {
|
||||
return $saved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether PHP profiling is available.
|
||||
*
|
||||
* This check ensures that one of the available PHP Profiling extensions is available.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function profiling_available() {
|
||||
$hasextension = extension_loaded('tideways_xhprof');
|
||||
$hasextension = $hasextension || extension_loaded('tideways');
|
||||
$hasextension = $hasextension || extension_loaded('xhprof');
|
||||
|
||||
return $hasextension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start profiling observing all the configuration
|
||||
*/
|
||||
@@ -69,7 +84,7 @@ function profiling_start() {
|
||||
global $CFG, $SESSION, $SCRIPT;
|
||||
|
||||
// If profiling isn't available, nothing to start
|
||||
if (!extension_loaded('xhprof') && !extension_loaded('tideways')) {
|
||||
if (!profiling_available()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -146,7 +161,9 @@ function profiling_start() {
|
||||
|
||||
// Arrived here, the script is going to be profiled, let's do it
|
||||
$ignore = array('call_user_func', 'call_user_func_array');
|
||||
if (extension_loaded('tideways')) {
|
||||
if (extension_loaded('tideways_xhprof')) {
|
||||
tideways_xhprof_enable(TIDEWAYS_XHPROF_FLAGS_CPU + TIDEWAYS_XHPROF_FLAGS_MEMORY);
|
||||
} else if (extension_loaded('tideways')) {
|
||||
tideways_enable(TIDEWAYS_FLAGS_CPU + TIDEWAYS_FLAGS_MEMORY, array('ignored_functions' => $ignore));
|
||||
} else {
|
||||
xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY, array('ignored_functions' => $ignore));
|
||||
@@ -164,7 +181,7 @@ function profiling_stop() {
|
||||
global $CFG, $DB, $SCRIPT;
|
||||
|
||||
// If profiling isn't available, nothing to stop
|
||||
if (!extension_loaded('xhprof') && !extension_loaded('tideways')) {
|
||||
if (!profiling_available()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -183,7 +200,9 @@ function profiling_stop() {
|
||||
|
||||
// Arrived here, profiling is running, stop and save everything
|
||||
profiling_is_running(false);
|
||||
if (extension_loaded('tideways')) {
|
||||
if (extension_loaded('tideways_xhprof')) {
|
||||
$data = tideways_xhprof_disable();
|
||||
} else if (extension_loaded('tideways')) {
|
||||
$data = tideways_disable();
|
||||
} else {
|
||||
$data = xhprof_disable();
|
||||
|
||||
@@ -8225,6 +8225,7 @@ class assign_portfolio_caller extends portfolio_module_caller_base {
|
||||
* @throws portfolio_caller_exception
|
||||
*/
|
||||
public function load_data() {
|
||||
global $DB;
|
||||
|
||||
$context = context_module::instance($this->cmid);
|
||||
|
||||
@@ -8233,6 +8234,23 @@ class assign_portfolio_caller extends portfolio_module_caller_base {
|
||||
throw new portfolio_caller_exception('invalidfileandsubmissionid', 'mod_assign');
|
||||
}
|
||||
|
||||
$submission = $DB->get_record('assign_submission', array('id' => $this->sid));
|
||||
} else {
|
||||
$submissionid = $DB->get_field('files', 'itemid', array('id' => $this->fileid, 'contextid' => $context->id));
|
||||
if ($submissionid) {
|
||||
$submission = $DB->get_record('assign_submission', array('id' => $submissionid));
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($submission)) {
|
||||
throw new portfolio_caller_exception('filenotfound');
|
||||
} else if ($submission->userid == 0) {
|
||||
// This must be a group submission.
|
||||
if (!groups_is_member($submission->groupid, $this->user->id)) {
|
||||
throw new portfolio_caller_exception('filenotfound');
|
||||
}
|
||||
} else if ($this->user->id != $submission->userid) {
|
||||
throw new portfolio_caller_exception('filenotfound');
|
||||
}
|
||||
|
||||
// Export either an area of files or a single file (see function for more detail).
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Tests behaviour of the assign_portfolio_caller class.
|
||||
*
|
||||
* @package mod_assign
|
||||
* @category test
|
||||
* @copyright Brendan Cox <brendan.cox@totaralearning.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/assign/locallib.php');
|
||||
require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
|
||||
require_once($CFG->dirroot . '/group/lib.php');
|
||||
|
||||
/**
|
||||
* Class mod_assign_portfolio_caller_testcase
|
||||
*
|
||||
* Tests behaviour of the assign_portfolio_caller class.
|
||||
*
|
||||
* @copyright Brendan Cox <brendan.cox@totaralearning.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_assign_portfolio_caller_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test an assignment file is loaded for a user who submitted it.
|
||||
*/
|
||||
public function test_user_submission_file_is_loaded() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
/* @var mod_assign_generator $assigngenerator */
|
||||
$assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
|
||||
|
||||
$activityrecord = $assigngenerator->create_instance(array('course' => $course->id));
|
||||
$cm = get_coursemodule_from_instance('assign', $activityrecord->id);
|
||||
$context = context_module::instance($cm->id);
|
||||
$assign = new testable_assign($context, $cm, $course);
|
||||
|
||||
$submission = $assign->get_user_submission($user->id, true);
|
||||
|
||||
$fs = get_file_storage();
|
||||
$dummy = (object) array(
|
||||
'contextid' => $context->id,
|
||||
'component' => 'assignsubmission_file',
|
||||
'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA,
|
||||
'itemid' => $submission->id,
|
||||
'filepath' => '/',
|
||||
'filename' => 'myassignmnent.pdf'
|
||||
);
|
||||
$file = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename);
|
||||
|
||||
$caller = new assign_portfolio_caller(array('cmid' => $cm->id, 'fileid' => $file->get_id()));
|
||||
$caller->set('user', $user);
|
||||
$caller->load_data();
|
||||
$this->assertEquals($file->get_contenthash(), $caller->get_sha1_file());
|
||||
|
||||
// This processes the file either by fileid or by other fields in the file table.
|
||||
// We should get the same outcome with either approach.
|
||||
$caller = new assign_portfolio_caller(
|
||||
array(
|
||||
'cmid' => $cm->id,
|
||||
'sid' => $submission->id,
|
||||
'area' => ASSIGNSUBMISSION_FILE_FILEAREA,
|
||||
'component' => 'assignsubmission_file',
|
||||
)
|
||||
);
|
||||
$caller->set('user', $user);
|
||||
$caller->load_data();
|
||||
$this->assertEquals($file->get_contenthash(), $caller->get_sha1_file());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an assignment file is not loaded for a user that did not submit it.
|
||||
*/
|
||||
public function test_different_user_submission_file_is_not_loaded() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
/* @var mod_assign_generator $assigngenerator */
|
||||
$assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
|
||||
|
||||
$activityrecord = $assigngenerator->create_instance(array('course' => $course->id));
|
||||
$cm = get_coursemodule_from_instance('assign', $activityrecord->id);
|
||||
$context = context_module::instance($cm->id);
|
||||
$assign = new testable_assign($context, $cm, $course);
|
||||
|
||||
$submission = $assign->get_user_submission($user->id, true);
|
||||
|
||||
$fs = get_file_storage();
|
||||
$dummy = (object) array(
|
||||
'contextid' => $context->id,
|
||||
'component' => 'assignsubmission_file',
|
||||
'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA,
|
||||
'itemid' => $submission->id,
|
||||
'filepath' => '/',
|
||||
'filename' => 'myassignmnent.pdf'
|
||||
);
|
||||
$file = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename);
|
||||
|
||||
// Now add second user.
|
||||
$wronguser = $this->getDataGenerator()->create_user();
|
||||
|
||||
$caller = new assign_portfolio_caller(array('cmid' => $cm->id, 'fileid' => $file->get_id()));
|
||||
$caller->set('user', $wronguser);
|
||||
|
||||
$this->setExpectedException('portfolio_caller_exception', 'Sorry, the requested file could not be found');
|
||||
|
||||
$caller->load_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an assignment file is loaded for a user who is part of a group that submitted it.
|
||||
*/
|
||||
public function test_group_submission_file_is_loaded() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
$groupdata = new stdClass();
|
||||
$groupdata->courseid = $course->id;
|
||||
$groupdata->name = 'group1';
|
||||
$groupid = groups_create_group($groupdata);
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id);
|
||||
groups_add_member($groupid, $user);
|
||||
|
||||
/* @var mod_assign_generator $assigngenerator */
|
||||
$assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
|
||||
|
||||
$activityrecord = $assigngenerator->create_instance(array('course' => $course->id));
|
||||
$cm = get_coursemodule_from_instance('assign', $activityrecord->id);
|
||||
$context = context_module::instance($cm->id);
|
||||
$assign = new testable_assign($context, $cm, $course);
|
||||
|
||||
$submission = $assign->get_group_submission($user->id, $groupid, true);
|
||||
|
||||
$fs = get_file_storage();
|
||||
$dummy = (object) array(
|
||||
'contextid' => $context->id,
|
||||
'component' => 'assignsubmission_file',
|
||||
'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA,
|
||||
'itemid' => $submission->id,
|
||||
'filepath' => '/',
|
||||
'filename' => 'myassignmnent.pdf'
|
||||
);
|
||||
$file = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename);
|
||||
|
||||
$caller = new assign_portfolio_caller(array('cmid' => $cm->id, 'fileid' => $file->get_id()));
|
||||
$caller->set('user', $user);
|
||||
$caller->load_data();
|
||||
$this->assertEquals($file->get_contenthash(), $caller->get_sha1_file());
|
||||
|
||||
// This processes the file either by fileid or by other fields in the file table.
|
||||
// We should get the same outcome with either approach.
|
||||
$caller = new assign_portfolio_caller(
|
||||
array(
|
||||
'cmid' => $cm->id,
|
||||
'sid' => $submission->id,
|
||||
'area' => ASSIGNSUBMISSION_FILE_FILEAREA,
|
||||
'component' => 'assignsubmission_file',
|
||||
)
|
||||
);
|
||||
$caller->set('user', $user);
|
||||
$caller->load_data();
|
||||
$this->assertEquals($file->get_contenthash(), $caller->get_sha1_file());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an assignment file is not loaded for a user who is not part of a group that submitted it.
|
||||
*/
|
||||
public function test_different_group_submission_file_is_not_loaded() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
$groupdata = new stdClass();
|
||||
$groupdata->courseid = $course->id;
|
||||
$groupdata->name = 'group1';
|
||||
$groupid = groups_create_group($groupdata);
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id);
|
||||
groups_add_member($groupid, $user);
|
||||
|
||||
/* @var mod_assign_generator $assigngenerator */
|
||||
$assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
|
||||
|
||||
$activityrecord = $assigngenerator->create_instance(array('course' => $course->id));
|
||||
$cm = get_coursemodule_from_instance('assign', $activityrecord->id);
|
||||
$context = context_module::instance($cm->id);
|
||||
$assign = new testable_assign($context, $cm, $course);
|
||||
|
||||
$submission = $assign->get_group_submission($user->id, $groupid,true);
|
||||
|
||||
$fs = get_file_storage();
|
||||
$dummy = (object) array(
|
||||
'contextid' => $context->id,
|
||||
'component' => 'assignsubmission_file',
|
||||
'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA,
|
||||
'itemid' => $submission->id,
|
||||
'filepath' => '/',
|
||||
'filename' => 'myassignmnent.pdf'
|
||||
);
|
||||
$file = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename);
|
||||
|
||||
// Now add second user.
|
||||
$wronguser = $this->getDataGenerator()->create_user();
|
||||
|
||||
// Create a new group for the wrong user.
|
||||
$groupdata = new stdClass();
|
||||
$groupdata->courseid = $course->id;
|
||||
$groupdata->name = 'group2';
|
||||
$groupid = groups_create_group($groupdata);
|
||||
$this->getDataGenerator()->enrol_user($wronguser->id, $course->id);
|
||||
groups_add_member($groupid, $wronguser);
|
||||
|
||||
// In the negative test for the user, we loaded the caller via fileid. Switching to the other approach this time.
|
||||
$caller = new assign_portfolio_caller(
|
||||
array(
|
||||
'cmid' => $cm->id,
|
||||
'sid' => $submission->id,
|
||||
'area' => ASSIGNSUBMISSION_FILE_FILEAREA,
|
||||
'component' => 'assignsubmission_file',
|
||||
)
|
||||
);
|
||||
$caller->set('user', $wronguser);
|
||||
|
||||
$this->setExpectedException('portfolio_caller_exception', 'Sorry, the requested file could not be found');
|
||||
|
||||
$caller->load_data();
|
||||
}
|
||||
}
|
||||
@@ -95,6 +95,12 @@ class forum_portfolio_caller extends portfolio_module_caller_base {
|
||||
$fs = get_file_storage();
|
||||
if ($this->post) {
|
||||
if ($this->attachment) {
|
||||
// Make sure the requested file belongs to this post.
|
||||
$file = $fs->get_file_by_id($this->attachment);
|
||||
if ($file->get_contextid() != $this->modcontext->id
|
||||
|| $file->get_itemid() != $this->post->id) {
|
||||
throw new portfolio_caller_exception('filenotfound');
|
||||
}
|
||||
$this->set_file_and_format_data($this->attachment);
|
||||
} else {
|
||||
$attach = $fs->get_area_files($this->modcontext->id, 'mod_forum', 'attachment', $this->post->id, 'timemodified', false);
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The portfolio forum tests.
|
||||
*
|
||||
* @package mod_forum
|
||||
* @copyright 2018 onwards Totara Learning Solutions LTD {@link http://www.totaralms.com/}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Brendan Cox <brendan.cox@totaralearning.com>
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class mod_forum_portfolio_caller_testcase
|
||||
*
|
||||
* Tests behaviour of the forum_portfolio_caller class.
|
||||
*/
|
||||
class mod_forum_portfolio_caller_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Ensure that a file will be loaded in an instance of the caller when supplied valid and
|
||||
* accessible post and attachment file ids.
|
||||
*/
|
||||
public function test_file_in_user_post_is_loaded() {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/forum/locallib.php');
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
|
||||
$context = context_module::instance($forum->cmid);
|
||||
|
||||
/* @var mod_forum_generator $forumgenerator */
|
||||
$forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
|
||||
$discussion = $forumgenerator->create_discussion(
|
||||
array(
|
||||
'course' => $course->id,
|
||||
'forum' => $forum->id,
|
||||
'userid' => $user->id,
|
||||
'attachment' => 1
|
||||
)
|
||||
);
|
||||
|
||||
$fs = get_file_storage();
|
||||
$dummy = (object) array(
|
||||
'contextid' => $context->id,
|
||||
'component' => 'mod_forum',
|
||||
'filearea' => 'attachment',
|
||||
'itemid' => $discussion->firstpost,
|
||||
'filepath' => '/',
|
||||
'filename' => 'myassignmnent.pdf'
|
||||
);
|
||||
$firstpostfile = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename);
|
||||
|
||||
$caller = new forum_portfolio_caller(array(
|
||||
'postid' => $discussion->firstpost,
|
||||
'attachment' => $firstpostfile->get_id()
|
||||
));
|
||||
|
||||
$caller->load_data();
|
||||
$this->assertEquals($caller->get_sha1_file(), $firstpostfile->get_contenthash());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that files will not be loaded if the supplied attachment id is for a file that is not attached to
|
||||
* the supplied post id.
|
||||
*/
|
||||
public function test_file_not_in_user_post_not_loaded() {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/forum/locallib.php');
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
|
||||
$context = context_module::instance($forum->cmid);
|
||||
|
||||
/* @var mod_forum_generator $forumgenerator */
|
||||
$forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
|
||||
$discussion = $forumgenerator->create_discussion(
|
||||
array(
|
||||
'course' => $course->id,
|
||||
'forum' => $forum->id,
|
||||
'userid' => $user->id,
|
||||
'attachment' => 1
|
||||
)
|
||||
);
|
||||
|
||||
$fs = get_file_storage();
|
||||
$dummyone = (object) array(
|
||||
'contextid' => $context->id,
|
||||
'component' => 'mod_forum',
|
||||
'filearea' => 'attachment',
|
||||
'itemid' => $discussion->firstpost,
|
||||
'filepath' => '/',
|
||||
'filename' => 'myassignmnent.pdf'
|
||||
);
|
||||
$firstpostfile = $fs->create_file_from_string($dummyone, 'Content of ' . $dummyone->filename);
|
||||
|
||||
// Create a second post and add a file there.
|
||||
$secondpost = $forumgenerator->create_post(
|
||||
array(
|
||||
'discussion' => $discussion->id,
|
||||
'userid' => $user->id,
|
||||
'attachment' => 1
|
||||
)
|
||||
);
|
||||
$dummytwo = (object) array(
|
||||
'contextid' => $context->id,
|
||||
'component' => 'mod_forum',
|
||||
'filearea' => 'attachment',
|
||||
'itemid' => $secondpost->id,
|
||||
'filepath' => '/',
|
||||
'filename' => 'myotherthing.pdf'
|
||||
);
|
||||
$secondpostfile = $fs->create_file_from_string($dummytwo, 'Content of ' . $dummytwo->filename);
|
||||
|
||||
$caller = new forum_portfolio_caller(array(
|
||||
'postid' => $discussion->firstpost,
|
||||
'attachment' => $secondpostfile->get_id()
|
||||
));
|
||||
|
||||
$this->setExpectedException('portfolio_caller_exception', 'Sorry, the requested file could not be found');
|
||||
$caller->load_data();
|
||||
}
|
||||
}
|
||||
@@ -726,8 +726,8 @@ class qformat_default {
|
||||
// @@PLUGINFILE@@ with a real URL, but it doesn't matter what.
|
||||
// We use http://example.com/.
|
||||
$text = str_replace('@@PLUGINFILE@@/', 'http://example.com/', $question->questiontext);
|
||||
return html_to_text(format_text($text,
|
||||
$question->questiontextformat, $formatoptions), 0, false);
|
||||
return s(html_to_text(format_text($text,
|
||||
$question->questiontextformat, $formatoptions), 0, false));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Generated
+2075
-726
File diff suppressed because it is too large
Load Diff
@@ -11,5 +11,8 @@
|
||||
"grunt-contrib-uglify": "0.11.0",
|
||||
"grunt-contrib-watch": "0.6.1",
|
||||
"shifter": "0.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9 <9"
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -938,8 +938,8 @@ class qformat_default {
|
||||
* during import to let the user see roughly what is going on.
|
||||
*/
|
||||
protected function format_question_text($question) {
|
||||
return question_utils::to_plain_text($question->questiontext,
|
||||
$question->questiontextformat);
|
||||
return s(question_utils::to_plain_text($question->questiontext,
|
||||
$question->questiontextformat));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -313,7 +313,7 @@ class question_dataset_dependent_items_form extends question_wizard_form {
|
||||
// Decode equations in question text.
|
||||
$qtext = $this->qtypeobj->substitute_variables(
|
||||
$this->question->questiontext, $data);
|
||||
$textequations = $this->qtypeobj->find_math_equations($qtext);
|
||||
$textequations = $this->qtypeobj->find_formulas($qtext);
|
||||
if ($textequations != '' && count($textequations) > 0 ) {
|
||||
$mform->addElement('static', "divider1[{$j}]", '',
|
||||
'Formulas {=..} in question text');
|
||||
|
||||
@@ -58,10 +58,8 @@ class qtype_calculated_edit_form extends qtype_numerical_edit_form {
|
||||
if (isset($this->question->id)) {
|
||||
// Remove prefix #{..}# if exists.
|
||||
$this->initialname = $question->name;
|
||||
$regs= array();
|
||||
if (preg_match('~#\{([^[:space:]]*)#~', $question->name , $regs)) {
|
||||
$question->name = str_replace($regs[0], '', $question->name);
|
||||
};
|
||||
$question->name = question_bank::get_qtype($this->qtype())
|
||||
->clean_technical_prefix_from_question_name($question->name);
|
||||
}
|
||||
}
|
||||
parent::__construct($submiturl, $question, $category, $contexts, $formeditable);
|
||||
|
||||
@@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/question/type/questionbase.php');
|
||||
require_once($CFG->dirroot . '/question/type/numerical/question.php');
|
||||
require_once($CFG->dirroot . '/question/type/calculated/questiontype.php');
|
||||
|
||||
/**
|
||||
* Represents a calculated question.
|
||||
@@ -423,7 +424,13 @@ class qtype_calculated_variable_substituter {
|
||||
if ($error = qtype_calculated_find_formula_errors($expression)) {
|
||||
throw new moodle_exception('illegalformulasyntax', 'qtype_calculated', '', $error);
|
||||
}
|
||||
return $this->calculate_raw($this->substitute_values_for_eval($expression));
|
||||
$expression = $this->substitute_values_for_eval($expression);
|
||||
if ($datasets = question_bank::get_qtype('calculated')->find_dataset_names($expression)) {
|
||||
// Some placeholders were not substituted.
|
||||
throw new moodle_exception('illegalformulasyntax', 'qtype_calculated', '',
|
||||
'{' . reset($datasets) . '}');
|
||||
}
|
||||
return $this->calculate_raw($expression);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,8 +38,20 @@ require_once($CFG->dirroot . '/question/type/numerical/question.php');
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qtype_calculated extends question_type {
|
||||
/** Regular expression that finds the formulas in content. */
|
||||
const FORMULAS_IN_TEXT_REGEX = '~\{=([^{}]*(?:\{[^{}]+}[^{}]*)*)\}~';
|
||||
/**
|
||||
* @const string a placeholder is a letter, followed by almost any characters. (This should probably be restricted more.)
|
||||
*/
|
||||
const PLACEHOLDER_REGEX_PART = '[[:alpha:]][^>} <`{"\']*';
|
||||
|
||||
/**
|
||||
* @const string REGEXP for a placeholder, wrapped in its {...} delimiters, with capturing brackets around the name.
|
||||
*/
|
||||
const PLACEHODLER_REGEX = '~\{([[:alpha:]][^>} <`{"\']*)\}~';
|
||||
|
||||
/**
|
||||
* @const string Regular expression that finds the formulas in content, with capturing brackets to get the forumlas.
|
||||
*/
|
||||
const FORMULAS_IN_TEXT_REGEX = '~\{=([^{}]*(?:\{[[:alpha:]][^>} <`{"\']*\}[^{}]*)*)\}~';
|
||||
|
||||
const MAX_DATASET_ITEMS = 100;
|
||||
|
||||
@@ -486,6 +498,15 @@ class qtype_calculated extends question_type {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove prefix #{..}# if exists.
|
||||
* @param $name a question name,
|
||||
* @return string the cleaned up question name.
|
||||
*/
|
||||
public function clean_technical_prefix_from_question_name($name) {
|
||||
return preg_replace('~#\{([^[:space:]]*)#~', '', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method prepare the $datasets in a format similar to dadatesetdefinitions_form.php
|
||||
* so that they can be saved
|
||||
@@ -553,11 +574,7 @@ class qtype_calculated extends question_type {
|
||||
AND a.category != 0
|
||||
AND b.question = ?
|
||||
ORDER BY a.name ", array($question->id));
|
||||
$questionname = $question->name;
|
||||
$regs= array();
|
||||
if (preg_match('~#\{([^[:space:]]*)#~', $questionname , $regs)) {
|
||||
$questionname = str_replace($regs[0], '', $questionname);
|
||||
};
|
||||
$questionname = $this->clean_technical_prefix_from_question_name($question->name);
|
||||
|
||||
if (!empty($categorydatasetdefs)) {
|
||||
// There is at least one with the same name.
|
||||
@@ -1527,15 +1544,28 @@ class qtype_calculated extends question_type {
|
||||
: '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the names of all datasets mentioned in a piece of question content like the question text.
|
||||
* @param $text the text to analyse.
|
||||
* @return array with dataset name for both key and value.
|
||||
*/
|
||||
public function find_dataset_names($text) {
|
||||
// Returns the possible dataset names found in the text as an array.
|
||||
// The array has the dataset name for both key and value.
|
||||
$datasetnames = array();
|
||||
while (preg_match('~\\{([[:alpha:]][^>} <{"\']*)\\}~', $text, $regs)) {
|
||||
$datasetnames[$regs[1]] = $regs[1];
|
||||
$text = str_replace($regs[0], '', $text);
|
||||
}
|
||||
return $datasetnames;
|
||||
preg_match_all(self::PLACEHODLER_REGEX, $text, $matches);
|
||||
return array_combine($matches[1], $matches[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all the formulas in a bit of text.
|
||||
*
|
||||
* For example, called with "What is {a} plus {b}? (Hint, it is not {={a}*{b}}.)" this
|
||||
* returns ['{a}*{b}'].
|
||||
*
|
||||
* @param $text text to analyse.
|
||||
* @return array where they keys an values are the formulas.
|
||||
*/
|
||||
public function find_formulas($text) {
|
||||
preg_match_all(self::FORMULAS_IN_TEXT_REGEX, $text, $matches);
|
||||
return array_combine($matches[1], $matches[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1908,13 +1938,17 @@ function qtype_calculated_calculate_answer($formula, $individualdata,
|
||||
* @return string|boolean false if there are no problems. Otherwise a string error message.
|
||||
*/
|
||||
function qtype_calculated_find_formula_errors($formula) {
|
||||
foreach (['//', '/*', '#', '<?', '?>'] as $commentstart) {
|
||||
if (strpos($formula, $commentstart) !== false) {
|
||||
return get_string('illegalformulasyntax', 'qtype_calculated', $commentstart);
|
||||
}
|
||||
}
|
||||
|
||||
// Validates the formula submitted from the question edit page.
|
||||
// Returns false if everything is alright
|
||||
// otherwise it constructs an error message.
|
||||
// Strip away dataset names.
|
||||
while (preg_match('~\\{[[:alpha:]][^>} <{"\']*\\}~', $formula, $regs)) {
|
||||
$formula = str_replace($regs[0], '1', $formula);
|
||||
}
|
||||
// Strip away dataset names. Use 1.0 to catch illegal concatenation like {a}{b}.
|
||||
$formula = preg_replace(qtype_calculated::PLACEHODLER_REGEX, '1.0', $formula);
|
||||
|
||||
// Strip away empty space and lowercase it.
|
||||
$formula = strtolower(str_replace(' ', '', $formula));
|
||||
@@ -1978,14 +2012,14 @@ function qtype_calculated_find_formula_errors($formula) {
|
||||
return get_string('unsupportedformulafunction', 'qtype_calculated', $regs[2]);
|
||||
}
|
||||
|
||||
// Exchange the function call with '1' and then check for
|
||||
// Exchange the function call with '1.0' and then check for
|
||||
// another function call...
|
||||
if ($regs[1]) {
|
||||
// The function call is proceeded by an operator.
|
||||
$formula = str_replace($regs[0], $regs[1] . '1', $formula);
|
||||
$formula = str_replace($regs[0], $regs[1] . '1.0', $formula);
|
||||
} else {
|
||||
// The function call starts the formula.
|
||||
$formula = preg_replace("~^{$regs[2]}\\([^)]*\\)~", '1', $formula);
|
||||
$formula = preg_replace('~^' . preg_quote($regs[2], '~') . '\([^)]*\)~', '1.0', $formula);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2003,10 +2037,10 @@ function qtype_calculated_find_formula_errors($formula) {
|
||||
* @return string|boolean false if there are no problems. Otherwise a string error message.
|
||||
*/
|
||||
function qtype_calculated_find_formula_errors_in_text($text) {
|
||||
preg_match_all(qtype_calculated::FORMULAS_IN_TEXT_REGEX, $text, $matches);
|
||||
$formulas = question_bank::get_qtype('calculated')->find_formulas($text);
|
||||
|
||||
$errors = array();
|
||||
foreach ($matches[1] as $match) {
|
||||
foreach ($formulas as $match) {
|
||||
$error = qtype_calculated_find_formula_errors($match);
|
||||
if ($error) {
|
||||
$errors[] = $error;
|
||||
|
||||
@@ -46,6 +46,14 @@ class qtype_calculated_formula_validation_testcase extends basic_testcase {
|
||||
$this->assertFalse(qtype_calculated_find_formula_errors('1 + 1'));
|
||||
$this->assertFalse(qtype_calculated_find_formula_errors('{x} + {y}'));
|
||||
$this->assertFalse(qtype_calculated_find_formula_errors('{x}*{y}'));
|
||||
$this->assertFalse(qtype_calculated_find_formula_errors('{x}*({y}+1)'));
|
||||
}
|
||||
|
||||
public function test_simple_equations_errors() {
|
||||
$this->assert_nonempty_string(qtype_calculated_find_formula_errors('{a{b}}'));
|
||||
$this->assert_nonempty_string(qtype_calculated_find_formula_errors('{a{b}}'));
|
||||
$this->assert_nonempty_string(qtype_calculated_find_formula_errors('{a}({b})'));
|
||||
$this->assert_nonempty_string(qtype_calculated_find_formula_errors('2({b})'));
|
||||
}
|
||||
|
||||
public function test_safe_functions_ok() {
|
||||
@@ -59,6 +67,15 @@ class qtype_calculated_formula_validation_testcase extends basic_testcase {
|
||||
$this->assertFalse(qtype_calculated_find_formula_errors('max(1.0, 1.0, 2, 3)'));
|
||||
}
|
||||
|
||||
public function test_php_comments_blocked() {
|
||||
$this->assert_nonempty_string(qtype_calculated_find_formula_errors('# No need for this.'));
|
||||
$this->assert_nonempty_string(qtype_calculated_find_formula_errors('/* Also blocked. */'));
|
||||
$this->assert_nonempty_string(qtype_calculated_find_formula_errors('1 + 1 /* Blocked too. */'));
|
||||
$this->assert_nonempty_string(qtype_calculated_find_formula_errors('// As is this.'));
|
||||
$this->assert_nonempty_string(qtype_calculated_find_formula_errors('1/*2'));
|
||||
$this->assert_nonempty_string(qtype_calculated_find_formula_errors('/*{a*///{x}}'));
|
||||
}
|
||||
|
||||
public function test_dangerous_functions_blocked() {
|
||||
$this->assert_nonempty_string(qtype_calculated_find_formula_errors('eval(1)'));
|
||||
$this->assert_nonempty_string(qtype_calculated_find_formula_errors('system(1)'));
|
||||
|
||||
@@ -106,4 +106,37 @@ class qtype_calculated_test extends advanced_testcase {
|
||||
),
|
||||
), $this->qtype->get_possible_responses($q));
|
||||
}
|
||||
|
||||
public function test_placehodler_regex() {
|
||||
preg_match_all(qtype_calculated::PLACEHODLER_REGEX, '= {={a} + {b}}', $matches);
|
||||
$this->assertEquals([['{a}', '{b}'], ['a', 'b']], $matches);
|
||||
}
|
||||
|
||||
public function test_formulas_in_text_regex() {
|
||||
preg_match_all(qtype_calculated::FORMULAS_IN_TEXT_REGEX, '= {={a} + {b}}', $matches);
|
||||
$this->assertEquals([['{={a} + {b}}'], ['{a} + {b}']], $matches);
|
||||
}
|
||||
|
||||
public function test_find_dataset_names() {
|
||||
$this->assertEquals([], $this->qtype->find_dataset_names('Frog.'));
|
||||
|
||||
$this->assertEquals(['a' => 'a', 'b' => 'b'],
|
||||
$this->qtype->find_dataset_names('= {={a} + {b}}'));
|
||||
|
||||
$this->assertEquals(['a' => 'a', 'b' => 'b'],
|
||||
$this->qtype->find_dataset_names('What is {a} plus {b}? (Hint, it is not {={a}*{b}}.)'));
|
||||
|
||||
$this->assertEquals(['a' => 'a', 'b' => 'b', 'c' => 'c'],
|
||||
$this->qtype->find_dataset_names('
|
||||
<p>If called with $a = {a} and $b = {b}, what does this PHP function return?</p>
|
||||
<pre>
|
||||
/**
|
||||
* What does this do?
|
||||
*/
|
||||
function mystery($a, $b) {
|
||||
return {c}*$a + $b;
|
||||
}
|
||||
</pre>
|
||||
'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,12 @@ class qtype_calculated_variable_substituter_test extends advanced_testcase {
|
||||
$this->assertEquals('= 3', $vs->replace_expressions_in_text('= {={a} + {b}}'));
|
||||
}
|
||||
|
||||
public function test_expression_has_unmapped_placeholder() {
|
||||
$this->setExpectedException('moodle_exception', (get_string('illegalformulasyntax', 'qtype_calculated', '{c}')));
|
||||
$vs = new qtype_calculated_variable_substituter(array('a' => 1, 'b' => 2), '.');
|
||||
$vs->calculate('{c} - {a} + {b}');
|
||||
}
|
||||
|
||||
public function test_replace_expressions_in_text_negative() {
|
||||
$vs = new qtype_calculated_variable_substituter(array('a' => -1, 'b' => 2), '.');
|
||||
$this->assertEquals('temperatures -1 and 2',
|
||||
|
||||
@@ -317,7 +317,7 @@ class qtype_calculatedmulti_qe2_attempt_updater extends question_qtype_attempt_u
|
||||
*/
|
||||
public function replace_expressions_in_text($text, $length = null, $format = null) {
|
||||
$vs = $this; // Can't see to use $this in a PHP closure.
|
||||
$text = preg_replace_callback('~\{=([^{}]*(?:\{[^{}]+}[^{}]*)*)}~',
|
||||
$text = preg_replace_callback(qtype_calculated::FORMULAS_IN_TEXT_REGEX,
|
||||
function ($matches) use ($vs, $format, $length) {
|
||||
return $vs->format_float($vs->calculate($matches[1]), $length, $format);
|
||||
}, $text);
|
||||
|
||||
@@ -54,10 +54,8 @@ class qtype_calculatedmulti_edit_form extends question_edit_form {
|
||||
if (isset($this->question->id)) {
|
||||
// Remove prefix #{..}# if exists.
|
||||
$this->initialname = $question->name;
|
||||
$regs= array();
|
||||
if (preg_match('~#\{([^[:space:]]*)#~', $question->name , $regs)) {
|
||||
$question->name = str_replace($regs[0], '', $question->name);
|
||||
};
|
||||
$question->name = question_bank::get_qtype('calculated')
|
||||
->clean_technical_prefix_from_question_name($question->name);
|
||||
}
|
||||
}
|
||||
parent::__construct($submiturl, $question, $category, $contexts, $formeditable);
|
||||
|
||||
@@ -231,36 +231,27 @@ class qtype_calculatedmulti extends qtype_calculated {
|
||||
|
||||
public function comment_on_datasetitems($qtypeobj, $questionid, $questiontext,
|
||||
$answers, $data, $number) {
|
||||
global $DB;
|
||||
|
||||
$comment = new stdClass();
|
||||
$comment->stranswers = array();
|
||||
$comment->outsidelimit = false;
|
||||
$comment->answers = array();
|
||||
|
||||
$answers = fullclone($answers);
|
||||
$errors = '';
|
||||
$delimiter = ': ';
|
||||
foreach ($answers as $key => $answer) {
|
||||
$anssubstituted = $this->substitute_variables($answer->answer, $data);
|
||||
// Evaluate the equations i.e {=5+4).
|
||||
$anstext = '';
|
||||
$anstextremaining = $anssubstituted;
|
||||
while (preg_match('~\{=([^[:space:]}]*)}~', $anstextremaining, $regs1)) {
|
||||
$anstextsplits = explode($regs1[0], $anstextremaining, 2);
|
||||
$anstext =$anstext.$anstextsplits[0];
|
||||
$anstextremaining = $anstextsplits[1];
|
||||
if (empty($regs1[1])) {
|
||||
$str = '';
|
||||
$anssubstituted = $this->substitute_variables($answer->answer, $data);
|
||||
$formulas = $this->find_formulas($anssubstituted);
|
||||
$replaces = [];
|
||||
foreach ($formulas as $formula) {
|
||||
if ($formulaerrors = qtype_calculated_find_formula_errors($formula)) {
|
||||
$str = $formulaerrors;
|
||||
} else {
|
||||
if ($formulaerrors = qtype_calculated_find_formula_errors($regs1[1])) {
|
||||
$str=$formulaerrors;
|
||||
} else {
|
||||
eval('$str = '.$regs1[1].';');
|
||||
}
|
||||
eval('$str = ' . $formula . ';');
|
||||
}
|
||||
$anstext = $anstext.$str;
|
||||
$replaces[$formula] = $str;
|
||||
}
|
||||
$anstext .= $anstextremaining;
|
||||
$anstext = str_replace(array_keys($replaces), array_values($replaces), $anssubstituted);
|
||||
$comment->stranswers[$key] = $anssubstituted.'<br/>'.$anstext;
|
||||
}
|
||||
return fullclone($comment);
|
||||
|
||||
+2
-2
@@ -29,11 +29,11 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2016052311.00; // 20160523 = branching date YYYYMMDD - do not modify!
|
||||
$version = 2016052313.00; // 20160523 = branching date YYYYMMDD - do not modify!
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
|
||||
$release = '3.1.11 (Build: 20180319)'; // Human-friendly version name
|
||||
$release = '3.1.13 (Build: 20180709)'; // Human-friendly version name
|
||||
|
||||
$branch = '31'; // This version's branch.
|
||||
$maturity = MATURITY_STABLE; // This version's maturity level.
|
||||
|
||||
Reference in New Issue
Block a user