diff --git a/admin/uploadpicture.php b/admin/uploadpicture.php index e8da6dcb698c4..e078cd48d0d21 100644 --- a/admin/uploadpicture.php +++ b/admin/uploadpicture.php @@ -241,12 +241,8 @@ function process_file ($file, $userfield, $overwrite) { * @return bool */ function my_save_profile_image($id, $originalfile) { - $destination = create_profile_image_destination($id, 'user'); - if ($destination === false) { - return false; - } - - return process_profile_image($originalfile, $destination); + $context = get_context_instance(CONTEXT_USER, $id); + return process_new_icon($context, 'user', 'icon', 0, $originalfile); } diff --git a/enrol/imsenterprise/enrol.php b/enrol/imsenterprise/enrol.php index 1575f28905ffc..e10bb2415aefc 100644 --- a/enrol/imsenterprise/enrol.php +++ b/enrol/imsenterprise/enrol.php @@ -661,7 +661,7 @@ function process_person_tag($tagcontents){ $person->picture = 1; //Llibreria creada per nosaltres mateixos. require_once($CFG->dirroot.'/lib/gdlib.php'); - if ($usernew->picture = save_profile_image($id, $person->urlphoto,'user')) { + if ($usernew->picture = save_profile_image($id, $person->urlphoto,'user')) { TODO: use process_new_icon() instead $DB->set_field('user', 'picture', $usernew->picture, array('id'=>$id)); /// Note picture in DB } } diff --git a/file.php b/file.php index 661453e646b7f..5f9a61c89e5d2 100644 --- a/file.php +++ b/file.php @@ -17,10 +17,8 @@ /** * This script fetches legacy course files in dataroot directory, it is enabled - * only if course->legacyfiles == 2. + * only if course->legacyfiles == 2. DO not link to this file in new code. * - * You should use the get_file_url() function, available in lib/filelib.php, to link to file.php. - * This ensures proper formatting and offers useful options. * Syntax: file.php/courseid/dir/dir/dir/filename.ext * file.php/courseid/dir/dir/dir/filename.ext?forcedownload=1 (download instead of inline) * file.php/courseid/dir (returns index.html from dir) diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 04350addefab0..d4df931d8b3c9 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -4723,12 +4723,18 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2010071000); } - if ($oldversion < 2010071001) { + if ($oldversion < 2010071001) { // purge obsolete stats settings unset_config('statscatdepth'); upgrade_main_savepoint(true, 2010071001); } + if ($oldversion < 2010071100) { + // move user icons to file storage pool + upgrade_migrate_user_icons(); + upgrade_main_savepoint(true, 2010071100); + } + return true; } diff --git a/lib/db/upgradelib.php b/lib/db/upgradelib.php index e3a018f192c83..410cd6f982172 100644 --- a/lib/db/upgradelib.php +++ b/lib/db/upgradelib.php @@ -79,6 +79,58 @@ function upgrade_migrate_files_courses() { return true; } +/** + * Internal function - do not use directly + */ +function upgrade_migrate_user_icons() { + global $CFG, $OUTPUT, $DB; + + $fs = get_file_storage(); + + $icon = array('component'=>'user', 'filearea'=>'icon', 'itemid'=>0, 'filepath'=>'/'); + + $count = $DB->count_records('user', array('picture'=>1, 'deleted'=>0)); + $pbar = new progress_bar('migratecoursefiles', 500, true); + + $rs = $DB->get_recordset('user', array('picture'=>1, 'deleted'=>0), 'id ASC', 'id, picture'); + $i = 0; + foreach ($rs as $user) { + $i++; + upgrade_set_timeout(60); /// Give upgrade at least 60 more seconds + $pbar->update($i, $count, "Migrated course files - course $i/$count."); + + $context = get_context_instance(CONTEXT_USER, $user->id); + + if ($fs->file_exists($context->id, 'user', 'icon', 0, '/', 'f1.jpg')) { + // already converted! + continue; + } + + $level1 = floor($user->id / 1000) * 1000; + $userdir = "$CFG->dataroot/user/$level1/$user->id"; + if (!file_exists("$userdir/f1.jpg") or !file_exists("$userdir/f2.jpg")) { + $userdir = "$CFG->dataroot/users/$user->id"; + if (!file_exists("$userdir/f1.jpg") or !file_exists("$userdir/f2.jpg")) { + // no image found, sorry + $user->picture = 0; + $DB->update_record('user', $user); + continue; + } + } + + $icon['contextid'] = $context->id; + $icon['filename'] = 'f1.jpg'; + $fs->create_file_from_pathname($icon, "$userdir/f1.jpg"); + $icon['filename'] = 'f2.jpg'; + $fs->create_file_from_pathname($icon, "$userdir/f2.jpg"); + } + $rs->close(); + + // purge all old user image dirs +// remove_dir("$CFG->dataroot/user"); +// remove_dir("$CFG->dataroot/users"); +} + /** * Internal function - do not use directly */ diff --git a/lib/formslib.php b/lib/formslib.php index e0e29a4f3e24e..590b46c436e43 100644 --- a/lib/formslib.php +++ b/lib/formslib.php @@ -591,7 +591,6 @@ function save_file($elname, $pathname, $override=false) { if (!$this->is_submitted() or !$this->is_validated()) { return false; } - if (file_exists($pathname)) { if ($override) { if (!@unlink($pathname)) { @@ -626,6 +625,31 @@ function save_file($elname, $pathname, $override=false) { return false; } + /** + * Returns a temporary file, do not forget to delete after not needed any more. + * + * @param string $elname + * @return string or false + */ + function save_temp_file($elname) { + if (!$this->get_new_filename($elname)) { + return false; + } + if (!$dir = make_upload_directory('temp/forms')) { + return false; + } + if (!$tempfile = tempnam($dir, 'tempup_')) { + return false; + } + if (!$this->save_file($elname, $tempfile, true)) { + // something went wrong + @unlink($tempfile); + return false; + } + + return $tempfile; + } + /** * Get draft files of a form element * This is a protected method which will be used only inside moodleforms diff --git a/lib/gdlib.php b/lib/gdlib.php index 1982a94ebccf2..7b110af9a2f87 100644 --- a/lib/gdlib.php +++ b/lib/gdlib.php @@ -95,6 +95,8 @@ function ImageCopyBicubic ($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, $ function delete_profile_image($id, $dir='users') { global $CFG; +//TODO: deprecate + require_once $CFG->libdir.'/filelib.php'; $location = $CFG->dataroot .'/'. $dir .'/'. $id; @@ -117,6 +119,8 @@ function delete_profile_image($id, $dir='users') { function create_profile_image_destination($id, $dir='user') { global $CFG; +//TODO: deprecate + umask(0000); if (!file_exists($CFG->dataroot .'/'. $dir)) { @@ -139,6 +143,121 @@ function create_profile_image_destination($id, $dir='user') { return $destination; } +/** + * Stores optimised icon images in icon file area + * + * @param $context + * @param component + * @param $itemid + * @param $originalfile + * @return success + */ +function process_new_icon($context, $component, $filearea, $itemid, $originalfile) { + global $CFG; + + if (empty($CFG->gdversion)) { + return false; + } + + if (!is_file($originalfile)) { + return false; + } + + $imageinfo = GetImageSize($originalfile); + + if (empty($imageinfo)) { + return false; + } + + $image->width = $imageinfo[0]; + $image->height = $imageinfo[1]; + $image->type = $imageinfo[2]; + + switch ($image->type) { + case IMAGETYPE_GIF: + if (function_exists('ImageCreateFromGIF')) { + $im = ImageCreateFromGIF($originalfile); + } else { + debugging('GIF not supported on this server'); + return false; + } + break; + case IMAGETYPE_JPEG: + if (function_exists('ImageCreateFromJPEG')) { + $im = ImageCreateFromJPEG($originalfile); + } else { + debugging('JPEG not supported on this server'); + return false; + } + break; + case IMAGETYPE_PNG: + if (function_exists('ImageCreateFromPNG')) { + $im = ImageCreateFromPNG($originalfile); + } else { + debugging('PNG not supported on this server'); + return false; + } + break; + default: + return false; + } + + + if (function_exists('ImageCreateTrueColor') and $CFG->gdversion >= 2) { + $im1 = ImageCreateTrueColor(100,100); + $im2 = ImageCreateTrueColor(35,35); + } else { + $im1 = ImageCreate(100,100); + $im2 = ImageCreate(35,35); + } + + $cx = $image->width / 2; + $cy = $image->height / 2; + + if ($image->width < $image->height) { + $half = floor($image->width / 2.0); + } else { + $half = floor($image->height / 2.0); + } + + ImageCopyBicubic($im1, $im, 0, 0, $cx-$half, $cy-$half, 100, 100, $half*2, $half*2); + ImageCopyBicubic($im2, $im, 0, 0, $cx-$half, $cy-$half, 35, 35, $half*2, $half*2); + + if (!function_exists('ImageJpeg')) { + debugging('Jpeg not supported on this server, please fix server configuration'); + return false; + } + + $fs = get_file_storage(); + + $icon = array('contextid'=>$context->id, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid, 'filepath'=>'/'); + + ob_start(); + if (!ImageJpeg($im1, NULL, 90)) { + // keep old icons + ob_end_clean(); + return false; + } + $data = ob_get_clean(); + ImageDestroy($im1); + $icon['filename'] = 'f1.jpg'; + $fs->delete_area_files($context->id, $component, $filearea, $itemid); + $fs->create_file_from_string($icon, $data); + + ob_start(); + if (!ImageJpeg($im2, NULL, 95)) { + ob_end_clean(); + $fs->delete_area_files($context->id, $component, $filearea, $itemid); + return false; + } + $data = ob_get_clean(); + ImageDestroy($im2); + $icon['filename'] = 'f2.jpg'; + $fs->create_file_from_string($icon, $data); + + return true; +} + /** * Given an upload manager with the right settings, this function performs a virus scan, and then scales and crops * it and saves it in the right place to be a "user" or "group" image. @@ -150,6 +269,8 @@ function create_profile_image_destination($id, $dir='user') { */ function save_profile_image($id, $userform, $dir='user') { +//TODO: deprecate + $destination = create_profile_image_destination($id, $dir); if ($destination === false) { return false; @@ -177,6 +298,8 @@ function save_profile_image($id, $userform, $dir='user') { function process_profile_image($originalfile, $destination) { global $CFG, $OUTPUT; +//TODO: deprecate + if(!(is_file($originalfile) && is_dir($destination))) { return false; } @@ -280,6 +403,8 @@ function process_profile_image($originalfile, $destination) { function upgrade_profile_image($id, $dir='users') { global $CFG, $OUTPUT; +//TODO: deprecate + $im = ImageCreateFromJPEG($CFG->dataroot .'/'. $dir .'/'. $id .'/f1.jpg'); if (function_exists('ImageCreateTrueColor') and $CFG->gdversion >= 2) { diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 0e74d26a4b4b4..834eb1adc3938 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -3445,7 +3445,7 @@ function delete_user($user) { // last course access not necessary either $DB->delete_records('user_lastaccess', array('userid'=>$user->id)); - // final accesslib cleanup - removes all role assignments in user context and context itself + // final accesslib cleanup - removes all role assignments in user context and context itself, files, etc. delete_context(CONTEXT_USER, $user->id); require_once($CFG->dirroot.'/tag/lib.php'); diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index 0c67eaafa59a7..f49cec98bab2b 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -1728,9 +1728,13 @@ protected function render_user_picture(user_picture $userpicture) { $class = $userpicture->class; - if (!empty($user->picture)) { - require_once($CFG->libdir.'/filelib.php'); - $src = new moodle_url(get_file_url($user->id.'/'.$file.'.jpg', null, 'user')); + if ($user->picture == 1) { + $usercontext = get_context_instance(CONTEXT_USER, $user->id); + $src = moodle_url::make_pluginfile_url($usercontext->id, 'user', 'icon', NULL, '/', $file); + + } else if ($user->picture == 2) { + //TODO: gravatar user icon support + } else { // Print default user pictures (use theme version if available) $class .= ' defaultuserpic'; $src = $this->pix_url('u/' . $file); diff --git a/pluginfile.php b/pluginfile.php index 98d4048bb0984..d8df6f011f28d 100644 --- a/pluginfile.php +++ b/pluginfile.php @@ -293,7 +293,22 @@ // ======================================================================================================================== } else if ($component === 'user') { - if ($filearea === 'private' and $context->contextlevel == CONTEXT_USER) { + if ($filearea === 'icon' and $context->contextlevel == CONTEXT_USER) { + if (!empty($CFG->forcelogin) and !isloggedin()) { + // protect images if login required and not logged in; + // do not use require_login() because it is expensive and not suitable here anyway + redirect($OUTPUT->pix_url('u/f1')); + } + $filename = array_pop($args); + if ($filename !== 'f1' and $filename !== 'f2') { + redirect($OUTPUT->pix_url('u/f1')); + } + if (!$file = $fs->get_file($context->id, 'user', 'icon', 0, '/', $filename.'/.jpg')) { + redirect($OUTPUT->pix_url('u/f1')); + } + send_stored_file($file, 60*60*24); // enable long caching, there are many images on each page + + } else if ($filearea === 'private' and $context->contextlevel == CONTEXT_USER) { require_login(); if (isguestuser()) { diff --git a/user/editlib.php b/user/editlib.php index 409c9bcfbcb2f..315aa480ee172 100644 --- a/user/editlib.php +++ b/user/editlib.php @@ -36,14 +36,18 @@ function useredit_update_user_preference($usernew) { function useredit_update_picture(&$usernew, $userform) { global $CFG, $DB; + $fs = get_file_storage(); + $context = get_context_instance(CONTEXT_USER, $usernew->id, MUST_EXIST); + if (isset($usernew->deletepicture) and $usernew->deletepicture) { - $location = make_user_directory($usernew->id, true); - @remove_dir($location); + $fs->delete_area_files($context->id, 'user', 'icon'); // drop all areas $DB->set_field('user', 'picture', 0, array('id'=>$usernew->id)); - } else if ($userform->get_new_filename('imagefile')) { - $usernew->picture = (int)save_profile_image($usernew->id, $userform, 'user', 'imagefile'); - $DB->set_field('user', 'picture', $usernew->picture, array('id'=>$usernew->id)); + } else if ($iconfile = $userform->save_temp_file('imagefile')) { + if (process_new_icon($context, 'user', 'icon', 0, $iconfile)) { + $DB->set_field('user', 'picture', 1, array('id'=>$usernew->id)); + } + @unlink($iconfile); } } @@ -239,7 +243,7 @@ function useredit_shared_definition(&$mform, $editoroptions = null) { $mform->addElement('static', 'currentpicture', get_string('currentpicture')); $mform->addElement('checkbox', 'deletepicture', get_string('delete')); - $mform->setDefault('deletepicture',false); + $mform->setDefault('deletepicture', 0); $mform->addElement('filepicker', 'imagefile', get_string('newpicture')); $mform->addHelpButton('imagefile', 'newpicture'); diff --git a/user/pix.php b/user/pix.php deleted file mode 100644 index ecb8408929c11..0000000000000 --- a/user/pix.php +++ /dev/null @@ -1,35 +0,0 @@ -libdir.'/filelib.php'); - - if (!empty($CFG->forcelogin) and !isloggedin()) { - // protect images if login required and not logged in; - // do not use require_login() because it is expensive and not suitable here anyway - redirect($OUTPUT->pix_url('u/f1')); - } - - $relativepath = get_file_argument(); - - $args = explode('/', trim($relativepath, '/')); - - if (count($args) == 2) { - $userid = (integer)$args[0]; - // do not serve images of deleted users - if ($user = $DB->get_record('user', array('id'=>$userid, 'deleted'=>0, 'picture'=>1))) { - $image = $args[1]; - $pathname = make_user_directory($userid, true) . "/$image"; - if (file_exists($pathname) and !is_dir($pathname)) { - send_file($pathname, $image); - } - } - } - - // picture was deleted - use default instead - redirect($OUTPUT->pix_url('u/f1')); diff --git a/userpix/index.php b/userpix/index.php index 73d98e8aeb3d1..9c234a6c09474 100644 --- a/userpix/index.php +++ b/userpix/index.php @@ -1,40 +1,36 @@ set_url('/userpix/index.php'); +$PAGE->set_url('/userpix/index.php'); - require_login(); +require_login(); /// Remove the following three lines if you want everyone to access it - require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM)); - - if (!$users = $DB->get_records("user", array("picture"=>"1"), "lastaccess DESC", "id,firstname,lastname")) { - print_error("nousers"); - } - - $title = get_string("users"); - - $PAGE->navbar->add($title); - $PAGE->set_title($title); - $PAGE->set_heading($title); - echo $OUTPUT->header(); - - foreach ($users as $user) { - $fullname = fullname($user); - echo "wwwroot/user/view.php?id=$user->id&course=1\" ". - "title=\"$fullname\">"; - require_once($CFG->libdir.'/filelib.php'); - $userpictureurl = get_file_url($user->id.'/f1.jpg', null, 'user'); - echo ''.$fullname.''; - echo " \n"; - } - - echo $OUTPUT->footer(); +require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM)); + +$title = get_string("users"); + +$PAGE->navbar->add($title); +$PAGE->set_title($title); +$PAGE->set_heading($title); +echo $OUTPUT->header(); + +$rs = $DB->get_recordset_select("user", "deleted = 0 AND picture > 0", array(), "lastaccess DESC", user_picture::fields()); +foreach ($rs as $user) { + $fullname = fullname($user); + echo "wwwroot/user/view.php?id=$user->id&course=1\" ". + "title=\"$fullname\">"; + require_once($CFG->libdir.'/filelib.php'); + echo $OUTPUT->user_picture($user); + echo " \n"; +} +$rs->close(); + +echo $OUTPUT->footer(); diff --git a/userpix/upgrade.php b/userpix/upgrade.php deleted file mode 100644 index 8017dcccb2fe0..0000000000000 --- a/userpix/upgrade.php +++ /dev/null @@ -1,37 +0,0 @@ -set_url('/userpix/upgrade.php'); - - require_login(); - - require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM)); - - if (!$users = $DB->get_records("user", array("picture"=>"1"), "lastaccess DESC", "id,firstname,lastname")) { - print_error('nousers'); - } - - $title = get_string("users"); - - $PAGE->navbar->add($title); - $PAGE->set_title($title); - $PAGE->set_heading($title); - echo $OUTPUT->header(); - - foreach ($users as $user) { - upgrade_profile_image($user->id); - $fullname = fullname($user); - echo "wwwroot/user/view.php?id=$user->id&course=1\"". - "title=\"$fullname\">"; - require_once($CFG->libdir.'/filelib.php'); - $userpictureurl = get_file_url($user->id.'/f1.jpg', null, 'user'); - echo ''.$fullname.''; - echo " \n"; - } - - echo $OUTPUT->footer(); diff --git a/version.php b/version.php index 82e930d5342c2..17aad27bfbeb9 100644 --- a/version.php +++ b/version.php @@ -6,7 +6,7 @@ // This is compared against the values stored in the database to determine // whether upgrades should be performed (see lib/db/*.php) - $version = 2010071001; // YYYYMMDD = date of the last version bump + $version = 2010071100; // YYYYMMDD = date of the last version bump // XX = daily increments $release = '2.0 Preview 4+ (Build: 20100711)'; // Human-friendly version name