Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'MOODLE_23_STABLE' into install_23_STABLE

  • Loading branch information...
commit f84f35dda1001ca379c9a99c8429ef2848d016c3 2 parents ba1d10c + 71e12dc
AMOS bot authored

Showing 70 changed files with 1,019 additions and 364 deletions. Show diff stats Hide diff stats

  1. +117 1 admin/environment.xml
  2. +7 2 backup/moodle2/backup_custom_fields.php
  3. +17 0 backup/upgrade.txt
  4. +9 5 backup/util/dbops/backup_plan_dbops.class.php
  5. +11 1 backup/util/dbops/restore_dbops.class.php
  6. +44 24 backup/util/helper/backup_cron_helper.class.php
  7. +11 0 backup/util/plan/backup_structure_step.class.php
  8. +8 2 backup/util/plan/restore_structure_step.class.php
  9. +73 0 backup/util/structure/backup_nested_element.class.php
  10. +3 0  backup/util/ui/backup_ui_stage.class.php
  11. +3 0  backup/util/ui/restore_ui_stage.class.php
  12. +15 13 blocks/completionstatus/block_completionstatus.php
  13. +127 112 blocks/completionstatus/details.php
  14. +1 0  blocks/completionstatus/lang/en/block_completionstatus.php
  15. +9 0 blog/external_blogs.php
  16. +2 3 blog/locallib.php
  17. +1 1  enrol/manual/yui/quickenrolment/quickenrolment.js
  18. +11 11 enrol/paypal/ipn.php
  19. +3 0  filter/mediaplugin/tests/filter_test.php
  20. +2 1  grade/edit/tree/category_form.php
  21. +1 1  lang/en/admin.php
  22. +2 0  lang/en/backup.php
  23. +2 0  lang/en/block.php
  24. +1 0  lang/en/moodle.php
  25. +1 1  lib/accesslib.php
  26. +48 8 lib/blocklib.php
  27. +2 2 lib/conditionlib.php
  28. +10 0 lib/db/upgrade.php
  29. +47 2 lib/db/upgradelib.php
  30. +11 13 lib/googleapi.php
  31. +11 6 lib/medialib.php
  32. +33 3 lib/moodlelib.php
  33. +1 1  lib/pluginlib.php
  34. +7 5 lib/questionlib.php
  35. +60 0 lib/tests/moodlelib_test.php
  36. +38 9 lib/tests/pluginlib_test.php
  37. +11 24 message/lib.php
  38. +1 1  mod/data/field/checkbox/mod.html
  39. +5 4 mod/data/field/latlong/field.class.php
  40. +1 1  mod/data/field/menu/mod.html
  41. +1 1  mod/data/field/multimenu/mod.html
  42. +2 2 mod/data/field/picture/field.class.php
  43. +4 4 mod/data/field/picture/mod.html
  44. +1 1  mod/data/field/radiobutton/mod.html
  45. +2 2 mod/data/field/textarea/mod.html
  46. +11 17 mod/data/lib.php
  47. +32 0 mod/data/styles.css
  48. +7 7 mod/data/templates.php
  49. +17 1 mod/quiz/lib.php
  50. +1 0  mod/quiz/styles.css
  51. +6 1 mod/workshop/form/comments/backup/moodle1/lib.php
  52. +4 1 mod/workshop/form/numerrors/backup/moodle1/lib.php
  53. +4 1 mod/workshop/form/rubric/backup/moodle1/lib.php
  54. +5 2 question/category_class.php
  55. +3 0  question/type/multichoice/styles.css
  56. +10 3 report/backups/index.php
  57. +2 2 report/stats/lib.php
  58. +1 1  report/stats/settings.php
  59. +33 9 repository/flickr/lib.php
  60. +44 25 repository/flickr_public/lib.php
  61. +3 1 repository/googledocs/lib.php
  62. +6 0 repository/lib.php
  63. +10 6 repository/manage_instances.php
  64. +5 1 theme/afterburner/style/afterburner_styles.css
  65. +1 2  theme/anomaly/style/general.css
  66. +1 0  theme/base/style/admin.css
  67. +2 1  theme/yui_combo.php
  68. +30 13 user/profile.php
  69. +2 1  user/selector/module.js
  70. +2 2 version.php
118 admin/environment.xml
@@ -659,5 +659,121 @@
659 659 </FEEDBACK>
660 660 </PHP_SETTING>
661 661 </PHP_SETTINGS>
662   -</MOODLE>
  662 + </MOODLE>
  663 + <MOODLE version="2.4" requires="2.2">
  664 + <UNICODE level="required">
  665 + <FEEDBACK>
  666 + <ON_ERROR message="unicoderequired" />
  667 + </FEEDBACK>
  668 + </UNICODE>
  669 + <DATABASE level="required">
  670 + <VENDOR name="mysql" version="5.1.33" />
  671 + <VENDOR name="postgres" version="8.3" />
  672 + <VENDOR name="mssql" version="9.0" />
  673 + <VENDOR name="odbc_mssql" version="9.0" />
  674 + <VENDOR name="mssql_n" version="9.0" />
  675 + <VENDOR name="oracle" version="10.2" />
  676 + <VENDOR name="sqlite" version="2.0" />
  677 + </DATABASE>
  678 + <PHP version="5.3.2" level="required">
  679 + </PHP>
  680 + <PCREUNICODE level="optional">
  681 + <FEEDBACK>
  682 + <ON_CHECK message="pcreunicodewarning" />
  683 + </FEEDBACK>
  684 + </PCREUNICODE>
  685 + <PHP_EXTENSIONS>
  686 + <PHP_EXTENSION name="iconv" level="required">
  687 + <FEEDBACK>
  688 + <ON_CHECK message="iconvrequired" />
  689 + </FEEDBACK>
  690 + </PHP_EXTENSION>
  691 + <PHP_EXTENSION name="mbstring" level="optional">
  692 + <FEEDBACK>
  693 + <ON_CHECK message="mbstringrecommended" />
  694 + </FEEDBACK>
  695 + </PHP_EXTENSION>
  696 + <PHP_EXTENSION name="curl" level="required">
  697 + <FEEDBACK>
  698 + <ON_CHECK message="curlrequired" />
  699 + </FEEDBACK>
  700 + </PHP_EXTENSION>
  701 + <PHP_EXTENSION name="openssl" level="optional">
  702 + <FEEDBACK>
  703 + <ON_CHECK message="opensslrecommended" />
  704 + </FEEDBACK>
  705 + </PHP_EXTENSION>
  706 + <PHP_EXTENSION name="tokenizer" level="optional">
  707 + <FEEDBACK>
  708 + <ON_CHECK message="tokenizerrecommended" />
  709 + </FEEDBACK>
  710 + </PHP_EXTENSION>
  711 + <PHP_EXTENSION name="xmlrpc" level="optional">
  712 + <FEEDBACK>
  713 + <ON_CHECK message="xmlrpcrecommended" />
  714 + </FEEDBACK>
  715 + </PHP_EXTENSION>
  716 + <PHP_EXTENSION name="soap" level="optional">
  717 + <FEEDBACK>
  718 + <ON_CHECK message="soaprecommended" />
  719 + </FEEDBACK>
  720 + </PHP_EXTENSION>
  721 + <PHP_EXTENSION name="ctype" level="required">
  722 + <FEEDBACK>
  723 + <ON_ERROR message="ctyperequired" />
  724 + </FEEDBACK>
  725 + </PHP_EXTENSION>
  726 + <PHP_EXTENSION name="zip" level="required">
  727 + <FEEDBACK>
  728 + <ON_ERROR message="ziprequired" />
  729 + </FEEDBACK>
  730 + </PHP_EXTENSION>
  731 + <PHP_EXTENSION name="gd" level="optional">
  732 + <FEEDBACK>
  733 + <ON_CHECK message="gdrecommended" />
  734 + </FEEDBACK>
  735 + </PHP_EXTENSION>
  736 + <PHP_EXTENSION name="simplexml" level="required">
  737 + <FEEDBACK>
  738 + <ON_CHECK message="simplexmlrequired" />
  739 + </FEEDBACK>
  740 + </PHP_EXTENSION>
  741 + <PHP_EXTENSION name="spl" level="required">
  742 + <FEEDBACK>
  743 + <ON_CHECK message="splrequired" />
  744 + </FEEDBACK>
  745 + </PHP_EXTENSION>
  746 + <PHP_EXTENSION name="pcre" level="required">
  747 + </PHP_EXTENSION>
  748 + <PHP_EXTENSION name="dom" level="required">
  749 + </PHP_EXTENSION>
  750 + <PHP_EXTENSION name="xml" level="required">
  751 + </PHP_EXTENSION>
  752 + <PHP_EXTENSION name="intl" level="optional">
  753 + <FEEDBACK>
  754 + <ON_CHECK message="intlrecommended" />
  755 + </FEEDBACK>
  756 + </PHP_EXTENSION>
  757 + <PHP_EXTENSION name="json" level="required">
  758 + </PHP_EXTENSION>
  759 + <PHP_EXTENSION name="hash" level="required"/>
  760 + </PHP_EXTENSIONS>
  761 + <PHP_SETTINGS>
  762 + <PHP_SETTING name="memory_limit" value="40M" level="required">
  763 + <FEEDBACK>
  764 + <ON_ERROR message="settingmemorylimit" />
  765 + </FEEDBACK>
  766 + </PHP_SETTING>
  767 + <PHP_SETTING name="safe_mode" value="0" level="optional">
  768 + <FEEDBACK>
  769 + <ON_CHECK message="settingsafemode" />
  770 + </FEEDBACK>
  771 + </PHP_SETTING>
  772 + <PHP_SETTING name="file_uploads" value="1" level="optional">
  773 + <FEEDBACK>
  774 + <ON_CHECK message="settingfileuploads" />
  775 + </FEEDBACK>
  776 + </PHP_SETTING>
  777 + </PHP_SETTINGS>
  778 + </MOODLE>
663 779 </COMPATIBILITY_MATRIX>
9 backup/moodle2/backup_custom_fields.php
@@ -96,14 +96,19 @@ public function process($processor) {
96 96 if (is_null($this->backupid)) {
97 97 $this->backupid = $processor->get_var(backup::VAR_BACKUPID);
98 98 }
99   - parent::process($processor);
  99 + return parent::process($processor);
100 100 }
101 101
102 102 public function fill_values($values) {
103 103 // Fill values
104 104 parent::fill_values($values);
105 105 // Do our own tasks (copy file from moodle to backup)
106   - backup_file_manager::copy_file_moodle2backup($this->backupid, $values);
  106 + try {
  107 + backup_file_manager::copy_file_moodle2backup($this->backupid, $values);
  108 + } catch (file_exception $e) {
  109 + $this->add_result(array('missing_files_in_pool' => true));
  110 + $this->add_log('missing file in pool: ' . $e->debuginfo, backup::LOG_WARNING);
  111 + }
107 112 }
108 113 }
109 114
17 backup/upgrade.txt
... ... @@ -0,0 +1,17 @@
  1 +This files describes API changes in /backup/*,
  2 +information provided here is intended especially for developers.
  3 +
  4 +=== 2.4 ===
  5 +
  6 +* Since 2.3.1+ the backup file name schema has changed. The ID of the course will always be part of
  7 + the filename regardless of the setting 'backup_shortname'. See MDL-33812.
  8 +
  9 +=== 2.3 ===
  10 +
  11 +* Since 2.3.1+ the backup file name schema has changed. The ID of the course will always be part of
  12 + the filename regardless of the setting 'backup_shortname'. See MDL-33812.
  13 +
  14 +=== 2.2 ===
  15 +
  16 +* Since 2.2.4+ the backup file name schema has changed. The ID of the course will always be part of
  17 + the filename regardless of the setting 'backup_shortname'. See MDL-33812.
14 backup/util/dbops/backup_plan_dbops.class.php
@@ -197,19 +197,19 @@ public static function get_mnet_localhost_wwwroot() {
197 197 * @param int $courseid/$sectionid/$cmid
198 198 * @param bool $users Should be true is users were included in the backup
199 199 * @param bool $anonymised Should be true is user information was anonymized.
200   - * @param bool $useidasname true to use id, false to use strings (default)
  200 + * @param bool $useidonly only use the ID in the file name
201 201 * @return string The filename to use
202 202 */
203   - public static function get_default_backup_filename($format, $type, $id, $users, $anonymised, $useidasname = false) {
  203 + public static function get_default_backup_filename($format, $type, $id, $users, $anonymised, $useidonly = false) {
204 204 global $DB;
205 205
206 206 // Calculate backup word
207 207 $backupword = str_replace(' ', '_', textlib::strtolower(get_string('backupfilename')));
208 208 $backupword = trim(clean_filename($backupword), '_');
209 209
  210 + // Not $useidonly, lets fetch the name
210 211 $shortname = '';
211   - // Not $useidasname, lets calculate it, else $id will be used
212   - if (!$useidasname) {
  212 + if (!$useidonly) {
213 213 // Calculate proper name element (based on type)
214 214 switch ($type) {
215 215 case backup::TYPE_1COURSE:
@@ -231,7 +231,11 @@ public static function get_default_backup_filename($format, $type, $id, $users,
231 231 $shortname = textlib::strtolower(trim(clean_filename($shortname), '_'));
232 232 }
233 233
234   - $name = empty($shortname) ? $id : $shortname;
  234 + // The name will always contain the ID, but we append the course short name if requested.
  235 + $name = $id;
  236 + if (!$useidonly && $shortname != '') {
  237 + $name .= '-' . $shortname;
  238 + }
235 239
236 240 // Calculate date
237 241 $backupdateformat = str_replace(' ', '_', get_string('backupnameformat', 'langconfig'));
12 backup/util/dbops/restore_dbops.class.php
@@ -819,10 +819,13 @@ public static function restore_get_questions($restoreid, $qcatid) {
819 819 * @param int|null $olditemid
820 820 * @param int|null $forcenewcontextid explicit value for the new contextid (skip mapping)
821 821 * @param bool $skipparentitemidctxmatch
  822 + * @return array of result object
822 823 */
823 824 public static function send_files_to_pool($basepath, $restoreid, $component, $filearea, $oldcontextid, $dfltuserid, $itemname = null, $olditemid = null, $forcenewcontextid = null, $skipparentitemidctxmatch = false) {
824 825 global $DB;
825 826
  827 + $results = array();
  828 +
826 829 if ($forcenewcontextid) {
827 830 // Some components can have "forced" new contexts (example: questions can end belonging to non-standard context mappings,
828 831 // with questions originally at system/coursecat context in source being restored to course context in target). So we need
@@ -902,8 +905,14 @@ public static function send_files_to_pool($basepath, $restoreid, $component, $fi
902 905 // this is a regular file, it must be present in the backup pool
903 906 $backuppath = $basepath . backup_file_manager::get_backup_content_file_location($file->contenthash);
904 907
  908 + // The file is not found in the backup.
905 909 if (!file_exists($backuppath)) {
906   - throw new restore_dbops_exception('file_not_found_in_pool', $file);
  910 + $result = new stdClass();
  911 + $result->code = 'file_missing_in_backup';
  912 + $result->message = sprintf('missing file %s%s in backup', $file->filepath, $file->filename);
  913 + $result->level = backup::LOG_WARNING;
  914 + $results[] = $result;
  915 + continue;
907 916 }
908 917
909 918 // create the file in the filepool if it does not exist yet
@@ -960,6 +969,7 @@ public static function send_files_to_pool($basepath, $restoreid, $component, $fi
960 969 }
961 970 }
962 971 $rs->close();
  972 + return $results;
963 973 }
964 974
965 975 /**
68 backup/util/helper/backup_cron_helper.class.php
@@ -46,6 +46,8 @@
46 46 const BACKUP_STATUS_UNFINISHED = 2;
47 47 /** Course automated backup was skipped */
48 48 const BACKUP_STATUS_SKIPPED = 3;
  49 + /** Course automated backup had warnings */
  50 + const BACKUP_STATUS_WARNING = 4;
49 51
50 52 /** Run if required by the schedule set in config. Default. **/
51 53 const RUN_ON_SCHEDULE = 0;
@@ -139,7 +141,7 @@ public static function run_automated_backup($rundirective = self::RUN_ON_SCHEDUL
139 141 $params = array('courseid' => $course->id, 'time' => $now-31*24*60*60, 'action' => '%view%');
140 142 $logexists = $DB->record_exists_select('log', $sqlwhere, $params);
141 143 if (!$logexists) {
142   - $backupcourse->laststatus = backup_cron_automated_helper::BACKUP_STATUS_SKIPPED;
  144 + $backupcourse->laststatus = self::BACKUP_STATUS_SKIPPED;
143 145 $backupcourse->nextstarttime = $nextstarttime;
144 146 $DB->update_record('backup_courses', $backupcourse);
145 147 mtrace('Skipping unchanged course '.$course->fullname);
@@ -160,7 +162,7 @@ public static function run_automated_backup($rundirective = self::RUN_ON_SCHEDUL
160 162 $starttime = time();
161 163
162 164 $backupcourse->laststarttime = time();
163   - $backupcourse->laststatus = backup_cron_automated_helper::BACKUP_STATUS_UNFINISHED;
  165 + $backupcourse->laststatus = self::BACKUP_STATUS_UNFINISHED;
164 166 $DB->update_record('backup_courses', $backupcourse);
165 167
166 168 $backupcourse->laststatus = backup_cron_automated_helper::launch_automated_backup($course, $backupcourse->laststarttime, $admin->id);
@@ -169,7 +171,7 @@ public static function run_automated_backup($rundirective = self::RUN_ON_SCHEDUL
169 171
170 172 $DB->update_record('backup_courses', $backupcourse);
171 173
172   - if ($backupcourse->laststatus) {
  174 + if ($backupcourse->laststatus === self::BACKUP_STATUS_OK) {
173 175 // Clean up any excess course backups now that we have
174 176 // taken a successful backup.
175 177 $removedcount = backup_cron_automated_helper::remove_excess_backups($course);
@@ -188,17 +190,18 @@ public static function run_automated_backup($rundirective = self::RUN_ON_SCHEDUL
188 190 $message = "";
189 191
190 192 $count = backup_cron_automated_helper::get_backup_status_array();
191   - $haserrors = ($count[backup_cron_automated_helper::BACKUP_STATUS_ERROR] != 0 || $count[backup_cron_automated_helper::BACKUP_STATUS_UNFINISHED] != 0);
  193 + $haserrors = ($count[self::BACKUP_STATUS_ERROR] != 0 || $count[self::BACKUP_STATUS_UNFINISHED] != 0);
192 194
193 195 //Build the message text
194 196 //Summary
195 197 $message .= get_string('summary')."\n";
196 198 $message .= "==================================================\n";
197 199 $message .= " ".get_string('courses').": ".array_sum($count)."\n";
198   - $message .= " ".get_string('ok').": ".$count[backup_cron_automated_helper::BACKUP_STATUS_OK]."\n";
199   - $message .= " ".get_string('skipped').": ".$count[backup_cron_automated_helper::BACKUP_STATUS_SKIPPED]."\n";
200   - $message .= " ".get_string('error').": ".$count[backup_cron_automated_helper::BACKUP_STATUS_ERROR]."\n";
201   - $message .= " ".get_string('unfinished').": ".$count[backup_cron_automated_helper::BACKUP_STATUS_UNFINISHED]."\n\n";
  200 + $message .= " ".get_string('ok').": ".$count[self::BACKUP_STATUS_OK]."\n";
  201 + $message .= " ".get_string('skipped').": ".$count[self::BACKUP_STATUS_SKIPPED]."\n";
  202 + $message .= " ".get_string('error').": ".$count[self::BACKUP_STATUS_ERROR]."\n";
  203 + $message .= " ".get_string('unfinished').": ".$count[self::BACKUP_STATUS_UNFINISHED]."\n";
  204 + $message .= " ".get_string('warning').": ".$count[self::BACKUP_STATUS_WARNING]."\n\n";
202 205
203 206 //Reference
204 207 if ($haserrors) {
@@ -261,6 +264,7 @@ public static function get_backup_status_array() {
261 264 self::BACKUP_STATUS_OK => 0,
262 265 self::BACKUP_STATUS_UNFINISHED => 0,
263 266 self::BACKUP_STATUS_SKIPPED => 0,
  267 + self::BACKUP_STATUS_WARNING => 0
264 268 );
265 269
266 270 $statuses = $DB->get_records_sql('SELECT DISTINCT bc.laststatus, COUNT(bc.courseid) AS statuscount FROM {backup_courses} bc GROUP BY bc.laststatus');
@@ -334,7 +338,7 @@ public static function calculate_next_automated_backup($timezone, $now) {
334 338 */
335 339 public static function launch_automated_backup($course, $starttime, $userid) {
336 340
337   - $outcome = true;
  341 + $outcome = self::BACKUP_STATUS_OK;
338 342 $config = get_config('backup');
339 343 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_AUTOMATED, $userid);
340 344
@@ -369,6 +373,7 @@ public static function launch_automated_backup($course, $starttime, $userid) {
369 373
370 374 $bc->execute_plan();
371 375 $results = $bc->get_results();
  376 + $outcome = self::outcome_from_results($results);
372 377 $file = $results['backup_destination']; // may be empty if file already moved to target location
373 378 $dir = $config->backup_auto_destination;
374 379 $storage = (int)$config->backup_auto_storage;
@@ -377,8 +382,10 @@ public static function launch_automated_backup($course, $starttime, $userid) {
377 382 }
378 383 if ($file && !empty($dir) && $storage !== 0) {
379 384 $filename = backup_plan_dbops::get_default_backup_filename($format, $type, $course->id, $users, $anonymised, !$config->backup_shortname);
380   - $outcome = $file->copy_content_to($dir.'/'.$filename);
381   - if ($outcome && $storage === 1) {
  385 + if (!$file->copy_content_to($dir.'/'.$filename)) {
  386 + $outcome = self::BACKUP_STATUS_ERROR;
  387 + }
  388 + if ($outcome != self::BACKUP_STATUS_ERROR && $storage === 1) {
382 389 $file->delete();
383 390 }
384 391 }
@@ -387,7 +394,7 @@ public static function launch_automated_backup($course, $starttime, $userid) {
387 394 $bc->log('backup_auto_failed_on_course', backup::LOG_ERROR, $course->shortname); // Log error header.
388 395 $bc->log('Exception: ' . $e->errorcode, backup::LOG_ERROR, $e->a, 1); // Log original exception problem.
389 396 $bc->log('Debug: ' . $e->debuginfo, backup::LOG_DEBUG, null, 1); // Log original debug information.
390   - $outcome = false;
  397 + $outcome = self::BACKUP_STATUS_ERROR;
391 398 }
392 399
393 400 $bc->destroy();
@@ -397,6 +404,30 @@ public static function launch_automated_backup($course, $starttime, $userid) {
397 404 }
398 405
399 406 /**
  407 + * Returns the backup outcome by analysing its results.
  408 + *
  409 + * @param array $results returned by a backup
  410 + * @return int {@link self::BACKUP_STATUS_OK} and other constants
  411 + */
  412 + public static function outcome_from_results($results) {
  413 + $outcome = self::BACKUP_STATUS_OK;
  414 + foreach ($results as $code => $value) {
  415 + // Each possible error and warning code has to be specified in this switch
  416 + // which basically analyses the results to return the correct backup status.
  417 + switch ($code) {
  418 + case 'missing_files_in_pool':
  419 + $outcome = self::BACKUP_STATUS_WARNING;
  420 + break;
  421 + }
  422 + // If we found the highest error level, we exit the loop.
  423 + if ($outcome == self::BACKUP_STATUS_ERROR) {
  424 + break;
  425 + }
  426 + }
  427 + return $outcome;
  428 + }
  429 +
  430 + /**
400 431 * Removes deleted courses fromn the backup_courses table so that we don't
401 432 * waste time backing them up.
402 433 *
@@ -530,18 +561,7 @@ public static function remove_excess_backups($course) {
530 561 if (!empty($dir) && ($storage == 1 || $storage == 2)) {
531 562 // Calculate backup filename regex, ignoring the date/time/info parts that can be
532 563 // variable, depending of languages, formats and automated backup settings
533   -
534   -
535   - // MDL-33531: use different filenames depending on backup_shortname option
536   - if ( !empty($config->backup_shortname) ) {
537   - $context = get_context_instance(CONTEXT_COURSE, $course->id);
538   - $courseref = format_string($course->shortname, true, array('context' => $context));
539   - $courseref = str_replace(' ', '_', $courseref);
540   - $courseref = textlib::strtolower(trim(clean_filename($courseref), '_'));
541   - } else {
542   - $courseref = $course->id;
543   - }
544   - $filename = $backupword . '-' . backup::FORMAT_MOODLE . '-' . backup::TYPE_1COURSE . '-' .$courseref . '-';
  564 + $filename = $backupword . '-' . backup::FORMAT_MOODLE . '-' . backup::TYPE_1COURSE . '-' .$course->id . '-';
545 565 $regex = '#^'.preg_quote($filename, '#').'.*\.mbz$#';
546 566
547 567 // Store all the matching files into fullpath => timemodified array
11 backup/util/plan/backup_structure_step.class.php
@@ -94,11 +94,22 @@ public function execute() {
94 94 // Process structure definition
95 95 $structure->process($pr);
96 96
  97 + // Get the results from the nested elements
  98 + $results = $structure->get_results();
  99 +
  100 + // Get the log messages to append to the log
  101 + $logs = $structure->get_logs();
  102 + foreach ($logs as $log) {
  103 + $this->log($log->message, $log->level, $log->a, $log->depth, $log->display);
  104 + }
  105 +
97 106 // Close everything
98 107 $xw->stop();
99 108
100 109 // Destroy the structure. It helps PHP 5.2 memory a lot!
101 110 $structure->destroy();
  111 +
  112 + return $results;
102 113 }
103 114
104 115 /**
10 backup/util/plan/restore_structure_step.class.php
@@ -218,8 +218,14 @@ public function get_mapping($itemname, $oldid) {
218 218 */
219 219 public function add_related_files($component, $filearea, $mappingitemname, $filesctxid = null, $olditemid = null) {
220 220 $filesctxid = is_null($filesctxid) ? $this->task->get_old_contextid() : $filesctxid;
221   - restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), $component,
222   - $filearea, $filesctxid, $this->task->get_userid(), $mappingitemname, $olditemid);
  221 + $results = restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), $component,
  222 + $filearea, $filesctxid, $this->task->get_userid(), $mappingitemname, $olditemid);
  223 + $resultstoadd = array();
  224 + foreach ($results as $result) {
  225 + $this->log($result->message, $result->level);
  226 + $resultstoadd[$result->code] = true;
  227 + }
  228 + $this->task->add_result($resultstoadd);
223 229 }
224 230
225 231 /**
73 backup/util/structure/backup_nested_element.class.php
@@ -37,6 +37,8 @@ class backup_nested_element extends base_nested_element implements processable {
37 37 protected $aliases; // Define DB->final element aliases
38 38 protected $fileannotations; // array of file areas to be searched by file annotations
39 39 protected $counter; // Number of instances of this element that have been processed
  40 + protected $results; // Logs the results we encounter during the process.
  41 + protected $logs; // Some log messages that could be retrieved later.
40 42
41 43 /**
42 44 * Constructor - instantiates one backup_nested_element, specifying its basic info.
@@ -55,8 +57,16 @@ public function __construct($name, $attributes = null, $final_elements = null) {
55 57 $this->aliases = array();
56 58 $this->fileannotations = array();
57 59 $this->counter = 0;
  60 + $this->results = array();
  61 + $this->logs = array();
58 62 }
59 63
  64 + /**
  65 + * Process the nested element
  66 + *
  67 + * @param object $processor the processor
  68 + * @return void
  69 + */
60 70 public function process($processor) {
61 71 if (!$processor instanceof base_processor) { // No correct processor, throw exception
62 72 throw new base_element_struct_exception('incorrect_processor');
@@ -113,6 +123,69 @@ public function process($processor) {
113 123 $iterator->close();
114 124 }
115 125
  126 + /**
  127 + * Saves a log message to an array
  128 + *
  129 + * @see backup_helper::log()
  130 + * @param string $message to add to the logs
  131 + * @param int $level level of importance {@link backup::LOG_DEBUG} and other constants
  132 + * @param mixed $a to be included in $message
  133 + * @param int $depth of the message
  134 + * @param display $bool supporting translation via get_string() if true
  135 + * @return void
  136 + */
  137 + protected function add_log($message, $level, $a = null, $depth = null, $display = false) {
  138 + // Adding the result to the oldest parent.
  139 + if ($this->get_parent()) {
  140 + $parent = $this->get_grandparent();
  141 + $parent->add_log($message, $level, $a, $depth, $display);
  142 + } else {
  143 + $log = new stdClass();
  144 + $log->message = $message;
  145 + $log->level = $level;
  146 + $log->a = $a;
  147 + $log->depth = $depth;
  148 + $log->display = $display;
  149 + $this->logs[] = $log;
  150 + }
  151 + }
  152 +
  153 + /**
  154 + * Saves the results to an array
  155 + *
  156 + * @param array $result associative array
  157 + * @return void
  158 + */
  159 + protected function add_result($result) {
  160 + if (is_array($result)) {
  161 + // Adding the result to the oldest parent.
  162 + if ($this->get_parent()) {
  163 + $parent = $this->get_grandparent();
  164 + $parent->add_result($result);
  165 + } else {
  166 + $this->results = array_merge($this->results, $result);
  167 + }
  168 + }
  169 + }
  170 +
  171 + /**
  172 + * Returns the logs
  173 + *
  174 + * @return array of log objects
  175 + */
  176 + public function get_logs() {
  177 + return $this->logs;
  178 + }
  179 +
  180 + /**
  181 + * Returns the results
  182 + *
  183 + * @return associative array of results
  184 + */
  185 + public function get_results() {
  186 + return $this->results;
  187 + }
  188 +
116 189 public function set_source_array($arr) {
117 190 // TODO: Only elements having final elements can set source
118 191 $this->var_array = $arr;
3  backup/util/ui/backup_ui_stage.class.php
@@ -487,6 +487,9 @@ public function display(core_backup_renderer $renderer) {
487 487 if (!empty($this->results['include_file_references_to_external_content'])) {
488 488 $output .= $renderer->notification(get_string('filereferencesincluded', 'backup'), 'notifyproblem');
489 489 }
  490 + if (!empty($this->results['missing_files_in_pool'])) {
  491 + $output .= $renderer->notification(get_string('missingfilesinpool', 'backup'), 'notifyproblem');
  492 + }
490 493 $output .= $renderer->notification(get_string('executionsuccess', 'backup'), 'notifysuccess');
491 494 $output .= $renderer->continue_button($restorerul);
492 495 $output .= $renderer->box_end();
3  backup/util/ui/restore_ui_stage.class.php
@@ -772,6 +772,9 @@ public function display(core_backup_renderer $renderer) {
772 772 $html .= $renderer->box_end();
773 773 }
774 774 $html .= $renderer->box_start();
  775 + if (array_key_exists('file_missing_in_backup', $this->results)) {
  776 + $html .= $renderer->notification(get_string('restorefileweremissing', 'backup'), 'notifyproblem');
  777 + }
775 778 $html .= $renderer->notification(get_string('restoreexecutionsuccess', 'backup'), 'notifysuccess');
776 779 $html .= $renderer->continue_button(new moodle_url('/course/view.php', array(
777 780 'id' => $this->get_ui()->get_controller()->get_courseid())), 'get');
28 blocks/completionstatus/block_completionstatus.php
@@ -19,15 +19,14 @@
19 19 *
20 20 * @package block
21 21 * @subpackage completion
22   - * @copyright 2009 Catalyst IT Ltd
  22 + * @copyright 2009-2012 Catalyst IT Ltd
23 23 * @author Aaron Barnes <aaronb@catalyst.net.nz>
24 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 25 */
26 26
27 27 defined('MOODLE_INTERNAL') || die();
28 28
29   -
30   -require_once($CFG->libdir.'/completionlib.php');
  29 +require_once("{$CFG->libdir}/completionlib.php");
31 30
32 31 /**
33 32 * Course completion status
@@ -36,25 +35,27 @@
36 35 class block_completionstatus extends block_base {
37 36
38 37 public function init() {
39   - $this->title = get_string('pluginname', 'block_completionstatus');
  38 + $this->title = get_string('pluginname', 'block_completionstatus');
40 39 }
41 40
42 41 public function get_content() {
43   - global $USER, $CFG, $DB, $COURSE;
  42 + global $USER;
44 43
45 44 // If content is cached
46 45 if ($this->content !== NULL) {
47 46 return $this->content;
48 47 }
49 48
  49 + $course = $this->page->course;
  50 +
50 51 // Create empty content
51   - $this->content = new stdClass;
  52 + $this->content = new stdClass();
52 53
53 54 // Can edit settings?
54   - $can_edit = has_capability('moodle/course:update', get_context_instance(CONTEXT_COURSE, $this->page->course->id));
  55 + $can_edit = has_capability('moodle/course:update', get_context_instance(CONTEXT_COURSE, $course->id));
55 56
56 57 // Get course completion data
57   - $info = new completion_info($this->page->course);
  58 + $info = new completion_info($course);
58 59
59 60 // Don't display if completion isn't enabled!
60 61 if (!completion_info::is_enabled_for_site()) {
@@ -84,9 +85,9 @@ public function get_content() {
84 85 // Check this user is enroled
85 86 if (!$info->is_tracked_user($USER->id)) {
86 87 // If not enrolled, but are can view the report:
87   - if (has_capability('report/completion:view', get_context_instance(CONTEXT_COURSE, $COURSE->id))) {
88   - $this->content->text = '<a href="'.$CFG->wwwroot.'/report/completion/index.php?course='.$COURSE->id.
89   - '">'.get_string('viewcoursereport', 'completion').'</a>';
  88 + if (has_capability('report/completion:view', get_context_instance(CONTEXT_COURSE, $course->id))) {
  89 + $report = new moodle_url('/report/completion/index.php', array('course' => $course->id));
  90 + $this->content->text = '<a href="'.$report->out().'">'.get_string('viewcoursereport', 'completion').'</a>';
90 91 return $this->content;
91 92 }
92 93
@@ -187,7 +188,7 @@ public function get_content() {
187 188 // Load course completion
188 189 $params = array(
189 190 'userid' => $USER->id,
190   - 'course' => $COURSE->id
  191 + 'course' => $course->id
191 192 );
192 193 $ccompletion = new completion_completion($params);
193 194
@@ -221,7 +222,8 @@ public function get_content() {
221 222 $this->content->text .= $shtml.'</tbody></table>';
222 223
223 224 // Display link to detailed view
224   - $this->content->footer = '<br><a href="'.$CFG->wwwroot.'/blocks/completionstatus/details.php?course='.$COURSE->id.'">'.get_string('moredetails', 'completion').'</a>';
  225 + $details = new moodle_url('/blocks/completionstatus/details.php', array('course' => $course->id));
  226 + $this->content->footer = '<br><a href="'.$details->out().'">'.get_string('moredetails', 'completion').'</a>';
225 227
226 228 return $this->content;
227 229 }
239 blocks/completionstatus/details.php
@@ -19,27 +19,23 @@
19 19 *
20 20 * @package block
21 21 * @subpackage completion
22   - * @copyright 2009 Catalyst IT Ltd
  22 + * @copyright 2009-2012 Catalyst IT Ltd
23 23 * @author Aaron Barnes <aaronb@catalyst.net.nz>
24 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 25 */
26 26
27   -require_once('../../config.php');
28   -require_once($CFG->libdir.'/completionlib.php');
29   -
30   -
31   -// TODO: Make this page Moodle 2.0 compliant
  27 +require_once(dirname(__FILE__).'/../../config.php');
  28 +require_once("{$CFG->libdir}/completionlib.php");
32 29
33 30
34 31 ///
35 32 /// Load data
36 33 ///
37 34 $id = required_param('course', PARAM_INT);
38   -// User id
39 35 $userid = optional_param('user', 0, PARAM_INT);
40 36
41 37 // Load course
42   -$course = $DB->get_record('course', array('id' => $id));
  38 +$course = $DB->get_record('course', array('id' => $id), '*', MUST_EXIST);
43 39
44 40 // Load user
45 41 if ($userid) {
@@ -76,21 +72,13 @@
76 72 // Load completion data
77 73 $info = new completion_info($course);
78 74
79   -$returnurl = "{$CFG->wwwroot}/course/view.php?id={$id}";
  75 +$returnurl = new moodle_url('/course/view.php', array('id' => $id));
80 76
81 77 // Don't display if completion isn't enabled!
82 78 if (!$info->is_enabled()) {
83 79 print_error('completionnotenabled', 'completion', $returnurl);
84 80 }
85 81
86   -// Load criteria to display
87   -$completions = $info->get_completions($user->id);
88   -
89   -// Check if this course has any criteria
90   -if (empty($completions)) {
91   - print_error('nocriteriaset', 'completion', $returnurl);
92   -}
93   -
94 82 // Check this user is enroled
95 83 if (!$info->is_tracked_user($user->id)) {
96 84 if ($USER->id == $user->id) {
@@ -104,6 +92,7 @@
104 92 ///
105 93 /// Display page
106 94 ///
  95 +$PAGE->set_context(context_course::instance($course->id));
107 96
108 97 // Print header
109 98 $page = get_string('completionprogressdetails', 'block_completionstatus');
@@ -111,7 +100,7 @@
111 100
112 101 $PAGE->navbar->add($page);
113 102 $PAGE->set_pagelayout('standard');
114   -$PAGE->set_url('/blocks/completionstatus/details.php', array('course' => $course->id));
  103 +$PAGE->set_url('/blocks/completionstatus/details.php', array('course' => $course->id, 'user' => $user->id));
115 104 $PAGE->set_title(get_string('course') . ': ' . $course->fullname);
116 105 $PAGE->set_heading($title);
117 106 echo $OUTPUT->header();
@@ -135,122 +124,148 @@
135 124 // Has this user completed any criteria?
136 125 $criteriacomplete = $info->count_course_user_data($user->id);
137 126
  127 +// Load course completion
  128 +$params = array(
  129 + 'userid' => $user->id,
  130 + 'course' => $course->id,
  131 +);
  132 +$ccompletion = new completion_completion($params);
  133 +
138 134 if ($coursecomplete) {
139 135 echo get_string('complete');
140   -} else if (!$criteriacomplete) {
  136 +} else if (!$criteriacomplete && !$ccompletion->timestarted) {
141 137 echo '<i>'.get_string('notyetstarted', 'completion').'</i>';
142 138 } else {
143 139 echo '<i>'.get_string('inprogress','completion').'</i>';
144 140 }
145 141
146 142 echo '</td></tr>';
147   -echo '<tr><td colspan="2"><b>'.get_string('required').':</b> ';
148 143
149   -// Get overall aggregation method
150   -$overall = $info->get_aggregation_method();
  144 +// Load criteria to display
  145 +$completions = $info->get_completions($user->id);
151 146
152   -if ($overall == COMPLETION_AGGREGATION_ALL) {
153   - echo get_string('criteriarequiredall', 'completion');
  147 +// Check if this course has any criteria
  148 +if (empty($completions)) {
  149 + echo '<tr><td colspan="2"><br />';
  150 + echo $OUTPUT->box(get_string('err_nocriteria', 'completion'), 'noticebox');
  151 + echo '</td></tr></tbody></table>';
154 152 } else {
155   - echo get_string('criteriarequiredany', 'completion');
156   -}
  153 + echo '<tr><td colspan="2"><b>'.get_string('required').':</b> ';
157 154
158   -echo '</td></tr></tbody></table>';
159   -
160   -// Generate markup for criteria statuses
161   -echo '<table class="generalbox boxaligncenter" cellpadding="3"><tbody>';
162   -echo '<tr class="ccheader">';
163   -echo '<th class="c0 header" scope="col">'.get_string('criteriagroup', 'block_completionstatus').'</th>';
164   -echo '<th class="c1 header" scope="col">'.get_string('criteria', 'completion').'</th>';
165   -echo '<th class="c2 header" scope="col">'.get_string('requirement', 'block_completionstatus').'</th>';
166   -echo '<th class="c3 header" scope="col">'.get_string('status').'</th>';
167   -echo '<th class="c4 header" scope="col">'.get_string('complete').'</th>';
168   -echo '<th class="c5 header" scope="col">'.get_string('completiondate', 'report_completion').'</th>';
169   -echo '</tr>';
170   -
171   -// Save row data
172   -$rows = array();
173   -
174   -global $COMPLETION_CRITERIA_TYPES;
175   -
176   -// Loop through course criteria
177   -foreach ($completions as $completion) {
178   - $criteria = $completion->get_criteria();
179   - $complete = $completion->is_complete();
180   -
181   - $row = array();
182   - $row['type'] = $criteria->criteriatype;
183   - $row['title'] = $criteria->get_title();
184   - $row['status'] = $completion->get_status();
185   - $row['timecompleted'] = $completion->timecompleted;
186   - $row['details'] = $criteria->get_details($completion);
187   - $rows[] = $row;
188   -}
  155 + // Get overall aggregation method
  156 + $overall = $info->get_aggregation_method();
189 157
190   -// Print table
191   -$last_type = '';
192   -$agg_type = false;
  158 + if ($overall == COMPLETION_AGGREGATION_ALL) {
  159 + echo get_string('criteriarequiredall', 'completion');
  160 + } else {
  161 + echo get_string('criteriarequiredany', 'completion');
  162 + }
  163 +
  164 + echo '</td></tr></tbody></table>';
  165 +
  166 + // Generate markup for criteria statuses
  167 + echo '<table class="generalbox logtable boxaligncenter" id="criteriastatus" width="100%"><tbody>';
  168 + echo '<tr class="ccheader">';
  169 + echo '<th class="c0 header" scope="col">'.get_string('criteriagroup', 'block_completionstatus').'</th>';
  170 + echo '<th class="c1 header" scope="col">'.get_string('criteria', 'completion').'</th>';
  171 + echo '<th class="c2 header" scope="col">'.get_string('requirement', 'block_completionstatus').'</th>';
  172 + echo '<th class="c3 header" scope="col">'.get_string('status').'</th>';
  173 + echo '<th class="c4 header" scope="col">'.get_string('complete').'</th>';
  174 + echo '<th class="c5 header" scope="col">'.get_string('completiondate', 'report_completion').'</th>';
  175 + echo '</tr>';
193 176
194   -foreach ($rows as $row) {
  177 + // Save row data
  178 + $rows = array();
  179 +
  180 + // Loop through course criteria
  181 + foreach ($completions as $completion) {
  182 + $criteria = $completion->get_criteria();
  183 +
  184 + $row = array();
  185 + $row['type'] = $criteria->criteriatype;
  186 + $row['title'] = $criteria->get_title();
  187 + $row['status'] = $completion->get_status();
  188 + $row['complete'] = $completion->is_complete();
  189 + $row['timecompleted'] = $completion->timecompleted;
  190 + $row['details'] = $criteria->get_details($completion);
  191 + $rows[] = $row;
  192 + }
195 193
196   - // Criteria group
197   - echo '<td class="c0">';
198   - if ($last_type !== $row['details']['type']) {
199   - $last_type = $row['details']['type'];
200   - echo $last_type;
  194 + // Print table
  195 + $last_type = '';
  196 + $agg_type = false;
  197 + $oddeven = 0;
201 198
202   - // Reset agg type
203   - $agg_type = true;
204   - } else {
205   - // Display aggregation type
206   - if ($agg_type) {
207   - $agg = $info->get_aggregation_method($row['type']);
  199 + foreach ($rows as $row) {
208 200
209   - echo '(<i>';
  201 + echo '<tr class="r' . $oddeven . '">';
210 202
211   - if ($agg == COMPLETION_AGGREGATION_ALL) {
212   - echo strtolower(get_string('all', 'completion'));
213   - } else {
214   - echo strtolower(get_string('any', 'completion'));
215   - }
  203 + // Criteria group
  204 + echo '<td class="cell c0">';
  205 + if ($last_type !== $row['details']['type']) {
  206 + $last_type = $row['details']['type'];
  207 + echo $last_type;
  208 +
  209 + // Reset agg type
  210 + $agg_type = true;
  211 + } else {
  212 + // Display aggregation type
  213 + if ($agg_type) {
  214 + $agg = $info->get_aggregation_method($row['type']);
216 215
217   - echo '</i> '.strtolower(get_string('required')).')';
218   - $agg_type = false;
  216 + echo '(<i>';
  217 +
  218 + if ($agg == COMPLETION_AGGREGATION_ALL) {
  219 + echo strtolower(get_string('aggregateall', 'completion'));
  220 + } else {
  221 + echo strtolower(get_string('aggregateany', 'completion'));
  222 + }
  223 +
  224 + echo '</i> '.strtolower(get_string('required')).')';
  225 + $agg_type = false;
  226 + }
219 227 }
  228 + echo '</td>';
  229 +
  230 + // Criteria title
  231 + echo '<td class="cell c1">';
  232 + echo $row['details']['criteria'];
  233 + echo '</td>';
  234 +
  235 + // Requirement
  236 + echo '<td class="cell c2">';
  237 + echo $row['details']['requirement'];
  238 + echo '</td>';
  239 +
  240 + // Status
  241 + echo '<td class="cell c3">';
  242 + echo $row['details']['status'];
  243 + echo '</td>';
  244 +
  245 + // Is complete
  246 + echo '<td class="cell c4">';
  247 + echo $row['complete'] ? get_string('yes') : get_string('no');
  248 + echo '</td>';
  249 +
  250 + // Completion data
  251 + echo '<td class="cell c5">';
  252 + if ($row['timecompleted']) {
  253 + echo userdate($row['timecompleted'], get_string('strftimedate', 'langconfig'));
  254 + } else {
  255 + echo '-';
  256 + }
  257 + echo '</td>';
  258 + echo '</tr>';
  259 + // for row striping
  260 + $oddeven = $oddeven ? 0 : 1;
220 261 }
221   - echo '</td>';
222   -
223   - // Criteria title
224   - echo '<td class="c1">';
225   - echo $row['details']['criteria'];
226   - echo '</td>';
227   -
228   - // Requirement
229   - echo '<td class="c2">';
230   - echo $row['details']['requirement'];
231   - echo '</td>';
232   -
233   - // Status
234   - echo '<td class="c3">';
235   - echo $row['details']['status'];
236   - echo '</td>';
237   -
238   - // Is complete
239   - echo '<td class="c4">';
240   - echo ($row['status'] === get_string('yes')) ? get_string('yes') : get_string('no');
241   - echo '</td>';
242   -
243   - // Completion data
244   - echo '<td class="c5">';
245   - if ($row['timecompleted']) {
246   - echo userdate($row['timecompleted'], '%e %B %G');
247   - } else {
248   - echo '-';
249   - }
250   - echo '</td>';
251   - echo '</tr>';
  262 +
  263 + echo '</tbody></table>';
252 264 }
253 265
254   -echo '</tbody></table>';
  266 +echo '<div class="buttons">';
  267 +$courseurl = new moodle_url("/course/view.php", array('id' => $course->id));
  268 +echo $OUTPUT->single_button($courseurl, get_string('returntocourse', 'block_completionstatus'), 'get');
  269 +echo '</div>';
255 270
256 271 echo $OUTPUT->footer();
1  blocks/completionstatus/lang/en/block_completionstatus.php
@@ -5,3 +5,4 @@
5 5 $string['firstofsecond'] = '{$a->first} of {$a->second}';
6 6 $string['pluginname'] = 'Course completion status';
7 7 $string['requirement'] = 'Requirement';
  8 +$string['returntocourse'] = 'Return to course';
9 blog/external_blogs.php
@@ -44,7 +44,16 @@
44 44 if ($delete && confirm_sesskey()) {
45 45 $externalbloguserid = $DB->get_field('blog_external', 'userid', array('id' => $delete));
46 46 if ($externalbloguserid == $USER->id) {
  47 + // Delete the external blog
47 48 $DB->delete_records('blog_external', array('id' => $delete));
  49 +
  50 + // Delete the external blog's posts
  51 + $deletewhere = 'module = :module
  52 + AND userid = :userid
  53 + AND ' . $DB->sql_isnotempty('post', 'uniquehash', false, false) . '
  54 + AND ' . $DB->sql_compare_text('content') . ' = ' . $DB->sql_compare_text(':delete');
  55 + $DB->delete_records_select('post', $deletewhere, array('module' => 'blog_external', 'userid' => $USER->id, 'delete' => $delete));
  56 +
48 57 $message = get_string('externalblogdeleted', 'blog');
49 58 }
50 59 }
5 blog/locallib.php
@@ -405,11 +405,10 @@ public function edit($params=array(), $form=null, $summaryoptions=array(), $atta
405 405 * @return void
406 406 */
407 407 public function delete() {
408   - global $DB, $USER;
409   -
410   - $returnurl = '';
  408 + global $DB;
411 409
412 410 $this->delete_attachments();
  411 + $this->remove_associations();
413 412
414 413 $DB->delete_records('post', array('id' => $this->id));
415 414 tag_set('post', $this->id, array());
2  enrol/manual/yui/quickenrolment/quickenrolment.js
@@ -339,7 +339,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
339 339 count++;
340 340 var user = result.response.users[i];
341 341 users.append(create('<div class="'+CSS.USER+' clearfix" rel="'+user.id+'"></div>')
342   - .addClass((i%2)?CSS.ODD:CSS.EVEN)
  342 + .addClass((count%2)?CSS.ODD:CSS.EVEN)
343 343 .append(create('<div class="'+CSS.COUNT+'">'+count+'</div>'))
344 344 .append(create('<div class="'+CSS.PICTURE+'"></div>')
345 345 .append(create(user.picture)))
22 enrol/paypal/ipn.php
@@ -34,6 +34,7 @@
34 34 require_once("lib.php");
35 35 require_once($CFG->libdir.'/eventslib.php');
36 36 require_once($CFG->libdir.'/enrollib.php');
  37 +require_once($CFG->libdir . '/filelib.php');
37 38
38 39
39 40 /// Keep out casual intruders
@@ -89,14 +90,17 @@
89 90 $plugin = enrol_get_plugin('paypal');
90 91
91 92 /// Open a connection back to PayPal to validate the data
92   -$header = '';
93   -$header .= "POST /cgi-bin/webscr HTTP/1.0\r\n";
94   -$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
95   -$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
  93 +$c = new curl();
  94 +$options = array(
  95 + 'returntransfer' => true,
  96 + 'httpheader' => array('application/x-www-form-urlencoded'),
  97 + 'timeout' => 30,
  98 +);
96 99 $paypaladdr = empty($CFG->usepaypalsandbox) ? 'www.paypal.com' : 'www.sandbox.paypal.com';
97   -$fp = fsockopen ($paypaladdr, 80, $errno, $errstr, 30);
  100 +$location = "https://$paypaladdr/cgi-bin/webscr";
  101 +$result = $c->post($location, $req, $options);
98 102
99   -if (!$fp) { /// Could not open a socket to PayPal - FAIL
  103 +if (!$result) { /// Could not connect to PayPal - FAIL
100 104 echo "<p>Error: could not access paypal.com</p>";
101 105 message_paypal_error_to_admin("Could not access paypal.com to verify payment", $data);
102 106 die;
@@ -104,12 +108,9 @@
104 108
105 109 /// Connection is OK, so now we post the data to validate it
106 110
107   -fputs ($fp, $header.$req);
108   -
109 111 /// Now read the response and check if everything is OK.
110 112
111   -while (!feof($fp)) {
112   - $result = fgets($fp, 1024);
  113 +if (strlen($result) > 0) {
113 114 if (strcmp($result, "VERIFIED") == 0) { // VALID PAYMENT!
114 115
115 116
@@ -296,7 +297,6 @@
296 297 }
297 298 }
298 299