diff --git a/admin/uploadpicture.php b/admin/uploadpicture.php index e8122bf6f1987..bce28a86d7b4a 100644 --- a/admin/uploadpicture.php +++ b/admin/uploadpicture.php @@ -26,6 +26,10 @@ require_once($CFG->libdir.'/gdlib.php'); require_once('uploadpicture_form.php'); +define ('PIX_FILE_UPDATED', 0); +define ('PIX_FILE_ERROR', 1); +define ('PIX_FILE_SKIPPED', 2); + $adminroot = admin_get_root(); admin_externalpage_setup('uploadpictures', $adminroot); @@ -46,8 +50,6 @@ $struser = get_string('user'); $strusersupdated = get_string('usersupdated'); $struploadpictures = get_string('uploadpictures','admin'); -$usersupdated = 0; -$userserrors = 0; $userfields = array ( 0 => 'username', @@ -91,60 +93,16 @@ // We don't need the zip file any longer, so delete it to make // it easier to process the rest of the files inside the directory. @unlink($dstfile); - if(! ($handle = opendir($zipdir))) { - notify(get_string('uploadpicture_cannotprocessdir','admin')); - } else { - while (false !== ($item = readdir($handle))) { - if($item != '.' && $item != '..' && is_file($zipdir.'/'.$item)) { - - // Add additional checks on the filenames, as they are user - // controlled and we don't want to open any security holes. - $path_parts = pathinfo(cleardoubleslashes($item)); - $basename = $path_parts['basename']; - $extension = $path_parts['extension']; - if ($basename != clean_param($basename, PARAM_CLEANFILE)) { - // The original picture file name has invalid characters - notify(get_string('uploadpicture_invalidfilename', 'admin', - clean_param($basename, PARAM_CLEANHTML))); - continue; - } - - // The picture file name (without extension) must match the - // userfield attribute. - $uservalue = substr($basename, 0, - strlen($basename) - - strlen($extension) - 1); - // userfield names are safe, so don't quote them. - if (!($user = $DB->get_record('user', array($userfields[$userfield], $uservalue)))) { - $userserrors++; - $a = new Object(); - $a->userfield = clean_param($userfields[$userfield], PARAM_CLEANHTML); - $a->uservalue = clean_param($uservalue, PARAM_CLEANHTML); - notify(get_string('uploadpicture_usernotfound', 'admin', $a)); - continue; - } - $haspicture = $DB->get_field('user', 'picture', array('id'=>$user->id)); - if ($haspicture && !$overwritepicture) { - notify(get_string('uploadpicture_userskipped', 'admin', $user->username)); - continue; - } - if (my_save_profile_image($user->id, $zipdir.'/'.$item)) { - $DB->set_field('user', 'picture', 1, array('id'=>$user->id)); - $usersupdated++; - notify(get_string('uploadpicture_userupdated', 'admin', $user->username)); - } else { - $userserrors++; - notify(get_string('uploadpicture_cannotsave', 'admin', $user->username)); - } - } - } - } - closedir($handle); + + $results = array ('errors' => 0,'updated' => 0); + + process_directory($zipdir, $userfields[$userfield], $overwritepicture, $results); + // Finally remove the temporary directory with all the user images and print some stats. remove_dir($zipdir); - notify(get_string('usersupdated', 'admin') . ": $usersupdated"); - notify(get_string('errors', 'admin') . ": $userserrors"); + notify(get_string('usersupdated', 'admin') . ": " . $results['updated']); + notify(get_string('errors', 'admin') . ": " . $results['errors']); echo '
'; } } @@ -156,6 +114,17 @@ // ----------- Internal functions ---------------- +/** + * Create a unique temporary directory with a given prefix name, + * inside a given directory, with given permissions. Return the + * full path to the newly created temp directory. + * + * @param string $dir where to create the temp directory. + * @param string $prefix prefix for the temp directory name (default '') + * @param string $mode permissions for the temp directory (default 700) + * + * @return string The full path to the temp directory. + */ function my_mktempdir($dir, $prefix='', $mode=0700) { if (substr($dir, -1) != '/') { $dir .= '/'; @@ -168,6 +137,117 @@ function my_mktempdir($dir, $prefix='', $mode=0700) { return $path; } +/** + * Recursively process a directory, picking regular files and feeding + * them to process_file(). + * + * @param string $dir the full path of the directory to process + * @param string $userfield the prefix_user table field to use to + * match picture files to users. + * @param bool $overwrite overwrite existing picture or not. + * @param array $results (by reference) accumulated statistics of + * users updated and errors. + * + * @return nothing + */ +function process_directory ($dir, $userfield, $overwrite, &$results) { + if(!($handle = opendir($dir))) { + notify(get_string('uploadpicture_cannotprocessdir','admin')); + return; + } + + while (false !== ($item = readdir($handle))) { + if ($item != '.' && $item != '..') { + if (is_dir($dir.'/'.$item)) { + process_directory($dir.'/'.$item, $userfield, $overwrite, $results); + } else if (is_file($dir.'/'.$item)) { + $result = process_file($dir.'/'.$item, $userfield, $overwrite); + switch ($result) { + case PIX_FILE_ERROR: + $results['errors']++; + break; + case PIX_FILE_UPDATED: + $results['updated']++; + break; + } + } + // Ignore anything else that is not a directory or a file (e.g., + // symbolic links, sockets, pipes, etc.) + } + } + closedir($handle); +} + +/** + * Given the full path of a file, try to find the user the file + * corresponds to and assign him/her this file as his/her picture. + * Make extensive checks to make sure we don't open any security holes + * and report back any success/error. + * + * @param string $file the full path of the file to process + * @param string $userfield the prefix_user table field to use to + * match picture files to users. + * @param bool $overwrite overwrite existing picture or not. + * + * @return integer either PIX_FILE_UPDATED, PIX_FILE_ERROR or + * PIX_FILE_SKIPPED + */ +function process_file ($file, $userfield, $overwrite) { + global $DB; + + // Add additional checks on the filenames, as they are user + // controlled and we don't want to open any security holes. + $path_parts = pathinfo(cleardoubleslashes($file)); + $basename = $path_parts['basename']; + $extension = $path_parts['extension']; + if ($basename != clean_param($basename, PARAM_CLEANFILE)) { + // The original picture file name has invalid characters + notify(get_string('uploadpicture_invalidfilename', 'admin', + clean_param($basename, PARAM_CLEANHTML))); + return PIX_FILE_ERROR; + } + + // The picture file name (without extension) must match the + // userfield attribute. + $uservalue = substr($basename, 0, + strlen($basename) - + strlen($extension) - 1); + + // userfield names are safe, so don't quote them. + if (!($user = $DB->get_record('user', array ($userfield => $uservalue)))) { + $a = new Object(); + $a->userfield = clean_param($userfield, PARAM_CLEANHTML); + $a->uservalue = clean_param($uservalue, PARAM_CLEANHTML); + notify(get_string('uploadpicture_usernotfound', 'admin', $a)); + return PIX_FILE_ERROR; + } + + $haspicture = $DB->get_field('user', 'picture', array('id'=>$user->id)); + if ($haspicture && !$overwrite) { + notify(get_string('uploadpicture_userskipped', 'admin', $user->username)); + return PIX_FILE_SKIPPED; + } + + if (my_save_profile_image($user->id, $file)) { + $DB->set_field('user', 'picture', 1, array('id'=>$user->id)); + notify(get_string('uploadpicture_userupdated', 'admin', $user->username)); + return PIX_FILE_UPDATED; + } else { + notify(get_string('uploadpicture_cannotsave', 'admin', $user->username)); + return PIX_FILE_ERROR; + } +} + +/** + * Try to save the given file (specified by its full path) as the + * picture for the user with the given id. + * + * @param integer $id the internal id of the user to assign the + * picture file to. + * @param string $originalfile the full path of the picture file. + * + * @return bool + */ function my_save_profile_image($id, $originalfile) { $destination = create_profile_image_destination($id, 'user'); if ($destination === false) {