Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

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