settings and avatars
Did considerable work on the settings section. Redesigned the DB to allow avatars. Each avatar image has a size and an URL. There can be multiple avatars per profile, just different sizes. Added accessors in Profile for avatar. Show the avatar in lots of places, where it makes sense. Constants for avatar sizes in common.php. darcs-hash:20080515162844-84dde-fe0630366e247c02ca8ca9d1cc6b963cfce57a26.gz
This commit is contained in:
parent
b6cfd2dffe
commit
fac522f4d7
91
actions/password.php
Normal file
91
actions/password.php
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?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 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);
|
||||
}
|
||||
common_start_element('form', array('method' => 'POST',
|
||||
'id' => 'password',
|
||||
'action' =>
|
||||
common_local_url('password')));
|
||||
common_password('oldpassword', _t('Old password'));
|
||||
common_password('newpassword', _t('New password'));
|
||||
common_password('confirm', _t('Confirm'));
|
||||
common_element('input', array('name' => 'submit',
|
||||
'type' => 'submit',
|
||||
'id' => 'submit'),
|
||||
_t('Login'));
|
||||
common_element('input', array('name' => 'cancel',
|
||||
'type' => 'button',
|
||||
'id' => 'cancel'),
|
||||
_t('Cancel'));
|
||||
}
|
||||
|
||||
function handle_post() {
|
||||
|
||||
$user = common_current_user();
|
||||
assert(!is_null($user)); # should already be checked
|
||||
|
||||
# FIXME: scrub input
|
||||
|
||||
$oldpassword = $this->arg('oldpassword');
|
||||
$newpassword = $this->arg('newpassword');
|
||||
$confirm = $this->arg('confirm');
|
||||
|
||||
if (0 != strcmp($newpassword, $confirm)) {
|
||||
$this->show_form(_t('Passwords don\'t match'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!common_check_user($user->nickname, $oldpassword)) {
|
||||
$this->show_form(_t('Incorrect old password'));
|
||||
return;
|
||||
}
|
||||
|
||||
$user->password = common_munge_password($newpassword, $user->id);
|
||||
|
||||
if (!$user->update()) {
|
||||
common_server_error(_t('Can\'t save new password.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$this->show_form(_t('Password saved'), true);
|
||||
}
|
||||
}
|
102
actions/profilesettings.php
Normal file
102
actions/profilesettings.php
Normal file
|
@ -0,0 +1,102 @@
|
|||
<?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 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);
|
||||
}
|
||||
common_start_element('form', array('method' => 'POST',
|
||||
'id' => 'profilesettings',
|
||||
'action' =>
|
||||
common_local_url('profilesettings')));
|
||||
common_input('nickname', _t('Nickname'));
|
||||
common_input('fullname', _t('Full name'));
|
||||
common_input('email', _t('Email address'));
|
||||
common_input('homepage', _t('Homepage'));
|
||||
common_input('bio', _t('Bio'));
|
||||
common_input('location', _t('Location'));
|
||||
common_element('input', array('name' => 'submit',
|
||||
'type' => 'submit',
|
||||
'id' => 'submit'),
|
||||
_t('Login'));
|
||||
common_element('input', array('name' => 'cancel',
|
||||
'type' => 'button',
|
||||
'id' => 'cancel'),
|
||||
_t('Cancel'));
|
||||
common_show_footer();
|
||||
}
|
||||
|
||||
function handle_post() {
|
||||
$nickname = $this->arg('nickname');
|
||||
$fullname = $this->arg('fullname');
|
||||
$email = $this->arg('email');
|
||||
$homepage = $this->arg('homepage');
|
||||
$bio = $this->arg('bio');
|
||||
$location = $this->arg('location');
|
||||
|
||||
$user = common_current_user();
|
||||
assert(!is_null($user)); # should already be checked
|
||||
|
||||
# FIXME: scrub input
|
||||
# FIXME: transaction!
|
||||
|
||||
$user->nickname = $this->arg('nickname');
|
||||
$user->email = $this->arg('email');
|
||||
|
||||
if (!$user->update()) {
|
||||
common_server_error(_t('Couldnt update user.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$profile = $user->getProfile();
|
||||
|
||||
$profile->nickname = $user->nickname;
|
||||
$profile->fullname = $this->arg('fullname');
|
||||
$profile->homepage = $this->arg('homepage');
|
||||
$profile->bio = $this->arg('bio');
|
||||
$profile->location = $this->arg('location');
|
||||
|
||||
if (!$profile->update()) {
|
||||
common_server_error(_t('Couldnt save profile.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$this->show_form(_t('Settings saved.'), TRUE);
|
||||
}
|
||||
}
|
|
@ -51,9 +51,21 @@ class ShownoticeAction extends Action {
|
|||
$profile = $notice->getProfile();
|
||||
# XXX: RDFa
|
||||
common_start_element('div', array('class' => 'notice'));
|
||||
# FIXME: add the avatar
|
||||
$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,
|
||||
'alt' =>
|
||||
($profile->fullname) ? $profile->fullname :
|
||||
$profile->nickname));
|
||||
}
|
||||
common_start_element('a', array('href' => $profile->profileurl,
|
||||
'class' => 'nickname'),
|
||||
'class' => 'nickname',
|
||||
'title' =>
|
||||
($profile->fullname) ? $profile->fullname :
|
||||
$profile->nickname)),
|
||||
$profile->nickname);
|
||||
# FIXME: URL, image, video, audio
|
||||
common_element('span', array('class' => 'content'),
|
||||
|
|
|
@ -86,6 +86,14 @@ class ShowstreamAction extends StreamAction {
|
|||
|
||||
function show_profile($profile) {
|
||||
common_start_element('div', 'profile');
|
||||
$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,
|
||||
'title' => $profile->nickname));
|
||||
}
|
||||
common_element('span', 'nickname', $profile->nickname);
|
||||
if ($profile->fullname) {
|
||||
if ($profile->homepage) {
|
||||
|
@ -144,8 +152,11 @@ class ShowstreamAction extends StreamAction {
|
|||
$subs->nickname,
|
||||
'href' => $subs->profileurl,
|
||||
'class' => 'subscription'));
|
||||
common_element('img', array('src' => $subs->avatar,
|
||||
'class' => 'avatar'));
|
||||
$avatar = $subs->getAvatar(AVATAR_MINI_SIZE);
|
||||
common_element('img', array('src' => (($avatar) ? $avatar->url : DEFAULT_MINI_AVATAR),
|
||||
'width' => AVATAR_MINI_SIZE,
|
||||
'height' => AVATAR_MINI_SIZE,
|
||||
'class' => 'avatar mini'));
|
||||
common_end_element('a');
|
||||
|
||||
if ($cnt % SUBSCRIPTIONS_PER_ROW == 0) {
|
||||
|
|
|
@ -64,9 +64,14 @@ class SubscribedAction extends Action {
|
|||
$subs->nickname,
|
||||
'href' => $subs->profileurl,
|
||||
'class' => 'subscription'));
|
||||
common_element('img', array('src' => $subs->avatar,
|
||||
'class' => 'avatar'));
|
||||
$avatar = $subs->getAvatar(AVATAR_STREAM_SIZE);
|
||||
common_element('img', array('src' => (($avatar) ? $avatar->url : DEFAULT_STREAM_AVATAR),
|
||||
'width' => AVATAR_STREAM_SIZE,
|
||||
'height' => AVATAR_STREAM_SIZE,
|
||||
'class' => 'avatar stream'));
|
||||
common_end_element('a');
|
||||
|
||||
# XXX: subscribe form here
|
||||
|
||||
if ($idx % SUBSCRIPTIONS_PER_ROW == 0) {
|
||||
common_end_element('div');
|
||||
|
|
|
@ -61,9 +61,14 @@ class SubscriptionsAction extends Action {
|
|||
$subs->nickname,
|
||||
'href' => $subs->profileurl,
|
||||
'class' => 'subscription'));
|
||||
common_element('img', array('src' => $subs->avatar,
|
||||
'class' => 'avatar'));
|
||||
$avatar = $subs->getAvatar(AVATAR_STREAM_SIZE);
|
||||
common_element('img', array('src' => (($avatar) ? $avatar->url : DEFAULT_STREAM_AVATAR),
|
||||
'width' => AVATAR_STREAM_SIZE,
|
||||
'height' => AVATAR_STREAM_SIZE,
|
||||
'class' => 'avatar stream'));
|
||||
common_end_element('a');
|
||||
|
||||
# XXX: subscribe form here
|
||||
|
||||
if ($idx % SUBSCRIPTIONS_PER_ROW == 0) {
|
||||
common_end_element('div');
|
||||
|
|
24
classes/Avatar.php
Normal file
24
classes/Avatar.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
/**
|
||||
* Table Definition for avatar
|
||||
*/
|
||||
require_once 'DB/DataObject.php';
|
||||
|
||||
class Avatar extends DB_DataObject
|
||||
{
|
||||
###START_AUTOCODE
|
||||
/* the code below is auto generated do not remove the above tag */
|
||||
|
||||
public $__table = 'avatar'; // table name
|
||||
public $profile_id; // int(4) primary_key not_null
|
||||
public $width; // int(4) primary_key not_null
|
||||
public $height; // int(4) primary_key not_null
|
||||
public $original; // tinyint(1)
|
||||
public $mediatype; // varchar(32) not_null
|
||||
|
||||
/* Static get */
|
||||
function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('Avatar',$k,$v); }
|
||||
|
||||
/* the code above is auto generated do not remove the tag below */
|
||||
###END_AUTOCODE
|
||||
}
|
|
@ -33,7 +33,6 @@ class Notice extends DB_DataObject
|
|||
public $id; // int(4) primary_key not_null
|
||||
public $profile_id; // int(4) not_null
|
||||
public $content; // varchar(140)
|
||||
public $rendered; // varchar(140)
|
||||
public $url; // varchar(255)
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
|
|
|
@ -45,4 +45,31 @@ class Profile extends DB_DataObject
|
|||
|
||||
/* the code above is auto generated do not remove the tag below */
|
||||
###END_AUTOCODE
|
||||
|
||||
function getAvatar($width, $height=NULL) {
|
||||
$avatar = DB_DataObject::factory('avatar');
|
||||
$avatar->profile_id = $this->id;
|
||||
$avatar->width = $width;
|
||||
if (is_null($height)) {
|
||||
$avatar->height = $width;
|
||||
} else {
|
||||
$avatar->height = $height;
|
||||
}
|
||||
if ($avatar->find(true)) {
|
||||
return $avatar;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
function getOriginalAvatar() {
|
||||
$avatar = DB_DataObject::factory('avatar');
|
||||
$avatar->profile_id = $this->id;
|
||||
$avatar->original = true;
|
||||
if ($avatar->find(true)) {
|
||||
return $avatar;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ class User extends DB_DataObject
|
|||
|
||||
public $__table = 'user'; // table name
|
||||
public $id; // int(4) primary_key not_null
|
||||
public $nickname; // varchar(64) unique_key
|
||||
public $password; // varchar(255)
|
||||
public $email; // varchar(255) unique_key
|
||||
public $created; // datetime() not_null
|
||||
|
|
|
@ -1,9 +1,20 @@
|
|||
|
||||
[avatar]
|
||||
profile_id = 129
|
||||
width = 129
|
||||
height = 129
|
||||
original = 17
|
||||
mediatype = 130
|
||||
|
||||
[avatar__keys]
|
||||
profile_id = K
|
||||
width = K
|
||||
height = K
|
||||
|
||||
[notice]
|
||||
id = 129
|
||||
profile_id = 129
|
||||
content = 2
|
||||
rendered = 2
|
||||
url = 2
|
||||
created = 142
|
||||
modified = 384
|
||||
|
@ -48,6 +59,7 @@ subscribed = K
|
|||
|
||||
[user]
|
||||
id = 129
|
||||
nickname = 2
|
||||
password = 2
|
||||
email = 2
|
||||
created = 142
|
||||
|
@ -55,4 +67,5 @@ modified = 384
|
|||
|
||||
[user__keys]
|
||||
id = K
|
||||
nickname = U
|
||||
email = U
|
||||
|
|
|
@ -14,6 +14,18 @@ create table profile (
|
|||
index profile_nickname_idx (nickname)
|
||||
);
|
||||
|
||||
create table avatar (
|
||||
profile_id integer not null comment 'foreign key to profile table' references profile (id),
|
||||
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',
|
||||
url varchar(255) unique key comment 'avatar location',
|
||||
|
||||
constraint primary key (profile_id, width, height),
|
||||
index avatar_profile_id_idx (profile_id),
|
||||
);
|
||||
|
||||
/* local users */
|
||||
|
||||
create table user (
|
||||
|
|
16
doc/TODO
16
doc/TODO
|
@ -1,6 +1,10 @@
|
|||
+ login
|
||||
+ register
|
||||
- settings
|
||||
+ settings
|
||||
- upload avatar
|
||||
- default avatar
|
||||
+ change password
|
||||
+ settings menu
|
||||
+ disallow login if user is logged in
|
||||
+ disallow register if user is logged in
|
||||
+ common_current_user()
|
||||
|
@ -14,6 +18,8 @@
|
|||
+ header menu
|
||||
+ footer menu
|
||||
+ disallow direct to PHP files
|
||||
- use only canonical usernames
|
||||
- use only canonical email addresses
|
||||
- require valid nicknames
|
||||
- common_local_url()
|
||||
- configuration system ($config)
|
||||
|
@ -23,8 +29,6 @@
|
|||
- RDF dump of entire site
|
||||
- FOAF dump for user
|
||||
- delete a notice
|
||||
- make sure canonical usernames are unique
|
||||
- upload avatar
|
||||
- licenses
|
||||
- design from Open Source Web Designs
|
||||
- release 0.1
|
||||
|
@ -37,6 +41,9 @@
|
|||
- tinyurl-ification of URLs
|
||||
- jQuery for as much as possible
|
||||
- themes
|
||||
- RDFa for stream pages
|
||||
- RDFa for subscriber pages
|
||||
- RDFa for subscribed pages
|
||||
- release 0.2
|
||||
- @ messages
|
||||
- # tags
|
||||
|
@ -51,6 +58,9 @@
|
|||
- forward notices to Jabber
|
||||
- forward notices to other IM
|
||||
- forward notices to mobile phone
|
||||
- receive notices from Jabber
|
||||
- receive notices from other IM
|
||||
- receive notices from mobile phone
|
||||
- machine tags
|
||||
- release 0.4
|
||||
- include twitter subscriptions
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
|
||||
if (!defined('LACONICA')) { exit(1) }
|
||||
|
||||
define('AVATAR_PROFILE_SIZE', 96);
|
||||
define('AVATAR_STREAM_SIZE', 48);
|
||||
define('AVATAR_MINI_SIZE', 24);
|
||||
|
||||
# global configuration object
|
||||
|
||||
// default configuration, overwritten in config.php
|
||||
|
@ -108,7 +112,7 @@ function common_head_menu() {
|
|||
common_menu_item(common_local_url('showstream', array('nickname' =>
|
||||
$user->nickname)),
|
||||
_t('Profile'), $user->fullname || $user->nickname);
|
||||
common_menu_item(common_local_url('settings'),
|
||||
common_menu_item(common_local_url('profilesettings'),
|
||||
_t('Settings'));
|
||||
common_menu_item(common_local_url('logout'),
|
||||
_t('Logout'));
|
||||
|
@ -141,6 +145,13 @@ function common_menu_item($url, $text, $title=NULL) {
|
|||
common_element_end('li');
|
||||
}
|
||||
|
||||
function common_input($id, $label) {
|
||||
common_element('label', array('for' => $id), $label);
|
||||
common_element('input', array('name' => $id,
|
||||
'type' => 'text',
|
||||
'id' => $id));
|
||||
}
|
||||
|
||||
# salted, hashed passwords are stored in the DB
|
||||
|
||||
function common_munge_password($id, $password) {
|
||||
|
|
|
@ -16,37 +16,23 @@
|
|||
* 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 SettingsAction extends Action {
|
||||
|
||||
|
||||
function handle($args) {
|
||||
parent::handle($args);
|
||||
if ($this->arg('METHOD') == 'POST') {
|
||||
$nickname = $this->arg('nickname');
|
||||
$fullname = $this->arg('fullname');
|
||||
$email = $this->arg('email');
|
||||
$homepage = $this->arg('homepage');
|
||||
$bio = $this->arg('bio');
|
||||
$location = $this->arg('location');
|
||||
$oldpass = $this->arg('oldpass');
|
||||
$password = $this->arg('password');
|
||||
$confirm = $this->arg('confirm');
|
||||
|
||||
if ($password) {
|
||||
if ($password != $confirm) {
|
||||
$this->show_form(_t('Passwords don\'t match.'));
|
||||
}
|
||||
} else if (
|
||||
|
||||
$error = $this->save_settings($nickname, $fullname, $email, $homepage,
|
||||
$bio, $location, $password);
|
||||
if (!$error) {
|
||||
$this->show_form(_t('Settings saved.'), TRUE);
|
||||
} else {
|
||||
$this->show_form($error);
|
||||
}
|
||||
} else {
|
||||
$this->show_form();
|
||||
}
|
||||
}
|
||||
|
||||
function settings_menu() {
|
||||
common_element_start('ul', 'headmenu');
|
||||
common_menu_item(common_local_url('editprofile'),
|
||||
_t('Profile'));
|
||||
common_menu_item(common_local_url('avatar'),
|
||||
_t('Avatar'));
|
||||
common_menu_item(common_local_url('password'),
|
||||
_t('Password'));
|
||||
common_element_end('ul');
|
||||
}
|
||||
}
|
|
@ -27,14 +27,24 @@ class StreamAction extends Action {
|
|||
parent::handle($args);
|
||||
}
|
||||
|
||||
# XXX: for 'showstream' repeats same avatar over and over
|
||||
function show_notice($notice) {
|
||||
$profile = $notice->getProfile();
|
||||
# XXX: RDFa
|
||||
common_start_element('div', array('class' => 'notice'));
|
||||
# FIXME: add the avatar
|
||||
common_start_element('a', array('href' => $profile->profileurl,
|
||||
'class' => 'nickname'),
|
||||
$profile->nickname);
|
||||
$avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
|
||||
common_start_element('a', array('href' => $profile->profileurl));
|
||||
common_element('img', array('src' => ($avatar) ? $avatar->url : DEFAULT_STREAM_AVATAR,
|
||||
'class' => 'avatar stream',
|
||||
'width' => AVATAR_STREAM_SIZE,
|
||||
'height' => AVATAR_STREAM_SIZE,
|
||||
'alt' =>
|
||||
($profile->fullname) ? $profile->fullname :
|
||||
$profile->nickname));
|
||||
common_end_element('a');
|
||||
common_element('a', array('href' => $profile->profileurl,
|
||||
'class' => 'nickname'),
|
||||
$profile->nickname);
|
||||
# FIXME: URL, image, video, audio
|
||||
common_element('span', array('class' => 'content'), $notice->content);
|
||||
common_element('span', array('class' => 'date'),
|
||||
|
|
Loading…
Reference in New Issue
Block a user