upload and change avatars

code to upload and change avatars.

combined some code in the settings area, too.

darcs-hash:20080517122045-84dde-8e13994e627805f29679c9533c2f62db81dc0925.gz
This commit is contained in:
Evan Prodromou 2008-05-17 08:20:45 -04:00
parent fac522f4d7
commit 3803cf2153
7 changed files with 270 additions and 39 deletions

208
actions/avatar.php Normal file
View File

@ -0,0 +1,208 @@
<?php
/*
* Laconica - a distributed open-source microblogging tool
* Copyright (C) 2008, Controlez-Vous, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('LACONICA')) { exit(1) }
class AvatarAction extends SettingsAction {
function show_form($msg=NULL, $success=false) {
common_show_header(_t('Avatar'));
$this->settings_menu();
$this->message($msg, $success);
$user = common_current_user();
$profile = $user->getProfile();
$original = $profile->getOriginal();
if ($original) {
common_element('img', array('src' => $original->url,
'class' => 'avatar original',
'width' => $original->width,
'height' => $original->height));
}
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
if ($avatar) {
common_element('img', array('src' => $avatar->url,
'class' => 'avatar profile',
'width' => AVATAR_PROFILE_SIZE,
'height' => AVATAR_PROFILE_SIZE));
}
common_start_element('form', array('enctype' => 'multipart/form-data',
'method' => 'POST',
'id' => 'avatar',
'action' =>
common_local_url('avatar')));
common_element('input', array('name' => 'MAX_FILE_SIZE',
'type' => 'hidden',
'id' => 'MAX_FILE_SIZE',
'value' => MAX_AVATAR_SIZE));
common_element('input', array('name' => 'avatarfile',
'type' => 'file',
'id' => 'avatarfile'));
common_element('input', array('name' => 'submit',
'type' => 'submit',
'id' => 'submit'),
_t('Upload'));
}
function handle_post() {
switch ($_FILES['avatarfile']['error']) {
case UPLOAD_ERR_OK: # success, jump out
break;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
$this->show_form(_t('That file is too big.'));
return;
case UPLOAD_ERR_PARTIAL:
@unlink($_FILES['avatarfile']['tmp_name']);
$this->show_form(_t('Partial upload.'));
return;
default:
$this->show_form(_t('System error uploading file.'));
return;
}
$info = @getimagesize($_FILES['avatarfile']['tmp_name']);
if (!$info) {
@unlink($_FILES['avatarfile']['tmp_name']);
$this->show_form(_t('Not an image or corrupt file.'));
return;
}
switch ($info[2]) {
case IMAGETYPE_GIF:
case IMAGETYPE_JPEG:
case IMAGETYPE_PNG:
break;
default:
$this->show_form(_t('Unsupported image file format.'));
return;
}
$user = common_current_user();
$filename = common_avatar_filename($user, image_type_to_extension($info[2]));
$filepath = common_avatar_path($filename);
if (!move_uploaded_file($_FILES['avatarfile']['tmp_name'], $filepath)) {
@unlink($_FILES['avatarfile']['tmp_name']);
$this->show_form(_t('System error uploading file.'));
return;
}
$avatar = DB_DataObject::factory('avatar');
$avatar->profile_id = $user->id;
$avatar->width = $info[0];
$avatar->height = $info[1];
$avatar->mediatype = image_type_to_mime_type($info[2]);
$avatar->filename = $filename;
$avatar->original = true;
$avatar->url = common_avatar_url($filename);
foreach (array(AVATAR_PROFILE_SIZE, AVATAR_STREAM_SIZE, AVATAR_MINI_SIZE) as $size) {
$scaled[] = $this->scale_avatar($user, $avatar, $size);
}
# XXX: start a transaction here
if (!$this->delete_old_avatars($user)) {
@unlink($filepath);
common_server_error(_t('Error deleting old avatars.'));
return;
}
if (!$avatar->insert()) {
@unlink($filepath);
common_server_error(_t('Error inserting avatar.'));
return;
}
foreach ($scaled as $s) {
if (!$s->insert()) {
common_server_error(_t('Error inserting scaled avatar.'));
return;
}
}
# XXX: end transaction here
$this->show_form(_t('Avatar updated.'), true);
}
function scale_avatar($user, $avatar, $size) {
$image_s = imagecreatetruecolor($size, $size);
$image_a = $this->avatar_to_image($avatar);
$square = min($avatar->width, $avatar->height);
imagecopyresampled($image_s, $image_a, 0, 0, 0, 0,
$size, $size, $square, $square);
$ext = ($avatar->mediattype == 'image/jpeg') ? ".jpg" : ".png";
$filename = common_avatar_filename($user, $ext, $size);
if ($avatar->mediatype == 'image/jpeg') {
imagejpeg($image_s, common_avatar_path($filename));
} else {
imagepng($image_s, common_avatar_path($filename));
}
$scaled = DB_DataObject::factory('avatar');
$scaled->profile_id = $avatar->profile_id;
$scaled->width = $size;
$scaled->height = $size;
$scaled->original = false;
$scaled->mediatype = ($avatar->mediattype == 'image/jpeg') ? 'image/jpeg' : 'image/png';
$scaled->filename = $filename;
$scaled->url = common_avatar_url($filename);
return $scaled;
}
function avatar_to_image($avatar) {
$filepath = common_avatar_path($avatar->filename);
if ($avatar->mediatype == 'image/gif') {
return imagecreatefromgif($filepath);
} else if ($avatar->mediatype == 'image/jpeg') {
return imagecreatefromjpeg($filepath);
} else if ($avatar->mediatype == 'image/png') {
return imagecreatefrompng($filepath);
} else {
common_server_error(_t('Unsupported image type:') . $avatar->mediatype);
return NULL;
}
}
function delete_old_avatars($user) {
$avatar = DB_DataObject::factory('avatar');
$avatar->profile_id = $user->id;
$avatar->find();
while ($avatar->fetch()) {
$avatar->delete();
}
}
}

View File

@ -21,26 +21,10 @@ if (!defined('LACONICA')) { exit(1) }
class PasswordAction extends SettingsAction {
function handle($args) {
parent::handle($args);
if (!common_logged_in()) {
common_user_error(_t('Not logged in.'));
return;
}
if ($this->arg('METHOD') == 'POST') {
$this->handle_post();
} else {
$this->show_form();
}
}
function show_form($msg=NULL, $success=false) {
common_show_header(_t('Change password'));
$this->settings_menu();
if ($msg) {
common_element('div', ($success) ? 'success' : 'error',
$msg);
}
$this->message($msg, $success);
common_start_element('form', array('method' => 'POST',
'id' => 'password',
'action' =>

View File

@ -21,26 +21,10 @@ if (!defined('LACONICA')) { exit(1) }
class ProfilesettingsAction extends SettingsAction {
function handle($args) {
parent::handle($args);
if (!common_logged_in()) {
common_user_error(_t('Not logged in.'));
return;
}
if ($this->arg('METHOD') == 'POST') {
$this->handle_post();
} else {
$this->show_form();
}
}
function show_form($msg=NULL, $success=false) {
common_show_header(_t('Profile settings'));
$this->settings_menu();
if ($msg) {
common_element('div', ($success) ? 'success' : 'error',
$msg);
}
$this->message($msg, $success);
common_start_element('form', array('method' => 'POST',
'id' => 'profilesettings',
'action' =>

View File

@ -16,10 +16,11 @@ create table profile (
create table avatar (
profile_id integer not null comment 'foreign key to profile table' references profile (id),
original boolean default false comment 'uploaded by user or generated?',
width integer not null comment 'image width',
height integer not null comment 'image height',
original boolean default false comment 'uploaded by user or generated?',
mediatype varchar(32) not null comment 'file type',
filename varchar(255) null comment 'local filename, if local',
url varchar(255) unique key comment 'avatar location',
constraint primary key (profile_id, width, height),

View File

@ -1,7 +1,7 @@
+ login
+ register
+ settings
- upload avatar
+ upload avatar
- default avatar
+ change password
+ settings menu
@ -18,9 +18,10 @@
+ header menu
+ footer menu
+ disallow direct to PHP files
- require valid nicknames
- store canonical username for comparison and fetch
- use only canonical usernames
- use only canonical email addresses
- require valid nicknames
- common_local_url()
- configuration system ($config)
- RSS 1.0 feeds of a user's notices
@ -28,10 +29,12 @@
- RSS 1.0 feed of all public notices
- RDF dump of entire site
- FOAF dump for user
- delete a notice
- licenses
- license on showstream
- license on shownotice
- design from Open Source Web Designs
- TOS checkbox on register
- release 0.1
- delete a notice
- gettext
- subscribe remote
- add subscriber remote

View File

@ -22,6 +22,7 @@ if (!defined('LACONICA')) { exit(1) }
define('AVATAR_PROFILE_SIZE', 96);
define('AVATAR_STREAM_SIZE', 48);
define('AVATAR_MINI_SIZE', 24);
define('MAX_AVATAR_SIZE', 256 * 1024);
# global configuration object
@ -30,6 +31,9 @@ define('AVATAR_MINI_SIZE', 24);
$config =
array('site' =>
array('name' => 'Just another µB'),
'avatar' =>
array('directory' => INSTALLDIR . 'files',
'path' => '/files'),
'dsn' =>
array('phptype' => 'mysql',
'username' => 'stoica',
@ -228,6 +232,28 @@ function common_render_content($text) {
return htmlspecialchars($text);
}
// where should the avatar go for this user?
function common_avatar_filename($user, $extension, $size=NULL) {
global $config;
if ($size) {
return $user->id . '-' . $size . $extension;
} else {
return $user->id . '-original' . $extension;
}
}
function common_avatar_path($filename) {
global $config;
return $config['avatar']['directory'] . '/' . $filename;
}
function common_avatar_url($filename) {
global $config;
return $config['avatar']['path'] . '/' . $filename;
}
// XXX: set up gettext
function _t($str) { $str }

View File

@ -23,8 +23,33 @@ class SettingsAction extends Action {
function handle($args) {
parent::handle($args);
if (!common_logged_in()) {
common_user_error(_t('Not logged in.'));
return;
}
if ($this->arg('METHOD') == 'POST') {
$this->handle_post();
} else {
$this->show_form();
}
}
# override!
function handle_post() {
return false;
}
function show_form($msg=NULL, $success=false) {
return false;
}
function show_message($msg, $success) {
if ($msg) {
common_element('div', ($success) ? 'success' : 'error',
$msg);
}
}
function settings_menu() {
common_element_start('ul', 'headmenu');
common_menu_item(common_local_url('editprofile'),