Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   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  namespace mod_assign;
  18  
  19  use context_module;
  20  use assign;
  21  
  22  /**
  23   * Downloader tests class for mod_assign.
  24   *
  25   * @package    mod_assign
  26   * @category   test
  27   * @copyright  2022 Ferran Recio <ferran@moodle.com>
  28   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  29   * @coversDefaultClass \mod_assign\downloader
  30   */
  31  class downloader_test extends \advanced_testcase {
  32      /**
  33       * Setup to ensure that fixtures are loaded.
  34       */
  35      public static function setupBeforeClass(): void {
  36          global $CFG;
  37          require_once($CFG->dirroot . '/mod/assign/locallib.php');
  38      }
  39  
  40      /**
  41       * Test for load_filelist method.
  42       *
  43       * @covers ::load_filelist
  44       * @dataProvider load_filelist_provider
  45       *
  46       * @param bool $teamsubmission if the assign must have team submissions
  47       * @param array $groupmembers the groups definition
  48       * @param array|null $filterusers the filtered users (null for all users)
  49       * @param bool $blindmarking if the assign has blind marking
  50       * @param bool $downloadasfolder if the download as folder preference is set
  51       * @param array $expected the expected file list
  52       */
  53      public function test_load_filelist(
  54          bool $teamsubmission,
  55          array $groupmembers,
  56          ?array $filterusers,
  57          bool $blindmarking,
  58          bool $downloadasfolder,
  59          array $expected
  60      ) {
  61          global $CFG;
  62          $this->resetAfterTest();
  63          $this->setAdminUser();
  64  
  65          if (!$downloadasfolder) {
  66              set_user_preference('assign_downloadasfolders', 0);
  67          }
  68  
  69          // Create course and enrols.
  70          $course = $this->getDataGenerator()->create_course();
  71          $users = [
  72              'student1' => $this->getDataGenerator()->create_and_enrol($course, 'student'),
  73              'student2' => $this->getDataGenerator()->create_and_enrol($course, 'student'),
  74              'student3' => $this->getDataGenerator()->create_and_enrol($course, 'student'),
  75              'student4' => $this->getDataGenerator()->create_and_enrol($course, 'student'),
  76              'student5' => $this->getDataGenerator()->create_and_enrol($course, 'student'),
  77          ];
  78  
  79          // Generate groups.
  80          $groups = [];
  81          foreach ($groupmembers as $groupname => $groupusers) {
  82              $group = $this->getDataGenerator()->create_group(['courseid' => $course->id, 'name' => $groupname]);
  83              foreach ($groupusers as $user) {
  84                  groups_add_member($group, $users[$user]);
  85              }
  86              $groups[$groupname] = $group;
  87          }
  88  
  89          // Create activity.
  90          $params = [
  91              'course' => $course,
  92              'assignsubmission_file_enabled' => 1,
  93              'assignsubmission_file_maxfiles' => 12,
  94              'assignsubmission_file_maxsizebytes' => 1024 * 1024,
  95          ];
  96          if ($teamsubmission) {
  97              $params['teamsubmission'] = 1;
  98              $params['preventsubmissionnotingroup'] = false;
  99          }
 100          if ($blindmarking) {
 101              $params['blindmarking'] = 1;
 102          }
 103          $activity = $this->getDataGenerator()->create_module('assign', $params);
 104          $cm = get_coursemodule_from_id('assign', $activity->cmid, 0, false, MUST_EXIST);
 105          $context = context_module::instance($cm->id);
 106  
 107          // Generate submissions.
 108          $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
 109          $files = [
 110              "mod/assign/tests/fixtures/submissionsample01.txt",
 111              "mod/assign/tests/fixtures/submissionsample02.txt"
 112          ];
 113          foreach ($users as $key => $user) {
 114              if ($key == 'student5') {
 115                  continue;
 116              }
 117              $datagenerator->create_submission([
 118                  'userid' => $user->id,
 119                  'assignid' => $cm->id,
 120                  'file' => implode(',', $files),
 121              ]);
 122          }
 123  
 124          // Generate file list.
 125          if ($filterusers) {
 126              foreach ($filterusers as $key => $identifier) {
 127                  $filterusers[$key] = $users[$identifier]->id;
 128              }
 129          }
 130          $manager = new assign($context, $cm, $course);
 131          $downloader = new downloader($manager, $filterusers);
 132          $hasfiles = $downloader->load_filelist();
 133  
 134          // Expose protected filelist attribute.
 135          $rc = new \ReflectionClass(downloader::class);
 136          $rcp = $rc->getProperty('filesforzipping');
 137          $rcp->setAccessible(true);
 138  
 139          // Add some replacements.
 140          $search = ['PARTICIPANT', 'DEFAULTTEAM'];
 141          $replace = [get_string('participant', 'mod_assign'), get_string('defaultteam', 'mod_assign')];
 142          foreach ($users as $identifier => $user) {
 143              $search[] = strtoupper($identifier . '.ID');
 144              $replace[] = $manager->get_uniqueid_for_user($user->id);
 145              $search[] = strtoupper($identifier);
 146              $replace[] = $this->prepare_filename_text(fullname($user));
 147          }
 148          foreach ($groups as $identifier => $group) {
 149              $search[] = strtoupper($identifier . '.ID');
 150              $replace[] = strtoupper($group->id);
 151              $search[] = strtoupper($identifier);
 152              $replace[] = $this->prepare_filename_text($group->name);
 153          }
 154  
 155          // Validate values.
 156          $filelist = $rcp->getValue($downloader);
 157          $result = array_keys($filelist);
 158  
 159          $this->assertEquals($hasfiles, !empty($expected));
 160          $this->assertCount(count($expected), $result);
 161          foreach ($expected as $path) {
 162              $value = str_replace($search, $replace, $path);
 163              $this->assertTrue(in_array($value, $result));
 164          }
 165      }
 166  
 167      /**
 168       * Internal helper to clean a filename text.
 169       *
 170       * @param string $text the text to transform
 171       * @return string the clean string
 172       */
 173      private function prepare_filename_text(string $text): string {
 174          return clean_filename(str_replace('_', ' ', $text));
 175      }
 176  
 177      /**
 178       * Data provider for test_load_filelist().
 179       *
 180       * @return array of scenarios
 181       */
 182      public function load_filelist_provider(): array {
 183          $downloadasfoldertests = $this->load_filelist_downloadasfolder_scenarios();
 184          $downloadasfilestests = $this->load_filelist_downloadasfiles_scenarios();
 185          return array_merge(
 186              $downloadasfoldertests,
 187              $downloadasfilestests,
 188          );
 189      }
 190  
 191      /**
 192       * Generate the standard test scenarios for load_filelist with download as file.
 193       *
 194       * The scenarios are the same as download as folder but replacing the "/" of the files
 195       * by a "_" and setting the downloadasfolder to false.
 196       *
 197       * @return array of scenarios
 198       */
 199      private function load_filelist_downloadasfiles_scenarios(): array {
 200          $result = $this->load_filelist_downloadasfolder_scenarios("Download as files:");
 201          // Transform paths from files.
 202          foreach ($result as $scenario => $info) {
 203              $info['downloadasfolder'] = false;
 204              foreach ($info['expected'] as $key => $path) {
 205                  $info['expected'][$key] = str_replace('/', '_', $path);
 206              }
 207              $result[$scenario] = $info;
 208          }
 209          return $result;
 210      }
 211  
 212      /**
 213       * Generate the standard test scenarios for load_filelist with download as folder.
 214       *
 215       * @param string $prefix the scenarios prefix
 216       * @return array of scenarios
 217       */
 218      private function load_filelist_downloadasfolder_scenarios(string $prefix = "Download as folders:"): array {
 219          return [
 220              // Test without team submissions.
 221              $prefix . ' All users without groups' => [
 222                  'teamsubmission' => false,
 223                  'groupmembers' => [],
 224                  'filterusers' => null,
 225                  'blindmarking' => false,
 226                  'downloadasfolder' => true,
 227                  'expected' => [
 228                      'STUDENT1_STUDENT1.ID_assignsubmission_file/submissionsample01.txt',
 229                      'STUDENT1_STUDENT1.ID_assignsubmission_file/submissionsample02.txt',
 230                      'STUDENT2_STUDENT2.ID_assignsubmission_file/submissionsample01.txt',
 231                      'STUDENT2_STUDENT2.ID_assignsubmission_file/submissionsample02.txt',
 232                      'STUDENT3_STUDENT3.ID_assignsubmission_file/submissionsample01.txt',
 233                      'STUDENT3_STUDENT3.ID_assignsubmission_file/submissionsample02.txt',
 234                      'STUDENT4_STUDENT4.ID_assignsubmission_file/submissionsample01.txt',
 235                      'STUDENT4_STUDENT4.ID_assignsubmission_file/submissionsample02.txt',
 236                  ],
 237              ],
 238              $prefix . ' Filtered users' => [
 239                  'teamsubmission' => false,
 240                  'groupmembers' => [],
 241                  'filterusers' => ['student1', 'student2'],
 242                  'blindmarking' => false,
 243                  'downloadasfolder' => true,
 244                  'expected' => [
 245                      'STUDENT1_STUDENT1.ID_assignsubmission_file/submissionsample01.txt',
 246                      'STUDENT1_STUDENT1.ID_assignsubmission_file/submissionsample02.txt',
 247                      'STUDENT2_STUDENT2.ID_assignsubmission_file/submissionsample01.txt',
 248                      'STUDENT2_STUDENT2.ID_assignsubmission_file/submissionsample02.txt',
 249                  ],
 250              ],
 251              $prefix . ' Filtering users without submissions' => [
 252                  'teamsubmission' => false,
 253                  'groupmembers' => [],
 254                  'filterusers' => ['student1', 'student5'],
 255                  'blindmarking' => false,
 256                  'downloadasfolder' => true,
 257                  'expected' => [
 258                      'STUDENT1_STUDENT1.ID_assignsubmission_file/submissionsample01.txt',
 259                      'STUDENT1_STUDENT1.ID_assignsubmission_file/submissionsample02.txt',
 260                  ],
 261              ],
 262              $prefix . ' Asking only for users without submissions' => [
 263                  'teamsubmission' => false,
 264                  'groupmembers' => [],
 265                  'filterusers' => ['student5'],
 266                  'blindmarking' => false,
 267                  'downloadasfolder' => true,
 268                  'expected' => [],
 269              ],
 270              // Test with team submissions and no default team.
 271              $prefix . ' All users with all users in groups' => [
 272                  'teamsubmission' => true,
 273                  'groupmembers' => [
 274                      'group1' => ['student1'],
 275                      'group2' => ['student2', 'student3'],
 276                      'group3' => ['student4', 'student5'],
 277                  ],
 278                  'filterusers' => null,
 279                  'blindmarking' => false,
 280                  'downloadasfolder' => true,
 281                  'expected' => [
 282                      'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample01.txt',
 283                      'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample02.txt',
 284                      'GROUP2_GROUP2.ID_assignsubmission_file/submissionsample01.txt',
 285                      'GROUP2_GROUP2.ID_assignsubmission_file/submissionsample02.txt',
 286                      'GROUP3_GROUP3.ID_assignsubmission_file/submissionsample01.txt',
 287                      'GROUP3_GROUP3.ID_assignsubmission_file/submissionsample02.txt',
 288                  ],
 289              ],
 290              $prefix . ' Filtering users with disjoined groups' => [
 291                  'teamsubmission' => true,
 292                  'groupmembers' => [
 293                      'group1' => ['student1'],
 294                      'group2' => ['student2', 'student3'],
 295                      'group3' => ['student4', 'student5'],
 296                  ],
 297                  'filterusers' => ['student1', 'student2'],
 298                  'blindmarking' => false,
 299                  'downloadasfolder' => true,
 300                  'expected' => [
 301                      'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample01.txt',
 302                      'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample02.txt',
 303                      'GROUP2_GROUP2.ID_assignsubmission_file/submissionsample01.txt',
 304                      'GROUP2_GROUP2.ID_assignsubmission_file/submissionsample02.txt',
 305                  ],
 306              ],
 307              $prefix . ' Filtering users with default teams who does not do a submission' => [
 308                  'teamsubmission' => true,
 309                  'groupmembers' => [
 310                      'group1' => ['student1'],
 311                      'group2' => ['student2', 'student3'],
 312                      'group3' => ['student4', 'student5'],
 313                  ],
 314                  'filterusers' => ['student1', 'student5'],
 315                  'blindmarking' => false,
 316                  'downloadasfolder' => true,
 317                  'expected' => [
 318                      'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample01.txt',
 319                      'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample02.txt',
 320                      'GROUP3_GROUP3.ID_assignsubmission_file/submissionsample01.txt',
 321                      'GROUP3_GROUP3.ID_assignsubmission_file/submissionsample02.txt',
 322                  ],
 323              ],
 324              $prefix . ' Filtering users without submission but member of a group' => [
 325                  'teamsubmission' => true,
 326                  'groupmembers' => [
 327                      'group1' => ['student1'],
 328                      'group2' => ['student2', 'student3'],
 329                      'group3' => ['student4', 'student5'],
 330                  ],
 331                  'filterusers' => [
 332                      'student5'
 333                  ],
 334                  'blindmarking' => false,
 335                  'downloadasfolder' => true,
 336                  'expected' => [
 337                      'GROUP3_GROUP3.ID_assignsubmission_file/submissionsample01.txt',
 338                      'GROUP3_GROUP3.ID_assignsubmission_file/submissionsample02.txt',
 339                  ],
 340              ],
 341              // Test with default team.
 342              $prefix . ' All users with users in the default team' => [
 343                  'teamsubmission' => true,
 344                  'groupmembers' => [
 345                      'group1' => ['student1', 'student2'],
 346                  ],
 347                  'filterusers' => null,
 348                  'blindmarking' => false,
 349                  'downloadasfolder' => true,
 350                  'expected' => [
 351                      'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample01.txt',
 352                      'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample02.txt',
 353                      'DEFAULTTEAM_assignsubmission_file/submissionsample01.txt',
 354                      'DEFAULTTEAM_assignsubmission_file/submissionsample02.txt',
 355                  ],
 356              ],
 357              $prefix . ' Filtered users in groups with users in the default team' => [
 358                  'teamsubmission' => true,
 359                  'groupmembers' => [
 360                      'group1' => ['student1', 'student2'],
 361                  ],
 362                  'filterusers' => ['student1', 'student2'],
 363                  'blindmarking' => false,
 364                  'downloadasfolder' => true,
 365                  'expected' => [
 366                      'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample01.txt',
 367                      'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample02.txt',
 368                  ],
 369              ],
 370              $prefix . ' Filtered users without groups with users in the default team' => [
 371                  'teamsubmission' => true,
 372                  'groupmembers' => [
 373                      'group1' => ['student1', 'student2'],
 374                  ],
 375                  'filterusers' => ['student3', 'student4'],
 376                  'blindmarking' => false,
 377                  'downloadasfolder' => true,
 378                  'expected' => [
 379                      'DEFAULTTEAM_assignsubmission_file/submissionsample01.txt',
 380                      'DEFAULTTEAM_assignsubmission_file/submissionsample02.txt',
 381                  ],
 382              ],
 383              $prefix . ' Filtered users with some users in the default team' => [
 384                  'teamsubmission' => true,
 385                  'groupmembers' => [
 386                      'group1' => ['student1', 'student2'],
 387                  ],
 388                  'filterusers' => ['student1', 'student3'],
 389                  'blindmarking' => false,
 390                  'downloadasfolder' => true,
 391                  'expected' => [
 392                      'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample01.txt',
 393                      'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample02.txt',
 394                      'DEFAULTTEAM_assignsubmission_file/submissionsample01.txt',
 395                      'DEFAULTTEAM_assignsubmission_file/submissionsample02.txt',
 396                  ],
 397              ],
 398              $prefix . ' Filtering users with joined groups' => [
 399                  'teamsubmission' => true,
 400                  'groupmembers' => [
 401                      'group1' => ['student1', 'student2'],
 402                      'group2' => ['student2', 'student3'],
 403                  ],
 404                  'filterusers' => ['student1', 'student2'],
 405                  'blindmarking' => false,
 406                  'downloadasfolder' => true,
 407                  'expected' => [
 408                      'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample01.txt',
 409                      'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample02.txt',
 410                      'DEFAULTTEAM_assignsubmission_file/submissionsample01.txt',
 411                      'DEFAULTTEAM_assignsubmission_file/submissionsample02.txt',
 412                  ],
 413              ],
 414              // Tests with blind marking.
 415              $prefix . ' All users without groups and blindmarking' => [
 416                  'teamsubmission' => false,
 417                  'groupmembers' => [],
 418                  'filterusers' => null,
 419                  'blindmarking' => true,
 420                  'downloadasfolder' => true,
 421                  'expected' => [
 422                      'PARTICIPANT_STUDENT1.ID_assignsubmission_file/submissionsample01.txt',
 423                      'PARTICIPANT_STUDENT1.ID_assignsubmission_file/submissionsample02.txt',
 424                      'PARTICIPANT_STUDENT2.ID_assignsubmission_file/submissionsample01.txt',
 425                      'PARTICIPANT_STUDENT2.ID_assignsubmission_file/submissionsample02.txt',
 426                      'PARTICIPANT_STUDENT3.ID_assignsubmission_file/submissionsample01.txt',
 427                      'PARTICIPANT_STUDENT3.ID_assignsubmission_file/submissionsample02.txt',
 428                      'PARTICIPANT_STUDENT4.ID_assignsubmission_file/submissionsample01.txt',
 429                      'PARTICIPANT_STUDENT4.ID_assignsubmission_file/submissionsample02.txt',
 430                  ],
 431              ],
 432              $prefix . ' Filtered users without groups and blindmarking' => [
 433                  'teamsubmission' => false,
 434                  'groupmembers' => [],
 435                  'filterusers' => ['student1', 'student2'],
 436                  'blindmarking' => true,
 437                  'downloadasfolder' => true,
 438                  'expected' => [
 439                      'PARTICIPANT_STUDENT1.ID_assignsubmission_file/submissionsample01.txt',
 440                      'PARTICIPANT_STUDENT1.ID_assignsubmission_file/submissionsample02.txt',
 441                      'PARTICIPANT_STUDENT2.ID_assignsubmission_file/submissionsample01.txt',
 442                      'PARTICIPANT_STUDENT2.ID_assignsubmission_file/submissionsample02.txt',
 443                  ],
 444              ],
 445          ];
 446      }
 447  }