Loading htdocs/artefact/file/lib.php +137 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading htdocs/module/mobileapi/download.php 0 → 100644 +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.'); } htdocs/module/mobileapi/lang/en.utf8/module.mobileapi.php +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading htdocs/module/mobileapi/webservice/functions/module_mobileapi_sync.php 0 → 100644 +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 htdocs/module/mobileapi/webservice/services.php +9 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ $services = array( 'Mahara Mobile API' => array( 'shortname' => 'maharamobile', 'functions' => [ 'module_mobileapi_get_user_profileicon', ], 'enabled' => 1, 'restrictedusers' => 0, Loading @@ -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
htdocs/artefact/file/lib.php +137 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading
htdocs/module/mobileapi/download.php 0 → 100644 +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.'); }
htdocs/module/mobileapi/lang/en.utf8/module.mobileapi.php +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
htdocs/module/mobileapi/webservice/functions/module_mobileapi_sync.php 0 → 100644 +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
htdocs/module/mobileapi/webservice/services.php +9 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ $services = array( 'Mahara Mobile API' => array( 'shortname' => 'maharamobile', 'functions' => [ 'module_mobileapi_get_user_profileicon', ], 'enabled' => 1, 'restrictedusers' => 0, Loading @@ -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', ), );