MDL-38509 Add new tool_installaddon_installer::extract_installfromzip_file() method
authorDavid Mudrák <david@moodle.com>
Fri, 22 Mar 2013 16:49:25 +0000 (17:49 +0100)
committerDavid Mudrák <david@moodle.com>
Thu, 28 Mar 2013 10:54:06 +0000 (11:54 +0100)
admin/tool/installaddon/classes/installer.php
admin/tool/installaddon/tests/fixtures/zips/invalidroot.zip [new file with mode: 0644]
admin/tool/installaddon/tests/installer_test.php

index b7f18bc..7353684 100644 (file)
@@ -43,7 +43,7 @@ class tool_installaddon_installer {
      * @return tool_installaddon_installer
      */
     public static function instance() {
-        return new self();
+        return new static();
     }
 
     /**
@@ -113,6 +113,37 @@ class tool_installaddon_installer {
         return $filename;
     }
 
+    /**
+     * Extracts the saved file previously saved by {self::save_installfromzip_file()}
+     *
+     * The list of files found in the ZIP is returned via $zipcontentfiles parameter
+     * by reference. The format of that list is array of (string)filerelpath => (bool|string)
+     * where the array value is either true or a string describing the problematic file.
+     *
+     * @see zip_packer::extract_to_pathname()
+     * @param string $zipfilepath full path to the saved ZIP file
+     * @param string $targetdir full path to the directory to extract the ZIP file to
+     * @param string $rootdir explicitly rename the root directory of the ZIP into this non-empty value
+     * @param array list of extracted files as returned by {@link zip_packer::extract_to_pathname()}
+     */
+    public function extract_installfromzip_file($zipfilepath, $targetdir, $rootdir = '') {
+        global $CFG;
+        require_once($CFG->libdir.'/filelib.php');
+
+        $fp = get_file_packer('application/zip');
+        $files = $fp->extract_to_pathname($zipfilepath, $targetdir);
+
+        if ($files) {
+            if (!empty($rootdir)) {
+                $files = $this->rename_extracted_rootdir($targetdir, $rootdir, $files);
+            }
+            return $files;
+
+        } else {
+            return array();
+        }
+    }
+
     /**
      * Returns localised list of available plugin types
      *
@@ -185,6 +216,12 @@ class tool_installaddon_installer {
 
     //// End of external API ///////////////////////////////////////////////////
 
+    /**
+     * @see self::instance()
+     */
+    protected function __construct() {
+    }
+
     /**
      * @return string this site full name
      */
@@ -235,4 +272,56 @@ class tool_installaddon_installer {
     protected function should_send_site_info() {
         return true;
     }
+
+    /**
+     * Renames the root directory of the extracted ZIP package.
+     *
+     * This method does not validate the presence of the single root directory
+     * (the validator does it later). It just searches for the first directory
+     * under the given location and renames it.
+     *
+     * The method will not rename the root if the requested location already
+     * exists.
+     *
+     * @param string $dirname the location of the extracted ZIP package
+     * @param string $rootdir the requested name of the root directory
+     * @param array $files list of extracted files
+     * @return array eventually amended list of extracted files
+     */
+    protected function rename_extracted_rootdir($dirname, $rootdir, array $files) {
+
+        if (!is_dir($dirname)) {
+            debugging('Unable to rename rootdir of non-existing content', DEBUG_DEVELOPER);
+            return $files;
+        }
+
+        if (file_exists($dirname.'/'.$rootdir)) {
+            debugging('Unable to rename rootdir to already existing folder', DEBUG_DEVELOPER);
+            return $files;
+        }
+
+        $found = null; // The name of the first subdirectory under the $dirname.
+        foreach (scandir($dirname) as $item) {
+            if (substr($item, 0, 1) === '.') {
+                continue;
+            }
+            if (is_dir($dirname.'/'.$item)) {
+                $found = $item;
+                break;
+            }
+        }
+
+        if (!is_null($found)) {
+            if (rename($dirname.'/'.$found, $dirname.'/'.$rootdir)) {
+                $newfiles = array();
+                foreach ($files as $filepath => $status) {
+                    $newpath = preg_replace('~^'.preg_quote($found.'/').'~', preg_quote($rootdir.'/'), $filepath);
+                    $newfiles[$newpath] = $status;
+                }
+                return $newfiles;
+            }
+        }
+
+        return $files;
+    }
 }
diff --git a/admin/tool/installaddon/tests/fixtures/zips/invalidroot.zip b/admin/tool/installaddon/tests/fixtures/zips/invalidroot.zip
new file mode 100644 (file)
index 0000000..a209025
Binary files /dev/null and b/admin/tool/installaddon/tests/fixtures/zips/invalidroot.zip differ
index a5028dd..f32bc19 100644 (file)
@@ -39,7 +39,7 @@ require_once($CFG->dirroot.'/'.$CFG->admin.'/tool/installaddon/classes/installer
 class tool_installaddon_installer_test extends advanced_testcase {
 
     public function test_get_addons_repository_url() {
-        $installer = new testable_tool_installaddon_installer();
+        $installer = testable_tool_installaddon_installer::instance();
         $url = $installer->get_addons_repository_url();
         $query = parse_url($url, PHP_URL_QUERY);
         $this->assertEquals(1, preg_match('~^site=(.+)$~', $query, $matches));
@@ -51,6 +51,29 @@ class tool_installaddon_installer_test extends advanced_testcase {
         $this->assertSame($installer->get_site_url(), $site['url']);
         $this->assertSame($installer->get_site_major_version(), $site['major_version']);
     }
+
+    public function test_extract_installfromzip_file() {
+        $jobid = md5(rand().uniqid('test_', true));
+        $sourcedir = make_temp_directory('tool_installaddon/'.$jobid.'/source');
+        $contentsdir = make_temp_directory('tool_installaddon/'.$jobid.'/contents');
+        copy(dirname(__FILE__).'/fixtures/zips/invalidroot.zip', $sourcedir.'/testinvalidroot.zip');
+
+        $installer = tool_installaddon_installer::instance();
+        $files = $installer->extract_installfromzip_file($sourcedir.'/testinvalidroot.zip', $contentsdir, 'fixed_root');
+        $this->assertEquals('array', gettype($files));
+        $this->assertEquals(4, count($files));
+        $this->assertSame(true, $files['fixed_root/']);
+        $this->assertSame(true, $files['fixed_root/lang/']);
+        $this->assertSame(true, $files['fixed_root/lang/en/']);
+        $this->assertSame(true, $files['fixed_root/lang/en/fixed_root.php']);
+        foreach ($files as $file => $status) {
+            if (substr($file, -1) === '/') {
+                $this->assertTrue(is_dir($contentsdir.'/'.$file));
+            } else {
+                $this->assertTrue(is_file($contentsdir.'/'.$file));
+            }
+        }
+    }
 }