Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402]

   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  declare(strict_types=1);
  18  
  19  namespace core\content\export\exportable_items;
  20  
  21  use advanced_testcase;
  22  use context;
  23  use context_module;
  24  use context_system;
  25  use core\content\export\zipwriter;
  26  use moodle_url;
  27  use stdClass;
  28  
  29  /**
  30   * Unit tests for the `exportable_textarea` export item class.
  31   *
  32   * @package     core
  33   * @category    test
  34   * @copyright   2020 Andrew Nicols <andrew@nicols.co.uk>
  35   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   * @covers      \core\content\exportable_items\exportable_textarea
  37   */
  38  class exportable_textarea_test extends advanced_testcase {
  39  
  40      /**
  41       * Ensure that an exportable textarea which does not relate to any content, does not attempt to export any content.
  42       */
  43      public function test_valid_table_without_content(): void {
  44          $this->resetAfterTest(true);
  45  
  46          $user = $this->getDataGenerator()->create_user();
  47  
  48          $context = context_system::instance();
  49          $component = 'page';
  50          $uservisiblename = 'Page content';
  51          $tablename = 'page';
  52          $fieldname = 'content';
  53          $fieldid = -1;
  54          $formatfieldname = 'contentformat';
  55          $filearea = 'content';
  56  
  57          $exportable = new exportable_textarea(
  58              $context,
  59              $component,
  60              $uservisiblename,
  61              $tablename,
  62              $fieldname,
  63              $fieldid,
  64              $formatfieldname
  65          );
  66  
  67          $this->assertInstanceOf(exportable_textarea::class, $exportable);
  68  
  69          $this->assert_exportable_empty($component, $user, $context, $exportable);
  70      }
  71  
  72      /**
  73       * Ensure that the an exportable textarea exports content from the appropriate locations, but without any files.
  74       */
  75      public function test_valid_table_with_content_no_filearea_specified(): void {
  76          $this->resetAfterTest(true);
  77  
  78          $content = '<h1>Hello</h1><p>World!</p>';
  79  
  80          $user = $this->getDataGenerator()->create_user();
  81          $course = $this->getDataGenerator()->create_course();
  82          $page = $this->getDataGenerator()->create_module('page', (object) [
  83              'course' => $course,
  84              'content' => $content,
  85              'contentformat' => FORMAT_HTML,
  86          ]);
  87  
  88          $context = context_module::instance($page->cmid);
  89          $expectedfiles = $this->create_files($context, 'mod_page', 'content', (int) $page->id, 5);
  90  
  91          // Unexpected files.
  92          $this->create_files($context, 'mod_page', 'content', (int) $page->id + 1, 5);
  93          $this->create_files($context, 'mod_page', 'othercontent', (int) $page->id, 5);
  94          $this->create_files($context, 'mod_foo', 'content', (int) $page->id, 5);
  95  
  96          $component = 'page';
  97          $uservisiblename = 'Page content';
  98          $tablename = 'page';
  99          $fieldname = 'content';
 100          $fieldid = (int) $page->id;
 101          $formatfieldname = 'contentformat';
 102  
 103          $exportable = new exportable_textarea(
 104              $context,
 105              $component,
 106              $uservisiblename,
 107              $tablename,
 108              $fieldname,
 109              $fieldid,
 110              $formatfieldname
 111          );
 112  
 113          $this->assertInstanceOf(exportable_textarea::class, $exportable);
 114  
 115          // Although files exist, the filearea and itemid were not included.
 116          $this->assert_exportable_matches_file($component, $user, $context, null, $content, [], '', $exportable);
 117      }
 118  
 119      /**
 120       * Ensure that the an exportable textarea exports content from the appropriate locations, but without any files.
 121       */
 122      public function test_valid_table_with_content_no_itemid_specified(): void {
 123          $this->resetAfterTest(true);
 124  
 125          $content = '<h1>Hello</h1><p>World!</p>';
 126  
 127          $user = $this->getDataGenerator()->create_user();
 128          $course = $this->getDataGenerator()->create_course();
 129          $page = $this->getDataGenerator()->create_module('page', (object) [
 130              'course' => $course,
 131              'content' => $content,
 132              'contentformat' => FORMAT_HTML,
 133          ]);
 134  
 135          $context = context_module::instance($page->cmid);
 136          $expectedfiles = $this->create_files($context, 'mod_page', 'content', (int) $page->id, 5);
 137  
 138          // Unexpected files.
 139          $this->create_files($context, 'mod_page', 'content', (int) $page->id + 1, 5);
 140          $this->create_files($context, 'mod_page', 'othercontent', (int) $page->id, 5);
 141          $this->create_files($context, 'mod_foo', 'content', (int) $page->id, 5);
 142  
 143          $component = 'page';
 144          $uservisiblename = 'Page content';
 145          $tablename = 'page';
 146          $fieldname = 'content';
 147          $fieldid = (int) $page->id;
 148          $formatfieldname = 'contentformat';
 149          $filearea = 'content';
 150  
 151          $exportable = new exportable_textarea(
 152              $context,
 153              $component,
 154              $uservisiblename,
 155              $tablename,
 156              $fieldname,
 157              $fieldid,
 158              $formatfieldname,
 159              $filearea
 160          );
 161  
 162          $this->assertInstanceOf(exportable_textarea::class, $exportable);
 163  
 164          // Although files exist, the filearea and itemid were not included.
 165          $this->assert_exportable_matches_file($component, $user, $context, null, $content, [], '', $exportable);
 166      }
 167  
 168      /**
 169       * Ensure that the an exportable textarea exports content from the appropriate locations, with files.
 170       */
 171      public function test_valid_table_with_content_and_files(): void {
 172          $this->resetAfterTest(true);
 173          $user = $this->getDataGenerator()->create_user();
 174  
 175          $contentin = <<<EOF
 176  <h1>Hello</h1><p>World!</p>
 177  <img src='@@PLUGINFILE@@/file.txt'>
 178  <img src='@@PLUGINFILE@@/other/file.txt'>
 179  EOF;
 180          $course = $this->getDataGenerator()->create_course();
 181          $page = $this->getDataGenerator()->create_module('page', (object) [
 182              'course' => $course,
 183              'content' => $contentin,
 184              'contentformat' => FORMAT_HTML,
 185          ]);
 186  
 187          $this->setUser($user);
 188  
 189          $context = context_module::instance($page->cmid);
 190          $expectedfiles = $this->create_files(
 191              $context,
 192              'mod_page',
 193              'content',
 194              (int) $page->id,
 195              5,
 196              'contentformat',
 197              'content',
 198              (int) $page->id,
 199              5
 200          );
 201  
 202          // Unexpected files.
 203          $this->create_files($context, 'mod_page', 'content', (int) $page->id + 1, 5);
 204          $this->create_files($context, 'mod_page', 'othercontent', (int) $page->id, 5);
 205          $this->create_files($context, 'mod_foo', 'content', (int) $page->id, 5);
 206  
 207          $component = 'mod_page';
 208          $uservisiblename = 'Page content';
 209          $tablename = 'page';
 210          $fieldname = 'content';
 211          $fieldid = (int) $page->id;
 212          $formatfieldname = 'contentformat';
 213          $filearea = 'content';
 214          $itemid = (int) $page->id;
 215  
 216          $exportable = new exportable_textarea(
 217              $context,
 218              $component,
 219              $uservisiblename,
 220              $tablename,
 221              $fieldname,
 222              $fieldid,
 223              $formatfieldname,
 224              $filearea,
 225              $itemid,
 226              null
 227          );
 228  
 229          $this->assertInstanceOf(exportable_textarea::class, $exportable);
 230  
 231          $pluginfilebase = moodle_url::make_pluginfile_url(
 232              $context->id, $component, $filearea, null, '', '', false, true
 233          )->out(false);
 234          $expectedcontent = <<<EOF
 235  <h1>Hello</h1><p>World!</p>
 236  <img src='content/file.txt'>
 237  <img src='{$pluginfilebase}/other/file.txt'>
 238  EOF;
 239  
 240          // Although files exist, the filearea and itemid were not included.
 241          $this->assert_exportable_matches_file(
 242              $component, $user, $context, $filearea, $expectedcontent, $expectedfiles, '', $exportable
 243          );
 244      }
 245  
 246      /**
 247       * Create files for use in testing.
 248       *
 249       * @param   context $context
 250       * @param   string $component
 251       * @param   string $filearea
 252       * @param   int $itemid
 253       * @param   int $count
 254       * @return  stored_file[]
 255       */
 256      protected function create_files(context $context, string $component, string $filearea, int $itemid, int $count = 1): array {
 257          $fs = get_file_storage();
 258  
 259          $files = [];
 260          for ($i = 0; $i < $count; $i++) {
 261  
 262              $filepath = '/';
 263              for ($j = 0; $j < $i; $j++) {
 264                  $filepath .= "{$j}/";
 265              }
 266  
 267              $files[] = $fs->create_file_from_string(
 268                  (object) [
 269                      'contextid' => $context->id,
 270                      'component' => $component,
 271                      'filearea' => $filearea,
 272                      'filepath' => $filepath,
 273                      'filename' => "file.txt",
 274                      'itemid' => $itemid,
 275                  ],
 276                  "File content: {$i}"
 277              );
 278          }
 279  
 280          return $files;
 281      }
 282  
 283      /**
 284       * Assert that the supplied expotable matches the supplied file.
 285       *
 286       * @param   string $component
 287       * @param   stdClass $user
 288       * @param   context $context
 289       * @param   string $filearea
 290       * @param   string $content
 291       * @param   stored_file[] $expectedfiles
 292       * @param   string $subdir
 293       * @param   exportable_textarea $exportable
 294       */
 295      protected function assert_exportable_matches_file(
 296          string $component,
 297          stdClass $user,
 298          context $context,
 299          ?string $filearea,
 300          string $content,
 301          array $expectedfiles,
 302          string $subdir,
 303          exportable_textarea $exportable
 304      ): void {
 305          $archive = $this->getMockBuilder(zipwriter::class)
 306              ->setConstructorArgs([$this->getMockBuilder(\ZipStream\ZipStream::class)->getmock()])
 307              ->onlyMethods([
 308                  'is_file_in_archive',
 309                  'add_file_from_string',
 310                  'add_file_from_stored_file',
 311              ])
 312              ->getMock();
 313  
 314          $archive->expects($this->any())
 315              ->method('is_file_in_archive')
 316              ->willReturn(true);
 317  
 318          $storedfileargs = [];
 319          foreach ($expectedfiles as $file) {
 320              $filepathinzip = dirname($subdir) . $file->get_filearea() . '/' . $file->get_filepath() . $file->get_filename();
 321              $filepathinzip = ltrim(preg_replace('#/+#', '/', $filepathinzip), '/');
 322              $storedfileargs[] = [
 323                  $this->equalTo($context),
 324                  $this->equalTo($filepathinzip),
 325                  $this->equalTo($file),
 326              ];
 327          }
 328  
 329          $archive->expects($this->exactly(count($expectedfiles)))
 330              ->method('add_file_from_stored_file')
 331              ->withConsecutive(...$storedfileargs);
 332  
 333          $archive->expects($this->never())
 334              ->method('add_file_from_string');
 335  
 336          $exportable->add_to_archive($archive);
 337      }
 338  
 339      /**
 340       * Assert that the supplied expotable matches the supplied file.
 341       *
 342       * @param   string $component
 343       * @param   stdClass $user
 344       * @param   context $context
 345       * @param   exportable_textarea $exportable
 346       */
 347      protected function assert_exportable_empty(
 348          string $component,
 349          stdClass $user,
 350          context $context,
 351          exportable_textarea $exportable
 352      ): void {
 353          $archive = $this->getMockBuilder(zipwriter::class)
 354              ->setConstructorArgs([$this->getMockBuilder(\ZipStream\ZipStream::class)->getmock()])
 355              ->onlyMethods([
 356                  'add_file_from_stored_file',
 357                  'add_file_from_string',
 358                  'add_file_from_template',
 359              ])
 360              ->getMock();
 361  
 362          $archive->expects($this->never())
 363              ->method('add_file_from_stored_file');
 364          $archive->expects($this->never())
 365              ->method('add_file_from_string');
 366          $archive->expects($this->never())
 367              ->method('add_file_from_template');
 368  
 369          $exportable->add_to_archive($archive);
 370      }
 371  }