MDL-14589 user icons now stored in file storage pool
authorPetr Skoda <skodak@moodle.org>
Sun, 11 Jul 2010 11:43:15 +0000 (11:43 +0000)
committerPetr Skoda <skodak@moodle.org>
Sun, 11 Jul 2010 11:43:15 +0000 (11:43 +0000)
15 files changed:
admin/uploadpicture.php
enrol/imsenterprise/enrol.php
file.php
lib/db/upgrade.php
lib/db/upgradelib.php
lib/formslib.php
lib/gdlib.php
lib/moodlelib.php
lib/outputrenderers.php
pluginfile.php
user/editlib.php
user/pix.php [deleted file]
userpix/index.php
userpix/upgrade.php [deleted file]
version.php

index e8da6dc..e078cd4 100644 (file)
@@ -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);
 }
 
 
index 1575f28..e10bb24 100644 (file)
@@ -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
                                    }
                                  }
index 661453e..5f9a61c 100644 (file)
--- a/file.php
+++ b/file.php
 
 /**
  * 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)
index 04350ad..d4df931 100644 (file)
@@ -4723,12 +4723,18 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL");
         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;
 }
index e3a018f..410cd6f 100644 (file)
@@ -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
  */
index e0e29a4..590b46c 100644 (file)
@@ -591,7 +591,6 @@ abstract class moodleform {
         if (!$this->is_submitted() or !$this->is_validated()) {
             return false;
         }
-
         if (file_exists($pathname)) {
             if ($override) {
                 if (!@unlink($pathname)) {
@@ -626,6 +625,31 @@ abstract class moodleform {
         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
index 1982a94..7b110af 100644 (file)
@@ -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) {
index 0e74d26..834eb1a 100644 (file)
@@ -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');
index 0c67eaa..f49cec9 100644 (file)
@@ -1728,9 +1728,13 @@ class core_renderer extends renderer_base {
 
         $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);
index 98d4048..d8df6f0 100644 (file)
@@ -293,7 +293,22 @@ if ($component === 'blog') {
 
 // ========================================================================================================================
 } 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()) {
index 409c9bc..315aa48 100644 (file)
@@ -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 (file)
index ecb8408..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-<?PHP
-      // This function fetches user pictures from the data directory
-      // Syntax:   pix.php/userid/f1.jpg or pix.php/userid/f2.jpg
-      //     OR:   ?file=userid/f1.jpg or ?file=userid/f2.jpg
-
-// disable moodle specific debug messages and any errors in output
-define('NO_DEBUG_DISPLAY', true);
-
-    require_once('../config.php');
-    require_once($CFG->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'));
index 73d98e8..9c234a6 100644 (file)
@@ -1,40 +1,36 @@
 <?php
-      // This simple script displays all the users with pictures on one page.
-      // By default it is not linked anywhere on the site.  If you want to
-      // make it available you should link it in yourself from somewhere.
-      // Remember also to comment or delete the lines restricting access
-      // to administrators only (see below)
+  // This simple script displays all the users with pictures on one page.
+  // By default it is not linked anywhere on the site.  If you want to
+  // make it available you should link it in yourself from somewhere.
+  // Remember also to comment or delete the lines restricting access
+  // to administrators only (see below)
 
 
-    include('../config.php');
+require('../config.php');
 
-    $PAGE->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 "<a href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=1\" ".
-             "title=\"$fullname\">";
-        require_once($CFG->libdir.'/filelib.php');
-        $userpictureurl = get_file_url($user->id.'/f1.jpg', null, 'user');
-        echo '<img src="'. $userpictureurl .'"'.
-            ' style="border:0px; width:100px; height:100px" alt="'.$fullname.'" />';
-        echo "</a> \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 "<a href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=1\" ".
+         "title=\"$fullname\">";
+    require_once($CFG->libdir.'/filelib.php');
+    echo $OUTPUT->user_picture($user);
+    echo "</a> \n";
+}
+$rs->close();
+
+echo $OUTPUT->footer();
diff --git a/userpix/upgrade.php b/userpix/upgrade.php
deleted file mode 100644 (file)
index 8017dcc..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-      // This script updates all users picturesi to remove black border.
-
-
-    include('../config.php');
-    include('../lib/gdlib.php');
-
-    $PAGE->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 "<a href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=1\"".
-             "title=\"$fullname\">";
-        require_once($CFG->libdir.'/filelib.php');
-        $userpictureurl = get_file_url($user->id.'/f1.jpg', null, 'user');
-        echo '<img src="'. $userpictureurl .'"'.
-            ' style="border:0px; width:100px; height:100px" alt="'.$fullname.'" />';
-        echo "</a> \n";
-    }
-
-    echo $OUTPUT->footer();
index 82e930d..17aad27 100644 (file)
@@ -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