summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvan Prodromou <evan@prodromou.name>2008-05-17 08:20:45 -0400
committerEvan Prodromou <evan@prodromou.name>2008-05-17 08:20:45 -0400
commit3803cf2153091c4f319c379c3ac24cc8fc844b0a (patch)
tree11819210fbca89a8293c2cba1c0b1042cf3ddf6c
parentfac522f4d7cce9a35e605fac2bba0b2d23616ad0 (diff)
upload and change avatars
code to upload and change avatars. combined some code in the settings area, too. darcs-hash:20080517122045-84dde-8e13994e627805f29679c9533c2f62db81dc0925.gz
-rw-r--r--actions/avatar.php208
-rw-r--r--actions/password.php18
-rw-r--r--actions/profilesettings.php18
-rw-r--r--db/stoica.sql3
-rw-r--r--doc/TODO11
-rw-r--r--lib/common.php26
-rw-r--r--lib/settingsaction.php25
7 files changed, 270 insertions, 39 deletions
diff --git a/actions/avatar.php b/actions/avatar.php
new file mode 100644
index 000000000..b715e02de
--- /dev/null
+++ b/actions/avatar.php
@@ -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();
+ }
+ }
+}
+
diff --git a/actions/password.php b/actions/password.php
index 31831d3a9..3fd94b66a 100644
--- a/actions/password.php
+++ b/actions/password.php
@@ -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' =>
diff --git a/actions/profilesettings.php b/actions/profilesettings.php
index b87cea7de..95462133e 100644
--- a/actions/profilesettings.php
+++ b/actions/profilesettings.php
@@ -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' =>
diff --git a/db/stoica.sql b/db/stoica.sql
index 7ee8f6ff8..1f2b28d95 100644
--- a/db/stoica.sql
+++ b/db/stoica.sql
@@ -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),
diff --git a/doc/TODO b/doc/TODO
index cd9ac05df..08df348e2 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -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
diff --git a/lib/common.php b/lib/common.php
index f30096796..b4c2f9b6e 100644
--- a/lib/common.php
+++ b/lib/common.php
@@ -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 }
diff --git a/lib/settingsaction.php b/lib/settingsaction.php
index db07bdef9..1795969f9 100644
--- a/lib/settingsaction.php
+++ b/lib/settingsaction.php
@@ -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'),