diff --git a/lang/en/repository.php b/lang/en/repository.php
index a8b62a74fa6..f14ef53a754 100644
--- a/lang/en/repository.php
+++ b/lang/en/repository.php
@@ -120,7 +120,7 @@ $string['filepicker'] = 'File picker';
 $string['filesizenull'] = 'File size cannot be determined';
 $string['folderexists'] = 'Folder name already being used, please use another name';
 $string['foldernotfound'] = 'Folder not found';
-$string['folderrecurse'] = 'Folder can not be moved to it\s own subfolder';
+$string['folderrecurse'] = 'Folder can not be moved to it\'s own subfolder';
 $string['getfile'] = 'Select this file';
 $string['hidden'] = 'Hidden';
 $string['help'] = 'Help';
diff --git a/repository/draftfiles_ajax.php b/repository/draftfiles_ajax.php
index 3f08b80b660..d962cd8d1a4 100644
--- a/repository/draftfiles_ajax.php
+++ b/repository/draftfiles_ajax.php
@@ -125,90 +125,33 @@ switch ($action) {
         // Allows to Rename file, move it to another directory, change it's license and author information in one request
         $filename    = required_param('filename', PARAM_FILE);
         $filepath    = required_param('filepath', PARAM_PATH);
-
-        $fs = get_file_storage();
-        if (!($file = $fs->get_file($user_context->id, 'user', 'draft', $draftid, $filepath, $filename))) {
-            die(json_encode((object)array('error' => get_string('filenotfound', 'error'))));
-        }
-
         $updatedata = array();
-        $updatedata['filename'] = $newfilename = optional_param('newfilename', $file->get_filename(), PARAM_FILE);
-        $updatedata['filepath'] = $newfilepath = optional_param('newfilepath', $file->get_filepath(), PARAM_PATH);
-        $updatedata['license'] = optional_param('newlicense', $file->get_license(), PARAM_TEXT);
-        $updatedata['author'] = optional_param('newauthor', $file->get_author(), PARAM_TEXT);
-        foreach ($updatedata as $key => $value) {
-            if (''.$value === ''.$file->{'get_'.$key}()) {
-                unset($updatedata[$key]);
-            }
+        $updatedata['filename'] = optional_param('newfilename', $filename, PARAM_FILE);
+        $updatedata['filepath'] = $newfilepath = optional_param('newfilepath', $filepath, PARAM_PATH);
+        if (($v = optional_param('newlicense', false, PARAM_TEXT)) !== false) {
+            $updatedata['license'] = $v;
         }
-
-        if (!empty($updatedata)) {
-            if (array_key_exists('filename', $updatedata) || array_key_exists('filepath', $updatedata)) {
-                // check that target file name does not exist
-                if ($fs->file_exists($user_context->id, 'user', 'draft', $draftid, $newfilepath, $newfilename)) {
-                    die(json_encode((object)array('error' => get_string('fileexists', 'repository'))));
-                }
-                $file->rename($newfilepath, $newfilename);
-            }
-            if (array_key_exists('license', $updatedata)) {
-                $file->set_license($updatedata['license']);
-            }
-            if (array_key_exists('author', $updatedata)) {
-                $file->set_author($updatedata['author']);
-            }
-            $changes = array_diff(array_keys($updatedata), array('filepath'));
-            if (!empty($changes)) {
-                // any change except for the moving to another folder alters 'Date modified' of the file
-                $file->set_timemodified(time());
-            }
+        if (($v = optional_param('newauthor', false, PARAM_TEXT)) !== false) {
+            $updatedata['author'] = $v;
+        }
+        try {
+            repository::update_draftfile($draftid, $filepath, $filename, $updatedata);
+        } catch (moodle_exception $e) {
+            die(json_encode((object)array('error' => $e->getMessage())));
         }
-
         die(json_encode((object)array('filepath' => $newfilepath)));
 
     case 'updatedir':
         $filepath = required_param('filepath', PARAM_PATH);
-        $fs = get_file_storage();
-        if (!$dir = $fs->get_file($user_context->id, 'user', 'draft', $draftid, $filepath, '.')) {
-            die(json_encode((object)array('error' => get_string('foldernotfound', 'repository'))));
-        }
-        $parts = explode('/', trim($dir->get_filepath(), '/'));
-        $dirname = end($parts);
         $newdirname = required_param('newdirname', PARAM_FILE);
         $parent = required_param('newfilepath', PARAM_PATH);
         $newfilepath = clean_param($parent . '/' . $newdirname . '/', PARAM_PATH);
-        if ($newfilepath == $filepath) {
-            // no action required
-            die(json_encode((object)array('filepath' => $parent)));
+        try {
+            repository::update_draftfile($draftid, $filepath, '.', array('filepath' => $newfilepath));
+        } catch (moodle_exception $e) {
+            die(json_encode((object)array('error' => $e->getMessage())));
         }
-        if ($fs->get_directory_files($user_context->id, 'user', 'draft', $draftid, $newfilepath, true)) {
-            //bad luck, we can not rename if something already exists there
-            die(json_encode((object)array('error' => get_string('folderexists', 'repository'))));
-        }
-        $xfilepath = preg_quote($filepath, '|');
-        if (preg_match("|^$xfilepath|", $parent)) {
-            // we can not move folder to it's own subfolder
-            die(json_encode((object)array('error' => get_string('folderrecurse', 'repository'))));
-        }
-
-        //we must update directory and all children too
-        $files = $fs->get_area_files($user_context->id, 'user', 'draft', $draftid);
-        foreach ($files as $file) {
-            if (!preg_match("|^$xfilepath|", $file->get_filepath())) {
-                continue;
-            }
-            // move one by one
-            $path = preg_replace("|^$xfilepath|", $newfilepath, $file->get_filepath());
-            if ($dirname !== $newdirname && $file->get_filepath() === $filepath && $file->get_filename() === '.') {
-                // this is the main directory we move/rename AND it has actually been renamed
-                $file->set_timemodified(time());
-            }
-            $file->rename($path, $file->get_filename());
-        }
-
-        $return = new stdClass();
-        $return->filepath = $parent;
-        echo json_encode($return);
-        die;
+        die(json_encode((object)array('filepath' => $parent)));
 
     case 'zip':
         $filepath = required_param('filepath', PARAM_PATH);
diff --git a/repository/draftfiles_manager.php b/repository/draftfiles_manager.php
index e398b5340d3..cc096419fd7 100644
--- a/repository/draftfiles_manager.php
+++ b/repository/draftfiles_manager.php
@@ -122,14 +122,7 @@ case 'renameform':
     break;
 
 case 'rename':
-
-    if ($fs->file_exists($user_context->id, 'user', 'draft', $itemid, $draftpath, $newfilename)) {
-        print_error('fileexists');
-    } else if ($file = $fs->get_file($user_context->id, 'user', 'draft', $itemid, $draftpath, $filename)) {
-        $newfile = $fs->create_file_from_storedfile(array('filename'=>$newfilename), $file);
-        $file->delete();
-    }
-
+    repository::update_draftfile($itemid, $draftpath, $filename, array('filename' => $newfilename));
     $home_url->param('action', 'browse');
     $home_url->param('draftpath', $draftpath);
     redirect($home_url);
@@ -194,15 +187,7 @@ case 'unzip':
 
 case 'movefile':
     if (!empty($targetpath)) {
-        if ($fs->file_exists($user_context->id, 'user', 'draft', $itemid, $targetpath, $filename)) {
-            print_error('cannotmovefile');
-        } else if ($file = $fs->get_file($user_context->id, 'user', 'draft', $itemid, $draftpath, $filename)) {
-            $newfile = $fs->create_file_from_storedfile(array('filepath'=>$targetpath), $file);
-            $file->delete();
-        } else {
-            var_dump('cannot find file');
-            die;
-        }
+        repository::update_draftfile($itemid, $draftpath, $filename, array('filepath' => $targetpath));
         $home_url->param('action', 'browse');
         $home_url->param('draftpath', $targetpath);
         redirect($home_url);
diff --git a/repository/lib.php b/repository/lib.php
index a12396ae5b9..0e28f1c3fa8 100644
--- a/repository/lib.php
+++ b/repository/lib.php
@@ -2574,6 +2574,102 @@ abstract class repository implements cacheable_object {
         return false;
     }
 
+    /**
+     * Updates a file in draft filearea.
+     *
+     * This function can only update fields filepath, filename, author, license.
+     * If anything (except filepath) is updated, timemodified is set to current time.
+     * If filename or filepath is updated the file unconnects from it's origin
+     * and therefore all references to it will be converted to copies when
+     * filearea is saved.
+     *
+     * @param int $draftid
+     * @param string $filepath path to the directory containing the file, or full path in case of directory
+     * @param string $filename name of the file, or '.' in case of directory
+     * @param array $updatedata array of fields to change (only filename, filepath, license and/or author can be updated)
+     * @throws moodle_exception if for any reason file can not be updated (file does not exist, target already exists, etc.)
+     */
+    public static function update_draftfile($draftid, $filepath, $filename, $updatedata) {
+        global $USER;
+        $fs = get_file_storage();
+        $usercontext = context_user::instance($USER->id);
+        // make sure filename and filepath are present in $updatedata
+        $updatedata = $updatedata + array('filepath' => $filepath, 'filename' => $filename);
+        $filemodified = false;
+        if (!$file = $fs->get_file($usercontext->id, 'user', 'draft', $draftid, $filepath, $filename)) {
+            if ($filename === '.') {
+                throw new moodle_exception('foldernotfound', 'repository');
+            } else {
+                throw new moodle_exception('filenotfound', 'error');
+            }
+        }
+        if (!$file->is_directory()) {
+            // This is a file
+            if ($updatedata['filepath'] !== $filepath || $updatedata['filename'] !== $filename) {
+                // Rename/move file: check that target file name does not exist.
+                if ($fs->file_exists($usercontext->id, 'user', 'draft', $draftid, $updatedata['filepath'], $updatedata['filename'])) {
+                    throw new moodle_exception('fileexists', 'repository');
+                }
+                if (($filesource = @unserialize($file->get_source())) && isset($filesource->original)) {
+                    unset($filesource->original);
+                    $file->set_source(serialize($filesource));
+                }
+                $file->rename($updatedata['filepath'], $updatedata['filename']);
+                // timemodified is updated only when file is renamed and not updated when file is moved.
+                $filemodified = $filemodified || ($updatedata['filename'] !== $filename);
+            }
+            if (array_key_exists('license', $updatedata) && $updatedata['license'] !== $file->get_license()) {
+                // Update license and timemodified.
+                $file->set_license($updatedata['license']);
+                $filemodified = true;
+            }
+            if (array_key_exists('author', $updatedata) && $updatedata['author'] !== $file->get_author()) {
+                // Update author and timemodified.
+                $file->set_author($updatedata['author']);
+                $filemodified = true;
+            }
+            // Update timemodified:
+            if ($filemodified) {
+                $file->set_timemodified(time());
+            }
+        } else {
+            // This is a directory - only filepath can be updated for a directory (it was moved).
+            if ($updatedata['filepath'] === $filepath) {
+                // nothing to update
+                return;
+            }
+            if ($fs->file_exists($usercontext->id, 'user', 'draft', $draftid, $updatedata['filepath'], '.')) {
+                // bad luck, we can not rename if something already exists there
+                throw new moodle_exception('folderexists', 'repository');
+            }
+            $xfilepath = preg_quote($filepath, '|');
+            if (preg_match("|^$xfilepath|", $updatedata['filepath'])) {
+                // we can not move folder to it's own subfolder
+                throw new moodle_exception('folderrecurse', 'repository');
+            }
+
+            // If directory changed the name, update timemodified.
+            $filemodified = (basename(rtrim($file->get_filepath(), '/')) !== basename(rtrim($updatedata['filepath'], '/')));
+
+            // Now update directory and all children.
+            $files = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftid);
+            foreach ($files as $f) {
+                if (preg_match("|^$xfilepath|", $f->get_filepath())) {
+                    $path = preg_replace("|^$xfilepath|", $updatedata['filepath'], $f->get_filepath());
+                    if (($filesource = @unserialize($f->get_source())) && isset($filesource->original)) {
+                        // unset original so the references are not shown any more
+                        unset($filesource->original);
+                        $f->set_source(serialize($filesource));
+                    }
+                    $f->rename($path, $f->get_filename());
+                    if ($filemodified && $f->get_filepath() === $updatedata['filepath'] && $f->get_filename() === $filename) {
+                        $f->set_timemodified(time());
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Delete a temp file from draft area
      *