Commit 29324842 authored by Aaron Wells's avatar Aaron Wells
Browse files

Bug 1620879: Adding script to help webservices download user icons

behatnotneeded: Covered by existing tests

Change-Id: Iec9939b02b7b38fbf0f3217d6be45d538e3a9760
parent 08f5a3af
Loading
Loading
Loading
Loading
+137 −0
Original line number Diff line number Diff line
@@ -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 {
+94 −0
Original line number Diff line number Diff line
<?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.');
}
+1 −0
Original line number Diff line number Diff line
@@ -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
+71 −0
Original line number Diff line number Diff line
<?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
+9 −0
Original line number Diff line number Diff line
@@ -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',
    ),
);
Loading