Search moodle.org's
Developer Documentation


  • Bug fixes for general core bugs in 2.8.x ended 9 November 2015 (12 months).
  • Bug fixes for security issues in 2.8.x ended 9 May 2016 (18 months).
  • minimum PHP 5.4.4 (always use latest PHP 5.4.x or 5.5.x on Windows - http://windows.php.net/download/), PHP 7 is NOT supported
  • Differences Between: [Versions 28 and 31] [Versions 28 and 32] [Versions 28 and 33] [Versions 28 and 34] [Versions 28 and 35] [Versions 28 and 36] [Versions 28 and 37]

       1  <?php
       2  // This file is part of Moodle - http://moodle.org/
       3  //
       4  // Moodle is free software: you can redistribute it and/or modify
       5  // it under the terms of the GNU General Public License as published by
       6  // the Free Software Foundation, either version 3 of the License, or
       7  // (at your option) any later version.
       8  //
       9  // Moodle is distributed in the hope that it will be useful,
      10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12  // GNU General Public License for more details.
      13  //
      14  // You should have received a copy of the GNU General Public License
      15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
      16  
      17  /**
      18   * This file contains the definition for the library class for file feedback plugin
      19   *
      20   *
      21   * @package   assignfeedback_file
      22   * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
      23   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      24   */
      25  
      26  defined('MOODLE_INTERNAL') || die();
      27  
      28  // File areas for file feedback assignment.
      29  define('ASSIGNFEEDBACK_FILE_FILEAREA', 'feedback_files');
      30  define('ASSIGNFEEDBACK_FILE_BATCH_FILEAREA', 'feedback_files_batch');
      31  define('ASSIGNFEEDBACK_FILE_IMPORT_FILEAREA', 'feedback_files_import');
      32  define('ASSIGNFEEDBACK_FILE_MAXSUMMARYFILES', 5);
      33  define('ASSIGNFEEDBACK_FILE_MAXSUMMARYUSERS', 5);
      34  define('ASSIGNFEEDBACK_FILE_MAXFILEUNZIPTIME', 120);
      35  
      36  /**
      37   * Library class for file feedback plugin extending feedback plugin base class.
      38   *
      39   * @package   assignfeedback_file
      40   * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
      41   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      42   */
      43  class assign_feedback_file extends assign_feedback_plugin {
      44  
      45      /**
      46       * Get the name of the file feedback plugin.
      47       *
      48       * @return string
      49       */
      50      public function get_name() {
      51          return get_string('file', 'assignfeedback_file');
      52      }
      53  
      54      /**
      55       * Get file feedback information from the database.
      56       *
      57       * @param int $gradeid
      58       * @return mixed
      59       */
      60      public function get_file_feedback($gradeid) {
      61          global $DB;
      62          return $DB->get_record('assignfeedback_file', array('grade'=>$gradeid));
      63      }
      64  
      65      /**
      66       * File format options.
      67       *
      68       * @return array
      69       */
      70      private function get_file_options() {
      71          global $COURSE;
      72  
      73          $fileoptions = array('subdirs'=>1,
      74                               'maxbytes'=>$COURSE->maxbytes,
      75                               'accepted_types'=>'*',
      76                               'return_types'=>FILE_INTERNAL);
      77          return $fileoptions;
      78      }
      79  
      80      /**
      81       * Copy all the files from one file area to another.
      82       *
      83       * @param file_storage $fs - The source context id
      84       * @param int $fromcontextid - The source context id
      85       * @param string $fromcomponent - The source component
      86       * @param string $fromfilearea - The source filearea
      87       * @param int $fromitemid - The source item id
      88       * @param int $tocontextid - The destination context id
      89       * @param string $tocomponent - The destination component
      90       * @param string $tofilearea - The destination filearea
      91       * @param int $toitemid - The destination item id
      92       * @return boolean
      93       */
      94      private function copy_area_files(file_storage $fs,
      95                                       $fromcontextid,
      96                                       $fromcomponent,
      97                                       $fromfilearea,
      98                                       $fromitemid,
      99                                       $tocontextid,
     100                                       $tocomponent,
     101                                       $tofilearea,
     102                                       $toitemid) {
     103  
     104          $newfilerecord = new stdClass();
     105          $newfilerecord->contextid = $tocontextid;
     106          $newfilerecord->component = $tocomponent;
     107          $newfilerecord->filearea = $tofilearea;
     108          $newfilerecord->itemid = $toitemid;
     109  
     110          if ($files = $fs->get_area_files($fromcontextid, $fromcomponent, $fromfilearea, $fromitemid)) {
     111              foreach ($files as $file) {
     112                  if ($file->is_directory() and $file->get_filepath() === '/') {
     113                      // We need a way to mark the age of each draft area.
     114                      // By not copying the root dir we force it to be created
     115                      // automatically with current timestamp.
     116                      continue;
     117                  }
     118                  $newfile = $fs->create_file_from_storedfile($newfilerecord, $file);
     119              }
     120          }
     121          return true;
     122      }
     123  
     124      /**
     125       * Get form elements for grading form.
     126       *
     127       * @param stdClass $grade
     128       * @param MoodleQuickForm $mform
     129       * @param stdClass $data
     130       * @param int $userid The userid we are currently grading
     131       * @return bool true if elements were added to the form
     132       */
     133      public function get_form_elements_for_user($grade, MoodleQuickForm $mform, stdClass $data, $userid) {
     134  
     135          $fileoptions = $this->get_file_options();
     136          $gradeid = $grade ? $grade->id : 0;
     137          $elementname = 'files_' . $userid;
     138  
     139          $data = file_prepare_standard_filemanager($data,
     140                                                    $elementname,
     141                                                    $fileoptions,
     142                                                    $this->assignment->get_context(),
     143                                                    'assignfeedback_file',
     144                                                    ASSIGNFEEDBACK_FILE_FILEAREA,
     145                                                    $gradeid);
     146          $mform->addElement('filemanager', $elementname . '_filemanager', $this->get_name(), null, $fileoptions);
     147  
     148          return true;
     149      }
     150  
     151      /**
     152       * Count the number of files.
     153       *
     154       * @param int $gradeid
     155       * @param string $area
     156       * @return int
     157       */
     158      private function count_files($gradeid, $area) {
     159  
     160          $fs = get_file_storage();
     161          $files = $fs->get_area_files($this->assignment->get_context()->id,
     162                                       'assignfeedback_file',
     163                                       $area,
     164                                       $gradeid,
     165                                       'id',
     166                                       false);
     167  
     168          return count($files);
     169      }
     170  
     171      /**
     172       * Update the number of files in the file area.
     173       *
     174       * @param stdClass $grade The grade record
     175       * @return bool - true if the value was saved
     176       */
     177      public function update_file_count($grade) {
     178          global $DB;
     179  
     180          $filefeedback = $this->get_file_feedback($grade->id);
     181          if ($filefeedback) {
     182              $filefeedback->numfiles = $this->count_files($grade->id, ASSIGNFEEDBACK_FILE_FILEAREA);
     183              return $DB->update_record('assignfeedback_file', $filefeedback);
     184          } else {
     185              $filefeedback = new stdClass();
     186              $filefeedback->numfiles = $this->count_files($grade->id, ASSIGNFEEDBACK_FILE_FILEAREA);
     187              $filefeedback->grade = $grade->id;
     188              $filefeedback->assignment = $this->assignment->get_instance()->id;
     189              return $DB->insert_record('assignfeedback_file', $filefeedback) > 0;
     190          }
     191      }
     192  
     193      /**
     194       * Save the feedback files.
     195       *
     196       * @param stdClass $grade
     197       * @param stdClass $data
     198       * @return bool
     199       */
     200      public function save(stdClass $grade, stdClass $data) {
     201          $fileoptions = $this->get_file_options();
     202  
     203          // The element name may have been for a different user.
     204          foreach ($data as $key => $value) {
     205              if (strpos($key, 'files_') === 0 && strpos($key, '_filemanager')) {
     206                  $elementname = substr($key, 0, strpos($key, '_filemanager'));
     207              }
     208          }
     209  
     210          $data = file_postupdate_standard_filemanager($data,
     211                                                       $elementname,
     212                                                       $fileoptions,
     213                                                       $this->assignment->get_context(),
     214                                                       'assignfeedback_file',
     215                                                       ASSIGNFEEDBACK_FILE_FILEAREA,
     216                                                       $grade->id);
     217  
     218          return $this->update_file_count($grade);
     219      }
     220  
     221      /**
     222       * Display the list of files in the feedback status table.
     223       *
     224       * @param stdClass $grade
     225       * @param bool $showviewlink - Set to true to show a link to see the full list of files
     226       * @return string
     227       */
     228      public function view_summary(stdClass $grade, & $showviewlink) {
     229  
     230          $count = $this->count_files($grade->id, ASSIGNFEEDBACK_FILE_FILEAREA);
     231  
     232          // Show a view all link if the number of files is over this limit.
     233          $showviewlink = $count > ASSIGNFEEDBACK_FILE_MAXSUMMARYFILES;
     234  
     235          if ($count <= ASSIGNFEEDBACK_FILE_MAXSUMMARYFILES) {
     236              return $this->assignment->render_area_files('assignfeedback_file',
     237                                                          ASSIGNFEEDBACK_FILE_FILEAREA,
     238                                                          $grade->id);
     239          } else {
     240              return get_string('countfiles', 'assignfeedback_file', $count);
     241          }
     242      }
     243  
     244      /**
     245       * Display the list of files in the feedback status table.
     246       *
     247       * @param stdClass $grade
     248       * @return string
     249       */
     250      public function view(stdClass $grade) {
     251          return $this->assignment->render_area_files('assignfeedback_file',
     252                                                      ASSIGNFEEDBACK_FILE_FILEAREA,
     253                                                      $grade->id);
     254      }
     255  
     256      /**
     257       * The assignment has been deleted - cleanup.
     258       *
     259       * @return bool
     260       */
     261      public function delete_instance() {
     262          global $DB;
     263          // Will throw exception on failure.
     264          $DB->delete_records('assignfeedback_file',
     265                              array('assignment'=>$this->assignment->get_instance()->id));
     266  
     267          return true;
     268      }
     269  
     270      /**
     271       * Return true if there are no feedback files.
     272       *
     273       * @param stdClass $grade
     274       */
     275      public function is_empty(stdClass $grade) {
     276          return $this->count_files($grade->id, ASSIGNFEEDBACK_FILE_FILEAREA) == 0;
     277      }
     278  
     279      /**
     280       * Get file areas returns a list of areas this plugin stores files.
     281       *
     282       * @return array - An array of fileareas (keys) and descriptions (values)
     283       */
     284      public function get_file_areas() {
     285          return array(ASSIGNFEEDBACK_FILE_FILEAREA=>$this->get_name());
     286      }
     287  
     288      /**
     289       * Return true if this plugin can upgrade an old Moodle 2.2 assignment of this type
     290       * and version.
     291       *
     292       * @param string $type old assignment subtype
     293       * @param int $version old assignment version
     294       * @return bool True if upgrade is possible
     295       */
     296      public function can_upgrade($type, $version) {
     297          if (($type == 'upload' || $type == 'uploadsingle') && $version >= 2011112900) {
     298              return true;
     299          }
     300          return false;
     301      }
     302  
     303      /**
     304       * Upgrade the settings from the old assignment to the new plugin based one.
     305       *
     306       * @param context $oldcontext - the context for the old assignment
     307       * @param stdClass $oldassignment - the data for the old assignment
     308       * @param string $log - can be appended to by the upgrade
     309       * @return bool was it a success? (false will trigger a rollback)
     310       */
     311      public function upgrade_settings(context $oldcontext, stdClass $oldassignment, & $log) {
     312          // First upgrade settings (nothing to do).
     313          return true;
     314      }
     315  
     316      /**
     317       * Upgrade the feedback from the old assignment to the new one.
     318       *
     319       * @param context $oldcontext - the database for the old assignment context
     320       * @param stdClass $oldassignment The data record for the old assignment
     321       * @param stdClass $oldsubmission The data record for the old submission
     322       * @param stdClass $grade The data record for the new grade
     323       * @param string $log Record upgrade messages in the log
     324       * @return bool true or false - false will trigger a rollback
     325       */
     326      public function upgrade(context $oldcontext,
     327                              stdClass $oldassignment,
     328                              stdClass $oldsubmission,
     329                              stdClass $grade,
     330                              & $log) {
     331          global $DB;
     332  
     333          // Now copy the area files.
     334          $this->assignment->copy_area_files_for_upgrade($oldcontext->id,
     335                                                          'mod_assignment',
     336                                                          'response',
     337                                                          $oldsubmission->id,
     338                                                          $this->assignment->get_context()->id,
     339                                                          'assignfeedback_file',
     340                                                          ASSIGNFEEDBACK_FILE_FILEAREA,
     341                                                          $grade->id);
     342  
     343          // Now count them!
     344          $filefeedback = new stdClass();
     345          $filefeedback->numfiles = $this->count_files($grade->id, ASSIGNFEEDBACK_FILE_FILEAREA);
     346          $filefeedback->grade = $grade->id;
     347          $filefeedback->assignment = $this->assignment->get_instance()->id;
     348          if (!$DB->insert_record('assignfeedback_file', $filefeedback) > 0) {
     349              $log .= get_string('couldnotconvertgrade', 'mod_assign', $grade->userid);
     350              return false;
     351          }
     352          return true;
     353      }
     354  
     355      /**
     356       * Return a list of the batch grading operations performed by this plugin.
     357       * This plugin supports batch upload files and upload zip.
     358       *
     359       * @return array The list of batch grading operations
     360       */
     361      public function get_grading_batch_operations() {
     362          return array('uploadfiles'=>get_string('uploadfiles', 'assignfeedback_file'));
     363      }
     364  
     365      /**
     366       * Upload files and send them to multiple users.
     367       *
     368       * @param array $users - An array of user ids
     369       * @return string - The response html
     370       */
     371      public function view_batch_upload_files($users) {
     372          global $CFG, $DB, $USER;
     373  
     374          require_capability('mod/assign:grade', $this->assignment->get_context());
     375          require_once($CFG->dirroot . '/mod/assign/feedback/file/batchuploadfilesform.php');
     376          require_once($CFG->dirroot . '/mod/assign/renderable.php');
     377  
     378          $formparams = array('cm'=>$this->assignment->get_course_module()->id,
     379                              'users'=>$users,
     380                              'context'=>$this->assignment->get_context());
     381  
     382          $usershtml = '';
     383  
     384          $usercount = 0;
     385          foreach ($users as $userid) {
     386              if ($usercount >= ASSIGNFEEDBACK_FILE_MAXSUMMARYUSERS) {
     387                  $moreuserscount = count($users) - ASSIGNFEEDBACK_FILE_MAXSUMMARYUSERS;
     388                  $usershtml .= get_string('moreusers', 'assignfeedback_file', $moreuserscount);
     389                  break;
     390              }
     391              $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
     392  
     393              $usersummary = new assign_user_summary($user,
     394                                                     $this->assignment->get_course()->id,
     395                                                     has_capability('moodle/site:viewfullnames',
     396                                                     $this->assignment->get_course_context()),
     397                                                     $this->assignment->is_blind_marking(),
     398                                                     $this->assignment->get_uniqueid_for_user($user->id),
     399                                                     get_extra_user_fields($this->assignment->get_context()));
     400              $usershtml .= $this->assignment->get_renderer()->render($usersummary);
     401              $usercount += 1;
     402          }
     403  
     404          $formparams['usershtml'] = $usershtml;
     405  
     406          $mform = new assignfeedback_file_batch_upload_files_form(null, $formparams);
     407  
     408          if ($mform->is_cancelled()) {
     409              redirect(new moodle_url('view.php',
     410                                      array('id'=>$this->assignment->get_course_module()->id,
     411                                            'action'=>'grading')));
     412              return;
     413          } else if ($data = $mform->get_data()) {
     414              // Copy the files from the draft area to a temporary import area.
     415              $data = file_postupdate_standard_filemanager($data,
     416                                                           'files',
     417                                                           $this->get_file_options(),
     418                                                           $this->assignment->get_context(),
     419                                                           'assignfeedback_file',
     420                                                           ASSIGNFEEDBACK_FILE_BATCH_FILEAREA,
     421                                                           $USER->id);
     422              $fs = get_file_storage();
     423  
     424              // Now copy each of these files to the users feedback file area.
     425              foreach ($users as $userid) {
     426                  $grade = $this->assignment->get_user_grade($userid, true);
     427                  $this->assignment->notify_grade_modified($grade);
     428  
     429                  $this->copy_area_files($fs,
     430                                         $this->assignment->get_context()->id,
     431                                         'assignfeedback_file',
     432                                         ASSIGNFEEDBACK_FILE_BATCH_FILEAREA,
     433                                         $USER->id,
     434                                         $this->assignment->get_context()->id,
     435                                         'assignfeedback_file',
     436                                         ASSIGNFEEDBACK_FILE_FILEAREA,
     437                                         $grade->id);
     438  
     439                  $filefeedback = $this->get_file_feedback($grade->id);
     440                  if ($filefeedback) {
     441                      $filefeedback->numfiles = $this->count_files($grade->id,
     442                                                                   ASSIGNFEEDBACK_FILE_FILEAREA);
     443                      $DB->update_record('assignfeedback_file', $filefeedback);
     444                  } else {
     445                      $filefeedback = new stdClass();
     446                      $filefeedback->numfiles = $this->count_files($grade->id,
     447                                                                   ASSIGNFEEDBACK_FILE_FILEAREA);
     448                      $filefeedback->grade = $grade->id;
     449                      $filefeedback->assignment = $this->assignment->get_instance()->id;
     450                      $DB->insert_record('assignfeedback_file', $filefeedback);
     451                  }
     452              }
     453  
     454              // Now delete the temporary import area.
     455              $fs->delete_area_files($this->assignment->get_context()->id,
     456                                     'assignfeedback_file',
     457                                     ASSIGNFEEDBACK_FILE_BATCH_FILEAREA,
     458                                     $USER->id);
     459  
     460              redirect(new moodle_url('view.php',
     461                                      array('id'=>$this->assignment->get_course_module()->id,
     462                                            'action'=>'grading')));
     463              return;
     464          } else {
     465  
     466              $header = new assign_header($this->assignment->get_instance(),
     467                                          $this->assignment->get_context(),
     468                                          false,
     469                                          $this->assignment->get_course_module()->id,
     470                                          get_string('batchuploadfiles', 'assignfeedback_file'));
     471              $o = '';
     472              $o .= $this->assignment->get_renderer()->render($header);
     473              $o .= $this->assignment->get_renderer()->render(new assign_form('batchuploadfiles', $mform));
     474              $o .= $this->assignment->get_renderer()->render_footer();
     475          }
     476  
     477          return $o;
     478      }
     479  
     480      /**
     481       * User has chosen a custom grading batch operation and selected some users.
     482       *
     483       * @param string $action - The chosen action
     484       * @param array $users - An array of user ids
     485       * @return string - The response html
     486       */
     487      public function grading_batch_operation($action, $users) {
     488  
     489          if ($action == 'uploadfiles') {
     490              return $this->view_batch_upload_files($users);
     491          }
     492          return '';
     493      }
     494  
     495      /**
     496       * View the upload zip form.
     497       *
     498       * @return string - The html response
     499       */
     500      public function view_upload_zip() {
     501          global $CFG, $USER;
     502  
     503          require_capability('mod/assign:grade', $this->assignment->get_context());
     504          require_once($CFG->dirroot . '/mod/assign/feedback/file/uploadzipform.php');
     505          require_once($CFG->dirroot . '/mod/assign/feedback/file/importziplib.php');
     506          require_once($CFG->dirroot . '/mod/assign/feedback/file/importzipform.php');
     507  
     508          $formparams = array('context'=>$this->assignment->get_context(),
     509                              'cm'=>$this->assignment->get_course_module()->id);
     510          $mform = new assignfeedback_file_upload_zip_form(null, $formparams);
     511  
     512          $o = '';
     513  
     514          $confirm = optional_param('confirm', 0, PARAM_BOOL);
     515          $renderer = $this->assignment->get_renderer();
     516  
     517          // Delete any existing files.
     518          $importer = new assignfeedback_file_zip_importer();
     519          $contextid = $this->assignment->get_context()->id;
     520  
     521          if ($mform->is_cancelled()) {
     522              $importer->delete_import_files($contextid);
     523              $urlparams = array('id'=>$this->assignment->get_course_module()->id,
     524                                 'action'=>'grading');
     525              $url = new moodle_url('view.php', $urlparams);
     526              redirect($url);
     527              return;
     528          } else if ($confirm) {
     529              $params = array('assignment'=>$this->assignment, 'importer'=>$importer);
     530  
     531              $mform = new assignfeedback_file_import_zip_form(null, $params);
     532              if ($mform->is_cancelled()) {
     533                  $importer->delete_import_files($contextid);
     534                  $urlparams = array('id'=>$this->assignment->get_course_module()->id,
     535                                     'action'=>'grading');
     536                  $url = new moodle_url('view.php', $urlparams);
     537                  redirect($url);
     538                  return;
     539              }
     540  
     541              $o .= $importer->import_zip_files($this->assignment, $this);
     542              $importer->delete_import_files($contextid);
     543          } else if (($data = $mform->get_data()) &&
     544                     ($zipfile = $mform->save_stored_file('feedbackzip',
     545                                                          $contextid,
     546                                                          'assignfeedback_file',
     547                                                          ASSIGNFEEDBACK_FILE_IMPORT_FILEAREA,
     548                                                          $USER->id,
     549                                                          '/',
     550                                                          'import.zip',
     551                                                          true))) {
     552  
     553              $importer->extract_files_from_zip($zipfile, $contextid);
     554  
     555              $params = array('assignment'=>$this->assignment, 'importer'=>$importer);
     556  
     557              $mform = new assignfeedback_file_import_zip_form(null, $params);
     558  
     559              $header = new assign_header($this->assignment->get_instance(),
     560                                          $this->assignment->get_context(),
     561                                          false,
     562                                          $this->assignment->get_course_module()->id,
     563                                          get_string('confirmuploadzip', 'assignfeedback_file'));
     564              $o .= $renderer->render($header);
     565              $o .= $renderer->render(new assign_form('confirmimportzip', $mform));
     566              $o .= $renderer->render_footer();
     567  
     568          } else {
     569  
     570              $header = new assign_header($this->assignment->get_instance(),
     571                                          $this->assignment->get_context(),
     572                                          false,
     573                                          $this->assignment->get_course_module()->id,
     574                                          get_string('uploadzip', 'assignfeedback_file'));
     575              $o .= $renderer->render($header);
     576              $o .= $renderer->render(new assign_form('uploadfeedbackzip', $mform));
     577              $o .= $renderer->render_footer();
     578          }
     579  
     580          return $o;
     581      }
     582  
     583      /**
     584       * Called by the assignment module when someone chooses something from the
     585       * grading navigation or batch operations list.
     586       *
     587       * @param string $action - The page to view
     588       * @return string - The html response
     589       */
     590      public function view_page($action) {
     591          if ($action == 'uploadfiles') {
     592              $users = required_param('selectedusers', PARAM_SEQUENCE);
     593              return $this->view_batch_upload_files(explode(',', $users));
     594          }
     595          if ($action == 'uploadzip') {
     596              return $this->view_upload_zip();
     597          }
     598  
     599          return '';
     600      }
     601  
     602      /**
     603       * Return a list of the grading actions performed by this plugin.
     604       * This plugin supports upload zip.
     605       *
     606       * @return array The list of grading actions
     607       */
     608      public function get_grading_actions() {
     609          return array('uploadzip'=>get_string('uploadzip', 'assignfeedback_file'));
     610      }
     611  
     612      /**
     613       * Return a description of external params suitable for uploading a feedback file from a webservice.
     614       *
     615       * @return external_description|null
     616       */
     617      public function get_external_parameters() {
     618          return array(
     619              'files_filemanager' => new external_value(
     620                  PARAM_INT,
     621                  'The id of a draft area containing files for this feedback.'
     622              )
     623          );
     624      }
     625  
     626  }
    

    Search This Site: