Compare commits
517 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 803ddf9af3 | |||
| 35842d38af | |||
| e29bdea42e | |||
| a866ad40be | |||
| 125cb6df08 | |||
| e88e7ae3ad | |||
| bacb95612e | |||
| 4752706116 | |||
| 07ff315180 | |||
| 4800d01e2e | |||
| e9d3aad54c | |||
| 8b91ec3dda | |||
| 3925a3e287 | |||
| 489e91282b | |||
| a618d35082 | |||
| d8b19527fe | |||
| 7ef5172818 | |||
| 2b46342766 | |||
| a647d27ad7 | |||
| 3c2b67f5d4 | |||
| c812956efd | |||
| 4289728d7c | |||
| ce45e0b188 | |||
| f427d8efd6 | |||
| be17a78d5c | |||
| 7ce72e385d | |||
| 250b70735b | |||
| 9aefa9c8d3 | |||
| 24422c9028 | |||
| 845b9de5c6 | |||
| 691bb2a90f | |||
| 98d1b938a3 | |||
| 13523e51e4 | |||
| 0db87f6d4a | |||
| d91b8d1c6f | |||
| 1b1634740e | |||
| bddb47374d | |||
| 478ab0f602 | |||
| d5fc05e2a0 | |||
| 17a90193bb | |||
| 2f87351d54 | |||
| e1331607a8 | |||
| cc74fd9ea2 | |||
| 44e712e9b7 | |||
| 932694ca59 | |||
| bac38b11ab | |||
| ab96babbb4 | |||
| 324b018bbc | |||
| 3e312a16f4 | |||
| 3aa9d93c7a | |||
| 3b8876f5ef | |||
| 263f78b8b8 | |||
| 04f2e83ce7 | |||
| 25612b59ee | |||
| 84baa6b141 | |||
| 2cb907d11d | |||
| 1dd7fab104 | |||
| 8bf49b7377 | |||
| d3e7b45d27 | |||
| 10b07bf8cc | |||
| 07304c8ee7 | |||
| c2b8c7c8c2 | |||
| 2d10a2cebb | |||
| aba092e128 | |||
| f8bf49f137 | |||
| b6bf71702d | |||
| 5d45f54b6c | |||
| b37e3a818b | |||
| 6eef37f045 | |||
| 82bf77561f | |||
| c268bbc9e6 | |||
| c3d1ced72e | |||
| 78ac8f6045 | |||
| 52184908fa | |||
| cdb1de64e4 | |||
| abacf844f9 | |||
| ac6e453d11 | |||
| 244be92c8e | |||
| f7b5782d5c | |||
| 1d17b54861 | |||
| 3d5e1f113d | |||
| ecc8244e62 | |||
| dd22636bef | |||
| 6a59a3e073 | |||
| 7892e8dd6a | |||
| 6d5a4e733b | |||
| eccad2d4bc | |||
| 9a08527acf | |||
| 0081072ff1 | |||
| 906ecbfd05 | |||
| aa39745d05 | |||
| 7dcabab098 | |||
| eede7b3efe | |||
| 3315c01e0f | |||
| d9e9e79954 | |||
| 5906d379db | |||
| f4ec918f9b | |||
| aa4318a823 | |||
| 917d5e1caa | |||
| b33d633510 | |||
| f9aab389e6 | |||
| df0a353377 | |||
| e92a430405 | |||
| cdb3a6b6c1 | |||
| dad46c5c1f | |||
| e1cc83875f | |||
| c40485aeee | |||
| 99cf374a42 | |||
| 203ef3bd3e | |||
| af4417f0aa | |||
| 37e7a3849b | |||
| fc8984234c | |||
| c00787ae08 | |||
| 2fe3a171c4 | |||
| 451ea7b118 | |||
| a6113f4dd1 | |||
| e8db7737da | |||
| 321bfdbedf | |||
| 86594f9a95 | |||
| d42ba3d0af | |||
| 3803574711 | |||
| 25c57e8cc2 | |||
| d8c1ffd0a0 | |||
| 5776292b94 | |||
| 43163295a5 | |||
| 63f1e3b287 | |||
| 09d83a4038 | |||
| 76b6d2e750 | |||
| f3e6ff8a6c | |||
| 61685bb0df | |||
| c18c38f3f6 | |||
| 3e0fd2ed8e | |||
| 1a67124e67 | |||
| fc9ff77426 | |||
| ff8b2535d7 | |||
| 7047ebb7a3 | |||
| 3217e572cb | |||
| a4e7898729 | |||
| c187aa03c4 | |||
| 2ebfef8f7b | |||
| 784b3b9262 | |||
| 8f089f100c | |||
| 2be10633d8 | |||
| e6d8ca248d | |||
| d61b59175c | |||
| 8afccaf846 | |||
| 3d86260c08 | |||
| a0088bb79b | |||
| f393fbb08b | |||
| 73715b3aa8 | |||
| 61cd8bb3cd | |||
| 19089bc416 | |||
| e6035a8b38 | |||
| edfa06b103 | |||
| deb9221748 | |||
| 09352c5677 | |||
| 779d68eb8a | |||
| 441a8a209b | |||
| ba639bb069 | |||
| e68b0e289d | |||
| 6606760811 | |||
| c43bfca338 | |||
| 1ecb6d4d1b | |||
| 148a0db4cf | |||
| cdc2fedcac | |||
| 3c795ec9cb | |||
| e7b026846b | |||
| db2ba9f335 | |||
| c3d5443a43 | |||
| cc9e6ebbf7 | |||
| 04cc05c0d3 | |||
| eba4b02677 | |||
| eefedf7a06 | |||
| 139c36d5d4 | |||
| d7ee0ce9db | |||
| 9c630abcd7 | |||
| 191dc626dd | |||
| 4b9d9d281c | |||
| eceb11d200 | |||
| 9cc49c2f58 | |||
| 2fe83c2c9b | |||
| c274f4dd62 | |||
| 9e6714f148 | |||
| 87e7e6a130 | |||
| 5151147372 | |||
| 0fdb3d3fdc | |||
| 702e2eb195 | |||
| 7cdb32a8ea | |||
| 08574f01cc | |||
| b12d387f93 | |||
| 8cdcbf3455 | |||
| 41ce6946d2 | |||
| 0930a859ac | |||
| 2c1a9dcca6 | |||
| 813e70a35a | |||
| 520308af7e | |||
| 7bb3575813 | |||
| 362d958e5a | |||
| d313ad40e6 | |||
| bb7030cf98 | |||
| 9812ea0a62 | |||
| c5b928ce4a | |||
| f5beaa7e00 | |||
| d8a7d44a65 | |||
| bbe7727499 | |||
| 97f240e7b6 | |||
| a23348da27 | |||
| 49e057453f | |||
| 8d76895d08 | |||
| 19e095a757 | |||
| f7492e8526 | |||
| 0cd220c870 | |||
| 7c23f7f865 | |||
| fb7c991ed5 | |||
| fcc5996cda | |||
| 458cb3927a | |||
| 3eeb53655e | |||
| 5728a1f51c | |||
| c91ddd50e6 | |||
| 4fab973d4b | |||
| b41bcab492 | |||
| 875f3b001c | |||
| bb4245f1a4 | |||
| c6458c381a | |||
| 898f7f9eab | |||
| af236997dd | |||
| 5033b3cf73 | |||
| 7d92cb5719 | |||
| 5d82824d6d | |||
| c58758d207 | |||
| 255d191a1d | |||
| 85e3c16479 | |||
| 30cc99cd98 | |||
| e1250d89fb | |||
| a14b534965 | |||
| 6d126348e2 | |||
| 8b3c12ec5b | |||
| 0b2494f82d | |||
| 7364b0fea5 | |||
| b57cfa8462 | |||
| f6b9c0f77b | |||
| f1c933f470 | |||
| c396f64224 | |||
| 295f32737f | |||
| 450e3f27c9 | |||
| 45f4833f90 | |||
| 05c1422566 | |||
| a58d2935dd | |||
| 7252078d48 | |||
| fd03bcae29 | |||
| 0cda7b5785 | |||
| d1a370838c | |||
| 84dd6d42ce | |||
| 2baeeeb9fa | |||
| 0914be8afe | |||
| 4ad4c19b58 | |||
| a35e7562d5 | |||
| 8c0c30dd66 | |||
| 6e2ae29005 | |||
| cdc1f7f709 | |||
| d3046f1761 | |||
| 5719e3f0fa | |||
| 6a10ec4260 | |||
| 124da5b9bf | |||
| 2d022e4c3e | |||
| 893ded8a91 | |||
| 8af706e33b | |||
| ddda9e2d5f | |||
| 60f462b19d | |||
| b530fdbb36 | |||
| 79d84a5c72 | |||
| a8812800e5 | |||
| 3ef132fe0e | |||
| 15dfb316cf | |||
| 92cdd5338b | |||
| d682a9d31d | |||
| da1e9ff3af | |||
| 74d0792515 | |||
| 3a77bc290a | |||
| 1e10504ff8 | |||
| a4534af8b7 | |||
| 2cfba2cb2e | |||
| 80165b6ee8 | |||
| 2e16cac8a2 | |||
| 80802921d4 | |||
| e3eea31d67 | |||
| dae0eba253 | |||
| 94b702b49b | |||
| 3281f6b0ad | |||
| c2b4959b36 | |||
| 256616c5e0 | |||
| 1d09472f1f | |||
| 4e6d7fb30b | |||
| fce57fd26e | |||
| 58f87868d0 | |||
| e22af76803 | |||
| 98844557ae | |||
| abb876c334 | |||
| d5eea92499 | |||
| 5de218bcb3 | |||
| fcb3fe7238 | |||
| 5d0c6ae61e | |||
| 9d873205b1 | |||
| 647c6bf8a5 | |||
| 8669550962 | |||
| 17bdcc11aa | |||
| d2d568a034 | |||
| ac4f596acd | |||
| 24b4ee9814 | |||
| 2e261e4a88 | |||
| b2ea054081 | |||
| db94a9a86e | |||
| a7566c53e5 | |||
| 68153616be | |||
| 079944a878 | |||
| a55a982746 | |||
| ad3d8dbaa3 | |||
| 78ed559f5f | |||
| fbb6559b82 | |||
| d0b0227d4e | |||
| f812f9b6d2 | |||
| e680e70098 | |||
| 25a487b90a | |||
| 29507f2816 | |||
| c9a4dda024 | |||
| 885b34c751 | |||
| 805122a72f | |||
| 23ba64bdc3 | |||
| b1cdf13013 | |||
| f4df7d7961 | |||
| 670eb0f171 | |||
| 0d7c8bb4b0 | |||
| 1da93e09d0 | |||
| cdf3cdd608 | |||
| f857cd49d9 | |||
| 9c8f956e9d | |||
| 8e0093973f | |||
| 6cc503ee87 | |||
| c00fb90718 | |||
| f0f3d01025 | |||
| ac1751b29a | |||
| c90137561b | |||
| 5d39d47ab6 | |||
| 622c15ca7c | |||
| 91d693643b | |||
| 3ddbd23f09 | |||
| 2e17bba40b | |||
| 85cb248688 | |||
| 59ae54dffe | |||
| c4829d57e2 | |||
| ca4bb8441c | |||
| 6657094417 | |||
| 196f94ec09 | |||
| 495ab49e82 | |||
| 7f6875287f | |||
| 395c27abcf | |||
| b79a21df51 | |||
| 6fdf8bcc53 | |||
| 6b57d95fe3 | |||
| 43bac317f8 | |||
| 3234f4324e | |||
| 6e0760db2b | |||
| 1fcb39fbb2 | |||
| 3d9daae5a9 | |||
| 5f78ef781f | |||
| c05e69c836 | |||
| e0acb17830 | |||
| dfefc00f56 | |||
| 3f6243f4be | |||
| 015d47f67d | |||
| 37fe4b68ea | |||
| 95944b32fd | |||
| 7686d4b3b7 | |||
| ad39e5550e | |||
| 3b008d30cb | |||
| 6e9aeadd75 | |||
| dea57f0107 | |||
| eb2e5d8a83 | |||
| 0e290bc008 | |||
| df9589e32c | |||
| 6b4ab27d4f | |||
| 207f4b22cc | |||
| 4a3dd577a9 | |||
| d1ab9d16ac | |||
| 387ec15d2d | |||
| 8c85081478 | |||
| c52defd7c4 | |||
| 3c3012388d | |||
| 68406d778c | |||
| d593522699 | |||
| 5921dfb9c4 | |||
| 155e70008b | |||
| b4dad816b1 | |||
| 2795a7cee0 | |||
| 5d6d351f02 | |||
| d9016fd581 | |||
| bacf193e66 | |||
| ea6b6393e4 | |||
| ba50cd880c | |||
| 86f2e36939 | |||
| d56c53c90e | |||
| f5fb4991bd | |||
| 8bcc9fbff7 | |||
| 1d4b21c07d | |||
| 292f350282 | |||
| 2893aa4904 | |||
| a733c2ce43 | |||
| 7e36f977b1 | |||
| 42db1010f0 | |||
| dec70bc47e | |||
| 300510afc1 | |||
| c3676d6c33 | |||
| 6edd1ef671 | |||
| fe38ae10d3 | |||
| 7a7546eb64 | |||
| ac9425c993 | |||
| 2898461aec | |||
| 77c9d6d7e5 | |||
| 9dfb587ff8 | |||
| d629afa0e2 | |||
| 81c4057908 | |||
| 8152e9fa60 | |||
| 73c01865f6 | |||
| 8349864b0e | |||
| 8da5cf92c9 | |||
| 9efa6cf36d | |||
| 074b467000 | |||
| 3024caa173 | |||
| 439a1a4d8c | |||
| 35fa1656ab | |||
| 0bdbe84828 | |||
| 7972603cc4 | |||
| 1780c99e06 | |||
| bd2519c596 | |||
| df45195a04 | |||
| a0f94236fa | |||
| b5ecf0dbae | |||
| e7697e57f7 | |||
| 7194a13a33 | |||
| aace1408ec | |||
| 8693c8f373 | |||
| 86df808aa5 | |||
| d2c77503c5 | |||
| e7da6db9ca | |||
| f2211e7c9a | |||
| 9f54825718 | |||
| 86e09bfb9c | |||
| 574b53efde | |||
| b189a26f3d | |||
| 3c354a42ed | |||
| 4712967f4c | |||
| 2181953d43 | |||
| ca01459727 | |||
| 9a45421093 | |||
| ee566d2482 | |||
| ce31bc5d36 | |||
| c7dfb90672 | |||
| 871f534785 | |||
| f374e53b8d | |||
| 19b0b0bb7f | |||
| 6f7320673b | |||
| 12a88823f0 | |||
| 28dfc4eebc | |||
| 3bafe8d0c4 | |||
| 074aec1846 | |||
| aef4661635 | |||
| e9d4d084e3 | |||
| 5101f9b131 | |||
| 0a70f84dde | |||
| a8d69de7bd | |||
| b30bf418c8 | |||
| 20758f187d | |||
| 589e140d83 | |||
| 78c55e13a2 | |||
| b5415ecd9d | |||
| 3ae804809e | |||
| 90f7b090b9 | |||
| d0965c1dc4 | |||
| 7f0bfd592e | |||
| 309c82bf0e | |||
| aec38d6b33 | |||
| c73d96bc91 | |||
| 937ccd60b2 | |||
| 98cf5b4f0c | |||
| 01afa79fe7 | |||
| 9b31e3bc3f | |||
| ee977370d2 | |||
| 5e61e7b667 | |||
| 9ded19fe12 | |||
| 95afe143d7 | |||
| dc2e48bda7 | |||
| 5260b523c3 | |||
| 55c051f352 | |||
| f0dd12dfc1 | |||
| 55111d2bf4 | |||
| b7b5d98a24 | |||
| 09deaef4e9 | |||
| bd8eae5245 | |||
| 7b6eb2a1ac | |||
| 36d9887093 | |||
| 16ebf7a574 | |||
| 1d02898b38 | |||
| cb81111118 | |||
| 5bc6887180 | |||
| 18aba19678 | |||
| ad2b5cdd9f | |||
| 92f89f593c | |||
| 4ce01c9d96 | |||
| 5c779defab | |||
| 7740ad458d | |||
| 6797273854 | |||
| d99bb10f68 | |||
| 9d4861fa75 | |||
| db765d575b | |||
| 91f1dbd319 | |||
| a02eefb35e | |||
| 6a449a9719 |
@@ -0,0 +1,202 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* MySQL table row compression tool tool.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2014 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @author Petr Skoda <petr.skoda@totaralms.com>
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(dirname(__FILE__).'/../../config.php');
|
||||
require_once($CFG->libdir . '/clilib.php');
|
||||
|
||||
if ($DB->get_dbfamily() !== 'mysql') {
|
||||
cli_error('This script is used for MySQL databases only.');
|
||||
}
|
||||
|
||||
$engine = strtolower($DB->get_dbengine());
|
||||
if ($engine !== 'innodb' and $engine !== 'xtradb') {
|
||||
cli_error('This script is for MySQL servers using InnoDB or XtraDB engines only.');
|
||||
}
|
||||
|
||||
list($options, $unrecognized) = cli_get_params(
|
||||
array('help' => false, 'info' => false, 'list' => false, 'fix' => false, 'showsql' => false),
|
||||
array('h' => 'help', 'i' => 'info', 'l' => 'list', 'f' => 'fix', 's' => 'showsql')
|
||||
);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
$help =
|
||||
"Script for detection of row size problems in MySQL InnoDB tables.
|
||||
|
||||
By default InnoDB storage table is using legacy Antelope file format
|
||||
which has major restriction on database row size.
|
||||
Use this script to detect and fix database tables with potential data
|
||||
overflow problems.
|
||||
|
||||
Options:
|
||||
-i, --info Show database information
|
||||
-l, --list List problematic tables
|
||||
-f, --fix Attempt to fix all tables (requires SUPER privilege)
|
||||
-s, --showsql Print SQL statements for fixing of tables
|
||||
-h, --help Print out this help
|
||||
|
||||
Example:
|
||||
\$ sudo -u www-data /usr/bin/php admin/cli/mysql_compressed_rows.php -l
|
||||
";
|
||||
|
||||
/** @var mysql_sql_generator $generator */
|
||||
$generator = $DB->get_manager()->generator;
|
||||
|
||||
$info = $DB->get_server_info();
|
||||
$filepertable = $DB->get_record_sql("SHOW VARIABLES LIKE 'innodb_file_per_table'");
|
||||
$filepertable = $filepertable ? $filepertable->value : '';
|
||||
$fileformat = $DB->get_record_sql("SHOW VARIABLES LIKE 'innodb_file_format'");
|
||||
$fileformat = $fileformat ? $fileformat->value : '';
|
||||
$prefix = $DB->get_prefix();
|
||||
$database = $CFG->dbname;
|
||||
|
||||
if (!empty($options['info'])) {
|
||||
echo "Database version: " . $info['description'] . "\n";
|
||||
echo "Database name: $database\n";
|
||||
echo "Database engine: " . $DB->get_dbengine() . "\n";
|
||||
echo "innodb_file_per_table: $filepertable\n";
|
||||
echo "innodb_file_format: $fileformat\n";
|
||||
|
||||
exit(0);
|
||||
|
||||
} else if (!empty($options['list'])) {
|
||||
$problem = false;
|
||||
foreach ($DB->get_tables(false) as $table) {
|
||||
$columns = $DB->get_columns($table, false);
|
||||
$size = $generator->guess_antolope_row_size($columns);
|
||||
$format = $DB->get_row_format($table);
|
||||
if ($size <= $generator::ANTELOPE_MAX_ROW_SIZE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
echo str_pad($prefix . $table, 32, ' ', STR_PAD_RIGHT);
|
||||
echo str_pad($format, 11, ' ', STR_PAD_RIGHT);
|
||||
|
||||
if ($format === 'Compact' or $format === 'Redundant') {
|
||||
$problem = true;
|
||||
echo " (needs fixing)\n";
|
||||
|
||||
} else if ($format !== 'Compressed' and $format !== 'Dynamic') {
|
||||
echo " (unknown)\n";
|
||||
|
||||
} else {
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($problem) {
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
|
||||
} else if (!empty($options['fix'])) {
|
||||
$fixtables = array();
|
||||
foreach ($DB->get_tables(false) as $table) {
|
||||
$columns = $DB->get_columns($table, false);
|
||||
$size = $generator->guess_antolope_row_size($columns);
|
||||
$format = $DB->get_row_format($table);
|
||||
if ($size <= $generator::ANTELOPE_MAX_ROW_SIZE) {
|
||||
continue;
|
||||
}
|
||||
if ($format === 'Compact' or $format === 'Redundant') {
|
||||
$fixtables[$table] = $table;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$fixtables) {
|
||||
echo "No changes necessary\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($filepertable !== 'ON') {
|
||||
try {
|
||||
$DB->execute("SET GLOBAL innodb_file_per_table=1");
|
||||
} catch (dml_exception $e) {
|
||||
echo "Cannot enable GLOBAL innodb_file_per_table setting, use --showsql option and execute the statements manually.";
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
if ($fileformat !== 'Barracuda') {
|
||||
try {
|
||||
$DB->execute("SET GLOBAL innodb_file_format=Barracuda");
|
||||
} catch (dml_exception $e) {
|
||||
echo "Cannot change GLOBAL innodb_file_format setting, use --showsql option and execute the statements manually.";
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$DB->is_compressed_row_format_supported(false)) {
|
||||
echo "MySQL server is not compatible with compressed row format.";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
foreach ($fixtables as $table) {
|
||||
$DB->change_database_structure("ALTER TABLE {$prefix}$table ROW_FORMAT=Compressed");
|
||||
echo str_pad($prefix . $table, 32, ' ', STR_PAD_RIGHT) . " ... Compressed\n";
|
||||
}
|
||||
|
||||
exit(0);
|
||||
|
||||
} else if (!empty($options['showsql'])) {
|
||||
$fixtables = array();
|
||||
|
||||
foreach ($DB->get_tables(false) as $table) {
|
||||
$columns = $DB->get_columns($table, false);
|
||||
$size = $generator->guess_antolope_row_size($columns);
|
||||
$format = $DB->get_row_format($table);
|
||||
if ($size <= $generator::ANTELOPE_MAX_ROW_SIZE) {
|
||||
continue;
|
||||
}
|
||||
if ($format === 'Compact' or $format === 'Redundant') {
|
||||
$fixtables[$table] = $table;
|
||||
}
|
||||
}
|
||||
if (!$fixtables) {
|
||||
echo "No changes necessary\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
echo "Copy the following SQL statements and execute them using account with SUPER privilege:\n\n";
|
||||
echo "USE $database;\n";
|
||||
echo "SET SESSION sql_mode=STRICT_ALL_TABLES;\n";
|
||||
echo "SET GLOBAL innodb_file_per_table=1;\n";
|
||||
echo "SET GLOBAL innodb_file_format=Barracuda;\n";
|
||||
foreach ($fixtables as $table) {
|
||||
echo "ALTER TABLE {$prefix}$table ROW_FORMAT=Compressed;\n";
|
||||
}
|
||||
echo "\n";
|
||||
exit(0);
|
||||
|
||||
} else {
|
||||
echo $help;
|
||||
die;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ admin_externalpage_setup('environment', '', $extraurlparams);
|
||||
// Handle the 'updatecomponent' action
|
||||
if ($action == 'updatecomponent' && confirm_sesskey()) {
|
||||
// Create component installer and execute it
|
||||
if ($cd = new component_installer('http://download.moodle.org',
|
||||
if ($cd = new component_installer('https://download.moodle.org',
|
||||
'environment',
|
||||
'environment.zip')) {
|
||||
$status = $cd->install(); //returns COMPONENT_(ERROR | UPTODATE | INSTALLED)
|
||||
@@ -53,7 +53,7 @@ if ($action == 'updatecomponent' && confirm_sesskey()) {
|
||||
case COMPONENT_ERROR:
|
||||
if ($cd->get_error() == 'remotedownloaderror') {
|
||||
$a = new stdClass();
|
||||
$a->url = 'http://download.moodle.org/environment/environment.zip';
|
||||
$a->url = 'https://download.moodle.org/environment/environment.zip';
|
||||
$a->dest = $CFG->dataroot . '/';
|
||||
print_error($cd->get_error(), 'error', $PAGE->url, $a);
|
||||
die();
|
||||
|
||||
@@ -1136,4 +1136,127 @@
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="2.8" requires="2.2">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="5.5.31" />
|
||||
<VENDOR name="mysql" version="5.5.31" />
|
||||
<VENDOR name="postgres" version="9.1" />
|
||||
<VENDOR name="mssql" version="10.0" />
|
||||
<VENDOR name="oracle" version="10.2" />
|
||||
</DATABASE>
|
||||
<PHP version="5.4.4" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="mbstringrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opensslrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="intlrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
+2
-1
@@ -555,7 +555,8 @@ if (isset($SESSION->pluginuninstallreturn)) {
|
||||
// Print default admin page with notifications.
|
||||
$errorsdisplayed = defined('WARN_DISPLAY_ERRORS_ENABLED');
|
||||
|
||||
$lastcron = $DB->get_field_sql('SELECT MAX(lastcron) FROM {modules}');
|
||||
// We make the assumption that at least one schedule task should run once per day.
|
||||
$lastcron = $DB->get_field_sql('SELECT MAX(lastruntime) FROM {task_scheduled}');
|
||||
$cronoverdue = ($lastcron < time() - 3600 * 24);
|
||||
$dbproblems = $DB->diagnose();
|
||||
$maintenancemode = !empty($CFG->maintenance_enabled);
|
||||
|
||||
+21
-12
@@ -1039,19 +1039,28 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
}
|
||||
|
||||
foreach ($plugin->get_other_required_plugins() as $component => $requiredversion) {
|
||||
$ok = true;
|
||||
$otherplugin = $pluginman->get_plugin_info($component);
|
||||
$actions = array();
|
||||
|
||||
if (is_null($otherplugin)) {
|
||||
$ok = false;
|
||||
} else if ($requiredversion != ANY_VERSION and $otherplugin->versiondisk < $requiredversion) {
|
||||
$ok = false;
|
||||
}
|
||||
// The required plugin is not installed.
|
||||
$class = 'requires-failed requires-missing';
|
||||
$installurl = new moodle_url('https://moodle.org/plugins/view.php', array('plugin' => $component));
|
||||
$uploadurl = new moodle_url('/admin/tool/installaddon/');
|
||||
$actions[] = html_writer::link($installurl, get_string('dependencyinstall', 'core_plugin'));
|
||||
$actions[] = html_writer::link($uploadurl, get_string('dependencyupload', 'core_plugin'));
|
||||
|
||||
} else if ($requiredversion != ANY_VERSION and $otherplugin->versiondisk < $requiredversion) {
|
||||
// The required plugin is installed but needs to be updated.
|
||||
$class = 'requires-failed requires-outdated';
|
||||
if (!$otherplugin->is_standard()) {
|
||||
$updateurl = new moodle_url($this->page->url, array('sesskey' => sesskey(), 'fetchupdates' => 1));
|
||||
$actions[] = html_writer::link($updateurl, get_string('checkforupdates', 'core_plugin'));
|
||||
}
|
||||
|
||||
if ($ok) {
|
||||
$class = 'requires-ok';
|
||||
} else {
|
||||
$class = 'requires-failed';
|
||||
// Already installed plugin with sufficient version.
|
||||
$class = 'requires-ok';
|
||||
}
|
||||
|
||||
if ($requiredversion != ANY_VERSION) {
|
||||
@@ -1059,11 +1068,11 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
} else {
|
||||
$str = 'otherplugin';
|
||||
}
|
||||
$componenturl = new moodle_url('https://moodle.org/plugins/view.php?plugin='.$component);
|
||||
$componenturl = html_writer::tag('a', $component, array('href' => $componenturl->out()));
|
||||
|
||||
$requires[] = html_writer::tag('li',
|
||||
get_string($str, 'core_plugin',
|
||||
array('component' => $componenturl, 'version' => $requiredversion)),
|
||||
html_writer::div(get_string($str, 'core_plugin',
|
||||
array('component' => $component, 'version' => $requiredversion)), 'component').
|
||||
html_writer::div(implode(' | ', $actions), 'actions'),
|
||||
array('class' => $class));
|
||||
}
|
||||
|
||||
|
||||
@@ -48,11 +48,12 @@ class core_role_potential_assignees_below_course extends core_role_assign_user_s
|
||||
$fields = 'SELECT ' . $this->required_fields_sql('u');
|
||||
$countfields = 'SELECT COUNT(u.id)';
|
||||
|
||||
$sql = " FROM {user} u
|
||||
LEFT JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.roleid = :roleid AND ra.contextid = :contextid)
|
||||
WHERE u.id IN ($enrolsql)
|
||||
$wherecondition
|
||||
AND ra.id IS NULL";
|
||||
$sql = " FROM ($enrolsql) enrolled_users_view
|
||||
JOIN {user} u ON u.id = enrolled_users_view.id
|
||||
LEFT JOIN {role_assignments} ra ON (ra.userid = enrolled_users_view.id AND
|
||||
ra.roleid = :roleid AND ra.contextid = :contextid)
|
||||
WHERE ra.id IS NULL
|
||||
$wherecondition";
|
||||
$params['contextid'] = $this->context->id;
|
||||
$params['roleid'] = $this->roleid;
|
||||
|
||||
|
||||
@@ -358,7 +358,7 @@ if ($hassiteconfig || has_capability('moodle/question:config', $systemcontext))
|
||||
2 => get_string('showmarkandmax', 'question'),
|
||||
);
|
||||
$settings->add(new admin_setting_configselect('question_preview/marks',
|
||||
get_string('marks', 'question'), '', 1, $marksoptions));
|
||||
get_string('marks', 'question'), '', 2, $marksoptions));
|
||||
|
||||
$settings->add(new admin_setting_configselect('question_preview/markdp',
|
||||
get_string('decimalplacesingrades', 'question'), '', 2, array(0, 1, 2, 3, 4, 5, 6, 7)));
|
||||
|
||||
@@ -253,24 +253,57 @@ Feature: Set up contextual data for tests
|
||||
And I set the field "groups" to "Group 2 (1)"
|
||||
And the "members" select box should contain "Student 2"
|
||||
|
||||
Scenario: Add cohorts with data generator
|
||||
Scenario: Add cohorts and cohort members with data generator
|
||||
Given the following "categories" exist:
|
||||
| name | category | idnumber |
|
||||
| Cat 1 | 0 | CAT1 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Student | 1 | student1@asd.com |
|
||||
| student2 | Student | 2 | student2@asd.com |
|
||||
And the following "cohorts" exist:
|
||||
| name | idnumber |
|
||||
| System cohort 1 | CH01 |
|
||||
| System cohort A | CHSA |
|
||||
And the following "cohorts" exist:
|
||||
| name | idnumber | contextlevel | reference |
|
||||
| System cohort 2 | CH02 | System | |
|
||||
| Cohort in category 1 | CH1 | Category | CAT1 |
|
||||
| System cohort B | CHSB | System | |
|
||||
| Cohort in category | CHC | Category | CAT1 |
|
||||
| Empty cohort | CHE | Category | CAT1 |
|
||||
And the following "cohort members" exist:
|
||||
| user | cohort |
|
||||
| student1 | CHSA |
|
||||
| student2 | CHSB |
|
||||
| student1 | CHSB |
|
||||
| student1 | CHC |
|
||||
When I log in as "admin"
|
||||
And I navigate to "Cohorts" node in "Site administration > Users > Accounts"
|
||||
Then I should see "System cohort 1"
|
||||
And I should see "System cohort 2"
|
||||
Then the following should exist in the "cohorts" table:
|
||||
| Name | Cohort size |
|
||||
| System cohort A | 1 |
|
||||
| System cohort B | 2 |
|
||||
And I should not see "Cohort in category"
|
||||
And I follow "Courses"
|
||||
And I follow "Cat 1"
|
||||
And I follow "Cohorts"
|
||||
And I should see "Cohort in category 1"
|
||||
And I should not see "System cohort"
|
||||
And the following should exist in the "cohorts" table:
|
||||
| Name | Cohort size |
|
||||
| Cohort in category | 1 |
|
||||
| Empty cohort | 0 |
|
||||
|
||||
Scenario: Add grade categories with data generator
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
And the following "grade categories" exist:
|
||||
| fullname | course |
|
||||
| Grade category 1 | C1|
|
||||
And the following "grade categories" exist:
|
||||
| fullname | course | gradecategory |
|
||||
| Grade sub category 2 | C1 | Grade category 1|
|
||||
When I log in as "admin"
|
||||
And I follow "Courses"
|
||||
And I follow "Course 1"
|
||||
And I navigate to "Grades" node in "Course administration"
|
||||
Then I should see "Grade category 1"
|
||||
And I should see "Grade sub category 2"
|
||||
|
||||
@@ -66,6 +66,7 @@ $string['validationmsg_filenotexists'] = 'Extracted file not found';
|
||||
$string['validationmsg_filesnumber'] = 'Not enough files found in the package';
|
||||
$string['validationmsg_filestatus'] = 'Unable to extract all files';
|
||||
$string['validationmsg_filestatus_info'] = 'Attempting to extract file {$a->file} resulted in error \'{$a->status}\'.';
|
||||
$string['validationmsg_foundlangfile'] = 'Found language file';
|
||||
$string['validationmsg_maturity'] = 'Declared maturity level';
|
||||
$string['validationmsg_maturity_help'] = 'The plugin can declare its maturity level. If the maintainer considers the plugin stable, the declared maturity level will read MATURITY_STABLE. All other maturity levels (such as alpha or beta) should be considered unstable and a warning is raised.';
|
||||
$string['validationmsg_missingexpectedlangenfile'] = 'English language file name mismatch';
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
/**
|
||||
* Fetches language packages from download.moodle.org server
|
||||
*
|
||||
* Language packages are available at http://download.moodle.org/langpack/
|
||||
* Language packages are available at https://download.moodle.org/langpack/
|
||||
* in ZIP format together with a file languages.md5 containing their hashes
|
||||
* and meta info.
|
||||
* Locally, language packs are saved into $CFG->dataroot/lang/
|
||||
|
||||
@@ -37,7 +37,7 @@ $string['missinglangparent'] = 'Missing parent language <em>{$a->parent}</em> of
|
||||
$string['nolangupdateneeded'] = 'All your language packs are up to date, no update is needed';
|
||||
$string['pluginname'] = 'Language packs';
|
||||
$string['purgestringcaches'] = 'Purge string caches';
|
||||
$string['remotelangnotavailable'] = 'Because Moodle cannot connect to download.moodle.org, it is not possible for language packs to be installed automatically. Please download the appropriate ZIP file(s) from <a href="http://download.moodle.org/langpack/">download.moodle.org/langpack</a>, copy them to your {$a} directory and unzip them manually.';
|
||||
$string['remotelangnotavailable'] = 'Because Moodle cannot connect to download.moodle.org, it is not possible for language packs to be installed automatically. Please download the appropriate ZIP file(s) from <a href="https://download.moodle.org/langpack/">download.moodle.org/langpack</a>, copy them to your {$a} directory and unzip them manually.';
|
||||
$string['uninstall'] = 'Uninstall selected language pack';
|
||||
$string['uninstallconfirm'] = 'You are about to completely uninstall language pack {$a}, are you sure?';
|
||||
$string['updatelangs'] = 'Update all installed language packs';
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
$message = '<br /><br />';
|
||||
$message .= $CFG->tempdir.'/olson.txt<br />';
|
||||
$message .= $CFG->tempdir.'/timezone.txt<br />';
|
||||
$message .= '<a href="http://download.moodle.org/timezone/">http://download.moodle.org/timezone/</a><br />';
|
||||
$message .= '<a href="https://download.moodle.org/timezone/">https://download.moodle.org/timezone/</a><br />';
|
||||
$message .= '<a href="'.$CFG->wwwroot.'/lib/timezone.txt">'.$CFG->dirroot.'/lib/timezone.txt</a><br />';
|
||||
$message .= '<br />';
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
}
|
||||
|
||||
/// Otherwise, let's try moodle.org's copy
|
||||
$source = 'http://download.moodle.org/timezone/';
|
||||
$source = 'https://download.moodle.org/timezone/';
|
||||
if (!$importdone && ($content=download_file_content($source))) {
|
||||
if ($file = fopen($CFG->tempdir.'/timezone.txt', 'w')) { // Make local copy
|
||||
fwrite($file, $content);
|
||||
|
||||
@@ -35,6 +35,13 @@ global $CFG;
|
||||
*/
|
||||
class tool_uploadcourse_course_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Tidy up open files that may be left open.
|
||||
*/
|
||||
protected function tearDown() {
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
public function test_proceed_without_prepare() {
|
||||
$this->resetAfterTest(true);
|
||||
$mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
|
||||
|
||||
@@ -36,6 +36,13 @@ require_once($CFG->libdir . '/csvlib.class.php');
|
||||
*/
|
||||
class tool_uploadcourse_processor_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Tidy up open files that may be left open.
|
||||
*/
|
||||
protected function tearDown() {
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
public function test_basic() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
@@ -256,17 +256,12 @@
|
||||
$table->colclasses = array();
|
||||
$table->head[] = $fullnamedisplay;
|
||||
$table->attributes['class'] = 'admintable generaltable';
|
||||
$table->colclasses[] = 'leftalign';
|
||||
foreach ($extracolumns as $field) {
|
||||
$table->head[] = ${$field};
|
||||
$table->colclasses[] = 'leftalign';
|
||||
}
|
||||
$table->head[] = $city;
|
||||
$table->colclasses[] = 'leftalign';
|
||||
$table->head[] = $country;
|
||||
$table->colclasses[] = 'leftalign';
|
||||
$table->head[] = $lastaccess;
|
||||
$table->colclasses[] = 'leftalign';
|
||||
$table->head[] = get_string('edit');
|
||||
$table->colclasses[] = 'centeralign';
|
||||
$table->head[] = "";
|
||||
|
||||
+3
-3
@@ -35,12 +35,12 @@ function get_selection_data($ufiltering) {
|
||||
|
||||
if ($scount) {
|
||||
if ($scount < MAX_BULK_USERS) {
|
||||
$in = implode(',', $SESSION->bulk_users);
|
||||
$bulkusers = $SESSION->bulk_users;
|
||||
} else {
|
||||
$bulkusers = array_slice($SESSION->bulk_users, 0, MAX_BULK_USERS, true);
|
||||
$in = implode(',', $bulkusers);
|
||||
}
|
||||
$userlist['susers'] = $DB->get_records_select_menu('user', "id IN ($in)", null, 'fullname', 'id,'.$DB->sql_fullname().' AS fullname');
|
||||
list($in, $inparams) = $DB->get_in_or_equal($bulkusers);
|
||||
$userlist['susers'] = $DB->get_records_select_menu('user', "id $in", $inparams, 'fullname', 'id,'.$DB->sql_fullname().' AS fullname');
|
||||
}
|
||||
|
||||
return $userlist;
|
||||
|
||||
+11
-12
@@ -52,14 +52,9 @@ class auth_db_testcase extends advanced_testcase {
|
||||
set_config('host', $CFG->dbhost.':'.$CFG->dboptions['dbport'], 'auth/db');
|
||||
}
|
||||
|
||||
switch (get_class($DB)) {
|
||||
case 'mssql_native_moodle_database':
|
||||
set_config('type', 'mssql_n', 'auth/db');
|
||||
set_config('sybasequoting', '1', 'auth/db');
|
||||
break;
|
||||
switch ($DB->get_dbfamily()) {
|
||||
|
||||
case 'mariadb_native_moodle_database':
|
||||
case 'mysqli_native_moodle_database':
|
||||
case 'mysql':
|
||||
set_config('type', 'mysqli', 'auth/db');
|
||||
set_config('setupsql', "SET NAMES 'UTF-8'", 'auth/db');
|
||||
set_config('sybasequoting', '0', 'auth/db');
|
||||
@@ -72,12 +67,12 @@ class auth_db_testcase extends advanced_testcase {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'oci_native_moodle_database':
|
||||
case 'oracle':
|
||||
set_config('type', 'oci8po', 'auth/db');
|
||||
set_config('sybasequoting', '1', 'auth/db');
|
||||
break;
|
||||
|
||||
case 'pgsql_native_moodle_database':
|
||||
case 'postgres':
|
||||
set_config('type', 'postgres7', 'auth/db');
|
||||
$setupsql = "SET NAMES 'UTF-8'";
|
||||
if (!empty($CFG->dboptions['dbschema'])) {
|
||||
@@ -98,13 +93,17 @@ class auth_db_testcase extends advanced_testcase {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'sqlsrv_native_moodle_database':
|
||||
set_config('type', 'mssqlnative', 'auth/db');
|
||||
case 'mssql':
|
||||
if (get_class($DB) == 'mssql_native_moodle_database') {
|
||||
set_config('type', 'mssql_n', 'auth/db');
|
||||
} else {
|
||||
set_config('type', 'mssqlnative', 'auth/db');
|
||||
}
|
||||
set_config('sybasequoting', '1', 'auth/db');
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new exception('Unknown database driver '.get_class($DB));
|
||||
throw new exception('Unknown database family ' . $DB->get_dbfamily());
|
||||
}
|
||||
|
||||
$table = new xmldb_table('auth_db_users');
|
||||
|
||||
@@ -737,6 +737,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
do {
|
||||
$value = ldap_get_values_len($ldapconnection, $entry, $this->config->user_attribute);
|
||||
$value = core_text::convert($value[0], $this->config->ldapencoding, 'utf-8');
|
||||
$value = trim($value);
|
||||
$this->ldap_bulk_insert($value);
|
||||
} while ($entry = ldap_next_entry($ldapconnection, $entry));
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ $string['auth_ldap_passtype'] = 'Specify the format of new or changed passwords
|
||||
$string['auth_ldap_passtype_key'] = 'Password format';
|
||||
$string['auth_ldap_passwdexpire_settings'] = 'LDAP password expiration settings.';
|
||||
$string['auth_ldap_preventpassindb'] = 'Select yes to prevent passwords from being stored in Moodle\'s DB.';
|
||||
$string['auth_ldap_preventpassindb_key'] = 'Hide passwords';
|
||||
$string['auth_ldap_preventpassindb_key'] = 'Cache passwords';
|
||||
$string['auth_ldap_search_sub'] = 'Search users from subcontexts.';
|
||||
$string['auth_ldap_search_sub_key'] = 'Search subcontexts';
|
||||
$string['auth_ldap_server_settings'] = 'LDAP server settings';
|
||||
|
||||
@@ -41,13 +41,16 @@ class auth_plugin_manual extends auth_plugin_base {
|
||||
* The name of the component. Used by the configuration.
|
||||
*/
|
||||
const COMPONENT_NAME = 'auth_manual';
|
||||
const LEGACY_COMPONENT_NAME = 'auth/manual';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
function auth_plugin_manual() {
|
||||
$this->authtype = 'manual';
|
||||
$this->config = get_config(self::COMPONENT_NAME);
|
||||
$config = get_config(self::COMPONENT_NAME);
|
||||
$legacyconfig = get_config(self::LEGACY_COMPONENT_NAME);
|
||||
$this->config = (object)array_merge((array)$legacyconfig, (array)$config);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,6 +49,9 @@ abstract class info {
|
||||
/** @var tree Availability configuration, decoded from JSON; null if unset */
|
||||
protected $availabilitytree;
|
||||
|
||||
/** @var array|null Array of information about current restore if any */
|
||||
protected static $restoreinfo = null;
|
||||
|
||||
/**
|
||||
* Constructs with item details.
|
||||
*
|
||||
@@ -307,9 +310,12 @@ abstract class info {
|
||||
* @param string $restoreid Restore identifier
|
||||
* @param int $courseid Target course id
|
||||
* @param \base_logger $logger Logger for any warnings
|
||||
* @param int $dateoffset Date offset to be added to any dates (0 = none)
|
||||
*/
|
||||
public function update_after_restore($restoreid, $courseid, \base_logger $logger) {
|
||||
public function update_after_restore($restoreid, $courseid, \base_logger $logger, $dateoffset) {
|
||||
$tree = $this->get_availability_tree();
|
||||
// Set static data for use by get_restore_date_offset function.
|
||||
self::$restoreinfo = array('restoreid' => $restoreid, 'dateoffset' => $dateoffset);
|
||||
$changed = $tree->update_after_restore($restoreid, $courseid, $logger,
|
||||
$this->get_thing_name());
|
||||
if ($changed) {
|
||||
@@ -319,6 +325,24 @@ abstract class info {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date offset (amount by which any date values should be
|
||||
* adjusted) for the current restore.
|
||||
*
|
||||
* @param string $restoreid Restore identifier
|
||||
* @return int Date offset (0 if none)
|
||||
* @throws coding_exception If not in a restore (or not in that restore)
|
||||
*/
|
||||
public static function get_restore_date_offset($restoreid) {
|
||||
if (!self::$restoreinfo) {
|
||||
throw new coding_exception('Only valid during restore');
|
||||
}
|
||||
if (self::$restoreinfo['restoreid'] !== $restoreid) {
|
||||
throw new coding_exception('Data not available for that restore id');
|
||||
}
|
||||
return self::$restoreinfo['dateoffset'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the name of the item (cm_info or section_info, at present) that
|
||||
* this is controlling availability of. Name should be formatted ready
|
||||
|
||||
@@ -187,6 +187,13 @@ class info_module extends info {
|
||||
// As a result we cannot take short cuts any longer and must get
|
||||
// standard modinfo.
|
||||
$modinfo = get_fast_modinfo($cm->course, $userid);
|
||||
return $modinfo->get_cm($cm->id)->uservisible;
|
||||
$cms = $modinfo->get_cms();
|
||||
if (!isset($cms[$cm->id])) {
|
||||
// In some cases this might get called with a cmid that is no longer
|
||||
// available, for example when a module is hidden at system level.
|
||||
debugging('info_module::is_user_visible called with invalid cmid ' . $cm->id, DEBUG_DEVELOPER);
|
||||
return false;
|
||||
}
|
||||
return $cms[$cm->id]->uservisible;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,10 +560,13 @@ class tree extends tree_node {
|
||||
public function save() {
|
||||
$result = new \stdClass();
|
||||
$result->op = $this->op;
|
||||
if ($this->op === self::OP_AND || $this->op === self::OP_NOT_OR) {
|
||||
$result->showc = $this->showchildren;
|
||||
} else {
|
||||
$result->show = $this->show;
|
||||
// Only root tree has the 'show' options.
|
||||
if ($this->root) {
|
||||
if ($this->op === self::OP_AND || $this->op === self::OP_NOT_OR) {
|
||||
$result->showc = $this->showchildren;
|
||||
} else {
|
||||
$result->show = $this->show;
|
||||
}
|
||||
}
|
||||
$result->c = array();
|
||||
foreach ($this->children as $child) {
|
||||
@@ -572,6 +575,14 @@ class tree extends tree_node {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this tree is empty (contains no children).
|
||||
* @return boolean True if empty
|
||||
*/
|
||||
public function is_empty() {
|
||||
return count($this->children) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively gets all children of a particular class (you can use a base
|
||||
* class to get all conditions, or a specific class).
|
||||
|
||||
@@ -87,6 +87,9 @@ abstract class tree_node {
|
||||
* The default behaviour is simply to return false. If there is a problem
|
||||
* with the update, $logger can be used to output a warning.
|
||||
*
|
||||
* Note: If you need information about the date offset, call
|
||||
* \core_availability\info::get_restore_date_offset($restoreid).
|
||||
*
|
||||
* @param string $restoreid Restore ID
|
||||
* @param int $courseid ID of target course
|
||||
* @param \base_logger $logger Logger for any warnings
|
||||
|
||||
@@ -209,4 +209,15 @@ class condition extends \core_availability\condition {
|
||||
protected static function is_midnight($time) {
|
||||
return usergetmidnight($time) == $time;
|
||||
}
|
||||
|
||||
public function update_after_restore(
|
||||
$restoreid, $courseid, \base_logger $logger, $name) {
|
||||
// Update the date, if restoring with changed date.
|
||||
$dateoffset = \core_availability\info::get_restore_date_offset($restoreid);
|
||||
if ($dateoffset) {
|
||||
$this->time += $dateoffset;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +176,12 @@ class condition extends \core_availability\condition {
|
||||
public function filter_user_list(array $users, $not, \core_availability\info $info,
|
||||
\core_availability\capability_checker $checker) {
|
||||
global $CFG, $DB;
|
||||
|
||||
// If the array is empty already, just return it.
|
||||
if (!$users) {
|
||||
return $users;
|
||||
}
|
||||
|
||||
require_once($CFG->libdir . '/grouplib.php');
|
||||
$course = $info->get_course();
|
||||
|
||||
|
||||
@@ -209,6 +209,11 @@ class condition extends \core_availability\condition {
|
||||
\core_availability\capability_checker $checker) {
|
||||
global $CFG, $DB;
|
||||
|
||||
// If the array is empty already, just return it.
|
||||
if (!$users) {
|
||||
return $users;
|
||||
}
|
||||
|
||||
// List users for this course who match the condition.
|
||||
$groupingusers = $DB->get_records_sql("
|
||||
SELECT DISTINCT gm.userid
|
||||
|
||||
@@ -416,6 +416,11 @@ class condition extends \core_availability\condition {
|
||||
\core_availability\capability_checker $checker) {
|
||||
global $CFG, $DB;
|
||||
|
||||
// If the array is empty already, just return it.
|
||||
if (!$users) {
|
||||
return $users;
|
||||
}
|
||||
|
||||
// Get all users from the list who match the condition.
|
||||
list ($sql, $params) = $DB->get_in_or_equal(array_keys($users));
|
||||
|
||||
|
||||
@@ -337,7 +337,8 @@ class availability_profile_condition_testcase extends advanced_testcase {
|
||||
// The list of fields should include the text field added in setUp(),
|
||||
// but should not include the textarea field added just now.
|
||||
$fields = condition::get_custom_profile_fields();
|
||||
$this->assertEquals(array('frogtype'), array_keys($fields));
|
||||
$this->assertArrayHasKey('frogtype', $fields);
|
||||
$this->assertArrayNotHasKey('longtext', $fields);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
@@ -74,6 +74,9 @@ M.availability_profile.form.getNode = function(json) {
|
||||
if (json.op !== undefined &&
|
||||
node.one('select[name=op] > option[value=' + json.op + ']')) {
|
||||
node.one('select[name=op]').set('value', json.op);
|
||||
if (json.op === 'isempty' || json.op === 'isnotempty') {
|
||||
node.one('input[name=value]').set('disabled', true);
|
||||
}
|
||||
}
|
||||
if (json.v !== undefined) {
|
||||
node.one('input').set('value', json.v);
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
YUI.add("moodle-availability_profile-form",function(e,t){M.availability_profile=M.availability_profile||{},M.availability_profile.form=e.Object(M.core_availability.plugin),M.availability_profile.form.profiles=null,M.availability_profile.form.initInner=function(e,t){this.standardFields=e,this.customFields=t},M.availability_profile.form.getNode=function(t){var n=M.str.availability_profile,r='<span class="availability-group"><label>'+n.conditiontitle+" "+'<select name="field">'+'<option value="choose">'+M.str.moodle.choosedots+"</option>",i;for(var s=0;s<this.standardFields.length;s++)i=this.standardFields[s],r+='<option value="sf_'+i.field+'">'+i.display+"</option>";for(s=0;s<this.customFields.length;s++)i=this.customFields[s],r+='<option value="cf_'+i.field+'">'+i.display+"</option>";r+='</select></label> <label><span class="accesshide">'+n.label_operator+' </span><select name="op" title="'+n.label_operator+'">';var o=["isequalto","contains","doesnotcontain","startswith","endswith","isempty","isnotempty"];for(s=0;s<o.length;s++)r+='<option value="'+o[s]+'">'+n["op_"+o[s]]+"</option>";r+='</select></label> <label><span class="accesshide">'+n.label_value+'</span><input name="value" type="text" style="width: 10em" title="'+n.label_value+'"/></label></span>';var u=e.Node.create("<span>"+r+"</span>");t.sf!==undefined&&u.one("select[name=field] > option[value=sf_"+t.sf+"]")?u.one("select[name=field]").set("value","sf_"+t.sf):t.cf!==undefined&&u.one("select[name=field] > option[value=cf_"+t.cf+"]")&&u.one("select[name=field]").set("value","cf_"+t.cf),t.op!==undefined&&u.one("select[name=op] > option[value="+t.op+"]")&&u.one("select[name=op]").set("value",t.op),t.v!==undefined&&u.one("input").set("value",t.v);if(!M.availability_profile.form.addedEvents){M.availability_profile.form.addedEvents=!0;var a=function(e){var t=e.ancestor("span.availability_profile"),n=t.one("select[name=op]"),r=n.get("value")==="isempty"||n.get("value")==="isnotempty";t.one("input[name=value]").set("disabled",r),M.core_availability.form.update()},f=e.one("#fitem_id_availabilityconditionsjson");f.delegate("change",function(){a(this)},".availability_profile select"),f.delegate("change",function(){a(this)},".availability_profile input[name=value]")}return u},M.availability_profile.form.fillValue=function(e,t){var n=t.one("select[name=field]").get("value");n.substr(0,3)==="sf_"?e.sf=n.substr(3):n.substr(0,3)==="cf_"&&(e.cf=n.substr(3)),e.op=t.one("select[name=op]").get("value");var r=t.one("input[name=value]");r.get("disabled")||(e.v=r.get("value"))},M.availability_profile.form.fillErrors=function(e,t){var n={};this.fillValue(n,t),n.sf===undefined&&n.cf===undefined&&e.push("availability_profile:error_selectfield"),n.v!==undefined&&/^\s*$/.test(n.v)&&e.push("availability_profile:error_setvalue")}},"@VERSION@",{requires:["base","node","event","moodle-core_availability-form"]});
|
||||
YUI.add("moodle-availability_profile-form",function(e,t){M.availability_profile=M.availability_profile||{},M.availability_profile.form=e.Object(M.core_availability.plugin),M.availability_profile.form.profiles=null,M.availability_profile.form.initInner=function(e,t){this.standardFields=e,this.customFields=t},M.availability_profile.form.getNode=function(t){var n=M.str.availability_profile,r='<span class="availability-group"><label>'+n.conditiontitle+" "+'<select name="field">'+'<option value="choose">'+M.str.moodle.choosedots+"</option>",i;for(var s=0;s<this.standardFields.length;s++)i=this.standardFields[s],r+='<option value="sf_'+i.field+'">'+i.display+"</option>";for(s=0;s<this.customFields.length;s++)i=this.customFields[s],r+='<option value="cf_'+i.field+'">'+i.display+"</option>";r+='</select></label> <label><span class="accesshide">'+n.label_operator+' </span><select name="op" title="'+n.label_operator+'">';var o=["isequalto","contains","doesnotcontain","startswith","endswith","isempty","isnotempty"];for(s=0;s<o.length;s++)r+='<option value="'+o[s]+'">'+n["op_"+o[s]]+"</option>";r+='</select></label> <label><span class="accesshide">'+n.label_value+'</span><input name="value" type="text" style="width: 10em" title="'+n.label_value+'"/></label></span>';var u=e.Node.create("<span>"+r+"</span>");t.sf!==undefined&&u.one("select[name=field] > option[value=sf_"+t.sf+"]")?u.one("select[name=field]").set("value","sf_"+t.sf):t.cf!==undefined&&u.one("select[name=field] > option[value=cf_"+t.cf+"]")&&u.one("select[name=field]").set("value","cf_"+t.cf),t.op!==undefined&&u.one("select[name=op] > option[value="+t.op+"]")&&(u.one("select[name=op]").set("value",t.op),(t.op==="isempty"||t.op==="isnotempty")&&u.one("input[name=value]").set("disabled",!0)),t.v!==undefined&&u.one("input").set("value",t.v);if(!M.availability_profile.form.addedEvents){M.availability_profile.form.addedEvents=!0;var a=function(e){var t=e.ancestor("span.availability_profile"),n=t.one("select[name=op]"),r=n.get("value")==="isempty"||n.get("value")==="isnotempty";t.one("input[name=value]").set("disabled",r),M.core_availability.form.update()},f=e.one("#fitem_id_availabilityconditionsjson");f.delegate("change",function(){a(this)},".availability_profile select"),f.delegate("change",function(){a(this)},".availability_profile input[name=value]")}return u},M.availability_profile.form.fillValue=function(e,t){var n=t.one("select[name=field]").get("value");n.substr(0,3)==="sf_"?e.sf=n.substr(3):n.substr(0,3)==="cf_"&&(e.cf=n.substr(3)),e.op=t.one("select[name=op]").get("value");var r=t.one("input[name=value]");r.get("disabled")||(e.v=r.get("value"))},M.availability_profile.form.fillErrors=function(e,t){var n={};this.fillValue(n,t),n.sf===undefined&&n.cf===undefined&&e.push("availability_profile:error_selectfield"),n.v!==undefined&&/^\s*$/.test(n.v)&&e.push("availability_profile:error_setvalue")}},"@VERSION@",{requires:["base","node","event","moodle-core_availability-form"]});
|
||||
|
||||
+3
@@ -74,6 +74,9 @@ M.availability_profile.form.getNode = function(json) {
|
||||
if (json.op !== undefined &&
|
||||
node.one('select[name=op] > option[value=' + json.op + ']')) {
|
||||
node.one('select[name=op]').set('value', json.op);
|
||||
if (json.op === 'isempty' || json.op === 'isnotempty') {
|
||||
node.one('input[name=value]').set('disabled', true);
|
||||
}
|
||||
}
|
||||
if (json.v !== undefined) {
|
||||
node.one('input').set('value', json.v);
|
||||
|
||||
@@ -72,6 +72,9 @@ M.availability_profile.form.getNode = function(json) {
|
||||
if (json.op !== undefined &&
|
||||
node.one('select[name=op] > option[value=' + json.op + ']')) {
|
||||
node.one('select[name=op]').set('value', json.op);
|
||||
if (json.op === 'isempty' || json.op === 'isnotempty') {
|
||||
node.one('input[name=value]').set('disabled', true);
|
||||
}
|
||||
}
|
||||
if (json.v !== undefined) {
|
||||
node.one('input').set('value', json.v);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
use core_availability\capability_checker;
|
||||
use \core_availability\tree;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
@@ -488,6 +489,21 @@ class tree_testcase extends \advanced_testcase {
|
||||
$tree->get_full_information($info));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the is_empty() function.
|
||||
*/
|
||||
public function test_is_empty() {
|
||||
// Tree with nothing in should be empty.
|
||||
$structure = self::tree(array());
|
||||
$tree = new tree($structure);
|
||||
$this->assertTrue($tree->is_empty());
|
||||
|
||||
// Tree with something in is not empty.
|
||||
$structure = self::tree(array(self::mock(array('m' => '1'))));
|
||||
$tree = new tree($structure);
|
||||
$this->assertFalse($tree->is_empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the get_all_children() function.
|
||||
*/
|
||||
|
||||
Vendored
+4
-6
@@ -928,20 +928,19 @@ M.core_availability.Item.prototype.pluginNode = null;
|
||||
M.core_availability.EyeIcon = function(individual, shown) {
|
||||
this.individual = individual;
|
||||
this.span = Y.Node.create('<a class="availability-eye" href="#" role="button">');
|
||||
var iconBase = M.cfg.wwwroot + '/theme/image.php/' + M.cfg.theme + '/core/' + M.cfg.themerev;
|
||||
var icon = Y.Node.create('<img />');
|
||||
this.span.appendChild(icon);
|
||||
|
||||
// Set up button text and icon.
|
||||
var suffix = individual ? '_individual' : '_all';
|
||||
var setHidden = function() {
|
||||
icon.set('src', iconBase + '/i/show');
|
||||
icon.set('src', M.util.image_url('i/show', 'core'));
|
||||
icon.set('alt', M.str.availability['hidden' + suffix]);
|
||||
this.span.set('title', M.str.availability['hidden' + suffix] + ' \u2022 ' +
|
||||
M.str.availability.show_verb);
|
||||
};
|
||||
var setShown = function() {
|
||||
icon.set('src', iconBase + '/i/hide');
|
||||
icon.set('src', M.util.image_url('i/hide', 'core'));
|
||||
icon.set('alt', M.str.availability['shown' + suffix]);
|
||||
this.span.set('title', M.str.availability['shown' + suffix] + ' \u2022 ' +
|
||||
M.str.availability.hide_verb);
|
||||
@@ -1006,9 +1005,8 @@ M.core_availability.EyeIcon.prototype.isHidden = function() {
|
||||
M.core_availability.DeleteIcon = function(toDelete) {
|
||||
this.span = Y.Node.create('<a class="availability-delete" href="#" title="' +
|
||||
M.str.moodle['delete'] + '" role="button">');
|
||||
var img = Y.Node.create('<img src="' +
|
||||
M.cfg.wwwroot + '/theme/image.php/' + M.cfg.theme + '/core/' + M.cfg.themerev +
|
||||
'/t/delete" alt="' + M.str.moodle['delete'] + '" />');
|
||||
var img = Y.Node.create('<img src="' + M.util.image_url('t/delete', 'core') +
|
||||
'" alt="' + M.str.moodle['delete'] + '" />');
|
||||
this.span.appendChild(img);
|
||||
var click = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+4
-6
@@ -928,20 +928,19 @@ M.core_availability.Item.prototype.pluginNode = null;
|
||||
M.core_availability.EyeIcon = function(individual, shown) {
|
||||
this.individual = individual;
|
||||
this.span = Y.Node.create('<a class="availability-eye" href="#" role="button">');
|
||||
var iconBase = M.cfg.wwwroot + '/theme/image.php/' + M.cfg.theme + '/core/' + M.cfg.themerev;
|
||||
var icon = Y.Node.create('<img />');
|
||||
this.span.appendChild(icon);
|
||||
|
||||
// Set up button text and icon.
|
||||
var suffix = individual ? '_individual' : '_all';
|
||||
var setHidden = function() {
|
||||
icon.set('src', iconBase + '/i/show');
|
||||
icon.set('src', M.util.image_url('i/show', 'core'));
|
||||
icon.set('alt', M.str.availability['hidden' + suffix]);
|
||||
this.span.set('title', M.str.availability['hidden' + suffix] + ' \u2022 ' +
|
||||
M.str.availability.show_verb);
|
||||
};
|
||||
var setShown = function() {
|
||||
icon.set('src', iconBase + '/i/hide');
|
||||
icon.set('src', M.util.image_url('i/hide', 'core'));
|
||||
icon.set('alt', M.str.availability['shown' + suffix]);
|
||||
this.span.set('title', M.str.availability['shown' + suffix] + ' \u2022 ' +
|
||||
M.str.availability.hide_verb);
|
||||
@@ -1006,9 +1005,8 @@ M.core_availability.EyeIcon.prototype.isHidden = function() {
|
||||
M.core_availability.DeleteIcon = function(toDelete) {
|
||||
this.span = Y.Node.create('<a class="availability-delete" href="#" title="' +
|
||||
M.str.moodle['delete'] + '" role="button">');
|
||||
var img = Y.Node.create('<img src="' +
|
||||
M.cfg.wwwroot + '/theme/image.php/' + M.cfg.theme + '/core/' + M.cfg.themerev +
|
||||
'/t/delete" alt="' + M.str.moodle['delete'] + '" />');
|
||||
var img = Y.Node.create('<img src="' + M.util.image_url('t/delete', 'core') +
|
||||
'" alt="' + M.str.moodle['delete'] + '" />');
|
||||
this.span.appendChild(img);
|
||||
var click = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
Vendored
+4
-6
@@ -926,20 +926,19 @@ M.core_availability.Item.prototype.pluginNode = null;
|
||||
M.core_availability.EyeIcon = function(individual, shown) {
|
||||
this.individual = individual;
|
||||
this.span = Y.Node.create('<a class="availability-eye" href="#" role="button">');
|
||||
var iconBase = M.cfg.wwwroot + '/theme/image.php/' + M.cfg.theme + '/core/' + M.cfg.themerev;
|
||||
var icon = Y.Node.create('<img />');
|
||||
this.span.appendChild(icon);
|
||||
|
||||
// Set up button text and icon.
|
||||
var suffix = individual ? '_individual' : '_all';
|
||||
var setHidden = function() {
|
||||
icon.set('src', iconBase + '/i/show');
|
||||
icon.set('src', M.util.image_url('i/show', 'core'));
|
||||
icon.set('alt', M.str.availability['hidden' + suffix]);
|
||||
this.span.set('title', M.str.availability['hidden' + suffix] + ' \u2022 ' +
|
||||
M.str.availability.show_verb);
|
||||
};
|
||||
var setShown = function() {
|
||||
icon.set('src', iconBase + '/i/hide');
|
||||
icon.set('src', M.util.image_url('i/hide', 'core'));
|
||||
icon.set('alt', M.str.availability['shown' + suffix]);
|
||||
this.span.set('title', M.str.availability['shown' + suffix] + ' \u2022 ' +
|
||||
M.str.availability.hide_verb);
|
||||
@@ -1004,9 +1003,8 @@ M.core_availability.EyeIcon.prototype.isHidden = function() {
|
||||
M.core_availability.DeleteIcon = function(toDelete) {
|
||||
this.span = Y.Node.create('<a class="availability-delete" href="#" title="' +
|
||||
M.str.moodle['delete'] + '" role="button">');
|
||||
var img = Y.Node.create('<img src="' +
|
||||
M.cfg.wwwroot + '/theme/image.php/' + M.cfg.theme + '/core/' + M.cfg.themerev +
|
||||
'/t/delete" alt="' + M.str.moodle['delete'] + '" />');
|
||||
var img = Y.Node.create('<img src="' + M.util.image_url('t/delete', 'core') +
|
||||
'" alt="' + M.str.moodle['delete'] + '" />');
|
||||
this.span.appendChild(img);
|
||||
var click = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -338,7 +338,7 @@ class cc2moodle {
|
||||
|
||||
$replace_values = array($i,
|
||||
$i - 1,
|
||||
$topic['title'],
|
||||
entities::safexml($topic['title']),
|
||||
$node_node_course_sections_section_mods_mod);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -1292,7 +1292,9 @@ class moodle1_question_bank_handler extends moodle1_xml_handler {
|
||||
* Closes the questions wrapper
|
||||
*/
|
||||
public function on_questions_end() {
|
||||
$this->xmlwriter->end_tag('questions');
|
||||
if ($this->questionswrapperwritten) {
|
||||
$this->xmlwriter->end_tag('questions');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -564,6 +564,9 @@ class restore_update_availability extends restore_execution_step {
|
||||
rebuild_course_cache($this->get_courseid(), true);
|
||||
$modinfo = get_fast_modinfo($this->get_courseid());
|
||||
|
||||
// Get the date offset for this restore.
|
||||
$dateoffset = $this->apply_date_offset(1) - 1;
|
||||
|
||||
// Update all sections that were restored.
|
||||
$params = array('backupid' => $this->get_restoreid(), 'itemname' => 'course_section');
|
||||
$rs = $DB->get_recordset('backup_ids_temp', $params, '', 'newitemid');
|
||||
@@ -586,7 +589,7 @@ class restore_update_availability extends restore_execution_step {
|
||||
if (!is_null($section->availability)) {
|
||||
$info = new \core_availability\info_section($section);
|
||||
$info->update_after_restore($this->get_restoreid(),
|
||||
$this->get_courseid(), $this->get_logger());
|
||||
$this->get_courseid(), $this->get_logger(), $dateoffset);
|
||||
}
|
||||
}
|
||||
$rs->close();
|
||||
@@ -606,7 +609,7 @@ class restore_update_availability extends restore_execution_step {
|
||||
if (!is_null($cm->availability)) {
|
||||
$info = new \core_availability\info_module($cm);
|
||||
$info->update_after_restore($this->get_restoreid(),
|
||||
$this->get_courseid(), $this->get_logger());
|
||||
$this->get_courseid(), $this->get_logger(), $dateoffset);
|
||||
}
|
||||
}
|
||||
$rs->close();
|
||||
@@ -2114,7 +2117,13 @@ class restore_badges_structure_step extends restore_structure_step {
|
||||
|
||||
$data = (object)$data;
|
||||
$data->usercreated = $this->get_mappingid('user', $data->usercreated);
|
||||
if (empty($data->usercreated)) {
|
||||
$data->usercreated = $this->task->get_userid();
|
||||
}
|
||||
$data->usermodified = $this->get_mappingid('user', $data->usermodified);
|
||||
if (empty($data->usermodified)) {
|
||||
$data->usermodified = $this->task->get_userid();
|
||||
}
|
||||
|
||||
// We'll restore the badge image.
|
||||
$restorefiles = true;
|
||||
@@ -2211,6 +2220,12 @@ class restore_badges_structure_step extends restore_structure_step {
|
||||
'issuerrole' => $role,
|
||||
'datemet' => $this->apply_date_offset($data->datemet)
|
||||
);
|
||||
|
||||
// Skip the manual award if recipient or issuer can not be mapped to.
|
||||
if (empty($award['recipientid']) || empty($award['issuerid'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$DB->insert_record('badge_manual_award', $award);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,13 @@ require_once($CFG->libdir . '/completionlib.php');
|
||||
*/
|
||||
class core_backup_moodle2_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Tidy up open files that may be left open.
|
||||
*/
|
||||
protected function tearDown() {
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the availability field on modules and sections is correctly
|
||||
* backed up and restored.
|
||||
@@ -331,13 +338,67 @@ class core_backup_moodle2_testcase extends advanced_testcase {
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* When restoring a course, you can change the start date, which shifts other
|
||||
* dates. This test checks that certain dates are correctly modified.
|
||||
*/
|
||||
public function test_restore_dates() {
|
||||
global $DB, $CFG;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
$CFG->enableavailability = true;
|
||||
|
||||
// Create a course with specific start date.
|
||||
$generator = $this->getDataGenerator();
|
||||
$course = $generator->create_course(array(
|
||||
'startdate' => strtotime('1 Jan 2014 00:00 GMT')));
|
||||
|
||||
// Add a forum with conditional availability date restriction, including
|
||||
// one of them nested inside a tree.
|
||||
$availability = '{"op":"&","showc":[true,true],"c":[' .
|
||||
'{"op":"&","c":[{"type":"date","d":">=","t":DATE1}]},' .
|
||||
'{"type":"date","d":"<","t":DATE2}]}';
|
||||
$before = str_replace(
|
||||
array('DATE1', 'DATE2'),
|
||||
array(strtotime('1 Feb 2014 00:00 GMT'), strtotime('10 Feb 2014 00:00 GMT')),
|
||||
$availability);
|
||||
$forum = $generator->create_module('forum', array('course' => $course->id,
|
||||
'availability' => $before));
|
||||
|
||||
// Add an assign with defined start date.
|
||||
$assign = $generator->create_module('assign', array('course' => $course->id,
|
||||
'allowsubmissionsfromdate' => strtotime('7 Jan 2014 16:00 GMT')));
|
||||
|
||||
// Do backup and restore.
|
||||
$newcourseid = $this->backup_and_restore($course, strtotime('3 Jan 2015 00:00 GMT'));
|
||||
|
||||
$modinfo = get_fast_modinfo($newcourseid);
|
||||
|
||||
// Check forum dates are modified by the same amount as the course start.
|
||||
$newforums = $modinfo->get_instances_of('forum');
|
||||
$newforum = reset($newforums);
|
||||
$after = str_replace(
|
||||
array('DATE1', 'DATE2'),
|
||||
array(strtotime('3 Feb 2015 00:00 GMT'), strtotime('12 Feb 2015 00:00 GMT')),
|
||||
$availability);
|
||||
$this->assertEquals($after, $newforum->availability);
|
||||
|
||||
// Check assign date.
|
||||
$newassigns = $modinfo->get_instances_of('assign');
|
||||
$newassign = reset($newassigns);
|
||||
$this->assertEquals(strtotime('9 Jan 2015 16:00 GMT'), $DB->get_field(
|
||||
'assign', 'allowsubmissionsfromdate', array('id' => $newassign->instance)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Backs a course up and restores it.
|
||||
*
|
||||
* @param stdClass $course Course object to backup
|
||||
* @param int $newdate If non-zero, specifies custom date for new course
|
||||
* @return int ID of newly restored course
|
||||
*/
|
||||
protected function backup_and_restore($course) {
|
||||
protected function backup_and_restore($course, $newdate = 0) {
|
||||
global $USER, $CFG;
|
||||
|
||||
// Turn off file logging, otherwise it can't delete the file (Windows).
|
||||
@@ -358,6 +419,9 @@ class core_backup_moodle2_testcase extends advanced_testcase {
|
||||
$rc = new restore_controller($backupid, $newcourseid,
|
||||
backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id,
|
||||
backup::TARGET_NEW_COURSE);
|
||||
if ($newdate) {
|
||||
$rc->get_plan()->get_setting('course_startdate')->set_value($newdate);
|
||||
}
|
||||
$this->assertTrue($rc->execute_precheck());
|
||||
$rc->execute_plan();
|
||||
$rc->destroy();
|
||||
|
||||
@@ -45,6 +45,55 @@ abstract class restore_step extends base_step {
|
||||
}
|
||||
return $this->task->get_restoreid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply course startdate offset based in original course startdate and course_offset_startdate setting
|
||||
* Note we are using one static cache here, but *by restoreid*, so it's ok for concurrence/multiple
|
||||
* executions in the same request
|
||||
*
|
||||
* @param int $value Time value (seconds since epoch), or empty for nothing
|
||||
* @return int Time value after applying the date offset, or empty for nothing
|
||||
*/
|
||||
public function apply_date_offset($value) {
|
||||
|
||||
// Empties don't offset - zeros (int and string), false and nulls return original value.
|
||||
if (empty($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
static $cache = array();
|
||||
// Lookup cache.
|
||||
if (isset($cache[$this->get_restoreid()])) {
|
||||
return $value + $cache[$this->get_restoreid()];
|
||||
}
|
||||
// No cache, let's calculate the offset.
|
||||
$original = $this->task->get_info()->original_course_startdate;
|
||||
$setting = 0;
|
||||
if ($this->setting_exists('course_startdate')) { // Seting may not exist (MDL-25019).
|
||||
$setting = $this->get_setting_value('course_startdate');
|
||||
}
|
||||
|
||||
if (empty($original) || empty($setting)) {
|
||||
// Original course has not startdate or setting doesn't exist, offset = 0.
|
||||
$cache[$this->get_restoreid()] = 0;
|
||||
|
||||
} else if (abs($setting - $original) < 24 * 60 * 60) {
|
||||
// Less than 24h of difference, offset = 0 (this avoids some problems with timezones).
|
||||
$cache[$this->get_restoreid()] = 0;
|
||||
|
||||
} else if (!has_capability('moodle/restore:rolldates',
|
||||
context_course::instance($this->get_courseid()), $this->task->get_userid())) {
|
||||
// Re-enforce 'moodle/restore:rolldates' capability for the user in the course, just in case.
|
||||
$cache[$this->get_restoreid()] = 0;
|
||||
|
||||
} else {
|
||||
// Arrived here, let's calculate the real offset.
|
||||
$cache[$this->get_restoreid()] = $setting - $original;
|
||||
}
|
||||
|
||||
// Return the passed value with cached offset applied.
|
||||
return $value + $cache[$this->get_restoreid()];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -245,53 +245,6 @@ abstract class restore_structure_step extends restore_step {
|
||||
$this->task->add_result($resultstoadd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply course startdate offset based in original course startdate and course_offset_startdate setting
|
||||
* Note we are using one static cache here, but *by restoreid*, so it's ok for concurrence/multiple
|
||||
* executions in the same request
|
||||
*/
|
||||
public function apply_date_offset($value) {
|
||||
|
||||
// empties don't offset - zeros (int and string), false and nulls return original value
|
||||
if (empty($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
static $cache = array();
|
||||
// Lookup cache
|
||||
if (isset($cache[$this->get_restoreid()])) {
|
||||
return $value + $cache[$this->get_restoreid()];
|
||||
}
|
||||
// No cache, let's calculate the offset
|
||||
$original = $this->task->get_info()->original_course_startdate;
|
||||
$setting = 0;
|
||||
if ($this->setting_exists('course_startdate')) { // Seting may not exist (MDL-25019)
|
||||
$setting = $this->get_setting_value('course_startdate');
|
||||
}
|
||||
|
||||
// Original course has not startdate or setting doesn't exist, offset = 0
|
||||
if (empty($original) || empty($setting)) {
|
||||
$cache[$this->get_restoreid()] = 0;
|
||||
|
||||
// Less than 24h of difference, offset = 0 (this avoids some problems with timezones)
|
||||
} else if (abs($setting - $original) < 24 * 60 * 60) {
|
||||
$cache[$this->get_restoreid()] = 0;
|
||||
|
||||
// Re-enforce 'moodle/restore:rolldates' capability for the user in the course, just in case
|
||||
} else if (!has_capability('moodle/restore:rolldates',
|
||||
context_course::instance($this->get_courseid()),
|
||||
$this->task->get_userid())) {
|
||||
$cache[$this->get_restoreid()] = 0;
|
||||
|
||||
// Arrived here, let's calculate the real offset
|
||||
} else {
|
||||
$cache[$this->get_restoreid()] = $setting - $original;
|
||||
}
|
||||
|
||||
// Return the passed value with cached offset applied
|
||||
return $value + $cache[$this->get_restoreid()];
|
||||
}
|
||||
|
||||
/**
|
||||
* As far as restore structure steps are implementing restore_plugin stuff, they need to
|
||||
* have the parent task available for wrapping purposes (get course/context....)
|
||||
|
||||
@@ -81,6 +81,14 @@ $output = $PAGE->get_renderer('core', 'badges');
|
||||
// Roles that can award this badge.
|
||||
$acceptedroles = array_keys($badge->criteria[BADGE_CRITERIA_TYPE_MANUAL]->params);
|
||||
|
||||
if (empty($acceptedroles)) {
|
||||
echo $OUTPUT->header();
|
||||
$return = html_writer::link(new moodle_url('recipients.php', array('id' => $badge->id)), $strrecipients);
|
||||
echo $OUTPUT->notification(get_string('notacceptedrole', 'badges', $return));
|
||||
echo $OUTPUT->footer();
|
||||
die();
|
||||
}
|
||||
|
||||
if (count($acceptedroles) > 1) {
|
||||
// If there is more than one role that can award a badge, prompt user to make a selection.
|
||||
// If it is an admin, include all accepted roles, otherwise only the ones that current user has in this context.
|
||||
|
||||
@@ -150,6 +150,11 @@ class award_criteria_manual extends award_criteria {
|
||||
public function review($userid, $filtered = false) {
|
||||
global $DB;
|
||||
|
||||
// Roles should always have a parameter.
|
||||
if (empty($this->params)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Users were already filtered by criteria completion.
|
||||
if ($filtered) {
|
||||
return true;
|
||||
|
||||
@@ -55,7 +55,7 @@ Feature: Block activity modules
|
||||
And I click on "Forums" "link" in the "Activities" "block"
|
||||
And I should see "Frontpage forum name"
|
||||
And I am on homepage
|
||||
And I click on "External Tools" "link" in the "Activities" "block"
|
||||
And I click on "External tools" "link" in the "Activities" "block"
|
||||
And I should see "Frontpage lti name"
|
||||
And I am on homepage
|
||||
And I click on "Quizzes" "link" in the "Activities" "block"
|
||||
@@ -134,7 +134,7 @@ Feature: Block activity modules
|
||||
And I click on "Forums" "link" in the "Activities" "block"
|
||||
And I should see "Test forum name"
|
||||
And I follow "Course 1"
|
||||
And I click on "External Tools" "link" in the "Activities" "block"
|
||||
And I click on "External tools" "link" in the "Activities" "block"
|
||||
And I should see "Test lti name"
|
||||
And I follow "Course 1"
|
||||
And I click on "Quizzes" "link" in the "Activities" "block"
|
||||
|
||||
@@ -78,7 +78,7 @@ class block_badges extends block_base {
|
||||
}
|
||||
|
||||
// Number of badges to display.
|
||||
if (empty($this->config->numberofbadges)) {
|
||||
if (!isset($this->config->numberofbadges)) {
|
||||
$this->config->numberofbadges = 10;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,10 +59,34 @@ function block_course_overview_update_mynumber($number) {
|
||||
/**
|
||||
* Sets user course sorting preference in course_overview block
|
||||
*
|
||||
* @param array $sortorder sort order of course
|
||||
* @param array $sortorder list of course ids
|
||||
*/
|
||||
function block_course_overview_update_myorder($sortorder) {
|
||||
set_user_preference('course_overview_course_order', serialize($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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets user course sorting preference in course_overview block
|
||||
*
|
||||
* @return array list of course ids
|
||||
*/
|
||||
function block_course_overview_get_myorder() {
|
||||
if ($value = get_user_preferences('course_overview_course_sortorder')) {
|
||||
return explode(',', $value);
|
||||
}
|
||||
// If preference was not found, look in the old location and convert if found.
|
||||
$order = array();
|
||||
if ($value = get_user_preferences('course_overview_course_order')) {
|
||||
$order = unserialize($value);
|
||||
block_course_overview_update_myorder($order);
|
||||
unset_user_preference('course_overview_course_order');
|
||||
}
|
||||
return $order;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,10 +191,7 @@ function block_course_overview_get_sorted_courses($showallcourses = false) {
|
||||
$courses[$remoteid] = $val;
|
||||
}
|
||||
|
||||
$order = array();
|
||||
if (!is_null($usersortorder = get_user_preferences('course_overview_course_order'))) {
|
||||
$order = unserialize($usersortorder);
|
||||
}
|
||||
$order = block_course_overview_get_myorder();
|
||||
|
||||
$sortedcourses = array();
|
||||
$counter = 0;
|
||||
|
||||
@@ -245,12 +245,5 @@ class block_glossary_random extends block_base {
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
function hide_header() {
|
||||
if (empty($this->config->title)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -294,12 +294,12 @@ class block_recent_activity extends block_base {
|
||||
if (!isset($modinfo->instances[$modname][$instanceid])) {
|
||||
continue;
|
||||
}
|
||||
$entry['cmid'] = $modinfo->instances[$modname][$instanceid]->id;
|
||||
if ($log->action == 'add mod') {
|
||||
$entry['action'] = 0;
|
||||
} else {
|
||||
$entry['action'] = 1;
|
||||
}
|
||||
$entry['cmid'] = $modinfo->instances[$modname][$instanceid]->id;
|
||||
}
|
||||
$entries[] = $entry;
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ class block_site_main_menu extends block_list {
|
||||
} else {
|
||||
$editbuttons = '';
|
||||
}
|
||||
if ($mod->visible || has_capability('moodle/course:viewhiddenactivities', $context)) {
|
||||
if ($mod->visible || has_capability('moodle/course:viewhiddenactivities', $mod->context)) {
|
||||
if ($ismoving) {
|
||||
if ($mod->id == $USER->activitycopy) {
|
||||
continue;
|
||||
|
||||
@@ -130,7 +130,7 @@ class block_social_activities extends block_list {
|
||||
} else {
|
||||
$editbuttons = '';
|
||||
}
|
||||
if ($mod->visible || has_capability('moodle/course:viewhiddenactivities', $context)) {
|
||||
if ($mod->visible || has_capability('moodle/course:viewhiddenactivities', $mod->context)) {
|
||||
if ($ismoving) {
|
||||
if ($mod->id == $USER->activitycopy) {
|
||||
continue;
|
||||
@@ -169,5 +169,3 @@ class block_social_activities extends block_list {
|
||||
return $this->content;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+38
-2
@@ -87,6 +87,12 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
|
||||
*/
|
||||
protected $definition;
|
||||
|
||||
/**
|
||||
* If true data going in and out will be encoded.
|
||||
* @var bool
|
||||
*/
|
||||
protected $encode = true;
|
||||
|
||||
/**
|
||||
* Default prefix for key names.
|
||||
* @var string
|
||||
@@ -150,6 +156,24 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
|
||||
}
|
||||
$this->definition = $definition;
|
||||
$this->isinitialised = true;
|
||||
$this->encode = self::require_encoding();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if encoding is going to be required.
|
||||
*
|
||||
* Prior to memcache 3.0.3 scalar data types were not preserved.
|
||||
* For earlier versions of the memcache extension we need to encode and decode scalar types
|
||||
* to ensure that it is preserved.
|
||||
*
|
||||
* @param string $version The version to check, if null it is fetched from PHP.
|
||||
* @return bool
|
||||
*/
|
||||
public static function require_encoding($version = null) {
|
||||
if (!$version) {
|
||||
$version = phpversion('memcache');
|
||||
}
|
||||
return (version_compare($version, '3.0.3', '<'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,7 +264,11 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
|
||||
* @return mixed The data that was associated with the key, or false if the key did not exist.
|
||||
*/
|
||||
public function get($key) {
|
||||
return $this->connection->get($this->parse_key($key));
|
||||
$result = $this->connection->get($this->parse_key($key));
|
||||
if ($this->encode && $result !== false) {
|
||||
return @unserialize($result);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,6 +295,9 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
|
||||
$return[$key] = false;
|
||||
} else {
|
||||
$return[$key] = $result[$mkey];
|
||||
if ($this->encode && $return[$key] !== false) {
|
||||
$return[$key] = @unserialize($return[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
@@ -280,6 +311,11 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
|
||||
* @return bool True if the operation was a success false otherwise.
|
||||
*/
|
||||
public function set($key, $data) {
|
||||
if ($this->encode) {
|
||||
// We must serialise this data.
|
||||
$data = serialize($data);
|
||||
}
|
||||
|
||||
return $this->connection->set($this->parse_key($key), $data, MEMCACHE_COMPRESSED, $this->definition->get_ttl());
|
||||
}
|
||||
|
||||
@@ -294,7 +330,7 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
|
||||
public function set_many(array $keyvaluearray) {
|
||||
$count = 0;
|
||||
foreach ($keyvaluearray as $pair) {
|
||||
if ($this->connection->set($this->parse_key($pair['key']), $pair['value'], MEMCACHE_COMPRESSED, $this->definition->get_ttl())) {
|
||||
if ($this->set($pair['key'], $pair['value'])) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
+26
@@ -90,4 +90,30 @@ class cachestore_memcache_test extends cachestore_tests {
|
||||
$this->assertEquals($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test our checks for encoding.
|
||||
*/
|
||||
public function test_require_encoding() {
|
||||
$this->assertTrue(cachestore_memcache::require_encoding('dev'));
|
||||
$this->assertTrue(cachestore_memcache::require_encoding('1.0'));
|
||||
$this->assertTrue(cachestore_memcache::require_encoding('1.0.0'));
|
||||
$this->assertTrue(cachestore_memcache::require_encoding('2.0'));
|
||||
$this->assertTrue(cachestore_memcache::require_encoding('2.0.8'));
|
||||
$this->assertTrue(cachestore_memcache::require_encoding('2.2.8'));
|
||||
$this->assertTrue(cachestore_memcache::require_encoding('3.0'));
|
||||
$this->assertTrue(cachestore_memcache::require_encoding('3.0-dev'));
|
||||
$this->assertTrue(cachestore_memcache::require_encoding('3.0.0'));
|
||||
$this->assertTrue(cachestore_memcache::require_encoding('3.0.1'));
|
||||
$this->assertTrue(cachestore_memcache::require_encoding('3.0.2-dev'));
|
||||
$this->assertTrue(cachestore_memcache::require_encoding('3.0.2'));
|
||||
$this->assertTrue(cachestore_memcache::require_encoding('3.0.3-dev'));
|
||||
$this->assertFalse(cachestore_memcache::require_encoding('3.0.3'));
|
||||
$this->assertFalse(cachestore_memcache::require_encoding('3.0.4'));
|
||||
$this->assertFalse(cachestore_memcache::require_encoding('3.0.4-dev'));
|
||||
$this->assertFalse(cachestore_memcache::require_encoding('3.0.8'));
|
||||
$this->assertFalse(cachestore_memcache::require_encoding('3.1.0'));
|
||||
$this->assertFalse(cachestore_memcache::require_encoding('3.1.2'));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+5
-2
@@ -247,16 +247,19 @@ class cachestore_memcached extends cache_store implements cache_is_configurable
|
||||
* be set to false.
|
||||
*/
|
||||
public function get_many($keys) {
|
||||
$return = array();
|
||||
$result = $this->connection->getMulti($keys);
|
||||
if (!is_array($result)) {
|
||||
$result = array();
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
if (!array_key_exists($key, $result)) {
|
||||
$result[$key] = false;
|
||||
$return[$key] = false;
|
||||
} else {
|
||||
$return[$key] = $result[$key];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+3
@@ -556,6 +556,9 @@ class cachestore_mongodb extends cache_store implements cache_is_configurable {
|
||||
}
|
||||
|
||||
$store = new cachestore_mongodb('Test mongodb', $configuration);
|
||||
if (!$store->is_ready()) {
|
||||
return false;
|
||||
}
|
||||
$store->initialise($definition);
|
||||
|
||||
return $store;
|
||||
|
||||
Vendored
+31
-11
@@ -86,19 +86,39 @@ abstract class cachestore_tests extends advanced_testcase {
|
||||
*/
|
||||
public function run_tests(cache_store $instance) {
|
||||
|
||||
// Test set.
|
||||
// Test set with a string.
|
||||
$this->assertTrue($instance->set('test1', 'test1'));
|
||||
$this->assertTrue($instance->set('test2', 'test2'));
|
||||
$this->assertTrue($instance->set('test3', '3'));
|
||||
|
||||
// Test get.
|
||||
$this->assertEquals('test1', $instance->get('test1'));
|
||||
$this->assertEquals('test2', $instance->get('test2'));
|
||||
// Test get with a string.
|
||||
$this->assertSame('test1', $instance->get('test1'));
|
||||
$this->assertSame('test2', $instance->get('test2'));
|
||||
$this->assertSame('3', $instance->get('test3'));
|
||||
|
||||
// Test set with an int.
|
||||
$this->assertTrue($instance->set('test1', 1));
|
||||
$this->assertTrue($instance->set('test2', 2));
|
||||
|
||||
// Test get with an int.
|
||||
$this->assertSame(1, $instance->get('test1'));
|
||||
$this->assertInternalType('int', $instance->get('test1'));
|
||||
$this->assertSame(2, $instance->get('test2'));
|
||||
$this->assertInternalType('int', $instance->get('test2'));
|
||||
|
||||
// Test set with a bool.
|
||||
$this->assertTrue($instance->set('test1', true));
|
||||
|
||||
// Test get with an bool.
|
||||
$this->assertSame(true, $instance->get('test1'));
|
||||
$this->assertInternalType('boolean', $instance->get('test1'));
|
||||
|
||||
// Test delete.
|
||||
$this->assertTrue($instance->delete('test1'));
|
||||
$this->assertTrue($instance->delete('test3'));
|
||||
$this->assertFalse($instance->delete('test3'));
|
||||
$this->assertFalse($instance->get('test1'));
|
||||
$this->assertEquals('test2', $instance->get('test2'));
|
||||
$this->assertSame(2, $instance->get('test2'));
|
||||
$this->assertTrue($instance->set('test1', 'test1'));
|
||||
|
||||
// Test purge.
|
||||
@@ -114,16 +134,16 @@ abstract class cachestore_tests extends advanced_testcase {
|
||||
array('key' => 'many4', 'value' => 'many4'),
|
||||
array('key' => 'many5', 'value' => 'many5')
|
||||
));
|
||||
$this->assertEquals(5, $outcome);
|
||||
$this->assertEquals('many1', $instance->get('many1'));
|
||||
$this->assertEquals('many5', $instance->get('many5'));
|
||||
$this->assertSame(5, $outcome);
|
||||
$this->assertSame('many1', $instance->get('many1'));
|
||||
$this->assertSame('many5', $instance->get('many5'));
|
||||
$this->assertFalse($instance->get('many6'));
|
||||
|
||||
// Test get_many.
|
||||
$result = $instance->get_many(array('many1', 'many3', 'many5', 'many6'));
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertCount(4, $result);
|
||||
$this->assertEquals(array(
|
||||
$this->assertSame(array(
|
||||
'many1' => 'many1',
|
||||
'many3' => 'many3',
|
||||
'many5' => 'many5',
|
||||
@@ -131,7 +151,7 @@ abstract class cachestore_tests extends advanced_testcase {
|
||||
), $result);
|
||||
|
||||
// Test delete_many.
|
||||
$this->assertEquals(3, $instance->delete_many(array('many2', 'many3', 'many4')));
|
||||
$this->assertEquals(2, $instance->delete_many(array('many1', 'many5', 'many6')));
|
||||
$this->assertSame(3, $instance->delete_many(array('many2', 'many3', 'many4')));
|
||||
$this->assertSame(2, $instance->delete_many(array('many1', 'many5', 'many6')));
|
||||
}
|
||||
}
|
||||
@@ -175,7 +175,7 @@ $ical->add_property('method', 'PUBLISH');
|
||||
foreach($events as $event) {
|
||||
if (!empty($event->modulename)) {
|
||||
$cm = get_coursemodule_from_instance($event->modulename, $event->instance);
|
||||
if (!\core_availability\info_module::is_user_visible($cm, 0, false)) {
|
||||
if (!\core_availability\info_module::is_user_visible($cm, $userid, false)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
+47
-34
@@ -318,9 +318,9 @@ function calendar_get_mini($courses, $groups, $users, $calmonth = false, $calyea
|
||||
}
|
||||
|
||||
// Now display all the calendar
|
||||
$daytime = $display->tstart - DAYSECS;
|
||||
$daytime = strtotime('-1 day', $display->tstart);
|
||||
for($day = 1; $day <= $display->maxdays; ++$day, ++$dayweek) {
|
||||
$daytime += DAYSECS;
|
||||
$daytime = strtotime('+1 day', $daytime);
|
||||
if($dayweek > $display->maxwday) {
|
||||
// We need to change week (table row)
|
||||
$content .= '</tr><tr>';
|
||||
@@ -683,19 +683,18 @@ function calendar_get_events($tstart, $tend, $users, $groups, $courses, $withdur
|
||||
global $DB;
|
||||
|
||||
$whereclause = '';
|
||||
$params = array();
|
||||
// Quick test
|
||||
if(is_bool($users) && is_bool($groups) && is_bool($courses)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if(is_array($users) && !empty($users)) {
|
||||
if ((is_array($users) && !empty($users)) or is_numeric($users)) {
|
||||
// Events from a number of users
|
||||
if(!empty($whereclause)) $whereclause .= ' OR';
|
||||
$whereclause .= ' (userid IN ('.implode(',', $users).') AND courseid = 0 AND groupid = 0)';
|
||||
} else if(is_numeric($users)) {
|
||||
// Events from one user
|
||||
if(!empty($whereclause)) $whereclause .= ' OR';
|
||||
$whereclause .= ' (userid = '.$users.' AND courseid = 0 AND groupid = 0)';
|
||||
list($insqlusers, $inparamsusers) = $DB->get_in_or_equal($users, SQL_PARAMS_NAMED);
|
||||
$whereclause .= " (userid $insqlusers AND courseid = 0 AND groupid = 0)";
|
||||
$params = array_merge($params, $inparamsusers);
|
||||
} else if($users === true) {
|
||||
// Events from ALL users
|
||||
if(!empty($whereclause)) $whereclause .= ' OR';
|
||||
@@ -704,14 +703,12 @@ function calendar_get_events($tstart, $tend, $users, $groups, $courses, $withdur
|
||||
// No user at all, do nothing
|
||||
}
|
||||
|
||||
if(is_array($groups) && !empty($groups)) {
|
||||
if ((is_array($groups) && !empty($groups)) or is_numeric($groups)) {
|
||||
// Events from a number of groups
|
||||
if(!empty($whereclause)) $whereclause .= ' OR';
|
||||
$whereclause .= ' groupid IN ('.implode(',', $groups).')';
|
||||
} else if(is_numeric($groups)) {
|
||||
// Events from one group
|
||||
if(!empty($whereclause)) $whereclause .= ' OR ';
|
||||
$whereclause .= ' groupid = '.$groups;
|
||||
list($insqlgroups, $inparamsgroups) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED);
|
||||
$whereclause .= " groupid $insqlgroups ";
|
||||
$params = array_merge($params, $inparamsgroups);
|
||||
} else if($groups === true) {
|
||||
// Events from ALL groups
|
||||
if(!empty($whereclause)) $whereclause .= ' OR ';
|
||||
@@ -719,15 +716,11 @@ function calendar_get_events($tstart, $tend, $users, $groups, $courses, $withdur
|
||||
}
|
||||
// boolean false (no groups at all): we don't need to do anything
|
||||
|
||||
if(is_array($courses) && !empty($courses)) {
|
||||
if(!empty($whereclause)) {
|
||||
$whereclause .= ' OR';
|
||||
}
|
||||
$whereclause .= ' (groupid = 0 AND courseid IN ('.implode(',', $courses).'))';
|
||||
} else if(is_numeric($courses)) {
|
||||
// One course
|
||||
if ((is_array($courses) && !empty($courses)) or is_numeric($courses)) {
|
||||
if(!empty($whereclause)) $whereclause .= ' OR';
|
||||
$whereclause .= ' (groupid = 0 AND courseid = '.$courses.')';
|
||||
list($insqlcourses, $inparamscourses) = $DB->get_in_or_equal($courses, SQL_PARAMS_NAMED);
|
||||
$whereclause .= " (groupid = 0 AND courseid $insqlcourses)";
|
||||
$params = array_merge($params, $inparamscourses);
|
||||
} else if ($courses === true) {
|
||||
// Events from ALL courses
|
||||
if(!empty($whereclause)) $whereclause .= ' OR';
|
||||
@@ -761,7 +754,7 @@ function calendar_get_events($tstart, $tend, $users, $groups, $courses, $withdur
|
||||
$whereclause .= ' AND visible = 1';
|
||||
}
|
||||
|
||||
$events = $DB->get_records_select('event', $whereclause, null, 'timestart');
|
||||
$events = $DB->get_records_select('event', $whereclause, $params, 'timestart');
|
||||
if ($events === false) {
|
||||
$events = array();
|
||||
}
|
||||
@@ -795,7 +788,7 @@ function calendar_get_events_by_id($eventids) {
|
||||
* @return string $content return available control for the calender in html
|
||||
*/
|
||||
function calendar_top_controls($type, $data) {
|
||||
global $PAGE;
|
||||
global $PAGE, $OUTPUT;
|
||||
|
||||
// Get the calendar type we are using.
|
||||
$calendartype = \core_calendar\type_factory::get_calendar_instance();
|
||||
@@ -923,7 +916,8 @@ function calendar_top_controls($type, $data) {
|
||||
}
|
||||
|
||||
$content .= html_writer::start_tag('div', array('class'=>'calendar-controls'));
|
||||
$content .= $left . '<span class="hide"> | </span><h1 class="current">'.userdate($time, get_string('strftimemonthyear'))."</h1>";
|
||||
$content .= $left . '<span class="hide"> | </span>';
|
||||
$content .= $OUTPUT->heading(userdate($time, get_string('strftimemonthyear')), 2, 'current');
|
||||
$content .= '<span class="hide"> | </span>' . $right;
|
||||
$content .= '<span class="clearer"><!-- --></span>';
|
||||
$content .= html_writer::end_tag('div')."\n";
|
||||
@@ -931,8 +925,8 @@ function calendar_top_controls($type, $data) {
|
||||
case 'day':
|
||||
$days = calendar_get_days();
|
||||
|
||||
$prevtimestamp = $time - DAYSECS;
|
||||
$nexttimestamp = $time + DAYSECS;
|
||||
$prevtimestamp = strtotime('-1 day', $time);
|
||||
$nexttimestamp = strtotime('+1 day', $time);
|
||||
|
||||
$prevdate = $calendartype->timestamp_to_date_array($prevtimestamp);
|
||||
$nextdate = $calendartype->timestamp_to_date_array($nexttimestamp);
|
||||
@@ -1678,7 +1672,7 @@ function calendar_format_event_time($event, $now, $linkparams = null, $usecommon
|
||||
$timeend = calendar_time_representation($event->timestart + $event->timeduration);
|
||||
|
||||
// Set printable representation.
|
||||
if ($now >= $usermidnightstart && $now < ($usermidnightstart + DAYSECS)) {
|
||||
if ($now >= $usermidnightstart && $now < strtotime('+1 day', $usermidnightstart)) {
|
||||
$url = calendar_get_link_href(new moodle_url(CALENDAR_URL . 'view.php', $linkparams), 0, 0, 0, $endtime);
|
||||
$eventtime = $timestart . ' <strong>»</strong> ' . html_writer::link($url, $dayend) . $timeend;
|
||||
} else {
|
||||
@@ -2774,7 +2768,7 @@ class calendar_information {
|
||||
* @return int tomorrow timestamp
|
||||
*/
|
||||
public function timestamp_tomorrow() {
|
||||
return $this->time + DAYSECS;
|
||||
return strtotime('+1 day', $this->time);
|
||||
}
|
||||
/**
|
||||
* Adds the pretend blocks for the calendar
|
||||
@@ -2887,10 +2881,11 @@ function calendar_add_subscription($sub) {
|
||||
* @param stdClass $event The RFC-2445 iCalendar event
|
||||
* @param int $courseid The course ID
|
||||
* @param int $subscriptionid The iCalendar subscription ID
|
||||
* @param string $timezone The X-WR-TIMEZONE iCalendar property if provided
|
||||
* @throws dml_exception A DML specific exception is thrown for invalid subscriptionids.
|
||||
* @return int Code: CALENDAR_IMPORT_EVENT_UPDATED = updated, CALENDAR_IMPORT_EVENT_INSERTED = inserted, 0 = error
|
||||
*/
|
||||
function calendar_add_icalendar_event($event, $courseid, $subscriptionid) {
|
||||
function calendar_add_icalendar_event($event, $courseid, $subscriptionid, $timezone='UTC') {
|
||||
global $DB;
|
||||
|
||||
// Probably an unsupported X-MICROSOFT-CDO-BUSYSTATUS event.
|
||||
@@ -2923,15 +2918,27 @@ function calendar_add_icalendar_event($event, $courseid, $subscriptionid) {
|
||||
|
||||
$defaulttz = date_default_timezone_get();
|
||||
$tz = isset($event->properties['DTSTART'][0]->parameters['TZID']) ? $event->properties['DTSTART'][0]->parameters['TZID'] :
|
||||
'UTC';
|
||||
$timezone;
|
||||
$eventrecord->timestart = strtotime($event->properties['DTSTART'][0]->value . ' ' . $tz);
|
||||
if (empty($event->properties['DTEND'])) {
|
||||
$eventrecord->timeduration = 3600; // one hour if no end time specified
|
||||
$eventrecord->timeduration = 0; // no duration if no end time specified
|
||||
} else {
|
||||
$endtz = isset($event->properties['DTEND'][0]->parameters['TZID']) ? $event->properties['DTEND'][0]->parameters['TZID'] :
|
||||
'UTC';
|
||||
$timezone;
|
||||
$eventrecord->timeduration = strtotime($event->properties['DTEND'][0]->value . ' ' . $endtz) - $eventrecord->timestart;
|
||||
}
|
||||
|
||||
// Check to see if it should be treated as an all day event.
|
||||
if ($eventrecord->timeduration == DAYSECS) {
|
||||
// Check to see if the event started at Midnight on the imported calendar.
|
||||
date_default_timezone_set($timezone);
|
||||
if (date('H:i:s', $eventrecord->timestart) === "00:00:00") {
|
||||
// This event should be an all day event.
|
||||
$eventrecord->timeduration = 0;
|
||||
}
|
||||
date_default_timezone_set($defaulttz);
|
||||
}
|
||||
|
||||
$eventrecord->uuid = $event->properties['UID'][0]->value;
|
||||
$eventrecord->timemodified = time();
|
||||
|
||||
@@ -3065,8 +3072,14 @@ function calendar_import_icalendar_events($ical, $courseid, $subscriptionid = nu
|
||||
$sql = "UPDATE {event} SET timemodified = :time WHERE subscriptionid = :id";
|
||||
$DB->execute($sql, array('time' => 0, 'id' => $subscriptionid));
|
||||
}
|
||||
// Grab the timezone from the iCalendar file to be used later.
|
||||
if (isset($ical->properties['X-WR-TIMEZONE'][0]->value)) {
|
||||
$timezone = $ical->properties['X-WR-TIMEZONE'][0]->value;
|
||||
} else {
|
||||
$timezone = 'UTC';
|
||||
}
|
||||
foreach ($ical->components['VEVENT'] as $event) {
|
||||
$res = calendar_add_icalendar_event($event, $courseid, $subscriptionid);
|
||||
$res = calendar_add_icalendar_event($event, $courseid, $subscriptionid, $timezone);
|
||||
switch ($res) {
|
||||
case CALENDAR_IMPORT_EVENT_UPDATED:
|
||||
$updatecount++;
|
||||
|
||||
@@ -503,9 +503,9 @@ class core_calendar_renderer extends plugin_renderer_base {
|
||||
$weekend = intval($CFG->calendar_weekend);
|
||||
}
|
||||
|
||||
$daytime = $display->tstart - DAYSECS;
|
||||
$daytime = strtotime('-1 day', $display->tstart);
|
||||
for ($day = 1; $day <= $display->maxdays; ++$day, ++$dayweek) {
|
||||
$daytime = $daytime + DAYSECS;
|
||||
$daytime = strtotime('+1 day', $daytime);
|
||||
if($dayweek > $display->maxwday) {
|
||||
// We need to change week (table row)
|
||||
$table->data[] = $row;
|
||||
|
||||
@@ -139,6 +139,10 @@ $CFG->admin = 'admin';
|
||||
// any existing key.
|
||||
// $CFG->mnetkeylifetime = 28;
|
||||
//
|
||||
// Not recommended: Set the following to true to allow the use
|
||||
// off non-Moodle standard characters in usernames.
|
||||
// $CFG->extendedusernamechars = true;
|
||||
//
|
||||
// Allow user passwords to be included in backup files. Very dangerous
|
||||
// setting as far as it publishes password hashes that can be unencrypted
|
||||
// if the backup file is publicy available. Use it only if you can guarantee
|
||||
|
||||
@@ -1180,6 +1180,15 @@ class core_course_management_renderer extends plugin_renderer_base {
|
||||
array('class' => 'action-edit')
|
||||
);
|
||||
}
|
||||
// Delete.
|
||||
if ($course->can_delete()) {
|
||||
$actions[] = $this->output->action_icon(
|
||||
new moodle_url('/course/delete.php', array('id' => $course->id)),
|
||||
new pix_icon('t/delete', get_string('delete')),
|
||||
null,
|
||||
array('class' => 'action-delete')
|
||||
);
|
||||
}
|
||||
// Show/Hide.
|
||||
if ($course->can_change_visibility()) {
|
||||
$actions[] = $this->output->action_icon(
|
||||
|
||||
@@ -101,6 +101,11 @@ class dndupload_handler {
|
||||
*/
|
||||
protected $filehandlers = array();
|
||||
|
||||
/**
|
||||
* @var context_course|null
|
||||
*/
|
||||
protected $context = null;
|
||||
|
||||
/**
|
||||
* Gather a list of dndupload handlers from the different mods
|
||||
*
|
||||
@@ -119,6 +124,8 @@ class dndupload_handler {
|
||||
$this->register_type('text', array('text', 'text/plain'), get_string('addpagehere', 'moodle'),
|
||||
get_string('nameforpage', 'moodle'), get_string('whatforpage', 'moodle'), 30);
|
||||
|
||||
$this->context = context_course::instance($course->id);
|
||||
|
||||
// Loop through all modules to find handlers.
|
||||
$mods = get_plugin_list_with_function('mod', 'dndupload_register');
|
||||
foreach ($mods as $component => $funcname) {
|
||||
@@ -335,7 +342,7 @@ class dndupload_handler {
|
||||
}
|
||||
|
||||
$ret->filehandlers = $this->filehandlers;
|
||||
$uploadrepo = repository::get_instances(array('type' => 'upload'));
|
||||
$uploadrepo = repository::get_instances(array('type' => 'upload', 'currentcontext' => $this->context));
|
||||
if (empty($uploadrepo)) {
|
||||
$ret->filehandlers = array(); // No upload repo => no file handlers.
|
||||
}
|
||||
@@ -481,7 +488,7 @@ class dndupload_ajax_processor {
|
||||
$draftitemid = file_get_unused_draft_itemid();
|
||||
$maxbytes = get_max_upload_file_size($CFG->maxbytes, $this->course->maxbytes);
|
||||
$types = $this->dnduploadhandler->get_handled_file_types($this->module->name);
|
||||
$repo = repository::get_instances(array('type' => 'upload'));
|
||||
$repo = repository::get_instances(array('type' => 'upload', 'currentcontext' => $this->context));
|
||||
if (empty($repo)) {
|
||||
throw new moodle_exception('errornouploadrepo', 'moodle');
|
||||
}
|
||||
|
||||
@@ -69,8 +69,7 @@ class format_singleactivity extends format_base {
|
||||
public function extend_course_navigation($navigation, navigation_node $node) {
|
||||
// Display orphaned activities for the users who can see them.
|
||||
$context = context_course::instance($this->courseid);
|
||||
if (has_all_capabilities(array('moodle/course:viewhiddensections',
|
||||
'moodle/course:viewhiddenactivities'), $context)) {
|
||||
if (has_capability('moodle/course:viewhiddensections', $context)) {
|
||||
$modinfo = get_fast_modinfo($this->courseid);
|
||||
if (!empty($modinfo->sections[1])) {
|
||||
$section1 = $modinfo->get_section_info(1);
|
||||
@@ -80,7 +79,9 @@ class format_singleactivity extends format_base {
|
||||
$orphanednode->nodetype = navigation_node::NODETYPE_BRANCH;
|
||||
$orphanednode->add_class('orphaned');
|
||||
foreach ($modinfo->sections[1] as $cmid) {
|
||||
$this->navigation_add_activity($orphanednode, $modinfo->cms[$cmid]);
|
||||
if (has_capability('moodle/course:viewhiddenactivities', context_module::instance($cmid))) {
|
||||
$this->navigation_add_activity($orphanednode, $modinfo->cms[$cmid]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,15 @@ function add_moduleinfo($moduleinfo, $course, $mform = null) {
|
||||
} else if (property_exists($moduleinfo, 'availability')) {
|
||||
$newcm->availability = $moduleinfo->availability;
|
||||
}
|
||||
// If there is any availability data, verify it.
|
||||
if ($newcm->availability) {
|
||||
$tree = new \core_availability\tree(json_decode($newcm->availability));
|
||||
// Save time and database space by setting null if the only data
|
||||
// is an empty tree.
|
||||
if ($tree->is_empty()) {
|
||||
$newcm->availability = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($moduleinfo->showdescription)) {
|
||||
$newcm->showdescription = $moduleinfo->showdescription;
|
||||
@@ -502,6 +511,15 @@ function update_moduleinfo($cm, $moduleinfo, $course, $mform = null) {
|
||||
} else if (property_exists($moduleinfo, 'availability')) {
|
||||
$cm->availability = $moduleinfo->availability;
|
||||
}
|
||||
// If there is any availability data, verify it.
|
||||
if ($cm->availability) {
|
||||
$tree = new \core_availability\tree(json_decode($cm->availability));
|
||||
// Save time and database space by setting null if the only data
|
||||
// is an empty tree.
|
||||
if ($tree->is_empty()) {
|
||||
$cm->availability = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($moduleinfo->showdescription)) {
|
||||
$cm->showdescription = $moduleinfo->showdescription;
|
||||
|
||||
@@ -295,6 +295,14 @@ abstract class moodleform_mod extends moodleform {
|
||||
}
|
||||
}
|
||||
|
||||
// Ratings: Don't let them select an aggregate type without selecting a scale.
|
||||
// If the user has selected to use ratings but has not chosen a scale or set max points then the form is
|
||||
// invalid. If ratings have been selected then the user must select either a scale or max points.
|
||||
// This matches (horrible) logic in data_preprocessing.
|
||||
if (isset($data['assessed']) && $data['assessed'] > 0 && empty($data['scale'])) {
|
||||
$errors['assessed'] = get_string('scaleselectionrequired', 'rating');
|
||||
}
|
||||
|
||||
// Completion: Don't let them choose automatic completion without turning
|
||||
// on some conditions. Ignore this check when completion settings are
|
||||
// locked, as the options are then disabled.
|
||||
|
||||
+18
-17
@@ -768,8 +768,7 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
if ($mod->uservisible) {
|
||||
$conditionalhidden = $this->is_cm_conditionally_hidden($mod);
|
||||
$accessiblebutdim = (!$mod->visible || $conditionalhidden) &&
|
||||
has_capability('moodle/course:viewhiddenactivities',
|
||||
context_course::instance($mod->course));
|
||||
has_capability('moodle/course:viewhiddenactivities', $mod->context);
|
||||
if ($accessiblebutdim) {
|
||||
$linkclasses .= ' dimmed';
|
||||
$textclasses .= ' dimmed_text';
|
||||
@@ -831,8 +830,7 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
if ($mod->uservisible) {
|
||||
$conditionalhidden = $this->is_cm_conditionally_hidden($mod);
|
||||
$accessiblebutdim = (!$mod->visible || $conditionalhidden) &&
|
||||
has_capability('moodle/course:viewhiddenactivities',
|
||||
context_course::instance($mod->course));
|
||||
has_capability('moodle/course:viewhiddenactivities', $mod->context);
|
||||
if ($accessiblebutdim) {
|
||||
$textclasses .= ' dimmed_text';
|
||||
if ($conditionalhidden) {
|
||||
@@ -851,8 +849,15 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
trim('contentafterlink ' . $textclasses)));
|
||||
}
|
||||
} else {
|
||||
$groupinglabel = '';
|
||||
if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', context_course::instance($mod->course))) {
|
||||
$groupings = groups_get_all_groupings($mod->course);
|
||||
$groupinglabel = html_writer::tag('span', '('.format_string($groupings[$mod->groupingid]->name).')',
|
||||
array('class' => 'groupinglabel '.$textclasses));
|
||||
}
|
||||
|
||||
// No link, so display only content.
|
||||
$output = html_writer::tag('div', $accesstext . $content,
|
||||
$output = html_writer::tag('div', $accesstext . $content . $groupinglabel,
|
||||
array('class' => 'contentwithoutlink ' . $textclasses));
|
||||
}
|
||||
return $output;
|
||||
@@ -1675,13 +1680,12 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
$site = get_site();
|
||||
$output = '';
|
||||
|
||||
$this->page->set_button($this->course_search_form('', 'navbar'));
|
||||
if (can_edit_in_category($category)) {
|
||||
// Add 'Manage' button if user has permissions to edit this category.
|
||||
$managebutton = $this->single_button(new moodle_url('/course/management.php'), get_string('managecourses'), 'get');
|
||||
$this->page->set_button($managebutton);
|
||||
}
|
||||
if (!$coursecat->id) {
|
||||
if (can_edit_in_category()) {
|
||||
// add 'Manage' button instead of course search form
|
||||
$managebutton = $this->single_button(new moodle_url('/course/management.php'), get_string('managecourses'), 'get');
|
||||
$this->page->set_button($managebutton);
|
||||
}
|
||||
if (coursecat::count_all() == 1) {
|
||||
// There exists only one category in the system, do not display link to it
|
||||
$coursecat = coursecat::get_default();
|
||||
@@ -1745,14 +1749,11 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
$catdisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'categories', 'page' => 1));
|
||||
}
|
||||
$chelper->set_courses_display_options($coursedisplayoptions)->set_categories_display_options($catdisplayoptions);
|
||||
|
||||
// Display course category tree
|
||||
// Display course category tree.
|
||||
$output .= $this->coursecat_tree($chelper, $coursecat);
|
||||
|
||||
// Add course search form (if we are inside category it was already added to the navbar)
|
||||
if (!$coursecat->id) {
|
||||
$output .= $this->course_search_form();
|
||||
}
|
||||
// Add course search form.
|
||||
$output .= $this->course_search_form();
|
||||
|
||||
// Add action buttons
|
||||
$output .= $this->container_start('buttons');
|
||||
|
||||
+3
-3
@@ -108,9 +108,9 @@ switch($requestmethod) {
|
||||
break;
|
||||
|
||||
case 'duplicate':
|
||||
require_capability('moodle/course:manageactivities', $modcontext);
|
||||
require_capability('moodle/backup:backuptargetimport', $modcontext);
|
||||
require_capability('moodle/restore:restoretargetimport', $modcontext);
|
||||
require_capability('moodle/course:manageactivities', $coursecontext);
|
||||
require_capability('moodle/backup:backuptargetimport', $coursecontext);
|
||||
require_capability('moodle/restore:restoretargetimport', $coursecontext);
|
||||
if (!course_allowed_module($course, $cm->modname)) {
|
||||
throw new moodle_exception('No permission to create that activity');
|
||||
}
|
||||
|
||||
@@ -33,6 +33,13 @@ require_once($CFG->dirroot . '/tag/lib.php');
|
||||
|
||||
class core_course_courselib_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Tidy up open files that may be left open.
|
||||
*/
|
||||
protected function tearDown() {
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set forum specific test values for calling create_module().
|
||||
*
|
||||
@@ -460,12 +467,12 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
|
||||
// Conditional activity.
|
||||
$coursegradeitem = grade_item::fetch_course_item($moduleinfo->course); //the activity will become available only when the user reach some grade into the course itself.
|
||||
$moduleinfo->availability = '{"op":"&","showc":[true,true],"c":[' .
|
||||
$moduleinfo->availability = '{"op":"&","showc":[true,true,true,true,true],"c":[' .
|
||||
'{"type":"date","d":">=","t":' . time() . '},' .
|
||||
'{"type":"date","d":"<","t":' . (time() + (7 * 24 * 3600)) . '}' .
|
||||
'{"type":"date","d":"<","t":' . (time() + (7 * 24 * 3600)) . '},' .
|
||||
'{"type":"grade","id":' . $coursegradeitem->id . ',"min":10,"max":80},' .
|
||||
'{"type":"profile","sf":"email","op":"contains","v":"@"},'.
|
||||
'{"type":"completion","id":'. $assigncm->id . ',"e":' . COMPLETION_COMPLETE . '}' .
|
||||
'{"type":"completion","cm":'. $assigncm->id . ',"e":' . COMPLETION_COMPLETE . '}' .
|
||||
']}';
|
||||
|
||||
// Grading and Advanced grading.
|
||||
@@ -2497,4 +2504,43 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
$this->assertEventLegacyLogData($expectedlegacydata, $event);
|
||||
$this->assertEventContextNotUsed($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that when creating or updating a module, if the availability settings
|
||||
* are present but set to an empty tree, availability is set to null in
|
||||
* database.
|
||||
*/
|
||||
public function test_empty_availability_settings() {
|
||||
global $DB;
|
||||
$this->setAdminUser();
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Enable availability.
|
||||
set_config('enableavailability', 1);
|
||||
|
||||
// Test add.
|
||||
$emptyavailability = '{"op":"&","c":[],"showc":[]}';
|
||||
$course = self::getDataGenerator()->create_course();
|
||||
$label = self::getDataGenerator()->create_module('label', array(
|
||||
'course' => $course, 'availability' => $emptyavailability));
|
||||
$this->assertNull($DB->get_field('course_modules', 'availability',
|
||||
array('id' => $label->cmid)));
|
||||
|
||||
// Test update.
|
||||
$formdata = $DB->get_record('course_modules', array('id' => $label->cmid));
|
||||
unset($formdata->availability);
|
||||
$formdata->availabilityconditionsjson = $emptyavailability;
|
||||
$formdata->modulename = 'label';
|
||||
$formdata->coursemodule = $label->cmid;
|
||||
$draftid = 0;
|
||||
file_prepare_draft_area($draftid, context_module::instance($label->cmid)->id,
|
||||
'mod_label', 'intro', 0);
|
||||
$formdata->introeditor = array(
|
||||
'itemid' => $draftid,
|
||||
'text' => '<p>Yo</p>',
|
||||
'format' => FORMAT_HTML);
|
||||
update_module($formdata);
|
||||
$this->assertNull($DB->get_field('course_modules', 'availability',
|
||||
array('id' => $label->cmid)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,13 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
require_once($CFG->dirroot . '/course/externallib.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tidy up open files that may be left open.
|
||||
*/
|
||||
protected function tearDown() {
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test create_categories
|
||||
*/
|
||||
@@ -582,7 +589,8 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
$label = $this->getDataGenerator()->create_module('label', array('course' => $course->id,
|
||||
'intro' => $labeldescription));
|
||||
$labelcm = get_coursemodule_from_instance('label', $label->id);
|
||||
$url = $this->getDataGenerator()->create_module('url', array('course' => $course->id));
|
||||
$url = $this->getDataGenerator()->create_module('url', array('course' => $course->id,
|
||||
'name' => 'URL: % & $ ../'));
|
||||
|
||||
// Set the required capabilities by the external function.
|
||||
$context = context_course::instance($course->id);
|
||||
|
||||
+3
-1
@@ -94,6 +94,9 @@
|
||||
|
||||
require_once($CFG->dirroot.'/calendar/lib.php'); /// This is after login because it needs $USER
|
||||
|
||||
// Must set layout before gettting section info. See MDL-47555.
|
||||
$PAGE->set_pagelayout('course');
|
||||
|
||||
if ($section and $section > 0) {
|
||||
|
||||
// Get section details and check it exists.
|
||||
@@ -112,7 +115,6 @@
|
||||
// Fix course format if it is no longer installed
|
||||
$course->format = course_get_format($course)->get_format();
|
||||
|
||||
$PAGE->set_pagelayout('course');
|
||||
$PAGE->set_pagetype('course-view-' . $course->format);
|
||||
$PAGE->set_other_editing_capability('moodle/course:update');
|
||||
$PAGE->set_other_editing_capability('moodle/course:manageactivities');
|
||||
|
||||
@@ -54,14 +54,9 @@ class enrol_database_testcase extends advanced_testcase {
|
||||
set_config('dbhost', $CFG->dbhost.':'.$CFG->dboptions['dbport'], 'enrol_database');
|
||||
}
|
||||
|
||||
switch (get_class($DB)) {
|
||||
case 'mssql_native_moodle_database':
|
||||
set_config('dbtype', 'mssql_n', 'enrol_database');
|
||||
set_config('dbsybasequoting', '1', 'enrol_database');
|
||||
break;
|
||||
switch ($DB->get_dbfamily()) {
|
||||
|
||||
case 'mariadb_native_moodle_database':
|
||||
case 'mysqli_native_moodle_database':
|
||||
case 'mysql':
|
||||
set_config('dbtype', 'mysqli', 'enrol_database');
|
||||
set_config('dbsetupsql', "SET NAMES 'UTF-8'", 'enrol_database');
|
||||
set_config('dbsybasequoting', '0', 'enrol_database');
|
||||
@@ -74,12 +69,12 @@ class enrol_database_testcase extends advanced_testcase {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'oci_native_moodle_database':
|
||||
case 'oracle':
|
||||
set_config('dbtype', 'oci8po', 'enrol_database');
|
||||
set_config('dbsybasequoting', '1', 'enrol_database');
|
||||
break;
|
||||
|
||||
case 'pgsql_native_moodle_database':
|
||||
case 'postgres':
|
||||
set_config('dbtype', 'postgres7', 'enrol_database');
|
||||
$setupsql = "SET NAMES 'UTF-8'";
|
||||
if (!empty($CFG->dboptions['dbschema'])) {
|
||||
@@ -100,8 +95,12 @@ class enrol_database_testcase extends advanced_testcase {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'sqlsrv_native_moodle_database':
|
||||
set_config('dbtype', 'mssqlnative', 'enrol_database');
|
||||
case 'mssql':
|
||||
if (get_class($DB) == 'mssql_native_moodle_database') {
|
||||
set_config('dbtype', 'mssql_n', 'enrol_database');
|
||||
} else {
|
||||
set_config('dbtype', 'mssqlnative', 'enrol_database');
|
||||
}
|
||||
set_config('dbsybasequoting', '1', 'enrol_database');
|
||||
break;
|
||||
|
||||
|
||||
+6
-1
@@ -130,7 +130,11 @@ class enrol_ldap_plugin extends enrol_plugin {
|
||||
global $DB;
|
||||
|
||||
// Do not try to print anything to the output because this method is called during interactive login.
|
||||
$trace = new error_log_progress_trace($this->errorlogtag);
|
||||
if (PHPUNIT_TEST) {
|
||||
$trace = new null_progress_trace();
|
||||
} else {
|
||||
$trace = new error_log_progress_trace($this->errorlogtag);
|
||||
}
|
||||
|
||||
if (!$this->ldap_connect($trace)) {
|
||||
$trace->finished();
|
||||
@@ -704,6 +708,7 @@ class enrol_ldap_plugin extends enrol_plugin {
|
||||
$usergroups = $this->ldap_find_user_groups($extmemberuid);
|
||||
if(count($usergroups) > 0) {
|
||||
foreach ($usergroups as $group) {
|
||||
$group = ldap_filter_addslashes($group);
|
||||
$ldap_search_pattern .= '('.$this->get_config('memberattribute_role'.$role->id).'='.$group.')';
|
||||
}
|
||||
}
|
||||
|
||||
+12
-5
@@ -1099,19 +1099,26 @@ class course_enrolment_manager {
|
||||
*/
|
||||
private function prepare_user_for_display($user, $extrafields, $now) {
|
||||
$details = array(
|
||||
'userid' => $user->id,
|
||||
'courseid' => $this->get_course()->id,
|
||||
'picture' => new user_picture($user),
|
||||
'firstname' => fullname($user, has_capability('moodle/site:viewfullnames', $this->get_context())),
|
||||
'lastseen' => get_string('never'),
|
||||
'userid' => $user->id,
|
||||
'courseid' => $this->get_course()->id,
|
||||
'picture' => new user_picture($user),
|
||||
'firstname' => fullname($user, has_capability('moodle/site:viewfullnames', $this->get_context())),
|
||||
'lastseen' => get_string('never'),
|
||||
'lastcourseaccess' => get_string('never'),
|
||||
);
|
||||
foreach ($extrafields as $field) {
|
||||
$details[$field] = $user->{$field};
|
||||
}
|
||||
|
||||
// Last time user has accessed the site.
|
||||
if ($user->lastaccess) {
|
||||
$details['lastseen'] = format_time($now - $user->lastaccess);
|
||||
}
|
||||
|
||||
// Last time user has accessed the course.
|
||||
if ($user->lastseen) {
|
||||
$details['lastcourseaccess'] = format_time($now - $user->lastseen);
|
||||
}
|
||||
return $details;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -186,7 +186,7 @@ foreach ($extrafields as $field) {
|
||||
|
||||
$fields = array(
|
||||
'userdetails' => $userdetails,
|
||||
'lastseen' => get_string('lastaccess'),
|
||||
'lastcourseaccess' => get_string('lastcourseaccess'),
|
||||
'role' => get_string('roles', 'role'),
|
||||
'group' => get_string('groups', 'group'),
|
||||
'enrol' => get_string('enrolmentinstances', 'enrol')
|
||||
@@ -196,7 +196,7 @@ $fields = array(
|
||||
if (!has_capability('moodle/course:viewhiddenuserfields', $context)) {
|
||||
$hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
|
||||
if (isset($hiddenfields['lastaccess'])) {
|
||||
unset($fields['lastseen']);
|
||||
unset($fields['lastcourseaccess']);
|
||||
}
|
||||
if (isset($hiddenfields['groups'])) {
|
||||
unset($fields['group']);
|
||||
|
||||
@@ -52,7 +52,7 @@ class core_files_external extends external_api {
|
||||
'filearea' => new external_value(PARAM_TEXT, 'file area'),
|
||||
'itemid' => new external_value(PARAM_INT, 'associated id'),
|
||||
'filepath' => new external_value(PARAM_PATH, 'file path'),
|
||||
'filename' => new external_value(PARAM_FILE, 'file name'),
|
||||
'filename' => new external_value(PARAM_TEXT, 'file name'),
|
||||
'modified' => new external_value(PARAM_INT, 'timestamp to return files changed after this time.', VALUE_DEFAULT, null),
|
||||
'contextlevel' => new external_value(PARAM_ALPHA, 'The context level for the file location.', VALUE_DEFAULT, null),
|
||||
'instanceid' => new external_value(PARAM_INT, 'The instance id for where the file is located.', VALUE_DEFAULT, null)
|
||||
@@ -112,9 +112,6 @@ class core_files_external extends external_api {
|
||||
if (empty($fileinfo['filearea'])) {
|
||||
$fileinfo['filearea'] = null;
|
||||
}
|
||||
if (empty($fileinfo['itemid'])) {
|
||||
$fileinfo['itemid'] = null;
|
||||
}
|
||||
if (empty($fileinfo['filename'])) {
|
||||
$fileinfo['filename'] = null;
|
||||
}
|
||||
@@ -209,7 +206,7 @@ class core_files_external extends external_api {
|
||||
'filearea' => new external_value(PARAM_AREA, ''),
|
||||
'itemid' => new external_value(PARAM_INT, ''),
|
||||
'filepath' => new external_value(PARAM_TEXT, ''),
|
||||
'filename' => new external_value(PARAM_FILE, ''),
|
||||
'filename' => new external_value(PARAM_TEXT, ''),
|
||||
'isdir' => new external_value(PARAM_BOOL, ''),
|
||||
'url' => new external_value(PARAM_TEXT, ''),
|
||||
'timemodified' => new external_value(PARAM_INT, ''),
|
||||
|
||||
+1
-1
@@ -798,7 +798,7 @@ class core_files_renderer extends plugin_renderer_base {
|
||||
<div class="fp-author">'.get_string('author', 'repository').'<span class="fp-value"></span></div>
|
||||
<div class="fp-dimensions">'.get_string('dimensions', 'repository').'<span class="fp-value"></span></div>
|
||||
</div>
|
||||
<div>
|
||||
</div>
|
||||
</div>';
|
||||
return $rv;
|
||||
}
|
||||
|
||||
@@ -75,15 +75,14 @@ class course_settings_form extends moodleform {
|
||||
|
||||
$options = array(-1 => get_string('default', 'grades'),
|
||||
GRADE_DISPLAY_TYPE_REAL => get_string('real', 'grades'),
|
||||
GRADE_DISPLAY_TYPE_PERCENTAGE => get_string('percentage', 'grades'),
|
||||
GRADE_DISPLAY_TYPE_LETTER => get_string('letter', 'grades'),
|
||||
GRADE_DISPLAY_TYPE_REAL_PERCENTAGE => get_string('realpercentage', 'grades'),
|
||||
GRADE_DISPLAY_TYPE_REAL_LETTER => get_string('realletter', 'grades'),
|
||||
GRADE_DISPLAY_TYPE_LETTER_REAL => get_string('letterreal', 'grades'),
|
||||
GRADE_DISPLAY_TYPE_LETTER_PERCENTAGE => get_string('letterpercentage', 'grades'),
|
||||
GRADE_DISPLAY_TYPE_PERCENTAGE => get_string('percentage', 'grades'),
|
||||
GRADE_DISPLAY_TYPE_PERCENTAGE_REAL => get_string('percentagereal', 'grades'),
|
||||
GRADE_DISPLAY_TYPE_PERCENTAGE_LETTER => get_string('percentageletter', 'grades'),
|
||||
GRADE_DISPLAY_TYPE_PERCENTAGE_REAL => get_string('percentagereal', 'grades'));
|
||||
asort($options);
|
||||
GRADE_DISPLAY_TYPE_LETTER => get_string('letter', 'grades'),
|
||||
GRADE_DISPLAY_TYPE_LETTER_REAL => get_string('letterreal', 'grades'),
|
||||
GRADE_DISPLAY_TYPE_LETTER_PERCENTAGE => get_string('letterpercentage', 'grades'));
|
||||
|
||||
$default_gradedisplaytype = $CFG->grade_displaytype;
|
||||
foreach ($options as $key=>$option) {
|
||||
@@ -94,7 +93,7 @@ class course_settings_form extends moodleform {
|
||||
}
|
||||
$mform->addElement('select', 'displaytype', get_string('gradedisplaytype', 'grades'), $options);
|
||||
$mform->addHelpButton('displaytype', 'gradedisplaytype', 'grades');
|
||||
|
||||
$mform->setDefault('displaytype', -1);
|
||||
|
||||
$options = array(-1=> get_string('defaultprev', 'grades', $CFG->grade_decimalpoints), 0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5);
|
||||
$mform->addElement('select', 'decimalpoints', get_string('decimalpoints', 'grades'), $options);
|
||||
|
||||
@@ -335,7 +335,7 @@ if (!$moving) {
|
||||
if (!$moving && count($grade_edit_tree->categories) > 1) {
|
||||
echo '<br /><br />';
|
||||
echo '<input type="hidden" name="bulkmove" value="0" id="bulkmoveinput" />';
|
||||
$attributes = array('id'=>'menumoveafter');
|
||||
$attributes = array('id'=>'menumoveafter', 'class' => 'ignoredirty');
|
||||
echo html_writer::label(get_string('moveselectedto', 'grades'), 'menumoveafter');
|
||||
echo html_writer::select($grade_edit_tree->categories, 'moveafter', '', array(''=>'choosedots'), $attributes);
|
||||
$OUTPUT->add_action_handler(new component_action('change', 'submit_bulk_move'), 'menumoveafter');
|
||||
@@ -368,6 +368,14 @@ if ($moving) {
|
||||
|
||||
echo $OUTPUT->container_end();
|
||||
|
||||
$PAGE->requires->yui_module('moodle-core-formchangechecker',
|
||||
'M.core_formchangechecker.init',
|
||||
array(array(
|
||||
'formid' => 'gradetreeform'
|
||||
))
|
||||
);
|
||||
$PAGE->requires->string_for_js('changesmadereallygoaway', 'moodle');
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
// Restore original show/hide preference if moving
|
||||
|
||||
@@ -180,7 +180,7 @@ class grade_edit_tree {
|
||||
$item = $category->get_grade_item();
|
||||
|
||||
// Add aggregation coef input if not a course item and if parent category has correct aggregation type
|
||||
$dimmed = ($item->is_hidden()) ? 'dimmed' : '';
|
||||
$dimmed = ($item->is_hidden()) ? 'dimmed_text' : '';
|
||||
|
||||
// Before we print the category's row, we must find out how many rows will appear below it (for the filler cell's rowspan)
|
||||
$aggregation_position = grade_get_setting($COURSE->id, 'aggregationposition', $CFG->grade_aggregationposition);
|
||||
@@ -671,6 +671,7 @@ class grade_edit_tree_column_aggregation extends grade_edit_tree_column_category
|
||||
} else {
|
||||
$attributes = array();
|
||||
$attributes['id'] = 'aggregation_'.$category->id;
|
||||
$attributes['class'] = 'ignoredirty';
|
||||
$aggregation = html_writer::label(get_string('aggregation', 'grades'), 'aggregation_'.$category->id, false, array('class' => 'accesshide'));
|
||||
$aggregation .= html_writer::select($options, 'aggregation_'.$category->id, $category->aggregation, null, $attributes);
|
||||
$action = new component_action('change', 'update_category_aggregation', array('courseid' => $params['id'], 'category' => $category->id, 'sesskey' => sesskey()));
|
||||
@@ -1198,7 +1199,7 @@ class grade_edit_tree_column_select extends grade_edit_tree_column {
|
||||
if ($params['itemtype'] != 'course' && $params['itemtype'] != 'category') {
|
||||
$itemselect = '<label class="accesshide" for="select_'.$params['eid'].'">'.
|
||||
get_string('select', 'grades', $item->itemname).'</label>
|
||||
<input class="itemselect" type="checkbox" name="select_'.$params['eid'].'" id="select_'.$params['eid'].
|
||||
<input class="itemselect ignoredirty" type="checkbox" name="select_'.$params['eid'].'" id="select_'.$params['eid'].
|
||||
'" onchange="toggleCategorySelector();"/>'; // TODO: convert to YUI handler
|
||||
}
|
||||
//html_writer::table() will wrap the item cell contents in a <TD> so don't do it here
|
||||
|
||||
@@ -645,7 +645,8 @@ class gradingform_guide_controller extends gradingform_controller {
|
||||
$returnvalue['maxscore'] = $maxscore;
|
||||
$returnvalue['minscore'] = 0;
|
||||
if (!empty($this->moduleinstance->grade)) {
|
||||
$returnvalue['modulegrade'] = $this->moduleinstance->grade;
|
||||
$graderange = make_grades_menu($this->moduleinstance->grade);
|
||||
$returnvalue['modulegrade'] = count($graderange) - 1;
|
||||
}
|
||||
return $returnvalue;
|
||||
}
|
||||
|
||||
@@ -97,6 +97,11 @@ if (!$iid) {
|
||||
$csvimport = new csv_import_reader($iid, 'grade');
|
||||
|
||||
$csvimport->load_csv_content($text, $formdata->encoding, $separator);
|
||||
if ($error = $csvimport->get_error()) {
|
||||
echo $OUTPUT->notification($error);
|
||||
echo $OUTPUT->footer();
|
||||
die();
|
||||
}
|
||||
|
||||
// --- get header (field names) ---
|
||||
$header = $csvimport->get_columns();
|
||||
|
||||
@@ -19,11 +19,11 @@ require_once '../../../config.php';
|
||||
require_once 'lib.php';
|
||||
require_once $CFG->libdir.'/filelib.php';
|
||||
|
||||
$url = required_param('url', PARAM_URL); // only real urls here
|
||||
$gradesurl = required_param('url', PARAM_URL); // only real urls here
|
||||
$id = required_param('id', PARAM_INT); // course id
|
||||
$feedback = optional_param('feedback', 0, PARAM_BOOL);
|
||||
|
||||
$url = new moodle_url('/grade/import/xml/import.php', array('id'=>$id,'url'=>$url));
|
||||
$url = new moodle_url('/grade/import/xml/import.php', array('id' => $id,'url' => $gradesurl));
|
||||
if ($feedback !== 0) {
|
||||
$url->param('feedback', $feedback);
|
||||
}
|
||||
@@ -46,9 +46,10 @@ require_capability('gradeimport/xml:view', $context);
|
||||
core_php_time_limit::raise();
|
||||
raise_memory_limit(MEMORY_EXTRA);
|
||||
|
||||
$text = download_file_content($url);
|
||||
$text = download_file_content($gradesurl);
|
||||
if ($text === false) {
|
||||
print_error('cannotreadfile');
|
||||
print_error('cannotreadfile', 'error',
|
||||
$CFG->wwwroot . '/grade/import/xml/index.php?id=' . $id, $gradesurl);
|
||||
}
|
||||
|
||||
$error = '';
|
||||
@@ -75,7 +76,6 @@ if ($importcode !== false) {
|
||||
}
|
||||
|
||||
} else {
|
||||
print_error('error', 'gradeimport_xml');
|
||||
print_error('errorduringimport', 'gradeimport_xml',
|
||||
$CFG->wwwroot . '/grade/import/xml/index.php?id=' . $id, $error);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ $string['errincorrectgradeidnumber'] = 'Error - idnumber \'{$a}\' from the impor
|
||||
$string['errincorrectidnumber'] = 'Error - incorrect idnumber';
|
||||
$string['errincorrectuseridnumber'] = 'Error - idnumber \'{$a}\' from the import file does not match any user.';
|
||||
$string['error'] = 'Errors occur';
|
||||
$string['errorduringimport'] = 'An error occurred when trying to import: {$a}';
|
||||
$string['fileurl'] = 'Remote file URL';
|
||||
$string['pluginname'] = 'XML file';
|
||||
$string['xml:publish'] = 'Publish import grades from XML';
|
||||
|
||||
@@ -89,6 +89,7 @@ function grade_get_course_grades($courseid, $userid_or_ids=null) {
|
||||
$grade->feedbackformat = $grade_grades[$userid]->feedbackformat;
|
||||
$grade->usermodified = $grade_grades[$userid]->usermodified;
|
||||
$grade->dategraded = $grade_grades[$userid]->get_dategraded();
|
||||
$grade->datesubmitted = $grade_grades[$userid]->get_datesubmitted();
|
||||
|
||||
// create text representation of grade
|
||||
if ($grade_item->needsupdate) {
|
||||
|
||||
@@ -184,6 +184,7 @@ if ($USER->gradeediting[$course->id] && ($report->get_pref('showquickfeedback')
|
||||
echo '<div>';
|
||||
echo '<input type="hidden" value="'.s($courseid).'" name="id" />';
|
||||
echo '<input type="hidden" value="'.sesskey().'" name="sesskey" />';
|
||||
echo '<input type="hidden" value="'.time().'" name="timepageload" />';
|
||||
echo '<input type="hidden" value="grader" name="report"/>';
|
||||
echo '<input type="hidden" value="'.$page.'" name="page"/>';
|
||||
echo $reporthtml;
|
||||
|
||||
@@ -177,6 +177,7 @@ class grade_report_grader extends grade_report {
|
||||
|
||||
// Were any changes made?
|
||||
$changedgrades = false;
|
||||
$timepageload = clean_param($data->timepageload, PARAM_INT);
|
||||
|
||||
foreach ($data as $varname => $students) {
|
||||
|
||||
@@ -251,8 +252,15 @@ class grade_report_grader extends grade_report {
|
||||
}
|
||||
|
||||
$errorstr = '';
|
||||
// Warn if the grade is out of bounds.
|
||||
if (!is_null($finalgrade)) {
|
||||
$skip = false;
|
||||
|
||||
$dategraded = $oldvalue->get_dategraded();
|
||||
if (!empty($dategraded) && $timepageload < $dategraded) {
|
||||
// Warn if the grade was updated while we were editing this form.
|
||||
$errorstr = 'gradewasmodifiedduringediting';
|
||||
$skip = true;
|
||||
} else if (!is_null($finalgrade)) {
|
||||
// Warn if the grade is out of bounds.
|
||||
$bounded = $gradeitem->bounded_grade($finalgrade);
|
||||
if ($bounded > $finalgrade) {
|
||||
$errorstr = 'lessthanmin';
|
||||
@@ -260,6 +268,7 @@ class grade_report_grader extends grade_report {
|
||||
$errorstr = 'morethanmax';
|
||||
}
|
||||
}
|
||||
|
||||
if ($errorstr) {
|
||||
$userfields = 'id, ' . get_all_user_name_fields(true);
|
||||
$user = $DB->get_record('user', array('id' => $userid), $userfields);
|
||||
@@ -267,6 +276,10 @@ class grade_report_grader extends grade_report {
|
||||
$gradestr->username = fullname($user);
|
||||
$gradestr->itemname = $gradeitem->get_name();
|
||||
$warnings[] = get_string($errorstr, 'grades', $gradestr);
|
||||
if ($skip) {
|
||||
// Skipping the update of this grade it failed the tests above.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
} else if ($datatype == 'feedback') {
|
||||
|
||||
+43
-23
@@ -215,16 +215,6 @@ class grade_report_user extends grade_report {
|
||||
// Grab the grade_tree for this course
|
||||
$this->gtree = new grade_tree($this->courseid, false, $this->switch, null, !$CFG->enableoutcomes);
|
||||
|
||||
// Determine the number of rows and indentation
|
||||
$this->maxdepth = 1;
|
||||
$this->inject_rowspans($this->gtree->top_element);
|
||||
$this->maxdepth++; // Need to account for the lead column that spans all children
|
||||
for ($i = 1; $i <= $this->maxdepth; $i++) {
|
||||
$this->evenodd[$i] = 0;
|
||||
}
|
||||
|
||||
$this->tabledata = array();
|
||||
|
||||
// Get the user (for full name).
|
||||
$this->user = $DB->get_record('user', array('id' => $userid));
|
||||
|
||||
@@ -238,6 +228,16 @@ class grade_report_user extends grade_report {
|
||||
$this->canviewhidden = has_capability('moodle/grade:viewhidden', $coursecontext);
|
||||
}
|
||||
|
||||
// Determine the number of rows and indentation.
|
||||
$this->maxdepth = 1;
|
||||
$this->inject_rowspans($this->gtree->top_element);
|
||||
$this->maxdepth++; // Need to account for the lead column that spans all children.
|
||||
for ($i = 1; $i <= $this->maxdepth; $i++) {
|
||||
$this->evenodd[$i] = 0;
|
||||
}
|
||||
|
||||
$this->tabledata = array();
|
||||
|
||||
// base url for sorting by first/last name
|
||||
$this->baseurl = $CFG->wwwroot.'/grade/report?id='.$courseid.'&userid='.$userid;
|
||||
$this->pbarurl = $this->baseurl;
|
||||
@@ -266,7 +266,15 @@ class grade_report_user extends grade_report {
|
||||
$count = 1;
|
||||
|
||||
foreach ($element['children'] as $key=>$child) {
|
||||
$count += $this->inject_rowspans($element['children'][$key]);
|
||||
// If category is hidden then do not include it in the rowspan.
|
||||
if ($child['type'] == 'category' && $child['object']->is_hidden() && !$this->canviewhidden
|
||||
&& ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN
|
||||
|| ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$child['object']->is_hiddenuntil()))) {
|
||||
// Just calculate the rowspans for children of this category, don't add them to the count.
|
||||
$this->inject_rowspans($element['children'][$key]);
|
||||
} else {
|
||||
$count += $this->inject_rowspans($element['children'][$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$element['rowspan'] = $count;
|
||||
@@ -433,6 +441,18 @@ class grade_report_user extends grade_report {
|
||||
|
||||
/// Actual Grade
|
||||
$gradeval = $grade_grade->finalgrade;
|
||||
if (!$this->canviewhidden) {
|
||||
/// Virtual Grade (may be calculated excluding hidden items etc).
|
||||
$adjustedgrade = $this->blank_hidden_total_and_adjust_bounds($this->courseid,
|
||||
$grade_grade->grade_item,
|
||||
$gradeval);
|
||||
$gradeval = $adjustedgrade['grade'];
|
||||
|
||||
// We temporarily adjust the view of this grade item - because the min and
|
||||
// max are affected by the hidden values in the aggregation.
|
||||
$grade_grade->grade_item->grademax = $adjustedgrade['grademax'];
|
||||
$grade_grade->grade_item->grademin = $adjustedgrade['grademin'];
|
||||
}
|
||||
|
||||
if ($this->showfeedback) {
|
||||
// Copy $class before appending itemcenter as feedback should not be centered
|
||||
@@ -460,20 +480,17 @@ class grade_report_user extends grade_report {
|
||||
$data['grade']['class'] = $class;
|
||||
$data['grade']['content'] = get_string('submittedon', 'grades', userdate($grade_grade->get_datesubmitted(), get_string('strftimedatetimeshort')));
|
||||
|
||||
} elseif ($grade_grade->is_hidden()) {
|
||||
$data['grade']['class'] = $class.' dimmed_text';
|
||||
$data['grade']['content'] = '-';
|
||||
} else if ($grade_grade->is_hidden()) {
|
||||
$data['grade']['class'] = $class.' dimmed_text';
|
||||
$data['grade']['content'] = '-';
|
||||
if ($this->canviewhidden) {
|
||||
$data['grade']['content'] = grade_format_gradevalue($gradeval,
|
||||
$grade_grade->grade_item,
|
||||
true);
|
||||
}
|
||||
} else {
|
||||
$data['grade']['class'] = $class;
|
||||
$adjustedgrade = $this->blank_hidden_total_and_adjust_bounds($this->courseid,
|
||||
$grade_grade->grade_item,
|
||||
$gradeval);
|
||||
|
||||
// We temporarily adjust the view of this grade item - because the min and
|
||||
// max are affected by the hidden values in the aggregation.
|
||||
$grade_grade->grade_item->grademax = $adjustedgrade['grademax'];
|
||||
$grade_grade->grade_item->grademin = $adjustedgrade['grademin'];
|
||||
$data['grade']['content'] = grade_format_gradevalue($adjustedgrade['grade'],
|
||||
$data['grade']['content'] = grade_format_gradevalue($gradeval,
|
||||
$grade_grade->grade_item,
|
||||
true);
|
||||
}
|
||||
@@ -495,6 +512,9 @@ class grade_report_user extends grade_report {
|
||||
} else if ($grade_grade->is_hidden()) {
|
||||
$data['percentage']['class'] = $class.' dimmed_text';
|
||||
$data['percentage']['content'] = '-';
|
||||
if ($this->canviewhidden) {
|
||||
$data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE);
|
||||
}
|
||||
} else {
|
||||
$data['percentage']['class'] = $class;
|
||||
$data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE);
|
||||
|
||||
@@ -10,20 +10,21 @@
|
||||
.user-grade thead {border-bottom: 3px double black;}
|
||||
.user-grade thead th {padding: 0.25em 0.75em;}
|
||||
.user-grade tbody th {text-align:left;}
|
||||
.user-grade td.oddd1,
|
||||
.user-grade th.oddd1 {background-color: #f3dfd0;}
|
||||
.user-grade td.oddd2,
|
||||
.user-grade th.oddd2 {background-color: #d0dbf3;}
|
||||
.user-grade td.oddd3,
|
||||
.user-grade th.oddd3 {background-color: #d0f3d6;}
|
||||
.user-grade td.oddd4,
|
||||
.user-grade th.oddd4 {background-color: #f0f0aa;}
|
||||
.user-grade td.evend2,
|
||||
.user-grade th.evend2 {background-color: #b0bbd3;}
|
||||
.user-grade td.evend3,
|
||||
.user-grade th.evend3 {background-color: #b0dfb6;}
|
||||
.user-grade td.evend4,
|
||||
.user-grade th.evend4 {background-color: #cac8be;}
|
||||
/* These need to be this specific to override the css from .generaltable */
|
||||
.user-grade tbody > tr:nth-child(n) > td.oddd1,
|
||||
.user-grade tbody > tr:nth-child(n) > th.oddd1 {background-color: #f3dfd0;}
|
||||
.user-grade tbody > tr:nth-child(n) > td.oddd2,
|
||||
.user-grade tbody > tr:nth-child(n) > th.oddd2 {background-color: #d0dbf3;}
|
||||
.user-grade tbody > tr:nth-child(n) > td.oddd3,
|
||||
.user-grade tbody > tr:nth-child(n) > th.oddd3 {background-color: #d0f3d6;}
|
||||
.user-grade tbody > tr:nth-child(n) > td.oddd4,
|
||||
.user-grade tbody > tr:nth-child(n) > th.oddd4 {background-color: #f0f0aa;}
|
||||
.user-grade tbody > tr:nth-child(n) > td.evend2,
|
||||
.user-grade tbody > tr:nth-child(n) > th.evend2 {background-color: #b0bbd3;}
|
||||
.user-grade tbody > tr:nth-child(n) > td.evend3,
|
||||
.user-grade tbody > tr:nth-child(n) > th.evend3 {background-color: #b0dfb6;}
|
||||
.user-grade tbody > tr:nth-child(n) > td.evend4,
|
||||
.user-grade tbody > tr:nth-child(n) > th.evend4 {background-color: #cac8be;}
|
||||
.user-grade td.b1t,
|
||||
.user-grade td.b2t,
|
||||
.user-grade th.b1t,
|
||||
|
||||
@@ -16,11 +16,26 @@ Feature: We can use calculated grade totals
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And the following "grade categories" exist:
|
||||
| fullname | course |
|
||||
| Sub category 1 | C1|
|
||||
| Sub category 2 | C1|
|
||||
And the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro |
|
||||
| assign | C1 | a1 | Test assignment one | Submit something! |
|
||||
| assign | C1 | a2 | Test assignment two | Submit something! |
|
||||
| assign | C1 | a3 | Test assignment three | Submit something! |
|
||||
| activity | course | idnumber | name | intro | grade |
|
||||
| assign | C1 | a1 | Test assignment one | Submit something! | 300 |
|
||||
| assign | C1 | a2 | Test assignment two | Submit something! | 100 |
|
||||
| assign | C1 | a3 | Test assignment three | Submit something! | 150 |
|
||||
| assign | C1 | a4 | Test assignment four | Submit nothing! | 150 |
|
||||
And the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro | gradecategory | grade |
|
||||
| assign | C1 | a5 | Test assignment five | Submit something! | Sub category 1 | 200
|
||||
| assign | C1 | a6 | Test assignment six | Submit something! | Sub category 1 | 100
|
||||
| assign | C1 | a7 | Test assignment seven | Submit nothing! | Sub category 1 | 150
|
||||
And the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro | gradecategory | grade |
|
||||
| assign | C1 | a8 | Test assignment eight | Submit something! | Sub category 2 | 200
|
||||
| assign | C1 | a9 | Test assignment nine | Submit something! | Sub category 2 | 100
|
||||
| assign | C1 | 10 | Test assignment ten | Submit nothing! | Sub category 2 | 150
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
@@ -28,10 +43,20 @@ Feature: We can use calculated grade totals
|
||||
And I give the grade "60.00" to the user "Student 1" for the grade item "Test assignment one"
|
||||
And I give the grade "20.00" to the user "Student 1" for the grade item "Test assignment two"
|
||||
And I give the grade "40.00" to the user "Student 1" for the grade item "Test assignment three"
|
||||
And I give the grade "10.00" to the user "Student 1" for the grade item "Test assignment five"
|
||||
And I give the grade "5.00" to the user "Student 1" for the grade item "Test assignment six"
|
||||
And I give the grade "10.00" to the user "Student 1" for the grade item "Test assignment eight"
|
||||
And I give the grade "5.00" to the user "Student 1" for the grade item "Test assignment nine"
|
||||
And I press "Update"
|
||||
And I click on "Edit assign Test assignment two" "link"
|
||||
And I click on "Hidden" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I click on "Edit assign Test assignment five" "link"
|
||||
And I click on "Hidden" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I click on "Edit assign Test assignment eight" "link"
|
||||
And I click on "Hidden" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I follow "Course grade settings"
|
||||
And I set the field "Grade display type" to "Real (percentage)"
|
||||
And I press "Save changes"
|
||||
@@ -41,8 +66,16 @@ Feature: We can use calculated grade totals
|
||||
And I follow "Edit Course 1"
|
||||
And I set the field "Aggregation" to "Mean of grades"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 1"
|
||||
And I set the field "Aggregation" to "Mean of grades"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 2"
|
||||
And I set the field "Aggregation" to "Mean of grades"
|
||||
And I click on "Show more..." "link"
|
||||
And I click on "Aggregate only non-empty grades" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I turn editing mode off
|
||||
Then I should see "40.00 (40.00 %)" in the ".course" "css_element"
|
||||
Then I should see "30.00 (30.00 %)" in the ".course" "css_element"
|
||||
And I follow "Course grade settings"
|
||||
And I set the field "Hide totals if they contain hidden items" to "Show totals excluding hidden items"
|
||||
And I press "Save changes"
|
||||
@@ -51,18 +84,30 @@ Feature: We can use calculated grade totals
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And I set the field "Grade report" to "Overview report"
|
||||
And I should see "50.00 (50.00 %)" in the "overview-grade" "table"
|
||||
And I should see "30.42 (30.42 %)" in the "overview-grade" "table"
|
||||
|
||||
@javascript
|
||||
Scenario: Weighted mean of grades aggregation
|
||||
And I follow "Edit Course 1"
|
||||
And I set the field "Aggregation" to "Weighted mean of grades"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 1"
|
||||
And I set the field "Aggregation" to "Weighted mean of grades"
|
||||
And I expand all fieldsets
|
||||
And I set the field "Item weight" to "1"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 2"
|
||||
And I set the field "Aggregation" to "Weighted mean of grades"
|
||||
And I expand all fieldsets
|
||||
And I set the field "Item weight" to "1"
|
||||
And I click on "Aggregate only non-empty grades" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I click on "Edit assign Test assignment one" "link"
|
||||
And I expand all fieldsets
|
||||
And I set the field "Item weight" to "3"
|
||||
And I press "Save changes"
|
||||
And I turn editing mode off
|
||||
Then I should see "48.00 (48.00 %)" in the ".course" "css_element"
|
||||
Then I should see "27.14 (27.14 %)" in the ".course" "css_element"
|
||||
And I follow "Course grade settings"
|
||||
And I set the field "Hide totals if they contain hidden items" to "Show totals excluding hidden items"
|
||||
And I press "Save changes"
|
||||
@@ -71,15 +116,27 @@ Feature: We can use calculated grade totals
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And I set the field "Grade report" to "Overview report"
|
||||
And I should see "55.00 (55.00 %)" in the "overview-grade" "table"
|
||||
And I should see "26.94 (26.94 %)" in the "overview-grade" "table"
|
||||
|
||||
@javascript
|
||||
Scenario: Simple weighted mean of grades aggregation
|
||||
And I follow "Edit Course 1"
|
||||
And I set the field "Aggregation" to "Simple weighted mean of grades"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 1"
|
||||
And I set the field "Aggregation" to "Simple weighted mean of grades"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 2"
|
||||
And I set the field "Aggregation" to "Simple weighted mean of grades"
|
||||
And I click on "Show more..." "link"
|
||||
And I click on "Aggregate only non-empty grades" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I click on "Edit assign Test assignment one" "link"
|
||||
And I expand all fieldsets
|
||||
And I click on "Extra credit" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I turn editing mode off
|
||||
Then I should see "40.00 (40.00 %)" in the ".course" "css_element"
|
||||
Then I should see "45.19 (45.19 %)" in the ".course" "css_element"
|
||||
And I follow "Course grade settings"
|
||||
And I set the field "Hide totals if they contain hidden items" to "Show totals excluding hidden items"
|
||||
And I press "Save changes"
|
||||
@@ -88,15 +145,27 @@ Feature: We can use calculated grade totals
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And I set the field "Grade report" to "Overview report"
|
||||
And I should see "50.00 (50.00 %)" in the "overview-grade" "table"
|
||||
And I should see "48.57 (48.57 %)" in the "overview-grade" "table"
|
||||
|
||||
@javascript
|
||||
Scenario: Mean of grades (with extra credits) aggregation
|
||||
And I follow "Edit Course 1"
|
||||
And I set the field "Aggregation" to "Mean of grades (with extra credits)"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 1"
|
||||
And I set the field "Aggregation" to "Mean of grades (with extra credits)"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 2"
|
||||
And I set the field "Aggregation" to "Mean of grades (with extra credits)"
|
||||
And I click on "Show more..." "link"
|
||||
And I click on "Aggregate only non-empty grades" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I click on "Edit assign Test assignment one" "link"
|
||||
And I expand all fieldsets
|
||||
And I set the field "Extra credit weight" to "2"
|
||||
And I press "Save changes"
|
||||
And I turn editing mode off
|
||||
Then I should see "40.00 (40.00 %)" in the ".course" "css_element"
|
||||
Then I should see "42.50 (42.50 %)" in the ".course" "css_element"
|
||||
And I follow "Course grade settings"
|
||||
And I set the field "Hide totals if they contain hidden items" to "Show totals excluding hidden items"
|
||||
And I press "Save changes"
|
||||
@@ -105,15 +174,82 @@ Feature: We can use calculated grade totals
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And I set the field "Grade report" to "Overview report"
|
||||
And I should see "50.00 (50.00 %)" in the "overview-grade" "table"
|
||||
And I should see "47.22 (47.22 %)" in the "overview-grade" "table"
|
||||
|
||||
@javascript
|
||||
Scenario: Median of grades aggregation
|
||||
And I follow "Edit Course 1"
|
||||
And I set the field "Aggregation" to "Median of grades"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 1"
|
||||
And I set the field "Aggregation" to "Median of grades"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 2"
|
||||
And I set the field "Aggregation" to "Median of grades"
|
||||
And I click on "Show more..." "link"
|
||||
And I click on "Aggregate only non-empty grades" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I turn editing mode off
|
||||
Then I should see "40.00 (40.00 %)" in the ".course" "css_element"
|
||||
Then I should see "26.67 (26.67 %)" in the ".course" "css_element"
|
||||
And I follow "Course grade settings"
|
||||
And I set the field "Hide totals if they contain hidden items" to "Show totals excluding hidden items"
|
||||
And I press "Save changes"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And I set the field "Grade report" to "Overview report"
|
||||
And I should see "25.83 (25.83 %)" in the "overview-grade" "table"
|
||||
|
||||
@javascript
|
||||
Scenario: Lowest grade aggregation
|
||||
And I follow "Edit Course 1"
|
||||
And I set the field "Aggregation" to "Lowest grade"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 1"
|
||||
And I set the field "Aggregation" to "Lowest grade"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 2"
|
||||
And I set the field "Aggregation" to "Lowest grade"
|
||||
And I click on "Show more..." "link"
|
||||
And I click on "Aggregate only non-empty grades" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I click on "Edit assign Test assignment five" "link"
|
||||
And I click on "Hidden" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I click on "Edit assign Test assignment four" "link"
|
||||
And I click on "Hidden" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I turn editing mode off
|
||||
Then I should see "0.00 (0.00 %)" in the ".course" "css_element"
|
||||
And I follow "Course grade settings"
|
||||
And I set the field "Hide totals if they contain hidden items" to "Show totals excluding hidden items"
|
||||
And I press "Save changes"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And I set the field "Grade report" to "Overview report"
|
||||
And I should see "0.00 (0.00 %)" in the "overview-grade" "table"
|
||||
|
||||
@javascript
|
||||
Scenario: Highest grade aggregation
|
||||
And I follow "Edit Course 1"
|
||||
And I set the field "Aggregation" to "Highest grade"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 1"
|
||||
And I set the field "Aggregation" to "Highest grade"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 2"
|
||||
And I set the field "Aggregation" to "Highest grade"
|
||||
And I click on "Show more..." "link"
|
||||
And I click on "Aggregate only non-empty grades" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I click on "Edit assign Test assignment one" "link"
|
||||
And I click on "Hidden" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I turn editing mode off
|
||||
Then I should see "50.00 (50.00 %)" in the ".course" "css_element"
|
||||
And I follow "Course grade settings"
|
||||
And I set the field "Hide totals if they contain hidden items" to "Show totals excluding hidden items"
|
||||
And I press "Save changes"
|
||||
@@ -124,53 +260,24 @@ Feature: We can use calculated grade totals
|
||||
And I set the field "Grade report" to "Overview report"
|
||||
And I should see "50.00 (50.00 %)" in the "overview-grade" "table"
|
||||
|
||||
@javascript
|
||||
Scenario: Lowest grade aggregation
|
||||
And I follow "Edit Course 1"
|
||||
And I set the field "Aggregation" to "Lowest grade"
|
||||
And I press "Save changes"
|
||||
And I turn editing mode off
|
||||
Then I should see "20.00 (20.00 %)" in the ".course" "css_element"
|
||||
And I follow "Course grade settings"
|
||||
And I set the field "Hide totals if they contain hidden items" to "Show totals excluding hidden items"
|
||||
And I press "Save changes"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And I set the field "Grade report" to "Overview report"
|
||||
And I should see "40.00 (40.00 %)" in the "overview-grade" "table"
|
||||
|
||||
@javascript
|
||||
Scenario: Highest grade aggregation
|
||||
And I follow "Edit Course 1"
|
||||
And I set the field "Aggregation" to "Highest grade"
|
||||
And I press "Save changes"
|
||||
And I click on "Edit assign Test assignment one" "link"
|
||||
And I click on "Hidden" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I turn editing mode off
|
||||
Then I should see "60.00 (60.00 %)" in the ".course" "css_element"
|
||||
And I follow "Course grade settings"
|
||||
And I set the field "Hide totals if they contain hidden items" to "Show totals excluding hidden items"
|
||||
And I press "Save changes"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And I set the field "Grade report" to "Overview report"
|
||||
And I should see "40.00 (40.00 %)" in the "overview-grade" "table"
|
||||
|
||||
@javascript
|
||||
Scenario: Mode of grades aggregation
|
||||
And I follow "Edit Course 1"
|
||||
And I set the field "Aggregation" to "Mode of grades"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 1"
|
||||
And I set the field "Aggregation" to "Mode of grades"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 2"
|
||||
And I set the field "Aggregation" to "Mode of grades"
|
||||
And I click on "Show more..." "link"
|
||||
And I click on "Aggregate only non-empty grades" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I click on "Edit assign Test assignment one" "link"
|
||||
And I click on "Hidden" "checkbox"
|
||||
And I press "Save changes"
|
||||
And I turn editing mode off
|
||||
Then I should see "60.00 (60.00 %)" in the ".course" "css_element"
|
||||
Then I should see "50.00 (50.00 %)" in the ".course" "css_element"
|
||||
And I follow "Course grade settings"
|
||||
And I set the field "Hide totals if they contain hidden items" to "Show totals excluding hidden items"
|
||||
And I press "Save changes"
|
||||
@@ -179,15 +286,24 @@ Feature: We can use calculated grade totals
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And I set the field "Grade report" to "Overview report"
|
||||
And I should see "40.00 (40.00 %)" in the "overview-grade" "table"
|
||||
And I should see "50.00 (50.00 %)" in the "overview-grade" "table"
|
||||
|
||||
@javascript
|
||||
Scenario: Sum of grades aggregation
|
||||
And I follow "Edit Sub category 1"
|
||||
And I set the field "Aggregation" to "Sum of grades"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 2"
|
||||
And I set the field "Aggregation" to "Sum of grades"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Course 1"
|
||||
And I set the field "Aggregation" to "Sum of grades"
|
||||
And I press "Save changes"
|
||||
And I click on "Edit assign Test assignment nine" "link"
|
||||
And I set the field "Extra credit" to "1"
|
||||
And I press "Save changes"
|
||||
And I turn editing mode off
|
||||
Then I should see "120.00 (40.00 %)" in the ".course" "css_element"
|
||||
Then I should see "150.00 (19.23 %)" in the ".course" "css_element"
|
||||
And I follow "Course grade settings"
|
||||
And I set the field "Hide totals if they contain hidden items" to "Show totals excluding hidden items"
|
||||
And I press "Save changes"
|
||||
@@ -196,4 +312,4 @@ Feature: We can use calculated grade totals
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And I set the field "Grade report" to "Overview report"
|
||||
And I should see "100.00 (50.00 %)" in the "overview-grade" "table"
|
||||
And I should see "110.00 (17.19 %)" in the "overview-grade" "table"
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
@core @core_grades
|
||||
Feature: View gradebook when scales are used
|
||||
In order to use scales to grade activities
|
||||
As an teacher
|
||||
I need to be able to view gradebook with scales
|
||||
|
||||
Background:
|
||||
Given I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
| grade_report_showranges | 1 |
|
||||
| grade_aggregations_visible | Mean of grades,Weighted mean of grades,Simple weighted mean of grades,Mean of grades (with extra credits),Median of grades,Lowest grade,Highest grade,Mode of grades,Sum of grades |
|
||||
And I navigate to "Scales" node in "Site administration > Grades"
|
||||
And I press "Add a new scale"
|
||||
And I set the following fields to these values:
|
||||
| Name | Letterscale |
|
||||
| Scale | F,D,C,B,A |
|
||||
And I press "Save changes"
|
||||
And I log out
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email | idnumber |
|
||||
| teacher1 | Teacher | 1 | teacher1@asd.com | t1 |
|
||||
| student1 | Student | 1 | student1@asd.com | s1 |
|
||||
| student2 | Student | 2 | student2@asd.com | s2 |
|
||||
| student3 | Student | 3 | student3@asd.com | s3 |
|
||||
| student4 | Student | 4 | student4@asd.com | s4 |
|
||||
| student5 | Student | 5 | student5@asd.com | s5 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
| student3 | C1 | student |
|
||||
| student4 | C1 | student |
|
||||
| student5 | C1 | student |
|
||||
And the following "grade categories" exist:
|
||||
| fullname | course |
|
||||
| Sub category 1 | C1 |
|
||||
And the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro | gradecategory |
|
||||
| assign | C1 | a1 | Test assignment one | Submit something! | Sub category 1 |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment one"
|
||||
And I follow "Edit settings"
|
||||
And I expand all fieldsets
|
||||
And I set the field "grade[modgrade_type]" to "Scale"
|
||||
And I set the field "grade[modgrade_scale]" to "Letterscale"
|
||||
And I press "Save and display"
|
||||
And I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Grade" to "A"
|
||||
And I press "Save and show next"
|
||||
And I set the field "Grade" to "B"
|
||||
And I press "Save and show next"
|
||||
And I set the field "Grade" to "C"
|
||||
And I press "Save and show next"
|
||||
And I set the field "Grade" to "D"
|
||||
And I press "Save and show next"
|
||||
And I set the field "Grade" to "F"
|
||||
And I press "Save changes"
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And I turn editing mode on
|
||||
|
||||
@javascript
|
||||
Scenario: Test displaying scales in gradebook in aggregation method Sum of grades
|
||||
When I follow "Edit Course 1"
|
||||
And I set the field "Aggregation" to "Sum of grades"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 1"
|
||||
And I set the field "Aggregation" to "Sum of grades"
|
||||
And I press "Save changes"
|
||||
And I turn editing mode off
|
||||
Then the following should exist in the "user-grades" table:
|
||||
| -1- | -4- | -5- | -6- |
|
||||
| Student 1 | A | 5.00 | 5.00 |
|
||||
| Student 2 | B | 4.00 | 4.00 |
|
||||
| Student 3 | C | 3.00 | 3.00 |
|
||||
| Student 4 | D | 2.00 | 2.00 |
|
||||
| Student 5 | F | 1.00 | 1.00 |
|
||||
And the following should exist in the "user-grades" table:
|
||||
| -1- | -2- | -3- | -4- |
|
||||
| Range | F–A | 0.00–5.00 | 0.00–5.00 |
|
||||
| Overall average | C | 3.00 | 3.00 |
|
||||
And I follow "User report"
|
||||
And I set the field "Select all or one user" to "Student 3"
|
||||
And I click on "Select all or one user" "select"
|
||||
And the following should exist in the "user-grade" table:
|
||||
| Grade item | Grade | Range | Percentage |
|
||||
| Test assignment one | C | F–A | 50.00 % |
|
||||
| Category total | 3.00 | 0–5 | 60.00 % |
|
||||
| Course total | 3.00 | 0–5 | 60.00 % |
|
||||
And I set the field "jump" to "Simple view"
|
||||
And the following should exist in the "grade_edit_tree_table" table:
|
||||
| Name | Max grade |
|
||||
| Test assignment one | 5.00 |
|
||||
| Category total | 5.00 |
|
||||
| Course total | 5.00 |
|
||||
And I log out
|
||||
And I log in as "student2"
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And the following should exist in the "user-grade" table:
|
||||
| Grade item | Grade | Range | Percentage |
|
||||
| Test assignment one | B | F–A | 75.00 % |
|
||||
| Category total | 4.00 | 0–5 | 80.00 % |
|
||||
| Course total | 4.00 | 0–5 | 80.00 % |
|
||||
|
||||
@javascript
|
||||
Scenario Outline: Test displaying scales in gradebook in all other aggregation methods
|
||||
When I follow "Edit Course 1"
|
||||
And I set the field "Aggregation" to "<aggregation>"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit Sub category 1"
|
||||
And I expand all fieldsets
|
||||
And I set the field "Aggregation" to "<aggregation>"
|
||||
And I set the field "Category name" to "Sub category (<aggregation>)"
|
||||
And I set the field "Maximum grade" to "5"
|
||||
And I set the field "Minimum grade" to "1"
|
||||
And I press "Save changes"
|
||||
And I turn editing mode off
|
||||
Then the following should exist in the "user-grades" table:
|
||||
| -1- | -4- | -5- | -6- |
|
||||
| Student 1 | A | 5.00 | <coursetotal1> |
|
||||
| Student 2 | B | 4.00 | <coursetotal2> |
|
||||
| Student 3 | C | 3.00 | <coursetotal3> |
|
||||
| Student 4 | D | 2.00 | <coursetotal4> |
|
||||
| Student 5 | F | 1.00 | <coursetotal5> |
|
||||
And the following should exist in the "user-grades" table:
|
||||
| -1- | -2- | -3- | -4- |
|
||||
| Range | F–A | 1.00–5.00 | 0.00–100.00 |
|
||||
| Overall average | C | 3.00 | <overallavg> |
|
||||
And I follow "User report"
|
||||
And I set the field "Select all or one user" to "Student 3"
|
||||
And I click on "Select all or one user" "select"
|
||||
And the following should exist in the "user-grade" table:
|
||||
| Grade item | Grade | Range | Percentage |
|
||||
| Test assignment one | C | F–A | 50.00 % |
|
||||
| Category total | 3.00 | 1–5 | 50.00 % |
|
||||
| Course total | <coursetotal3> | 0–100 | <courseperc3> |
|
||||
And I set the field "jump" to "Simple view"
|
||||
And the following should exist in the "grade_edit_tree_table" table:
|
||||
| Name | Max grade |
|
||||
| Test assignment one | A (5) |
|
||||
| Category total | |
|
||||
| Course total | |
|
||||
And I log out
|
||||
And I log in as "student2"
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And the following should exist in the "user-grade" table:
|
||||
| Grade item | Grade | Range | Percentage |
|
||||
| Test assignment one | B | F–A | 75.00 % |
|
||||
| Category total | 4.00 | 1–5 | 75.00 % |
|
||||
| Course total | <coursetotal2> | 0–100 | <courseperc2> |
|
||||
|
||||
Examples:
|
||||
| aggregation | coursetotal1 | coursetotal2 | coursetotal3 | coursetotal4 | coursetotal5 |overallavg | courseperc2 | courseperc3 |
|
||||
| Mean of grades | 100.00 | 75.00 | 50.00 | 25.00 | 0.00 | 50.00 | 75.00 % | 50.00 % |
|
||||
| Weighted mean of grades | - | - | - | - | - | - | - | - |
|
||||
| Simple weighted mean of grades | 100.00 | 75.00 | 50.00 | 25.00 | 0.00 | 50.00 | 75.00 % | 50.00 % |
|
||||
| Mean of grades (with extra credits) | 100.00 | 75.00 | 50.00 | 25.00 | 0.00 | 50.00 | 75.00 % | 50.00 % |
|
||||
| Median of grades | 100.00 | 75.00 | 50.00 | 25.00 | 0.00 | 50.00 | 75.00 % | 50.00 % |
|
||||
| Lowest grade | 100.00 | 75.00 | 50.00 | 25.00 | 0.00 | 50.00 | 75.00 % | 50.00 % |
|
||||
| Highest grade | 100.00 | 75.00 | 50.00 | 25.00 | 0.00 | 50.00 | 75.00 % | 50.00 % |
|
||||
| Mode of grades | 100.00 | 75.00 | 50.00 | 25.00 | 0.00 | 50.00 | 75.00 % | 50.00 % |
|
||||
@@ -65,6 +65,7 @@ class core_grade_report_graderlib_testcase extends advanced_testcase {
|
||||
$data = new stdClass();
|
||||
$data->id = $course->id;
|
||||
$data->report = 'grader';
|
||||
$data->timepageload = time();
|
||||
|
||||
$data->grade = array();
|
||||
$data->grade[$student->id] = array();
|
||||
@@ -79,6 +80,7 @@ class core_grade_report_graderlib_testcase extends advanced_testcase {
|
||||
// Grade above max. Should be pulled down to max.
|
||||
$toobig = 200.00;
|
||||
$data->grade[$student->id][$forum1->id] = $toobig;
|
||||
$data->timepageload = time();
|
||||
$warnings = $report->process_data($data);
|
||||
$this->assertEquals(count($warnings), 1);
|
||||
|
||||
@@ -88,6 +90,7 @@ class core_grade_report_graderlib_testcase extends advanced_testcase {
|
||||
// Grade below min. Should be pulled up to min.
|
||||
$toosmall = -10.00;
|
||||
$data->grade[$student->id][$forum1->id] = $toosmall;
|
||||
$data->timepageload = time();
|
||||
$warnings = $report->process_data($data);
|
||||
$this->assertEquals(count($warnings), 1);
|
||||
|
||||
@@ -98,6 +101,7 @@ class core_grade_report_graderlib_testcase extends advanced_testcase {
|
||||
$CFG->unlimitedgrades = 1;
|
||||
|
||||
$data->grade[$student->id][$forum1->id] = $toobig;
|
||||
$data->timepageload = time();
|
||||
$warnings = $report->process_data($data);
|
||||
$this->assertEquals(count($warnings), 0);
|
||||
|
||||
|
||||
+15
-15
@@ -136,43 +136,43 @@ echo $OUTPUT->header();
|
||||
?>
|
||||
<div id="addmembersform">
|
||||
<h3 class="main"><?php print_string('addgroupstogroupings', 'group'); echo ": $groupingname"; ?></h3>
|
||||
|
||||
<form id="assignform" method="post" action="">
|
||||
<div>
|
||||
<input type="hidden" name="sesskey" value="<?php p(sesskey()); ?>" />
|
||||
|
||||
<table summary="" cellpadding="5" cellspacing="0">
|
||||
<table summary="" class="generaltable generalbox groupmanagementtable boxaligncenter">
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<td id="existingcell">
|
||||
<label for="removeselect"><?php print_string('existingmembers', 'group', $currentmemberscount); ?></label>
|
||||
<br />
|
||||
<div class="userselector" id="removeselect_wrapper">
|
||||
<select name="removeselect[]" size="20" id="removeselect" multiple="multiple"
|
||||
onfocus="document.getElementById('assignform').add.disabled=true;
|
||||
document.getElementById('assignform').remove.disabled=false;
|
||||
document.getElementById('assignform').addselect.selectedIndex=-1;">
|
||||
<?php echo $currentmembersoptions ?>
|
||||
</select></td>
|
||||
<td valign="top">
|
||||
|
||||
</select></div></td>
|
||||
<td id="buttonscell">
|
||||
<p class="arrow_button">
|
||||
<input name="add" id="add" type="submit" value="<?php echo ' '.$OUTPUT->larrow().' '.get_string('add'); ?>" title="<?php print_string('add'); ?>" />
|
||||
<br />
|
||||
<input name="remove" id="remove" type="submit" value="<?php echo ' '.$OUTPUT->rarrow().' '.get_string('remove'); ?>" title="<?php print_string('remove'); ?>" />
|
||||
<input name="add" id="add" type="submit"
|
||||
value="<?php echo $OUTPUT->larrow().' '.get_string('add'); ?>"
|
||||
title="<?php print_string('add'); ?>" /><br>
|
||||
<input name="remove" id="remove" type="submit"
|
||||
value="<?php echo get_string('remove').' '.$OUTPUT->rarrow(); ?>"
|
||||
title="<?php print_string('remove'); ?>" />
|
||||
</p>
|
||||
</td>
|
||||
<td valign="top">
|
||||
<td id="potentialcell">
|
||||
<label for="addselect"><?php print_string('potentialmembers', 'group', $potentialmemberscount); ?></label>
|
||||
<br />
|
||||
<div class="userselector" id="addselect_wrapper">
|
||||
<select name="addselect[]" size="20" id="addselect" multiple="multiple"
|
||||
onfocus="document.getElementById('assignform').add.disabled=false;
|
||||
document.getElementById('assignform').remove.disabled=true;
|
||||
document.getElementById('assignform').removeselect.selectedIndex=-1;">
|
||||
<?php echo $potentialmembersoptions ?>
|
||||
</select>
|
||||
<br />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td>
|
||||
<tr><td colspan="3" id="backcell">
|
||||
<input type="submit" name="cancel" value="<?php print_string('backtogroupings', 'group'); ?>" />
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?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['availablelangs'] = 'Beskikbare taal pakkette';
|
||||
@@ -0,0 +1,44 @@
|
||||
<?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['clianswerno'] = 'n';
|
||||
$string['cliansweryes'] = 'y';
|
||||
$string['cliincorrectvalueerror'] = 'هەڵە، نرخی نادروستی "{$a->value}" بۆ "{$a->option}"';
|
||||
$string['cliincorrectvalueretry'] = 'نرخەکە نادرووستە، تکایە دووبارەبکەوە';
|
||||
$string['clitypevalue'] = 'نرخی جۆر';
|
||||
$string['clitypevaluedefault'] = 'نرخی جۆر، تکایە Enter دابگرە بۆ بەکارهێنانی نرخی بنەڕەتیی ({$a})';
|
||||
$string['cliunknowoption'] = 'هەلبژاردنە نەناسراوەکان:
|
||||
{$a}
|
||||
تکایە بەکاربهینە --هەلبژاردنى یارمەتى.';
|
||||
$string['cliyesnoprompt'] = 'جۆرى y (واتا بەڵێ) یاخود n (واتا نەخێر)';
|
||||
$string['environmentrequireinstall'] = 'پیویستى بە دامەزراندن و چالاکە';
|
||||
$string['environmentrequireversion'] = 'چاپ {$a->پیویست بوو} پیویست دەکات و ئایا بەکار دەخەى {$a->رەوان}';
|
||||
@@ -48,7 +48,7 @@ $string['environmenthead'] = 'Installationsvoraussetzungen werden geprüft ...';
|
||||
$string['environmentsub2'] = 'Jede Version hat Mindestvoraussetzungen für der PHP-Version und für verbindliche PHP-Extensions. Vor einer Installation oder einer Aktualisierung wird eine vollständige Prüfung durchgeführt. Bitte fragen Sie den Administrator des Servers, wenn Sie mit der Installation einer neuen Version oder mit der Aktivierung von PHP-Extensions nicht weiterkommen.';
|
||||
$string['errorsinenvironment'] = 'Fehler bei der Prüfung der Systemvoraussetzungen!';
|
||||
$string['installation'] = 'Installation';
|
||||
$string['langdownloaderror'] = 'Leider konnte das Sprachpaket \'{$a}\' nicht heruntergeladen werden. Die Installation wird in englischer Sprache fortgesetzt.';
|
||||
$string['langdownloaderror'] = 'Das Sprachpaket \'{$a}\' konnte nicht heruntergeladen werden. Die Installation wird in englischer Sprache fortgesetzt.';
|
||||
$string['memorylimithelp'] = '<p>Die PHP-Einstellung memory_limit Ihres Servers ist zur Zeit auf {$a} eingestellt. </p>
|
||||
<p>Wenn Sie Moodle mit vielen Aktivitäten oder vielen Nutzer/innen verwenden, wird dies vermutlich zu Problemen führen.</p>
|
||||
<p>Wir empfehlen die Einstellung wenn möglich zu erhöhen, z.B. auf 40M oder mehr. Dies können Sie auf verschiedene Arten machen:</p>
|
||||
|
||||
@@ -33,7 +33,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
$string['clianswerno'] = 'n';
|
||||
$string['cliansweryes'] = 's';
|
||||
$string['cliincorrectvalueerror'] = 'Error, valor incorrecto "{$a->value}" para "{$a->option}"';
|
||||
$string['cliincorrectvalueretry'] = 'Valor incorrecto, por favor, inténtelo de nuevo';
|
||||
$string['cliincorrectvalueretry'] = 'Valor incorrecto, por favor inténtelo de nuevo';
|
||||
$string['clitypevalue'] = 'valor del tipo';
|
||||
$string['clitypevaluedefault'] = 'valor del tipo, pulse Enter para utilizar el valor por defecto ({$a})';
|
||||
$string['cliunknowoption'] = 'Opciones no reconocidas:
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?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'] = 'es';
|
||||
$string['thislanguage'] = 'Español Venezuela';
|
||||
@@ -35,7 +35,7 @@ $string['availablelangs'] = 'Popis dostupnih jezika';
|
||||
$string['chooselanguagehead'] = 'Odaberite jezik';
|
||||
$string['chooselanguagesub'] = 'Molimo odaberite jezik instalacije. Ovaj jezik će biti korišten kao zadani jezik sustava, a kasnije to možete lagano promijeniti.';
|
||||
$string['clialreadyconfigured'] = 'Datoteka config.php već postoji. Upotrijebite naredbu admin/cli/install_database.php ako želite nastaviti instalaciju.';
|
||||
$string['clialreadyinstalled'] = 'Datoteka config.php već postoji. Upotrijebite naredbu admin/cli/upgrade.php ako želite nadograditi ovaj sustav.';
|
||||
$string['clialreadyinstalled'] = 'Datoteka config.php već postoji. Upotrijebite naredbu admin/cli/install_database.php ako želite nadograditi ovaj sustav.';
|
||||
$string['cliinstallheader'] = 'Moodle {$a} program za instalaciju iz naredbenog retka';
|
||||
$string['databasehost'] = 'Poslužitelj baze podataka (HOST)';
|
||||
$string['databasename'] = 'Naziv baze podataka';
|
||||
@@ -56,7 +56,7 @@ $string['memorylimithelp'] = '<p>PHP ograničenje memorije na poslužitelju je t
|
||||
|
||||
<p>Preporučujemo da konfigurirate PHP s većim ograničenjem ako je moguće, recimo 40M. Postoji nekoliko načina na koje to možete napraviti:</p>
|
||||
<ol>
|
||||
<li>Ako možete, rekompajlirajte PHP s <i>--enable-memory-limit</i>. Ovo će dozvoliti Moodle sustavu samostalno postavljanje memorijskog ograničenja.</li>
|
||||
<li>Ako možete, rekompajlirajte PHP s <i>--enable-memory-limit</i>. Ovo će dopustiti Moodle sustavu samostalno postavljanje memorijskog ograničenja.</li>
|
||||
<li>Ako imate pristup php.ini datoteci, možete promijeniti <b>memory_limit</b> vrijednost na 40M. Ako nemate pristup toj datoteci možete pitati svog administratora da to uradi.</li>
|
||||
<li>Na nekim PHP poslužiteljima možete napraviti .htaccess datoteku u Moodle mapi koja sadrži red: <p><blockquote>php_value memory_limit 40M</blockquote></p>
|
||||
<p>Uzmite u obzir da će na nekim poslužiteljima to spriječiti prikazivanje <b>svih</b> PHP stranica (bit će vam prikazana poruka o grešci), pa ćete na takvim poslužiteljima morati ukloniti .htaccess datoteku.</p></li> </ol>';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user