Commit 29324842 authored by Aaron Wells's avatar Aaron Wells

Bug 1620879: Adding script to help webservices download user icons

behatnotneeded: Covered by existing tests

Change-Id: Iec9939b02b7b38fbf0f3217d6be45d538e3a9760
parent 08f5a3af
......@@ -2423,6 +2423,143 @@ class ArtefactTypeProfileIcon extends ArtefactTypeImage {
public static function get_title_progressbar() {
return get_string('profileicon','artefact.file');
}
/**
* Render's the icon thumbnail for the specified user
*/
public static function download_thumbnail_for_user($userid) {
$size = get_imagesize_parameters();
$earlyexpiry = param_boolean('earlyexpiry', false);
// Convert ID of user to the ID of a profileicon
$data = get_record_sql('
SELECT u.profileicon, u.email, f.filetype
FROM {usr} u LEFT JOIN {artefact_file_files} f ON u.profileicon = f.artefact
WHERE u.id = ?',
array($userid)
);
// User has a profile icon file selected. Use it.
if (!empty($data->profileicon)) {
$id = $data->profileicon;
$mimetype = $data->filetype;
// Try to print the specified icon
static::download_thumbnail($id);
exit();
}
// No profile icon file selected. Go through fallback icons.
// Look for an appropriate image on gravatar.com
$useremail = $data->email;
if ($useremail and $gravatarurl = remote_avatar_url($useremail, $size)) {
redirect($gravatarurl);
}
// We couldn't find an image for this user. Attempt to use the 'no user
// photo' image for the current theme
if (!get_config('nocache')) {
// We can cache such images
$maxage = 604800; // 1 week
if ($earlyexpiry) {
$maxage = 600; // 10 minutes
}
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $maxage) .' GMT');
header('Cache-Control: max-age=' . $maxage);
header('Pragma: public');
}
if ($path = get_dataroot_image_path('artefact/file/profileicons/no_userphoto/' . $THEME->basename, 0, $size)) {
header('Content-type: ' . 'image/png');
readfile($path);
perf_to_log();
exit;
}
// If we couldn't find the no user photo picture, we put it into
// dataroot if we can
$nouserphotopic = $THEME->get_path('images/no_userphoto.png');
if ($nouserphotopic) {
// Move the file into the correct place.
$directory = get_config('dataroot') . 'artefact/file/profileicons/no_userphoto/' . $THEME->basename . '/originals/0/';
check_dir_exists($directory);
copy($nouserphotopic, $directory . '0');
// Now we can try and get the image in the correct size
if ($path = get_dataroot_image_path('artefact/file/profileicons/no_userphoto/' . $THEME->basename, 0, $size)) {
header('Content-type: ' . 'image/png');
readfile($path);
perf_to_log();
exit;
}
}
// Emergency fallback
header('Content-type: ' . 'image/png');
readfile($THEME->get_path('images/no_userphoto.png'));
perf_to_log();
exit;
}
/**
* Render's the icon's thumbnail and exits
*/
public static function download_thumbnail($artefactid) {
global $USER;
$id = $artefactid;
$size = get_imagesize_parameters();
$earlyexpiry = param_boolean('earlyexpiry', false);
$mimetype = get_field('artefact_file_files', 'filetype', 'artefact', $id);
if ($id && $fileid = get_field('artefact_file_files', 'fileid', 'artefact', $id)) {
// Check that the profile icon is allowed to be seen
// Any profileiconbyid file that has been set as a user's default icon is ok
// But icons that are not should only be seen by their owner
// Unless that owner places them in a view that the user can see
if (!get_field('usr', 'id', 'profileicon', $id)) {
$viewid = param_integer('view', 0);
$ok = false;
if ($viewid) {
$ok = artefact_in_view($id, $viewid);
}
if (!$ok) {
if (
($USER && !$USER->is_logged_in()) ||
($USER->is_logged_in() && $USER->get('id') != get_field('artefact', 'owner', 'id', $id))
) {
exit;
}
}
}
if ($path = get_dataroot_image_path('artefact/file/profileicons', $fileid, $size)) {
if ($mimetype) {
header('Content-type: ' . $mimetype);
if (!get_config('nocache')) {
// We can't cache 'profileicon' for as long, because the
// user can change it at any time. But we can cache
// 'profileiconbyid' for quite a while, because it will
// never change
if ($type == 'profileiconbyid' and !$earlyexpiry) {
$maxage = 604800; // 1 week
}
else {
$maxage = 600; // 10 minutes
}
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $maxage) .' GMT');
header('Cache-Control: max-age=' . $maxage);
header('Pragma: public');
}
readfile($path);
perf_to_log();
exit;
}
}
}
}
}
class ArtefactTypeArchive extends ArtefactTypeFile {
......
<?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/>.
/**
* A script to serve files from web service client
*
* @package core_webservice
* @copyright 2011 Dongsheng Cai <dongsheng@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* AJAX_SCRIPT - exception will be converted into JSON
*/
define('INTERNAL', 1);
define('JSON', 1);
define('NOSESSKEY', 1);
define('PUBLIC', 1);
require_once(dirname(dirname(dirname(__FILE__))) . '/init.php');
require_once($CFG->docroot . '/webservice/lib.php');
// Allow CORS requests.
header('Access-Control-Allow-Origin: *');
/**
* A "deconstructed" webserver class to handle only the parts of the
* webservice validation that I need.
*
* TODO: Generalize this into a more generic form, so it can be
* used for other webservices?
*/
class mobileapi_profileicon_webservice_server extends webservice_base_server {
public function __construct($authmethod = null) {
//authenticate the user
parent::__construct(WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN);
$this->token = param_alphanum('wstoken');
$this->functionname = param_alphanumext('wsfunction');
}
public function can_user_download_via_webservice() {
// Check that the token is valid.
// (This will also determine which service the token is for.)
$this->authenticate_user(EXTERNAL_TOKEN_USER);
// Make sure they're specifically accessing the maharamobile service.
$maharamobileserviceid = get_field('external_services', 'id', 'shortname', 'maharamobile', 'component', 'module/mobileapi');
if (!($maharamobileserviceid && $this->restricted_serviceid === $maharamobileserviceid )) {
throw new WebserviceAccessException(get_string('servicenotallowed', 'module.mobileapi'));
}
$this->load_function_info();
// If it hasn't crashed by now, they're good!
return true;
}
public function run(){}
protected function parse_request(){}
protected function send_response(){}
protected function send_error($ex = null){
echo json_encode(array('exception' => get_class($ex), 'errorcode' => (isset($ex->errorcode) ? $ex->errorcode : $ex->getCode()), 'message' => $ex->getMessage(), 'debuginfo' => (isset($ex->debuginfo) ? $ex->debuginfo : ''))) . "\n";
}
}
$server = new mobileapi_profileicon_webservice_server();
$server->can_user_download_via_webservice();
switch(param_alphanumext('wsfunction')) {
case 'module_mobileapi_get_user_profileicon':
require_once($CFG->docroot . 'lib/file.php');
safe_require('artefact', 'file');
// The underlying functions expect maxsize, not maxdimension
if (array_key_exists('maxdimension', $_REQUEST)) {
$_REQUEST['maxsize'] = $_REQUEST['maxdimension'];
}
ArtefactTypeProfileIcon::download_thumbnail_for_user($USER->get('id'));
exit();
break;
default:
throw new WebserviceInvalidResponseException('This function has nothing to download.');
}
......@@ -24,6 +24,7 @@ $string['noticenotenabled'] = 'The Mahara mobile apps API is <b>not</b> currentl
$string['notreadylabel'] = 'Not ready';
$string['readylabel'] = 'Ready';
$string['restprotocolenabled'] = 'REST protocol enabled';
$string['servicenotallowed'] = 'The credentials you have provided are not authorized to access this functionality.';
$string['webserviceproviderenabled'] = 'Incoming web service requests allowed';
// User management of webservice access tokens
......
<?php
/**
*
* @package mahara
* @subpackage module-mobileapi
* @author Catalyst IT Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
* @copyright For copyright information on Mahara, please see the README file distributed with this software.
*/
if (!defined('INTERNAL')) {
die();
}
require_once(get_config('docroot') . 'webservice/lib.php');
/**
* Functions needed by the Mahara Mobile app. The functions in this class fetch similar data
* to the legacy api/mobile/sync.php script.
*/
class module_mobileapi_sync extends external_api {
public static function get_user_profileicon_parameters() {
return new external_function_parameters(
array(
'maxdimension' => new external_value(PARAM_INT, "Scale icon so that height or width is this size (in px)", VALUE_DEFAULT, 0)
)
);
}
public static function get_user_profileicon_returns() {
return new external_single_structure(
array(
'name' => new external_value(PARAM_RAW, "Original filename of the profile icon"),
'desc' => new external_value(PARAM_RAW, "Descripion of the icon (usually same as filename)"),
'mimetype' => new external_value(PARAM_RAW, "Mimetype of the file"),
'bytes' => new external_value(PARAM_INT, "Size of the file, in bytes"),
),
"Metadata about the user's current profile icon"
);
}
public static function get_user_profileicon($maxdimension = 0) {
global $USER;
// Convert ID of user to the ID of a profileicon
$data = get_record_sql('
SELECT f.size, a.title, a.note, f.filetype
FROM
{usr} u
JOIN {artefact_file_files} f
ON u.profileicon = f.artefact
JOIN {artefact} a
ON a.id = u.profileicon
AND a.artefacttype=\'profileicon\'
WHERE u.id = ?',
array($USER->get('id'))
);
// TODO: Gravatar support
if (!$data) {
// No profile icon selected.
return null;
}
return array(
'name' => $data->note,
'desc' => $data->title,
'mimetype' => $data->filetype,
'bytes' => (int) $data->size,
);
}
}
\ No newline at end of file
......@@ -14,6 +14,7 @@ $services = array(
'Mahara Mobile API' => array(
'shortname' => 'maharamobile',
'functions' => [
'module_mobileapi_get_user_profileicon',
],
'enabled' => 1,
'restrictedusers' => 0,
......@@ -25,3 +26,11 @@ $services = array(
),
);
$functions = array(
'module_mobileapi_get_user_profileicon' => array(
'classname' => 'module_mobileapi_sync',
'methodname' => 'get_user_profileicon',
'description' => "Retrieve user's own profile icon",
'type' => 'read',
),
);
......@@ -19,123 +19,18 @@ require_once('user.php');
$type = param_alpha('type');
switch ($type) {
case 'profileiconbyid':
// A profile icon identified by user ID
case 'profileicon':
$id = param_integer('id', 0);
$size = get_imagesize_parameters();
$earlyexpiry = param_boolean('earlyexpiry');
$useremail = null;
if ($id) {
if ($type == 'profileicon') {
// Convert ID of user to the ID of a profileicon
$data = get_record_sql('
SELECT u.profileicon, u.email, f.filetype
FROM {usr} u LEFT JOIN {artefact_file_files} f ON u.profileicon = f.artefact
WHERE u.id = ?', array($id));
if (!empty($data->profileicon)) {
$id = $data->profileicon;
$mimetype = $data->filetype;
}
else {
$useremail = $data->email;
$id = null;
}
}
else {
$mimetype = get_field('artefact_file_files', 'filetype', 'artefact', $id);
}
}
if ($id && $fileid = get_field('artefact_file_files', 'fileid', 'artefact', $id)) {
// Check that the profile icon is allowed to be seen
// Any profileiconbyid file that has been set as a user's default icon is ok
// But icons that are not should only be seen by their owner
// Unless that owner places them in a view that the user can see
if ($type == 'profileiconbyid' && !get_field('usr', 'id', 'profileicon', $id)) {
$viewid = param_integer('view', 0);
$ok = false;
if ($viewid) {
$ok = artefact_in_view($id, $viewid);
}
if (!$ok) {
if (
($USER && !$USER->is_logged_in()) ||
($USER->is_logged_in() && $USER->get('id') != get_field('artefact', 'owner', 'id', $id))
) {
exit;
}
}
}
if ($path = get_dataroot_image_path('artefact/file/profileicons', $fileid, $size)) {
if ($mimetype) {
header('Content-type: ' . $mimetype);
if (!get_config('nocache')) {
// We can't cache 'profileicon' for as long, because the
// user can change it at any time. But we can cache
// 'profileiconbyid' for quite a while, because it will
// never change
if ($type == 'profileiconbyid' and !$earlyexpiry) {
$maxage = 604800; // 1 week
}
else {
$maxage = 600; // 10 minutes
}
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $maxage) .' GMT');
header('Cache-Control: max-age=' . $maxage);
header('Pragma: public');
}
readfile_exit($path);
}
}
}
// Look for an appropriate image on gravatar.com
if ($useremail and $gravatarurl = remote_avatar_url($useremail, $size)) {
redirect($gravatarurl);
}
// We couldn't find an image for this user. Attempt to use the 'no user
// photo' image for the current theme
if (!get_config('nocache')) {
// We can cache such images
$maxage = 604800; // 1 week
if ($earlyexpiry) {
$maxage = 600; // 10 minutes
}
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $maxage) .' GMT');
header('Cache-Control: max-age=' . $maxage);
header('Pragma: public');
}
if ($path = get_dataroot_image_path('artefact/file/profileicons/no_userphoto/' . $THEME->basename, 0, $size)) {
header('Content-type: ' . 'image/png');
readfile_exit($path);
}
// If we couldn't find the no user photo picture, we put it into
// dataroot if we can
$nouserphotopic = $THEME->get_path('images/no_userphoto.png');
if ($nouserphotopic) {
// Move the file into the correct place.
$directory = get_config('dataroot') . 'artefact/file/profileicons/no_userphoto/' . $THEME->basename . '/originals/0/';
check_dir_exists($directory);
copy($nouserphotopic, $directory . '0');
// Now we can try and get the image in the correct size
if ($path = get_dataroot_image_path('artefact/file/profileicons/no_userphoto/' . $THEME->basename, 0, $size)) {
header('Content-type: ' . 'image/png');
readfile_exit($path);
}
}
// Emergency fallback
header('Content-type: ' . 'image/png');
readfile_exit($THEME->get_path('images/no_userphoto.png'));
safe_require('artefact', 'file');
$userid = param_integer('id');
ArtefactTypeProfileIcon::download_thumbnail_for_user($userid);
exit();
// A profile icon identified by artefact ID
case 'profileiconbyid':
safe_require('artefact', 'file');
$artefactid = param_integer('id');
ArtefactTypeProfileIcon::download_thumbnail($artefactid);
exit();
case 'logobyid':
$filedata = get_record('artefact_file_files', 'artefact', param_integer('id'));
if ($path = get_dataroot_image_path('artefact/file/profileicons', $filedata->fileid, get_imagesize_parameters())) {
......
......@@ -1000,7 +1000,7 @@ abstract class webservice_server implements webservice_server_interface {
* @param $tokentype string tokentype constant
* @return $user object
*/
protected function authenticate_by_token($tokentype) {
public function authenticate_by_token($tokentype) {
global $WEBSERVICE_INSTITUTION;
if ($tokentype == EXTERNAL_TOKEN_OAUTH1) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment