diff options
-rw-r--r-- | actions/password.php | 91 | ||||
-rw-r--r-- | actions/profilesettings.php | 102 | ||||
-rw-r--r-- | actions/shownotice.php | 16 | ||||
-rw-r--r-- | actions/showstream.php | 15 | ||||
-rw-r--r-- | actions/subscribed.php | 9 | ||||
-rw-r--r-- | actions/subscriptions.php | 9 | ||||
-rw-r--r-- | classes/Avatar.php | 24 | ||||
-rw-r--r-- | classes/Notice.php | 1 | ||||
-rw-r--r-- | classes/Profile.php | 27 | ||||
-rw-r--r-- | classes/User.php | 1 | ||||
-rw-r--r-- | classes/stoica.ini | 15 | ||||
-rw-r--r-- | db/stoica.sql | 12 | ||||
-rw-r--r-- | doc/TODO | 16 | ||||
-rw-r--r-- | lib/common.php | 13 | ||||
-rw-r--r-- | lib/settingsaction.php (renamed from actions/settings.php) | 44 | ||||
-rw-r--r-- | lib/stream.php | 18 |
16 files changed, 366 insertions, 47 deletions
diff --git a/actions/password.php b/actions/password.php new file mode 100644 index 000000000..31831d3a9 --- /dev/null +++ b/actions/password.php @@ -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); + } +}
\ No newline at end of file diff --git a/actions/profilesettings.php b/actions/profilesettings.php new file mode 100644 index 000000000..b87cea7de --- /dev/null +++ b/actions/profilesettings.php @@ -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); + } +}
\ No newline at end of file diff --git a/actions/shownotice.php b/actions/shownotice.php index b0128e6e9..2e14963bb 100644 --- a/actions/shownotice.php +++ b/actions/shownotice.php @@ -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'), diff --git a/actions/showstream.php b/actions/showstream.php index d476bd297..8272e1038 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -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) { diff --git a/actions/subscribed.php b/actions/subscribed.php index 924e2b67b..f24470f02 100644 --- a/actions/subscribed.php +++ b/actions/subscribed.php @@ -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'); diff --git a/actions/subscriptions.php b/actions/subscriptions.php index 653c6d2b5..87b8a4e48 100644 --- a/actions/subscriptions.php +++ b/actions/subscriptions.php @@ -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'); diff --git a/classes/Avatar.php b/classes/Avatar.php new file mode 100644 index 000000000..222725b48 --- /dev/null +++ b/classes/Avatar.php @@ -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 +} diff --git a/classes/Notice.php b/classes/Notice.php index 94b06deb3..538ae9c01 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -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 diff --git a/classes/Profile.php b/classes/Profile.php index a5a565c69..4f756856e 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -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; + } + } } diff --git a/classes/User.php b/classes/User.php index e7d297b3c..2b8610a65 100644 --- a/classes/User.php +++ b/classes/User.php @@ -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 diff --git a/classes/stoica.ini b/classes/stoica.ini index 52686bcb5..6c951fcb9 100644 --- a/classes/stoica.ini +++ b/classes/stoica.ini @@ -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 diff --git a/db/stoica.sql b/db/stoica.sql index 28e8f7662..7ee8f6ff8 100644 --- a/db/stoica.sql +++ b/db/stoica.sql @@ -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 ( @@ -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 diff --git a/lib/common.php b/lib/common.php index 53cd66cbe..f30096796 100644 --- a/lib/common.php +++ b/lib/common.php @@ -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) { diff --git a/actions/settings.php b/lib/settingsaction.php index ea8074efc..db07bdef9 100644 --- a/actions/settings.php +++ b/lib/settingsaction.php @@ -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'); + } +} diff --git a/lib/stream.php b/lib/stream.php index b659eb8b5..912969380 100644 --- a/lib/stream.php +++ b/lib/stream.php @@ -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'), |