@@ -36,26 +36,40 @@ if ($_SERVER['REQUEST_METHOD'] === "GET") { | |||
// Select an image from own files | |||
if (isset($_POST['path'])) { | |||
//SECURITY TODO does this fully eliminate directory traversals? | |||
$path = stripslashes($_POST['path']); | |||
$avatar = OC::$SERVERROOT.'/data/'.$user.'/files'.$path; | |||
} | |||
if (isset($_POST['crop'])) { | |||
$crop = json_decode($_POST['crop'], true); | |||
if (!isset($path)) { | |||
// TODO get path to temporarily saved uploaded-avatar | |||
} | |||
$image = new \OC_Image($avatar); | |||
$image->crop($x, $y, $w, $h); | |||
$avatar = $image->data(); | |||
} | |||
// Upload a new image | |||
elseif (!empty($_FILES)) { | |||
if (!empty($_FILES)) { | |||
$files = $_FILES['files']; | |||
if ($files['error'][0] === 0) { | |||
$avatar = file_get_contents($files['tmp_name'][0]); | |||
unlink($files['tmp_name'][0]); | |||
// TODO make the tmp_name reusable, if the uploaded avatar is not square | |||
} | |||
} else { | |||
OC_JSON::error(); | |||
} | |||
try { | |||
\OC_Avatar::set($user, $avatar); | |||
OC_JSON::success(); | |||
} catch (\OC\NotSquareException $e) { | |||
$tmpname = \OC_Util::generate_random_bytes(10); | |||
// TODO Save the image temporarily here | |||
// TODO add a cronjob that cleans up stale tmpimages | |||
OC_JSON::error(array("data" => array("message" => "notsquare", "tmpname" => $tmpname) )); | |||
} catch (\Exception $e) { | |||
OC_JSON::error(array("data" => array ("message" => $e->getMessage()) )); | |||
OC_JSON::error(array("data" => array("message" => $e->getMessage()) )); | |||
} | |||
} elseif ($_SERVER['REQUEST_METHOD'] === "DELETE") { | |||
$user = OC_User::getUser(); |
@@ -26,7 +26,7 @@ class OC_Avatar { | |||
$ext = 'png'; | |||
} else { | |||
return false; | |||
} | |||
} | |||
$avatar = new OC_Image($view->file_get_contents('avatar.'.$ext)); | |||
$avatar->resize($size); | |||
@@ -38,7 +38,8 @@ class OC_Avatar { | |||
* @param $user string user to set the avatar for | |||
* @param $data mixed imagedata or path to set a new avatar | |||
* @throws Exception if the provided file is not a jpg or png image | |||
* @throws Exception if the provided image is not valid, or not a square | |||
* @throws Exception if the provided image is not valid | |||
* @throws \OC\NotSquareException if the image is not square | |||
* @return true on success | |||
*/ | |||
public static function set ($user, $data) { | |||
@@ -52,9 +53,13 @@ class OC_Avatar { | |||
throw new \Exception($l->t("Unknown filetype")); | |||
} | |||
if (!( $img->valid() && ($img->height() === $img->width()) )) { | |||
if (!$img->valid()) { | |||
$l = \OC_L10N::get('lib'); | |||
throw new \Exception($l->t("Invalid image, or the provided image is not square")); | |||
throw new \Excpeption($l->t("Invalid image")); | |||
} | |||
if (!($img->height() === $img->width())) { | |||
throw new \OC\NotSquareException(); | |||
} | |||
$view->unlink('avatar.jpg'); |
@@ -0,0 +1,12 @@ | |||
<?php | |||
/** | |||
* Copyright (c) 2013 Christopher Schäpers <christopher@schaepers.it> | |||
* This file is licensed under the Affero General Public License version 3 or | |||
* later. | |||
* See the COPYING-README file. | |||
*/ | |||
namespace OC; | |||
class NotSquareException extends \Exception { | |||
} |
@@ -12,8 +12,4 @@ class Avatar { | |||
public static function get ($user, $size = 64) { | |||
return \OC_Avatar::get($user, $size); | |||
} | |||
public static function getMode () { | |||
return \OC_Avatar::getMode(); | |||
} | |||
} |
@@ -21,6 +21,8 @@ input#openid, input#webdav { width:20em; } | |||
input#identity { width:20em; } | |||
#email { width: 17em; } | |||
#avatar .warning { width: 350px; } | |||
.msg.success{ color:#fff; background-color:#0f0; padding:3px; text-shadow:1px 1px #000; } | |||
.msg.error{ color:#fff; background-color:#f00; padding:3px; text-shadow:1px 1px #000; } | |||
@@ -45,17 +45,57 @@ function changeDisplayName(){ | |||
} | |||
function selectAvatar (path) { | |||
$.post(OC.filePath('', '', 'avatar.php'), {path: path}, function(data) { | |||
if (data.status === "success") { | |||
updateAvatar(); | |||
} else { | |||
OC.dialogs.alert(data.data.message, t('core', "Error")); | |||
} | |||
}); | |||
$.post(OC.filePath('', '', 'avatar.php'), {path: path}, avatarResponseHandler); | |||
} | |||
function updateAvatar () { | |||
$('#avatar img').attr('src', $('#avatar img').attr('src') + '#'); | |||
$avatarimg = $('#avatar img'); | |||
$avatarimg.attr('src', $avatarimg.attr('src') + '#'); | |||
} | |||
function showAvatarCropper() { | |||
OC.dialogs.message('', t('settings', 'Crop'), undefined, OCdialogs.OK_BUTTON, sendCropData); | |||
var $dialog = $('#oc-dialog-'+(OC.dialogs.dialogs_counter-1)+'-content'); | |||
var cropper = new Image(); | |||
$(cropper).load(function() { | |||
$(this).attr('id', 'cropper'); | |||
$('#oc-dialog-'+(OC.dialogs.dialogs_counter-1)+'-content').html(this); | |||
$(this).Jcrop({ | |||
onChange: saveCoords, | |||
onSelect: saveCoords, | |||
aspectRatio: 1 | |||
}); | |||
}).attr('src', OC.filePath('', '', 'avatar.php')+"?user="+OC.currentUser+"&size=512&tmp="+$('#avatar').data('tmpname')); | |||
} | |||
function sendCropData() { | |||
var tmp = $('#avatar').data('tmpname'); | |||
var cropperdata = $('#cropper').data(); | |||
var data = { | |||
x: cropperdata.x, | |||
y: cropperdata.y, | |||
w: cropperdata.w, | |||
h: cropperdata.h | |||
}; | |||
$.post(OC.filePath('', '', 'avatar.php'), {tmp:tmp, crop: data}, avatarResponseHandler); | |||
} | |||
function saveCoords(c) { | |||
$('#cropper').data(c); | |||
} | |||
function avatarResponseHandler(data) { | |||
$warning = $('#avatar .warning'); | |||
$warning.hide(); | |||
if (data.status === "success") { | |||
updateAvatar(); | |||
} else if (data.data.message === "notsquare") { | |||
$('#avatar').data('tmpname', data.data.tmpname); | |||
showAvatarCropper(); | |||
} else { | |||
$warning.show(); | |||
$warning.text(data.data.message); | |||
} | |||
} | |||
$(document).ready(function(){ | |||
@@ -149,11 +189,7 @@ $(document).ready(function(){ | |||
var uploadparms = { | |||
done: function(e, data) { | |||
if (data.result.status === "success") { | |||
updateAvatar(); | |||
} else { | |||
OC.dialogs.alert(data.result.data.message, t('core', "Error")); | |||
} | |||
avatarResponseHandler(data.result); | |||
} | |||
}; | |||
@@ -16,6 +16,8 @@ OC_Util::addStyle( 'settings', 'settings' ); | |||
OC_Util::addScript( '3rdparty', 'chosen/chosen.jquery.min' ); | |||
OC_Util::addStyle( '3rdparty', 'chosen' ); | |||
\OC_Util::addScript('files', 'jquery.fileupload'); | |||
\OC_Util::addScript('3rdparty/Jcrop', 'jquery.Jcrop.min'); | |||
\OC_Util::addStyle('3rdparty/Jcrop', 'jquery.Jcrop.min'); | |||
OC_App::setActiveNavigationEntry( 'personal' ); | |||
$storageInfo=OC_Helper::getStorageInfo(); |
@@ -88,6 +88,7 @@ if($_['passwordChangeSupported']) { | |||
<legend><strong><?php p($l->t('Profile Image')); ?></strong></legend> | |||
<img src="<?php print_unescaped(link_to('', 'avatar.php').'?user='.OC_User::getUser().'&size=128'); ?>"><br> | |||
<em><?php p($l->t('Has to be square and either PNG or JPG')); ?></em><br> | |||
<div class="warning hidden"></div> | |||
<div class="inlineblock button" id="uploadavatarbutton"><?php p($l->t('Upload new')); ?></div> | |||
<input type="file" class="hidden" name="files[]" id="uploadavatar"> | |||
<div class="inlineblock button" id="selectavatar"><?php p($l->t('Select new from files')); ?></div> |