]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
settings and avatars
authorEvan Prodromou <evan@prodromou.name>
Thu, 15 May 2008 16:28:44 +0000 (12:28 -0400)
committerEvan Prodromou <evan@prodromou.name>
Thu, 15 May 2008 16:28:44 +0000 (12:28 -0400)
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

17 files changed:
actions/password.php [new file with mode: 0644]
actions/profilesettings.php [new file with mode: 0644]
actions/settings.php [deleted file]
actions/shownotice.php
actions/showstream.php
actions/subscribed.php
actions/subscriptions.php
classes/Avatar.php [new file with mode: 0644]
classes/Notice.php
classes/Profile.php
classes/User.php
classes/stoica.ini
db/stoica.sql
doc/TODO
lib/common.php
lib/settingsaction.php [new file with mode: 0644]
lib/stream.php

diff --git a/actions/password.php b/actions/password.php
new file mode 100644 (file)
index 0000000..31831d3
--- /dev/null
@@ -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 (file)
index 0000000..b87cea7
--- /dev/null
@@ -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/settings.php b/actions/settings.php
deleted file mode 100644 (file)
index ea8074e..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<?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 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();
-               }
index b0128e6e967831a8b63948273760c4c941feebc9..2e14963bb5a876410589526ca026babda781bde1 100644 (file)
@@ -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'),
index d476bd297b40485d0be378c0f8d1b94acf66b67b..8272e1038477c347664433533da1d742b316481f 100644 (file)
@@ -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) {
index 924e2b67b3b6c4829bf00c781d1fe9b8c247c27c..f24470f0297529e9f2ad909fa7074651aea9a32c 100644 (file)
@@ -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');
index 653c6d2b5e5c2d0b52a016256f86bb73ae723702..87b8a4e48593415b67a42d5782dba3b7cbd6d096 100644 (file)
@@ -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 (file)
index 0000000..222725b
--- /dev/null
@@ -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
+}
index 94b06deb382264e31965d9eee4c8e7954161358a..538ae9c01b2d03afa1927aea4cf678009fa8b4c7 100644 (file)
@@ -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
index a5a565c699d9bf4b73bec0356386f5f8559f7e71..4f756856ec1dadbfe27b23353ce1ab83544f773a 100644 (file)
@@ -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;
+               }
+       }
 }
index e7d297b3c6947eaab61481301b88d85f5deb897d..2b8610a6520c6fd5a259743aa0e0c8d4901f5a33 100644 (file)
@@ -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
index 52686bcb56418b238c13362a3a286a6795dd5c6b..6c951fcb9e4969d16edeed3c3fcf491807f0d601 100644 (file)
@@ -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
index 28e8f7662c32d455f43752b2aa22e1baa61ebdcb..7ee8f6ff8b9bc32b4bf7ab0fb3105b73b3201cb6 100644 (file)
@@ -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 (
index 8b3fe5b75f695216aa7aef4cd8355aea902740eb..cd9ac05df055afef86c575f01047d47bdb3ac18a 100644 (file)
--- a/doc/TODO
+++ b/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
index 53cd66cbecd2628396f52d6ead7f7ad5f780ec9b..f30096796b1fc641ac2c5ebcf395d8af232a3d89 100644 (file)
 
 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/lib/settingsaction.php b/lib/settingsaction.php
new file mode 100644 (file)
index 0000000..db07bde
--- /dev/null
@@ -0,0 +1,38 @@
+<?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 SettingsAction extends Action {
+
+       function handle($args) {
+               parent::handle($args);
+       }
+
+       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');
+       }
+}
index b659eb8b5b171175fe5f8af8db054d2f0c356102..9129693804ef75c93f24915902c0b8bd7f72a32f 100644 (file)
@@ -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'),