525b635cfc
Introduces a new capability moodle/user:viewprofilepics, granted by default to 'user' and 'guest'. If the 'forcelogin' option or 'forceloginforprofileimage' option is enabled, then users must have this capability in order to see profile pictures (except their own).
382 lines
14 KiB
PHP
382 lines
14 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
namespace core\output;
|
|
|
|
use core\context\user as context_user;
|
|
use core\exception\coding_exception;
|
|
use moodle_page;
|
|
use moodle_url;
|
|
use stdClass;
|
|
|
|
/**
|
|
* Data structure representing a user picture.
|
|
*
|
|
* @copyright 2009 Nicolas Connault, 2010 Petr Skoda
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @since Modle 2.0
|
|
* @package core
|
|
* @category output
|
|
*/
|
|
class user_picture implements renderable {
|
|
/**
|
|
* @var stdClass A user object with at least fields all columns specified
|
|
* in $fields array constant set.
|
|
*/
|
|
public $user;
|
|
|
|
/**
|
|
* @var int The course id. Used when constructing the link to the user's
|
|
* profile, page course id used if not specified.
|
|
*/
|
|
public $courseid;
|
|
|
|
/**
|
|
* @var bool Add course profile link to image
|
|
*/
|
|
public $link = true;
|
|
|
|
/**
|
|
* @var int Size in pixels. Special values are (true/1 = 100px) and (false/0 = 35px) for backward compatibility.
|
|
* Recommended values (supporting user initials too): 16, 35, 64 and 100.
|
|
*/
|
|
public $size = 35;
|
|
|
|
/**
|
|
* @var bool Add non-blank alt-text to the image.
|
|
* Default true, set to false when image alt just duplicates text in screenreaders.
|
|
*/
|
|
public $alttext = true;
|
|
|
|
/**
|
|
* @var bool Whether or not to open the link in a popup window.
|
|
*/
|
|
public $popup = false;
|
|
|
|
/**
|
|
* @var string Image class attribute
|
|
*/
|
|
public $class = 'userpicture';
|
|
|
|
/**
|
|
* @var bool Whether to be visible to screen readers.
|
|
*/
|
|
public $visibletoscreenreaders = true;
|
|
|
|
/**
|
|
* @var bool Whether to include the fullname in the user picture link.
|
|
*/
|
|
public $includefullname = false;
|
|
|
|
/**
|
|
* @var mixed Include user authentication token. True indicates to generate a token for current user, and integer value
|
|
* indicates to generate a token for the user whose id is the value indicated.
|
|
*/
|
|
public $includetoken = false;
|
|
|
|
/**
|
|
* User picture constructor.
|
|
*
|
|
* @param stdClass $user user record with at least id, picture, imagealt, firstname and lastname set.
|
|
* It is recommended to add also contextid of the user for performance reasons.
|
|
*/
|
|
public function __construct(stdClass $user) {
|
|
global $DB;
|
|
|
|
if (empty($user->id)) {
|
|
throw new coding_exception('User id is required when printing user avatar image.');
|
|
}
|
|
|
|
// Only touch the DB if we are missing data and complain loudly...
|
|
$needrec = false;
|
|
foreach (\core_user\fields::get_picture_fields() as $field) {
|
|
if (!property_exists($user, $field)) {
|
|
$needrec = true;
|
|
debugging(
|
|
"Missing {$field} property in {$user} object, "
|
|
. "this is a performance problem that needs to be fixed by a developer. "
|
|
. 'Please use the \core_user\fields API to get the full list of required fields.',
|
|
DEBUG_DEVELOPER,
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($needrec) {
|
|
$this->user = $DB->get_record(
|
|
'user',
|
|
['id' => $user->id],
|
|
implode(',', \core_user\fields::get_picture_fields()),
|
|
MUST_EXIST
|
|
);
|
|
} else {
|
|
$this->user = clone($user);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a list of required user fields, useful when fetching required user info from db.
|
|
*
|
|
* In some cases we have to fetch the user data together with some other information,
|
|
* the idalias is useful there because the id would otherwise override the main
|
|
* id of the result record. Please note it has to be converted back to id before rendering.
|
|
*
|
|
* @param string $tableprefix name of database table prefix in query
|
|
* @param null|array $extrafields extra fields to be included in result
|
|
* Do not include TEXT columns because it would break SELECT DISTINCT in MSSQL and ORACLE.
|
|
* @param string $idalias alias of id field
|
|
* @param string $fieldprefix prefix to add to all columns in their aliases, does not apply to 'id'
|
|
* @return string
|
|
* @deprecated since Moodle 3.11 MDL-45242
|
|
* @see \core_user\fields
|
|
*/
|
|
public static function fields(
|
|
$tableprefix = '',
|
|
?array $extrafields = null,
|
|
$idalias = 'id',
|
|
$fieldprefix = '',
|
|
) {
|
|
debugging('user_picture::fields() is deprecated. Please use the \core_user\fields API instead.', DEBUG_DEVELOPER);
|
|
$userfields = \core_user\fields::for_userpic();
|
|
if ($extrafields) {
|
|
$userfields->including(...$extrafields);
|
|
}
|
|
$selects = $userfields->get_sql($tableprefix, false, $fieldprefix, $idalias, false)->selects;
|
|
if ($tableprefix === '') {
|
|
// If no table alias is specified, don't add {user}. in front of fields.
|
|
$selects = str_replace('{user}.', '', $selects);
|
|
}
|
|
// Maintain legacy behaviour where the field list was done with 'implode' and no spaces.
|
|
$selects = str_replace(', ', ',', $selects);
|
|
return $selects;
|
|
}
|
|
|
|
/**
|
|
* Extract the aliased user fields from a given record
|
|
*
|
|
* Given a record that was previously obtained using {@see self::fields()} with aliases,
|
|
* this method extracts user related unaliased fields.
|
|
*
|
|
* @param stdClass $record containing user picture fields
|
|
* @param null|array $extrafields extra fields included in the $record
|
|
* @param string $idalias alias of the id field
|
|
* @param string $fieldprefix prefix added to all columns in their aliases, does not apply to 'id'
|
|
* @return stdClass object with unaliased user fields
|
|
*/
|
|
public static function unalias(
|
|
stdClass $record,
|
|
?array $extrafields = null,
|
|
$idalias = 'id',
|
|
$fieldprefix = '',
|
|
) {
|
|
if (empty($idalias)) {
|
|
$idalias = 'id';
|
|
}
|
|
|
|
$return = new stdClass();
|
|
|
|
foreach (\core_user\fields::get_picture_fields() as $field) {
|
|
if ($field === 'id') {
|
|
if (property_exists($record, $idalias)) {
|
|
$return->id = $record->{$idalias};
|
|
}
|
|
} else {
|
|
if (property_exists($record, $fieldprefix . $field)) {
|
|
$return->{$field} = $record->{$fieldprefix . $field};
|
|
}
|
|
}
|
|
}
|
|
// Add extra fields if not already there.
|
|
if ($extrafields) {
|
|
foreach ($extrafields as $e) {
|
|
if ($e === 'id' || property_exists($return, $e)) {
|
|
continue;
|
|
}
|
|
$return->{$e} = $record->{$fieldprefix . $e};
|
|
}
|
|
}
|
|
|
|
return $return;
|
|
}
|
|
|
|
/**
|
|
* Checks if the current user is permitted to view user profile images.
|
|
*
|
|
* This is based on the forcelogin and forceloginforprofileimage config settings, and the
|
|
* moodle/user:viewprofilepictures capability.
|
|
*
|
|
* Logged-in users are allowed to view their own profile image regardless of capability.
|
|
*
|
|
* @param int $imageuserid User id of profile image being viewed
|
|
* @return bool True if current user can view profile images
|
|
*/
|
|
public static function allow_view(int $imageuserid): bool {
|
|
global $CFG, $USER;
|
|
|
|
// Not allowed to view profile images if forcelogin is enabled and not logged in (guest
|
|
// allowed), or forceloginforprofileimage is enabled and not logged in or guest.
|
|
if (
|
|
(!empty($CFG->forcelogin) && !isloggedin()) ||
|
|
(!empty($CFG->forceloginforprofileimage) && (!isloggedin() || isguestuser()))
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// Unless one of the forcelogin options is enabled, users can download profile pics
|
|
// without login, so the capability should not be checked as it might lead to a
|
|
// false sense of security (i.e. you log in as a test user, the HTML page doesn't
|
|
// show the picture, but they can still access it if they just log out).
|
|
// When the capability is checked, use system context for performance (if we check at
|
|
// user level, pages that show a lot of user pictures will individually load a lot of
|
|
// user contexts).
|
|
if (
|
|
(!empty($CFG->forcelogin) || !empty($CFG->forceloginforprofileimage)) &&
|
|
$USER->id != $imageuserid &&
|
|
!has_capability('moodle/user:viewprofilepictures', \context_system::instance())
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Works out the URL for the users picture.
|
|
*
|
|
* This method is recommended as it avoids costly redirects of user pictures
|
|
* if requests are made for non-existent files etc.
|
|
*
|
|
* @param moodle_page $page
|
|
* @param null|renderer_base $renderer
|
|
* @return moodle_url
|
|
*/
|
|
public function get_url(
|
|
moodle_page $page,
|
|
?renderer_base $renderer = null,
|
|
) {
|
|
global $CFG;
|
|
|
|
if (is_null($renderer)) {
|
|
$renderer = $page->get_renderer('core');
|
|
}
|
|
|
|
// Sort out the filename and size. Size is only required for the gravatar
|
|
// implementation presently.
|
|
if (empty($this->size)) {
|
|
$filename = 'f2';
|
|
$size = 35;
|
|
} else if ($this->size === true || $this->size == 1) {
|
|
$filename = 'f1';
|
|
$size = 100;
|
|
} else if ($this->size > 100) {
|
|
$filename = 'f3';
|
|
$size = (int)$this->size;
|
|
} else if ($this->size >= 50) {
|
|
$filename = 'f1';
|
|
$size = (int)$this->size;
|
|
} else {
|
|
$filename = 'f2';
|
|
$size = (int)$this->size;
|
|
}
|
|
|
|
$defaulturl = $renderer->image_url('u/' . $filename); // Default image.
|
|
|
|
if (!self::allow_view($this->user->id)) {
|
|
return $defaulturl;
|
|
}
|
|
|
|
// First try to detect deleted users - but do not read from database for performance reasons!
|
|
if (!empty($this->user->deleted) || !str_contains($this->user->email, '@')) {
|
|
// All deleted users should have email replaced by md5 hash,
|
|
// all active users are expected to have valid email.
|
|
return $defaulturl;
|
|
}
|
|
|
|
// Did the user upload a picture?
|
|
if ($this->user->picture > 0) {
|
|
if (!empty($this->user->contextid)) {
|
|
$contextid = $this->user->contextid;
|
|
} else {
|
|
$context = context_user::instance($this->user->id, IGNORE_MISSING);
|
|
if (!$context) {
|
|
// This must be an incorrectly deleted user, all other users have context.
|
|
return $defaulturl;
|
|
}
|
|
$contextid = $context->id;
|
|
}
|
|
|
|
$path = '/';
|
|
if (clean_param($page->theme->name, PARAM_THEME) == $page->theme->name) {
|
|
// We append the theme name to the file path if we have it so that
|
|
// in the circumstance that the profile picture is not available
|
|
// when the user actually requests it they still get the profile
|
|
// picture for the correct theme.
|
|
$path .= $page->theme->name . '/';
|
|
}
|
|
// Set the image URL to the URL for the uploaded file and return.
|
|
$url = moodle_url::make_pluginfile_url(
|
|
$contextid,
|
|
'user',
|
|
'icon',
|
|
null,
|
|
$path,
|
|
$filename,
|
|
false,
|
|
$this->includetoken
|
|
);
|
|
$url->param('rev', $this->user->picture);
|
|
return $url;
|
|
}
|
|
|
|
if ($this->user->picture == 0 && !empty($CFG->enablegravatar)) {
|
|
// Normalise the size variable to acceptable bounds.
|
|
if ($size < 1 || $size > 512) {
|
|
$size = 35;
|
|
}
|
|
// Hash the users email address.
|
|
$md5 = md5(strtolower(trim($this->user->email)));
|
|
// Build a gravatar URL with what we know.
|
|
|
|
// Find the best default image URL we can (MDL-35669).
|
|
if (empty($CFG->gravatardefaulturl)) {
|
|
$absoluteimagepath = $page->theme->resolve_image_location('u/' . $filename, 'core');
|
|
if (strpos($absoluteimagepath, $CFG->dirroot) === 0) {
|
|
$gravatardefault = $CFG->wwwroot . substr($absoluteimagepath, strlen($CFG->dirroot));
|
|
} else {
|
|
$gravatardefault = $CFG->wwwroot . '/pix/u/' . $filename . '.png';
|
|
}
|
|
} else {
|
|
$gravatardefault = $CFG->gravatardefaulturl;
|
|
}
|
|
|
|
// If the currently requested page is https then we'll return an
|
|
// https gravatar page.
|
|
if (is_https()) {
|
|
return new moodle_url("https://secure.gravatar.com/avatar/{$md5}", ['s' => $size, 'd' => $gravatardefault]);
|
|
} else {
|
|
return new moodle_url("http://www.gravatar.com/avatar/{$md5}", ['s' => $size, 'd' => $gravatardefault]);
|
|
}
|
|
}
|
|
|
|
return $defaulturl;
|
|
}
|
|
}
|
|
|
|
// Alias this class to the old name.
|
|
// This file will be autoloaded by the legacyclasses autoload system.
|
|
// In future all uses of this class will be corrected and the legacy references will be removed.
|
|
class_alias(user_picture::class, \user_picture::class);
|