MDL-39177 New function file_storage::get_unused_dirname()
authorMarina Glancy <marina@moodle.com>
Mon, 13 May 2013 11:20:54 +0000 (21:20 +1000)
committerMarina Glancy <marina@moodle.com>
Fri, 17 May 2013 03:43:45 +0000 (13:43 +1000)
lib/filestorage/file_storage.php
repository/draftfiles_ajax.php

index 7baf705..967a329 100644 (file)
@@ -263,6 +263,71 @@ class file_storage {
         return $newfilename;
     }
 
+    /**
+     * Return an available directory name.
+     *
+     * This will return the next available directory name in the area, adding/incrementing a suffix
+     * of the last portion of path, ie: /path/ > /path (1)/ > /path (2)/ > etc...
+     *
+     * If the file path passed is available without modification, it is returned as is.
+     *
+     * @param int $contextid context ID.
+     * @param string $component component.
+     * @param string $filearea file area.
+     * @param int $itemid area item ID.
+     * @param string $suggestedpath the suggested file path.
+     * @return string available file path
+     * @since 2.5
+     */
+    public function get_unused_dirname($contextid, $component, $filearea, $itemid, $suggestedpath) {
+        global $DB;
+
+        // Ensure suggestedpath has trailing '/'
+        $suggestedpath = rtrim($suggestedpath, '/'). '/';
+
+        // The directory does not exist, we return the same file path.
+        if (!$this->file_exists($contextid, $component, $filearea, $itemid, $suggestedpath, '.')) {
+            return $suggestedpath;
+        }
+
+        // Trying to locate a file path using the used pattern. We remove the used pattern from the path first.
+        if (preg_match('~^(/.+) \(([0-9]+)\)/$~', $suggestedpath, $matches)) {
+            $suggestedpath = $matches[1]. '/';
+        }
+
+        $filepathlike = $DB->sql_like_escape(rtrim($suggestedpath, '/')) . ' (%)/';
+
+        $filepathlikesql = $DB->sql_like('f.filepath', ':filepathlike');
+        $filepathlen = $DB->sql_length('f.filepath');
+        $sql = "SELECT filepath
+                FROM {files} f
+                WHERE
+                    f.contextid = :contextid AND
+                    f.component = :component AND
+                    f.filearea = :filearea AND
+                    f.itemid = :itemid AND
+                    f.filename = :filename AND
+                    $filepathlikesql
+                ORDER BY
+                    $filepathlen DESC,
+                    f.filepath DESC";
+        $params = array('contextid' => $contextid, 'component' => $component, 'filearea' => $filearea, 'itemid' => $itemid,
+                'filename' => '.', 'filepathlike' => $filepathlike);
+        $results = $DB->get_fieldset_sql($sql, $params, IGNORE_MULTIPLE);
+
+        // Loop over the results to make sure we are working on a valid file path. Because '/path (1)/' and '/path (copy)/'
+        // would both be returned, but only the one only containing digits should be used.
+        $number = 1;
+        foreach ($results as $result) {
+            if (preg_match('~ \(([0-9]+)\)/$~', $result, $matches)) {
+                $number = (int)($matches[1]) + 1;
+                break;
+            }
+        }
+
+        return rtrim($suggestedpath, '/'). ' (' . $number . ')/';
+    }
+
     /**
      * Generates a preview image for the stored file
      *
index b66e4bd..9a8acfa 100644 (file)
@@ -218,15 +218,7 @@ switch ($action) {
         $file = $fs->get_file($user_context->id, 'user', 'draft', $draftid, $filepath, $filename);
 
         // Find unused name for directory to extract the archive.
-        $temppath = $filepath. pathinfo($filename, PATHINFO_FILENAME). '/';
-        if ($fs->file_exists($user_context->id, 'user', 'draft', $draftid, $temppath, '.')) {
-            for ($i=0; $i<1000; $i++) {
-                if (!$fs->file_exists($user_context->id, 'user', 'draft', $draftid, rtrim($temppath, '/'). " ($i)/", '.')) {
-                    $temppath = rtrim($temppath, '/'). " ($i)/";
-                    break;
-                }
-            }
-        }
+        $temppath = $fs->get_unused_dirname($user_context->id, 'user', 'draft', $draftid, $filepath. pathinfo($filename, PATHINFO_FILENAME). '/');
         // Extract archive and move all files from $temppath to $filepath
         if ($file->extract_to_storage($zipper, $user_context->id, 'user', 'draft', $draftid, $temppath, $USER->id) !== false) {
             $extractedfiles = $fs->get_directory_files($user_context->id, 'user', 'draft', $draftid, $temppath, true);