Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 39 and 311]

   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * Allows user to allocate the submissions manually
  20   *
  21   * @package    workshopallocation
  22   * @subpackage manual
  23   * @copyright  2009 David Mudrak <david.mudrak@gmail.com>
  24   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  require_once (__DIR__ . '/../lib.php');            // interface definition
  30  require_once (__DIR__ . '/../../locallib.php');    // workshop internal API
  31  
  32  /**
  33   * Allows users to allocate submissions for review manually
  34   */
  35  class workshop_manual_allocator implements workshop_allocator {
  36  
  37      /** constants that are used to pass status messages between init() and ui() */
  38      const MSG_ADDED         = 1;
  39      const MSG_NOSUBMISSION  = 2;
  40      const MSG_EXISTS        = 3;
  41      const MSG_CONFIRM_DEL   = 4;
  42      const MSG_DELETED       = 5;
  43      const MSG_DELETE_ERROR  = 6;
  44  
  45      /** @var workshop instance */
  46      protected $workshop;
  47  
  48      /**
  49       * @param workshop $workshop Workshop API object
  50       */
  51      public function __construct(workshop $workshop) {
  52          $this->workshop = $workshop;
  53      }
  54  
  55      /**
  56       * Allocate submissions as requested by user
  57       *
  58       * @return workshop_allocation_result
  59       */
  60      public function init() {
  61          global $PAGE;
  62  
  63          $mode = optional_param('mode', 'display', PARAM_ALPHA);
  64          $perpage = optional_param('perpage', null, PARAM_INT);
  65  
  66          if ($perpage and $perpage > 0 and $perpage <= 1000) {
  67              require_sesskey();
  68              set_user_preference('workshopallocation_manual_perpage', $perpage);
  69              redirect($PAGE->url);
  70          }
  71  
  72          $result = new workshop_allocation_result($this);
  73  
  74          switch ($mode) {
  75          case 'new':
  76              if (!confirm_sesskey()) {
  77                  throw new moodle_exception('confirmsesskeybad');
  78              }
  79              $reviewerid = required_param('by', PARAM_INT);
  80              $authorid   = required_param('of', PARAM_INT);
  81              $m          = array();  // message object to be passed to the next page
  82              $submission = $this->workshop->get_submission_by_author($authorid);
  83              if (!$submission) {
  84                  // nothing submitted by the given user
  85                  $m[] = self::MSG_NOSUBMISSION;
  86                  $m[] = $authorid;
  87  
  88              } else {
  89                  // ok, we have the submission
  90                  $res = $this->workshop->add_allocation($submission, $reviewerid);
  91                  if ($res == workshop::ALLOCATION_EXISTS) {
  92                      $m[] = self::MSG_EXISTS;
  93                      $m[] = $submission->authorid;
  94                      $m[] = $reviewerid;
  95                  } else {
  96                      $m[] = self::MSG_ADDED;
  97                      $m[] = $submission->authorid;
  98                      $m[] = $reviewerid;
  99                  }
 100              }
 101              $m = implode('-', $m);  // serialize message object to be passed via URL
 102              redirect($PAGE->url->out(false, array('m' => $m)));
 103              break;
 104          case 'del':
 105              if (!confirm_sesskey()) {
 106                  throw new moodle_exception('confirmsesskeybad');
 107              }
 108              $assessmentid   = required_param('what', PARAM_INT);
 109              $confirmed      = optional_param('confirm', 0, PARAM_INT);
 110              $assessment     = $this->workshop->get_assessment_by_id($assessmentid);
 111              if ($assessment) {
 112                  if (!$confirmed) {
 113                      $m[] = self::MSG_CONFIRM_DEL;
 114                      $m[] = $assessment->id;
 115                      $m[] = $assessment->authorid;
 116                      $m[] = $assessment->reviewerid;
 117                      if (is_null($assessment->grade)) {
 118                          $m[] = 0;
 119                      } else {
 120                          $m[] = 1;
 121                      }
 122                  } else {
 123                      if($this->workshop->delete_assessment($assessment->id)) {
 124                          $m[] = self::MSG_DELETED;
 125                          $m[] = $assessment->authorid;
 126                          $m[] = $assessment->reviewerid;
 127                      } else {
 128                          $m[] = self::MSG_DELETE_ERROR;
 129                          $m[] = $assessment->authorid;
 130                          $m[] = $assessment->reviewerid;
 131                      }
 132                  }
 133                  $m = implode('-', $m);  // serialize message object to be passed via URL
 134                  redirect($PAGE->url->out(false, array('m' => $m)));
 135              }
 136              break;
 137          }
 138  
 139          $result->set_status(workshop_allocation_result::STATUS_VOID);
 140          return $result;
 141      }
 142  
 143      /**
 144       * Prints user interface - current allocation and a form to edit it
 145       */
 146      public function ui() {
 147          global $PAGE, $DB;
 148  
 149          $output     = $PAGE->get_renderer('workshopallocation_manual');
 150  
 151          $page       = optional_param('page', 0, PARAM_INT);
 152          $perpage    = get_user_preferences('workshopallocation_manual_perpage', 10);
 153          $groupid    = groups_get_activity_group($this->workshop->cm, true);
 154  
 155          $hlauthorid     = -1;           // highlight this author
 156          $hlreviewerid   = -1;           // highlight this reviewer
 157  
 158          $message        = new workshop_message();
 159  
 160          $m  = optional_param('m', '', PARAM_ALPHANUMEXT);   // message code
 161          if ($m) {
 162              $m = explode('-', $m);
 163              switch ($m[0]) {
 164              case self::MSG_ADDED:
 165                  $hlauthorid     = $m[1];
 166                  $hlreviewerid   = $m[2];
 167                  $message        = new workshop_message(get_string('allocationadded', 'workshopallocation_manual'),
 168                      workshop_message::TYPE_OK);
 169                  break;
 170              case self::MSG_EXISTS:
 171                  $hlauthorid     = $m[1];
 172                  $hlreviewerid   = $m[2];
 173                  $message        = new workshop_message(get_string('allocationexists', 'workshopallocation_manual'),
 174                      workshop_message::TYPE_INFO);
 175                  break;
 176              case self::MSG_NOSUBMISSION:
 177                  $hlauthorid     = $m[1];
 178                  $message        = new workshop_message(get_string('nosubmissionfound', 'workshop'),
 179                      workshop_message::TYPE_ERROR);
 180                  break;
 181              case self::MSG_CONFIRM_DEL:
 182                  $hlauthorid     = $m[2];
 183                  $hlreviewerid   = $m[3];
 184                  if ($m[4] == 0) {
 185                      $message    = new workshop_message(get_string('areyousuretodeallocate', 'workshopallocation_manual'),
 186                          workshop_message::TYPE_INFO);
 187                  } else {
 188                      $message    = new workshop_message(get_string('areyousuretodeallocategraded', 'workshopallocation_manual'),
 189                          workshop_message::TYPE_ERROR);
 190                  }
 191                  $url = new moodle_url($PAGE->url, array('mode' => 'del', 'what' => $m[1], 'confirm' => 1, 'sesskey' => sesskey()));
 192                  $label = get_string('iamsure', 'workshop');
 193                  $message->set_action($url, $label);
 194                  break;
 195              case self::MSG_DELETED:
 196                  $hlauthorid     = $m[1];
 197                  $hlreviewerid   = $m[2];
 198                  $message        = new workshop_message(get_string('assessmentdeleted', 'workshop'),
 199                      workshop_message::TYPE_OK);
 200                  break;
 201              case self::MSG_DELETE_ERROR:
 202                  $hlauthorid     = $m[1];
 203                  $hlreviewerid   = $m[2];
 204                  $message        = new workshop_message(get_string('assessmentnotdeleted', 'workshop'),
 205                      workshop_message::TYPE_ERROR);
 206                  break;
 207              }
 208          }
 209  
 210          // fetch the list of ids of all workshop participants
 211          $numofparticipants = $this->workshop->count_participants(false, $groupid);
 212          $participants = $this->workshop->get_participants(false, $groupid, $perpage * $page, $perpage);
 213  
 214          if ($hlauthorid > 0 and $hlreviewerid > 0) {
 215              // display just those two users
 216              $participants = array_intersect_key($participants, array($hlauthorid => null, $hlreviewerid => null));
 217              $button = $output->single_button($PAGE->url, get_string('showallparticipants', 'workshopallocation_manual'), 'get');
 218          } else {
 219              $button = '';
 220          }
 221  
 222          // this will hold the information needed to display user names and pictures
 223          $userinfo = $participants;
 224  
 225          // load the participants' submissions
 226          $submissions = $this->workshop->get_submissions(array_keys($participants));
 227          $allnames = \core_user\fields::get_name_fields();
 228          foreach ($submissions as $submission) {
 229              if (!isset($userinfo[$submission->authorid])) {
 230                  $userinfo[$submission->authorid]            = new stdclass();
 231                  $userinfo[$submission->authorid]->id        = $submission->authorid;
 232                  $userinfo[$submission->authorid]->picture   = $submission->authorpicture;
 233                  $userinfo[$submission->authorid]->imagealt  = $submission->authorimagealt;
 234                  $userinfo[$submission->authorid]->email     = $submission->authoremail;
 235                  foreach ($allnames as $addname) {
 236                      $temp = 'author' . $addname;
 237                      $userinfo[$submission->authorid]->$addname = $submission->$temp;
 238                  }
 239              }
 240          }
 241  
 242          // get current reviewers
 243          $reviewers = array();
 244          if ($submissions) {
 245              list($submissionids, $params) = $DB->get_in_or_equal(array_keys($submissions), SQL_PARAMS_NAMED);
 246              $userfieldsapi = \core_user\fields::for_userpic();
 247              $picturefields = $userfieldsapi->get_sql('r', false, '', 'reviewerid', false)->selects;
 248              $sql = "SELECT a.id AS assessmentid, a.submissionid, $picturefields,
 249                             s.id AS submissionid, s.authorid
 250                        FROM {workshop_assessments} a
 251                        JOIN {user} r ON (a.reviewerid = r.id)
 252                        JOIN {workshop_submissions} s ON (a.submissionid = s.id)
 253                       WHERE a.submissionid $submissionids";
 254              $reviewers = $DB->get_records_sql($sql, $params);
 255              foreach ($reviewers as $reviewer) {
 256                  if (!isset($userinfo[$reviewer->reviewerid])) {
 257                      $userinfo[$reviewer->reviewerid]            = new stdclass();
 258                      $userinfo[$reviewer->reviewerid]->id        = $reviewer->reviewerid;
 259                      $userinfo[$reviewer->reviewerid]->picture   = $reviewer->picture;
 260                      $userinfo[$reviewer->reviewerid]->imagealt  = $reviewer->imagealt;
 261                      $userinfo[$reviewer->reviewerid]->email     = $reviewer->email;
 262                      foreach ($allnames as $addname) {
 263                          $userinfo[$reviewer->reviewerid]->$addname = $reviewer->$addname;
 264                      }
 265                  }
 266              }
 267          }
 268  
 269          // get current reviewees
 270          $reviewees = array();
 271          if ($participants) {
 272              list($participantids, $params) = $DB->get_in_or_equal(array_keys($participants), SQL_PARAMS_NAMED);
 273              $userfieldsapi = \core_user\fields::for_name();
 274              $namefields = $userfieldsapi->get_sql('e', false, '', '', false)->selects;
 275              $params['workshopid'] = $this->workshop->id;
 276              $sql = "SELECT a.id AS assessmentid, a.submissionid,
 277                             u.id AS reviewerid,
 278                             s.id AS submissionid,
 279                             e.id AS revieweeid, e.lastname, e.firstname, $namefields, e.picture, e.imagealt, e.email
 280                        FROM {user} u
 281                        JOIN {workshop_assessments} a ON (a.reviewerid = u.id)
 282                        JOIN {workshop_submissions} s ON (a.submissionid = s.id)
 283                        JOIN {user} e ON (s.authorid = e.id)
 284                       WHERE u.id $participantids AND s.workshopid = :workshopid AND s.example = 0";
 285              $reviewees = $DB->get_records_sql($sql, $params);
 286              foreach ($reviewees as $reviewee) {
 287                  if (!isset($userinfo[$reviewee->revieweeid])) {
 288                      $userinfo[$reviewee->revieweeid]            = new stdclass();
 289                      $userinfo[$reviewee->revieweeid]->id        = $reviewee->revieweeid;
 290                      $userinfo[$reviewee->revieweeid]->firstname = $reviewee->firstname;
 291                      $userinfo[$reviewee->revieweeid]->lastname  = $reviewee->lastname;
 292                      $userinfo[$reviewee->revieweeid]->picture   = $reviewee->picture;
 293                      $userinfo[$reviewee->revieweeid]->imagealt  = $reviewee->imagealt;
 294                      $userinfo[$reviewee->revieweeid]->email     = $reviewee->email;
 295                      foreach ($allnames as $addname) {
 296                          $userinfo[$reviewee->revieweeid]->$addname = $reviewee->$addname;
 297                      }
 298                  }
 299              }
 300          }
 301  
 302          // the information about the allocations
 303          $allocations = array();
 304  
 305          foreach ($participants as $participant) {
 306              $allocations[$participant->id] = new stdClass();
 307              $allocations[$participant->id]->userid = $participant->id;
 308              $allocations[$participant->id]->submissionid = null;
 309              $allocations[$participant->id]->reviewedby = array();
 310              $allocations[$participant->id]->reviewerof = array();
 311          }
 312          unset($participants);
 313  
 314          foreach ($submissions as $submission) {
 315              $allocations[$submission->authorid]->submissionid = $submission->id;
 316              $allocations[$submission->authorid]->submissiontitle = $submission->title;
 317              $allocations[$submission->authorid]->submissiongrade = $submission->grade;
 318          }
 319          unset($submissions);
 320          foreach($reviewers as $reviewer) {
 321              $allocations[$reviewer->authorid]->reviewedby[$reviewer->reviewerid] = $reviewer->assessmentid;
 322          }
 323          unset($reviewers);
 324          foreach($reviewees as $reviewee) {
 325              $allocations[$reviewee->reviewerid]->reviewerof[$reviewee->revieweeid] = $reviewee->assessmentid;
 326          }
 327          unset($reviewees);
 328  
 329          // prepare data to be rendered
 330          $data                   = new workshopallocation_manual_allocations();
 331          $data->workshop         = $this->workshop;
 332          $data->allocations      = $allocations;
 333          $data->userinfo         = $userinfo;
 334          $data->authors          = $this->workshop->get_potential_authors();
 335          $data->reviewers        = $this->workshop->get_potential_reviewers();
 336          $data->hlauthorid       = $hlauthorid;
 337          $data->hlreviewerid     = $hlreviewerid;
 338          $data->selfassessment   = $this->workshop->useselfassessment;
 339  
 340          // prepare the group selector
 341          $groupselector = $output->container(groups_print_activity_menu($this->workshop->cm, $PAGE->url, true), 'groupwidget');
 342  
 343          // prepare paging bar
 344          $pagingbar              = new paging_bar($numofparticipants, $page, $perpage, $PAGE->url, 'page');
 345          $pagingbarout           = $output->render($pagingbar);
 346          $perpageselector        = $output->perpage_selector($perpage);
 347  
 348          return $groupselector . $pagingbarout . $output->render($message) . $output->render($data) . $button . $pagingbarout . $perpageselector;
 349      }
 350  
 351      /**
 352       * Delete all data related to a given workshop module instance
 353       *
 354       * This plugin does not store any data.
 355       *
 356       * @see workshop_delete_instance()
 357       * @param int $workshopid id of the workshop module instance being deleted
 358       * @return void
 359       */
 360      public static function delete_instance($workshopid) {
 361          return;
 362      }
 363  }
 364  
 365  /**
 366   * Contains all information needed to render current allocations and the allocator UI
 367   *
 368   * @see workshop_manual_allocator::ui()
 369   */
 370  class workshopallocation_manual_allocations implements renderable {
 371  
 372      /** @var workshop module instance */
 373      public $workshop;
 374  
 375      /** @var array of stdClass, indexed by userid, properties userid, submissionid, (array)reviewedby, (array)reviewerof */
 376      public $allocations;
 377  
 378      /** @var array of stdClass contains the data needed to display the user name and picture */
 379      public $userinfo;
 380  
 381      /* var array of stdClass potential authors */
 382      public $authors;
 383  
 384      /* var array of stdClass potential reviewers */
 385      public $reviewers;
 386  
 387      /* var int the id of the user to highlight as the author */
 388      public $hlauthorid;
 389  
 390      /* var int the id of the user to highlight as the reviewer */
 391      public $hlreviewerid;
 392  
 393      /* var bool should the selfassessment be allowed */
 394      public $selfassessment;
 395  }