diff options
Diffstat (limited to 'actions')
57 files changed, 1289 insertions, 956 deletions
diff --git a/actions/all.php b/actions/all.php index 428466f24..08dcccbdd 100644 --- a/actions/all.php +++ b/actions/all.php @@ -42,6 +42,9 @@ class AllAction extends Action if (!$this->page) { $this->page = 1; } + + common_set_returnto($this->selfUrl()); + return true; } @@ -66,13 +69,22 @@ class AllAction extends Action } } - function showFeeds() + function getFeeds() { - $this->element('link', array('rel' => 'alternate', - 'href' => common_local_url('allrss', array('nickname' => - $this->user->nickname)), - 'type' => 'application/rss+xml', - 'title' => sprintf(_('Feed for friends of %s'), $this->user->nickname))); + return array(new Feed(Feed::RSS1, + common_local_url('allrss', array('nickname' => + $this->user->nickname)), + sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)), + new Feed(Feed::RSS2, + common_local_url('api', array('apiaction' => 'statuses', + 'method' => 'friends', + 'argument' => $this->user->nickname.'.rss')), + sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)), + new Feed(Feed::ATOM, + common_local_url('api', array('apiaction' => 'statuses', + 'method' => 'friends', + 'argument' => $this->user->nickname.'.atom')), + sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname))); } function showLocalNav() @@ -81,15 +93,6 @@ class AllAction extends Action $nav->show(); } - function showExportData() - { - $fl = new FeedList($this); - $fl->show(array(0=>array('href'=>common_local_url('allrss', array('nickname' => $this->user->nickname)), - 'type' => 'rss', - 'version' => 'RSS 1.0', - 'item' => 'allrss'))); - } - function showContent() { $notice = $this->user->noticesWithFriends(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); @@ -101,4 +104,15 @@ class AllAction extends Action $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->page, 'all', array('nickname' => $this->user->nickname)); } + + function showPageTitle() + { + $user =& common_current_user(); + if ($user && ($user->id == $this->user->id)) { + $this->element('h1', NULL, _("You and friends")); + } else { + $this->element('h1', NULL, sprintf(_('%s and friends'), $this->user->nickname)); + } + } + } diff --git a/actions/allrss.php b/actions/allrss.php index 05787f3f7..0114c4396 100644 --- a/actions/allrss.php +++ b/actions/allrss.php @@ -53,7 +53,9 @@ class AllrssAction extends Rss10Action /** * Initialization. - * + * + * @param array $args Web and URL arguments + * * @return boolean false if user doesn't exist */ function prepare($args) @@ -81,7 +83,7 @@ class AllrssAction extends Rss10Action { $user = $this->user; $notice = $user->noticesWithFriends(0, $limit); - + while ($notice->fetch()) { $notices[] = clone($notice); } @@ -104,7 +106,8 @@ class AllrssAction extends Rss10Action 'link' => common_local_url('all', array('nickname' => $user->nickname)), - 'description' => sprintf(_('Feed for friends of %s'), $user->nickname)); + 'description' => sprintf(_('Feed for friends of %s'), + $user->nickname)); return $c; } @@ -123,10 +126,5 @@ class AllrssAction extends Rss10Action $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); return $avatar ? $avatar->url : null; } - - function isReadOnly() - { - return true; - } } diff --git a/actions/api.php b/actions/api.php index 21fe4eea3..a27d24492 100644 --- a/actions/api.php +++ b/actions/api.php @@ -131,14 +131,14 @@ class ApiAction extends Action 'statuses/followers', 'favorites/favorites'); - # If the site is "private", all API methods need authentication - + $fullname = "$this->api_action/$this->api_method"; + + // If the site is "private", all API methods except laconica/config + // need authentication if (common_config('site', 'private')) { - return true; + return $fullname != 'laconica/config' || false; } - $fullname = "$this->api_action/$this->api_method"; - if (in_array($fullname, $bareauth)) { # bareauth: only needs auth if without an argument if ($this->api_arg) { diff --git a/actions/avatarbynickname.php b/actions/avatarbynickname.php index 9bbdecefa..ca58c9653 100644 --- a/actions/avatarbynickname.php +++ b/actions/avatarbynickname.php @@ -90,9 +90,9 @@ class AvatarbynicknameAction extends Action $url = $avatar->url; } else { if ($size == 'original') { - $url = common_default_avatar(AVATAR_PROFILE_SIZE); + $url = Avatar::defaultImage(AVATAR_PROFILE_SIZE); } else { - $url = common_default_avatar($size+0); + $url = Avatar::defaultImage($size+0); } } common_redirect($url, 302); diff --git a/actions/avatarsettings.php b/actions/avatarsettings.php index 19f53b882..c2bb35a39 100644 --- a/actions/avatarsettings.php +++ b/actions/avatarsettings.php @@ -34,6 +34,8 @@ if (!defined('LACONICA')) { require_once INSTALLDIR.'/lib/accountsettingsaction.php'; +define('MAX_ORIGINAL', 480); + /** * Upload an avatar * @@ -73,7 +75,7 @@ class AvatarsettingsAction extends AccountSettingsAction function getInstructions() { - return _('You can upload your personal avatar.'); + return sprintf(_('You can upload your personal avatar. The maximum file size is %s.'), ImageFile::maxFileSize()); } /** @@ -143,6 +145,7 @@ class AvatarsettingsAction extends AccountSettingsAction 'height' => AVATAR_PROFILE_SIZE, 'alt' => $user->nickname)); $this->elementEnd('div'); + $this->submit('delete', _('Delete')); $this->elementEnd('li'); } @@ -153,7 +156,7 @@ class AvatarsettingsAction extends AccountSettingsAction $this->element('input', array('name' => 'MAX_FILE_SIZE', 'type' => 'hidden', 'id' => 'MAX_FILE_SIZE', - 'value' => MAX_AVATAR_SIZE)); + 'value' => ImageFile::maxFileSizeInt())); $this->elementEnd('li'); $this->elementEnd('ul'); @@ -198,7 +201,7 @@ class AvatarsettingsAction extends AccountSettingsAction 'class' => 'avatar_view')); $this->element('h2', null, _("Original")); $this->elementStart('div', array('id'=>'avatar_original_view')); - $this->element('img', array('src' => common_avatar_url($this->filedata['filename']), + $this->element('img', array('src' => Avatar::url($this->filedata['filename']), 'width' => $this->filedata['width'], 'height' => $this->filedata['height'], 'alt' => $user->nickname)); @@ -210,7 +213,7 @@ class AvatarsettingsAction extends AccountSettingsAction 'class' => 'avatar_view')); $this->element('h2', null, _("Preview")); $this->elementStart('div', array('id'=>'avatar_preview_view')); - $this->element('img', array('src' => common_avatar_url($this->filedata['filename']), + $this->element('img', array('src' => Avatar::url($this->filedata['filename']), 'width' => AVATAR_PROFILE_SIZE, 'height' => AVATAR_PROFILE_SIZE, 'alt' => $user->nickname)); @@ -254,6 +257,8 @@ class AvatarsettingsAction extends AccountSettingsAction $this->uploadAvatar(); } else if ($this->arg('crop')) { $this->cropAvatar(); + } else if ($this->arg('delete')) { + $this->deleteAvatar(); } else { $this->showForm(_('Unexpected form submission.')); } @@ -279,14 +284,14 @@ class AvatarsettingsAction extends AccountSettingsAction $cur = common_current_user(); - $filename = common_avatar_filename($cur->id, - image_type_to_extension($imagefile->type), - null, - 'tmp'.common_timestamp()); + $filename = Avatar::filename($cur->id, + image_type_to_extension($imagefile->type), + null, + 'tmp'.common_timestamp()); - $filepath = common_avatar_path($filename); + $filepath = Avatar::path($filename); - move_uploaded_file($imagefile->filename, $filepath); + move_uploaded_file($imagefile->filepath, $filepath); $filedata = array('filename' => $filename, 'filepath' => $filepath, @@ -312,15 +317,6 @@ class AvatarsettingsAction extends AccountSettingsAction function cropAvatar() { - $user = common_current_user(); - - $profile = $user->getProfile(); - - $x = $this->arg('avatar_crop_x'); - $y = $this->arg('avatar_crop_y'); - $w = $this->arg('avatar_crop_w'); - $h = $this->arg('avatar_crop_h'); - $filedata = $_SESSION['FILEDATA']; if (!$filedata) { @@ -328,75 +324,54 @@ class AvatarsettingsAction extends AccountSettingsAction return; } - $filepath = common_avatar_path($filedata['filename']); - - if (!file_exists($filepath)) { - $this->serverError(_('Lost our file.')); - return; - } + $file_d = ($filedata['width'] > $filedata['height']) + ? $filedata['height'] : $filedata['width']; - switch ($filedata['type']) { - case IMAGETYPE_GIF: - $image_src = imagecreatefromgif($filepath); - break; - case IMAGETYPE_JPEG: - $image_src = imagecreatefromjpeg($filepath); - break; - case IMAGETYPE_PNG: - $image_src = imagecreatefrompng($filepath); - break; - default: - $this->serverError(_('Unknown file type')); - return; - } - - common_debug("W = $w, H = $h, X = $x, Y = $y"); - - $image_dest = imagecreatetruecolor($w, $h); - - $background = imagecolorallocate($image_dest, 0, 0, 0); - ImageColorTransparent($image_dest, $background); - imagealphablending($image_dest, false); - - imagecopyresized($image_dest, $image_src, 0, 0, $x, $y, $w, $h, $w, $h); - - $cur = common_current_user(); - - $filename = common_avatar_filename($cur->id, - image_type_to_extension($filedata['type']), - null, - common_timestamp()); - - $filepath = common_avatar_path($filename); - - switch ($filedata['type']) { - case IMAGETYPE_GIF: - imagegif($image_dest, $filepath); - break; - case IMAGETYPE_JPEG: - imagejpeg($image_dest, $filepath); - break; - case IMAGETYPE_PNG: - imagepng($image_dest, $filepath); - break; - default: - $this->serverError(_('Unknown file type')); - return; - } + $dest_x = $this->arg('avatar_crop_x') ? $this->arg('avatar_crop_x'):0; + $dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0; + $dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$file_d; + $dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$file_d; + $size = min($dest_w, $dest_h, MAX_ORIGINAL); $user = common_current_user(); + $profile = $user->getProfile(); - $profile = $cur->getProfile(); + $imagefile = new ImageFile($user->id, $filedata['filepath']); + $filename = $imagefile->resize($size, $dest_x, $dest_y, $dest_w, $dest_h); - if ($profile->setOriginal($filepath)) { - @unlink(common_avatar_path($filedata['filename'])); + if ($profile->setOriginal($filename)) { + @unlink($filedata['filepath']); unset($_SESSION['FILEDATA']); $this->mode = 'upload'; $this->showForm(_('Avatar updated.'), true); + common_broadcast_profile($profile); } else { $this->showForm(_('Failed updating avatar.')); } } + + /** + * Get rid of the current avatar. + * + * @return void + */ + + function deleteAvatar() + { + $user = common_current_user(); + $profile = $user->getProfile(); + + $avatar = $profile->getOriginalAvatar(); + $avatar->delete(); + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + $avatar->delete(); + $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); + $avatar->delete(); + $avatar = $profile->getAvatar(AVATAR_MINI_SIZE); + $avatar->delete(); + + $this->showForm(_('Avatar deleted.'), true); + } /** * Add the jCrop stylesheet @@ -426,12 +401,14 @@ class AvatarsettingsAction extends AccountSettingsAction { parent::showScripts(); - $jcropPack = common_path('js/jcrop/jquery.Jcrop.pack.js'); - $jcropGo = common_path('js/jcrop/jquery.Jcrop.go.js'); + if ($this->mode == 'crop') { + $jcropPack = common_path('js/jcrop/jquery.Jcrop.pack.js'); + $jcropGo = common_path('js/jcrop/jquery.Jcrop.go.js'); - $this->element('script', array('type' => 'text/javascript', - 'src' => $jcropPack)); - $this->element('script', array('type' => 'text/javascript', - 'src' => $jcropGo)); + $this->element('script', array('type' => 'text/javascript', + 'src' => $jcropPack)); + $this->element('script', array('type' => 'text/javascript', + 'src' => $jcropGo)); + } } } diff --git a/actions/deletenotice.php b/actions/deletenotice.php index efef95441..16e2df889 100644 --- a/actions/deletenotice.php +++ b/actions/deletenotice.php @@ -71,7 +71,7 @@ class DeletenoticeAction extends DeleteAction function getInstructions() { return _('You are about to permanently delete a notice. ' . - 'Once this is done, it cannot be undone.'); + 'Once this is done, it cannot be undone.'); } function title() @@ -103,24 +103,18 @@ class DeletenoticeAction extends DeleteAction function showContent() { - $this->elementStart('form', array('id' => 'notice_delete_form', - 'method' => 'post', - 'action' => common_local_url('deletenotice'))); + $this->elementStart('form', array('id' => 'form_notice_delete', + 'class' => 'form_settings', + 'method' => 'post', + 'action' => common_local_url('deletenotice'))); + $this->elementStart('fieldset'); + $this->element('legend', null, _('Delete notice')); $this->hidden('token', common_session_token()); $this->hidden('notice', $this->trimmed('notice')); - $this->elementStart('p'); - $this->element('span', array('id' => 'confirmation_text'), - _('Are you sure you want to delete this notice?')); - - $this->element('input', array('id' => 'submit_no', - 'name' => 'submit', - 'type' => 'submit', - 'value' => _('No'))); - $this->element('input', array('id' => 'submit_yes', - 'name' => 'submit', - 'type' => 'submit', - 'value' => _('Yes'))); - $this->elementEnd('p'); + $this->element('p', null, _('Are you sure you want to delete this notice?')); + $this->submit('form_action-yes', _('Yes'), 'submit form_action-primary', 'yes'); + $this->submit('form_action-no', _('No'), 'submit form_action-secondary', 'no'); + $this->elementEnd('fieldset'); $this->elementEnd('form'); } @@ -131,35 +125,22 @@ class DeletenoticeAction extends DeleteAction if (!$token || $token != common_session_token()) { $this->showForm(_('There was a problem with your session token. ' . - ' Try again, please.')); + ' Try again, please.')); return; } - $url = common_get_returnto(); - $confirmed = $this->trimmed('submit'); - - if ($confirmed == _('Yes')) { - - $replies = new Reply; - $replies->get('notice_id', $this->notice->id); - - common_dequeue_notice($this->notice); - - if (common_config('memcached', 'enabled')) { - $notice->blowSubsCache(); - } - - $replies->delete(); + if ($this->arg('yes')) { $this->notice->delete(); + } - } else { + $url = common_get_returnto(); - if ($url) { - common_set_returnto(null); - } else { - $url = common_local_url('public'); - } + if ($url) { + common_set_returnto(null); + } else { + $url = common_local_url('public'); } + common_redirect($url); } } diff --git a/actions/disfavor.php b/actions/disfavor.php index 09b3bf18d..90bab3cca 100644 --- a/actions/disfavor.php +++ b/actions/disfavor.php @@ -89,7 +89,7 @@ class DisfavorAction extends Action } $user->blowFavesCache(); if ($this->boolean('ajax')) { - $this->startHTML('text/xml;charset=utf-8', true); + $this->startHTML('text/xml;charset=utf-8'); $this->elementStart('head'); $this->element('title', null, _('Add to favorites')); $this->elementEnd('head'); diff --git a/actions/doc.php b/actions/doc.php index 6957659ad..ebffb7c15 100644 --- a/actions/doc.php +++ b/actions/doc.php @@ -50,7 +50,7 @@ class DocAction extends Action /** * Class handler. - * + * * @param array $args array of arguments * * @return nothing @@ -59,7 +59,7 @@ class DocAction extends Action { parent::handle($args); $this->title = $this->trimmed('title'); - $this->filename = INSTALLDIR.'/doc/'.$this->title; + $this->filename = INSTALLDIR.'/doc-src/'.$this->title; if (!file_exists($this->filename)) { $this->clientError(_('No such document.')); return; @@ -71,14 +71,14 @@ class DocAction extends Action function showPageTitle() { $this->element('h1', array('class' => 'entry-title'), $this->title()); } - + // overrided to add hentry, and content-inner classes function showContentBlock() { $this->elementStart('div', array('id' => 'content', 'class' => 'hentry')); $this->showPageTitle(); $this->showPageNoticeBlock(); - $this->elementStart('div', array('id' => 'content_inner', + $this->elementStart('div', array('id' => 'content_inner', 'class' => 'entry-content')); // show the actual content (forms, lists, whatever) $this->showContent(); @@ -88,7 +88,7 @@ class DocAction extends Action /** * Display content. - * + * * @return nothing */ function showContent() @@ -100,7 +100,7 @@ class DocAction extends Action /** * Page title. - * + * * @return page title */ function title() diff --git a/actions/editgroup.php b/actions/editgroup.php index 98ebcb87a..e7e79040a 100644 --- a/actions/editgroup.php +++ b/actions/editgroup.php @@ -191,13 +191,13 @@ class EditgroupAction extends Action array('http', 'https')))) { $this->showForm(_('Homepage is not a valid URL.')); return; - } else if (!is_null($fullname) && strlen($fullname) > 255) { + } else if (!is_null($fullname) && mb_strlen($fullname) > 255) { $this->showForm(_('Full name is too long (max 255 chars).')); return; - } else if (!is_null($description) && strlen($description) > 140) { + } else if (!is_null($description) && mb_strlen($description) > 140) { $this->showForm(_('description is too long (max 140 chars).')); return; - } else if (!is_null($location) && strlen($location) > 255) { + } else if (!is_null($location) && mb_strlen($location) > 255) { $this->showForm(_('Location is too long (max 255 chars).')); return; } diff --git a/actions/emailsettings.php b/actions/emailsettings.php index b84acb214..634388fdd 100644 --- a/actions/emailsettings.php +++ b/actions/emailsettings.php @@ -164,6 +164,11 @@ class EmailsettingsAction extends AccountSettingsAction $user->emailnotifymsg); $this->elementEnd('li'); $this->elementStart('li'); + $this->checkbox('emailnotifyattn', + _('Send me email when someone sends me an "@-reply".'), + $user->emailnotifyattn); + $this->elementEnd('li'); + $this->elementStart('li'); $this->checkbox('emailnotifynudge', _('Allow friends to nudge me and send me an email.'), $user->emailnotifynudge); @@ -255,6 +260,7 @@ class EmailsettingsAction extends AccountSettingsAction $emailnotifyfav = $this->boolean('emailnotifyfav'); $emailnotifymsg = $this->boolean('emailnotifymsg'); $emailnotifynudge = $this->boolean('emailnotifynudge'); + $emailnotifyattn = $this->boolean('emailnotifyattn'); $emailmicroid = $this->boolean('emailmicroid'); $emailpost = $this->boolean('emailpost'); @@ -270,6 +276,7 @@ class EmailsettingsAction extends AccountSettingsAction $user->emailnotifyfav = $emailnotifyfav; $user->emailnotifymsg = $emailnotifymsg; $user->emailnotifynudge = $emailnotifynudge; + $user->emailnotifyattn = $emailnotifyattn; $user->emailmicroid = $emailmicroid; $user->emailpost = $emailpost; diff --git a/actions/facebookhome.php b/actions/facebookhome.php index 3558cf671..5946e6c98 100644 --- a/actions/facebookhome.php +++ b/actions/facebookhome.php @@ -43,11 +43,11 @@ class FacebookhomeAction extends FacebookAction function handle($args) { parent::handle($args); - + // If the user has opted not to initially allow the app to have // Facebook status update permission, store that preference. Only // promt the user the first time she uses the app - if ($this->arg('skip')) { + if ($this->arg('skip') || $args['fb_sig_request_method'] == 'GET') { $this->facebook->api_client->data_setUserPreference( FACEBOOK_PROMPTED_UPDATE_PREF, 'true'); } @@ -89,7 +89,7 @@ class FacebookhomeAction extends FacebookAction } } - + function login() { @@ -122,7 +122,6 @@ class FacebookhomeAction extends FacebookAction // XXX: Do some error handling here $this->setDefaults(); - //$this->showHome($flink, _('You can now use Identi.ca from Facebook!')); $this->getUpdatePermission(); return; @@ -133,6 +132,7 @@ class FacebookhomeAction extends FacebookAction } $this->showLoginForm($msg); + $this->showFooter(); } @@ -148,7 +148,6 @@ class FacebookhomeAction extends FacebookAction function showNoticeForm() { - $post_action = "$this->app_uri/index.php"; $notice_form = new FacebookNoticeForm($this, $post_action, null, @@ -167,7 +166,6 @@ class FacebookhomeAction extends FacebookAction function showContent() { - $notice = $this->user->noticesWithFriends(($this->page-1) * NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); @@ -177,7 +175,6 @@ class FacebookhomeAction extends FacebookAction $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->page, 'index.php', array('nickname' => $this->user->nickname)); - } function showNoticeList($notice) @@ -191,7 +188,7 @@ class FacebookhomeAction extends FacebookAction $this->showStylesheets(); - $this->elementStart('div', array('class' => 'content')); + $this->elementStart('div', array('class' => 'facebook_guide')); $instructions = sprintf(_('If you would like the %s app to automatically update ' . 'your Facebook status with your latest notice, you need ' . @@ -207,11 +204,19 @@ class FacebookhomeAction extends FacebookAction $this->elementStart('ul', array('id' => 'fb-permissions-list')); $this->elementStart('li', array('id' => 'fb-permissions-item')); - $this->elementStart('fb:prompt-permission', array('perms' => 'status_update', - 'next_fbjs' => 'document.setLocation(\'' . "$this->app_uri/index.php" . '\')')); - $this->element('span', array('class' => 'facebook-button'), - sprintf(_('Allow %s to update my Facebook status'), $this->app_name)); - $this->elementEnd('fb:prompt-permission'); + + $next = urlencode("$this->app_uri/index.php"); + $api_key = common_config('facebook', 'apikey'); + + $auth_url = 'http://www.facebook.com/authorize.php?api_key=' . + $api_key . '&v=1.0&ext_perm=status_update&next=' . $next . + '&next_cancel=' . $next . '&submit=skip'; + + $this->elementStart('span', array('class' => 'facebook-button')); + $this->element('a', array('href' => $auth_url), + sprintf(_('Okay, do it!'), $this->app_name)); + $this->elementEnd('span'); + $this->elementEnd('li'); $this->elementStart('li', array('id' => 'fb-permissions-item')); @@ -223,53 +228,6 @@ class FacebookhomeAction extends FacebookAction $this->elementEnd('div'); } - - function saveNewNotice() - { - - $user = $this->flink->getUser(); - - $content = $this->trimmed('status_textarea'); - - if (!$content) { - $this->showPage(_('No notice content!')); - return; - } else { - $content_shortened = common_shorten_links($content); - - if (mb_strlen($content_shortened) > 140) { - common_debug("Content = '$content_shortened'", __FILE__); - common_debug("mb_strlen(\$content) = " . mb_strlen($content_shortened), __FILE__); - $this->showPage(_('That\'s too long. Max notice size is 140 chars.')); - return; - } - } - - $inter = new CommandInterpreter(); - - $cmd = $inter->handle_command($user, $content_shortened); - - if ($cmd) { - - // XXX fix this - - $cmd->execute(new WebChannel()); - return; - } - - $replyto = $this->trimmed('inreplyto'); - - $notice = Notice::saveNew($user->id, $content, - 'Facebook', 1, ($replyto == 'false') ? null : $replyto); - - if (is_string($notice)) { - $this->showPage($notice); - return; - } - - common_broadcast_notice($notice); - - } /** * Generate pagination links diff --git a/actions/facebookinvite.php b/actions/facebookinvite.php index b7224783a..1302064ad 100644 --- a/actions/facebookinvite.php +++ b/actions/facebookinvite.php @@ -71,13 +71,13 @@ class FacebookinviteAction extends FacebookAction common_config('site', 'name'))); $this->element('p', null, _('Invitations have been sent to the following users:')); - $friend_ids = $_POST['ids']; // XXX: Hmm... is this the best way to acces the list? + $friend_ids = $_POST['ids']; // XXX: Hmm... is this the best way to access the list? - $this->elementStart("ul"); + $this->elementStart('ul', array('id' => 'facebook-friends')); foreach ($friend_ids as $friend) { $this->elementStart('li'); - $this->element('fb:profile-pic', array('uid' => $friend)); + $this->element('fb:profile-pic', array('uid' => $friend, 'size' => 'square')); $this->element('fb:name', array('uid' => $friend, 'capitalize' => 'true')); $this->elementEnd('li'); @@ -92,6 +92,12 @@ class FacebookinviteAction extends FacebookAction // Get a list of users who are already using the app for exclusion $exclude_ids = $this->facebook->api_client->friends_getAppUsers(); + $exclude_ids_csv = null; + + // fbml needs these as a csv string, not an array + if ($exclude_ids) { + $exclude_ids_csv = implode(',', $exclude_ids); + } $content = sprintf(_('You have been invited to %s'), common_config('site', 'name')) . htmlentities('<fb:req-choice url="' . $this->app_uri . '" label="Add"/>'); @@ -103,20 +109,27 @@ class FacebookinviteAction extends FacebookAction 'content' => $content)); $this->hidden('invite', 'true'); $actiontext = sprintf(_('Invite your friends to use %s'), common_config('site', 'name')); - $this->element('fb:multi-friend-selector', array('showborder' => 'false', - 'actiontext' => $actiontext, - 'exclude_ids' => implode(',', $exclude_ids), - 'bypass' => 'cancel')); + + $multi_params = array('showborder' => 'false'); + $multi_params['actiontext'] = $actiontext; + + if ($exclude_ids_csv) { + $multi_params['exclude_ids'] = $exclude_ids_csv; + } + + $multi_params['bypass'] = 'cancel'; + + $this->element('fb:multi-friend-selector', $multi_params); $this->elementEnd('fb:request-form'); $this->element('h2', null, sprintf(_('Friends already using %s:'), common_config('site', 'name'))); - $this->elementStart("ul"); - + $this->elementStart('ul', array('id' => 'facebook-friends')); + foreach ($exclude_ids as $friend) { $this->elementStart('li'); - $this->element('fb:profile-pic', array('uid' => $friend)); + $this->element('fb:profile-pic', array('uid' => $friend, 'size' => 'square')); $this->element('fb:name', array('uid' => $friend, 'capitalize' => 'true')); $this->elementEnd('li'); diff --git a/actions/favor.php b/actions/favor.php index 8ecde4b11..3940df688 100644 --- a/actions/favor.php +++ b/actions/favor.php @@ -89,7 +89,7 @@ class FavorAction extends Action $this->notify($notice, $user); $user->blowFavesCache(); if ($this->boolean('ajax')) { - $this->startHTML('text/xml;charset=utf-8', true); + $this->startHTML('text/xml;charset=utf-8'); $this->elementStart('head'); $this->element('title', null, _('Disfavor favorite')); $this->elementEnd('head'); diff --git a/actions/favorited.php b/actions/favorited.php index 4155b3a23..5082f4a4e 100644 --- a/actions/favorited.php +++ b/actions/favorited.php @@ -104,6 +104,9 @@ class FavoritedAction extends Action { parent::prepare($args); $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + + common_set_returnto($this->selfUrl()); + return true; } @@ -166,8 +169,14 @@ class FavoritedAction extends Action function showContent() { + if (common_config('db', 'type') == 'pgsql') { + $weightexpr='sum(exp(-extract(epoch from (now() - fave.modified)) / %s))'; + } else { + $weightexpr='sum(exp(-(now() - fave.modified) / %s))'; + } + $qry = 'SELECT notice.*, '. - 'sum(exp(-(now() - fave.modified) / %s)) as weight ' . + $weightexpr . ' as weight ' . 'FROM notice JOIN fave ON notice.id = fave.notice_id ' . 'GROUP BY fave.notice_id ' . 'ORDER BY weight DESC'; diff --git a/actions/favoritesrss.php b/actions/favoritesrss.php index ad72dce9e..f85bf1b19 100644 --- a/actions/favoritesrss.php +++ b/actions/favoritesrss.php @@ -44,22 +44,32 @@ require_once INSTALLDIR.'/lib/rssaction.php'; * @package Laconica * @author Evan Prodromou <evan@controlyourself.ca> * @author Robin Millette <millette@controlyourself.ca> + * @author Zach Copley <zach@controlyourself.ca> * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @link http://laconi.ca/ */ class FavoritesrssAction extends Rss10Action { - var $user = null; + /** The user whose favorites to display */ + + var $user = null; + /** - * Initialization. - * - * @return boolean false if user doesn't exist + * Find the user to display by supplied nickname + * + * @param array $args Arguments from $_REQUEST + * + * @return boolean success */ - function init() + + function prepare($args) { + parent::prepare($args); + $nickname = $this->trimmed('nickname'); $this->user = User::staticGet('nickname', $nickname); + if (!$this->user) { $this->clientError(_('No such user.')); return false; @@ -67,7 +77,7 @@ class FavoritesrssAction extends Rss10Action return true; } } - + /** * Get notices * @@ -94,30 +104,26 @@ class FavoritesrssAction extends Rss10Action function getChannel() { $user = $this->user; - $c = array('url' => common_local_url('favoritesrss', - array('nickname' => - $user->nickname)), + $c = array('url' => common_local_url('favoritesrss', + array('nickname' => + $user->nickname)), 'title' => sprintf(_("%s favorite notices"), $user->nickname), 'link' => common_local_url('showfavorites', - array('nickname' => - $user->nickname)), - 'description' => sprintf(_('Feed of favorite notices of %s'), $user->nickname)); + array('nickname' => + $user->nickname)), + 'description' => sprintf(_('Feed of favorite notices of %s'), + $user->nickname)); return $c; } /** * Get image. * - * @return voir + * @return void */ function getImage() { return null; } - function isReadOnly() - { - return true; - } } - diff --git a/actions/finishopenidlogin.php b/actions/finishopenidlogin.php index 880a9505b..6d92cb9aa 100644 --- a/actions/finishopenidlogin.php +++ b/actions/finishopenidlogin.php @@ -30,7 +30,7 @@ class FinishopenidloginAction extends Action function handle($args) { parent::handle($args); - if (common_logged_in()) { + if (common_is_real_login()) { $this->clientError(_('Already logged in.')); } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { $token = $this->trimmed('token'); @@ -83,7 +83,7 @@ class FinishopenidloginAction extends Action function showContent() { - if ($this->message_text) { + if (!empty($this->message_text)) { $this->element('p', null, $this->message); return; } @@ -232,7 +232,8 @@ class FinishopenidloginAction extends Action return; } - if ($sreg['country']) { + $location = ''; + if (!empty($sreg['country'])) { if ($sreg['postcode']) { # XXX: use postcode to get city and region # XXX: also, store postcode somewhere -- it's valuable! @@ -242,12 +243,16 @@ class FinishopenidloginAction extends Action } } - if ($sreg['fullname'] && strlen($sreg['fullname']) <= 255) { + if (!empty($sreg['fullname']) && mb_strlen($sreg['fullname']) <= 255) { $fullname = $sreg['fullname']; + } else { + $fullname = ''; } - if ($sreg['email'] && Validate::email($sreg['email'], true)) { + if (!empty($sreg['email']) && Validate::email($sreg['email'], true)) { $email = $sreg['email']; + } else { + $email = ''; } # XXX: add language @@ -328,7 +333,7 @@ class FinishopenidloginAction extends Action # Try the passed-in nickname - if ($sreg['nickname']) { + if (!empty($sreg['nickname'])) { $nickname = $this->nicknamize($sreg['nickname']); if ($this->isNewNickname($nickname)) { return $nickname; @@ -337,7 +342,7 @@ class FinishopenidloginAction extends Action # Try the full name - if ($sreg['fullname']) { + if (!empty($sreg['fullname'])) { $fullname = $this->nicknamize($sreg['fullname']); if ($this->isNewNickname($fullname)) { return $fullname; diff --git a/actions/finishremotesubscribe.php b/actions/finishremotesubscribe.php index f9094a50c..acfacbdc1 100644 --- a/actions/finishremotesubscribe.php +++ b/actions/finishremotesubscribe.php @@ -237,7 +237,13 @@ class FinishremotesubscribeAction extends Action { $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); copy($url, $temp_filename); - return $profile->setOriginal($temp_filename); + $imagefile = new ImageFile($profile->id, $temp_filename); + $filename = Avatar::filename($profile->id, + image_type_to_extension($imagefile->type), + null, + common_timestamp()); + rename($temp_filename, Avatar::path($filename)); + return $profile->setOriginal($filename); } function access_token($omb) @@ -277,7 +283,7 @@ class FinishremotesubscribeAction extends Action $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); $result = $fetcher->post($req->get_normalized_http_url(), $req->to_postdata(), - array('User-Agent' => 'Laconica/' . LACONICA_VERSION)); + array('User-Agent: Laconica/' . LACONICA_VERSION)); common_debug('got result: "'.print_r($result,true).'"', __FILE__); diff --git a/actions/grouplogo.php b/actions/grouplogo.php index fe42ab36c..499db4ae8 100644 --- a/actions/grouplogo.php +++ b/actions/grouplogo.php @@ -34,6 +34,8 @@ if (!defined('LACONICA')) { require_once INSTALLDIR.'/lib/accountsettingsaction.php'; +define('MAX_ORIGINAL', 480); + /** * Upload an avatar * @@ -53,6 +55,8 @@ class GrouplogoAction extends Action var $mode = null; var $imagefile = null; var $filename = null; + var $msg = null; + var $success = null; /** * Prepare to run @@ -121,9 +125,11 @@ class GrouplogoAction extends Action } } - function showForm($msg = null) + function showForm($msg = null, $success = false) { - $this->msg = $msg; + $this->msg = $msg; + $this->success = $success; + $this->showPage(); } @@ -146,7 +152,7 @@ class GrouplogoAction extends Action function getInstructions() { - return _('You can upload a logo image for your group.'); + return sprintf(_('You can upload a logo image for your group. The maximum file size is %s.'), ImageFile::maxFileSize()); } /** @@ -223,7 +229,7 @@ class GrouplogoAction extends Action $this->element('input', array('name' => 'MAX_FILE_SIZE', 'type' => 'hidden', 'id' => 'MAX_FILE_SIZE', - 'value' => MAX_AVATAR_SIZE)); + 'value' => ImageFile::maxFileSizeInt())); $this->elementEnd('li'); $this->elementEnd('ul'); @@ -257,7 +263,7 @@ class GrouplogoAction extends Action 'class' => 'avatar_view')); $this->element('h2', null, _("Original")); $this->elementStart('div', array('id'=>'avatar_original_view')); - $this->element('img', array('src' => common_avatar_url($this->filedata['filename']), + $this->element('img', array('src' => Avatar::url($this->filedata['filename']), 'width' => $this->filedata['width'], 'height' => $this->filedata['height'], 'alt' => $this->group->nickname)); @@ -269,7 +275,7 @@ class GrouplogoAction extends Action 'class' => 'avatar_view')); $this->element('h2', null, _("Preview")); $this->elementStart('div', array('id'=>'avatar_preview_view')); - $this->element('img', array('src' => common_avatar_url($this->filedata['filename']), + $this->element('img', array('src' => Avatar::url($this->filedata['filename']), 'width' => AVATAR_PROFILE_SIZE, 'height' => AVATAR_PROFILE_SIZE, 'alt' => $this->group->nickname)); @@ -281,6 +287,7 @@ class GrouplogoAction extends Action 'type' => 'hidden', 'id' => $crop_info)); } + $this->submit('crop', _('Crop')); $this->elementEnd('li'); @@ -310,9 +317,9 @@ class GrouplogoAction extends Action } if ($this->arg('upload')) { - $this->uploadAvatar(); + $this->uploadLogo(); } else if ($this->arg('crop')) { - $this->cropAvatar(); + $this->cropLogo(); } else { $this->showForm(_('Unexpected form submission.')); } @@ -327,7 +334,7 @@ class GrouplogoAction extends Action * @return void */ - function uploadAvatar() + function uploadLogo() { try { $imagefile = ImageFile::fromUpload('avatarfile'); @@ -336,14 +343,14 @@ class GrouplogoAction extends Action return; } - $filename = common_avatar_filename($this->group->id, - image_type_to_extension($imagefile->type), - null, - 'group-temp-'.common_timestamp()); + $filename = Avatar::filename($this->group->id, + image_type_to_extension($imagefile->type), + null, + 'group-temp-'.common_timestamp()); - $filepath = common_avatar_path($filename); + $filepath = Avatar::path($filename); - move_uploaded_file($imagefile->filename, $filepath); + move_uploaded_file($imagefile->filepath, $filepath); $filedata = array('filename' => $filename, 'filepath' => $filepath, @@ -357,7 +364,7 @@ class GrouplogoAction extends Action $this->mode = 'crop'; - $this->showForm(_('Pick a square area of the image to be your avatar'), + $this->showForm(_('Pick a square area of the image to be the logo.'), true); } @@ -367,17 +374,8 @@ class GrouplogoAction extends Action * @return void */ - function cropAvatar() + function cropLogo() { - $user = common_current_user(); - - $profile = $user->getProfile(); - - $x = $this->arg('avatar_crop_x'); - $y = $this->arg('avatar_crop_y'); - $w = $this->arg('avatar_crop_w'); - $h = $this->arg('avatar_crop_h'); - $filedata = $_SESSION['FILEDATA']; if (!$filedata) { @@ -385,64 +383,19 @@ class GrouplogoAction extends Action return; } - $filepath = common_avatar_path($filedata['filename']); - - if (!file_exists($filepath)) { - $this->serverError(_('Lost our file.')); - return; - } - - switch ($filedata['type']) { - case IMAGETYPE_GIF: - $image_src = imagecreatefromgif($filepath); - break; - case IMAGETYPE_JPEG: - $image_src = imagecreatefromjpeg($filepath); - break; - case IMAGETYPE_PNG: - $image_src = imagecreatefrompng($filepath); - break; - default: - $this->serverError(_('Unknown file type')); - return; - } - - common_debug("W = $w, H = $h, X = $x, Y = $y"); - - $image_dest = imagecreatetruecolor($w, $h); - - $background = imagecolorallocate($image_dest, 0, 0, 0); - ImageColorTransparent($image_dest, $background); - imagealphablending($image_dest, false); - - imagecopyresized($image_dest, $image_src, 0, 0, $x, $y, $w, $h, $w, $h); + // If image is not being cropped assume pos & dimentions of original + $dest_x = $this->arg('avatar_crop_x') ? $this->arg('avatar_crop_x'):0; + $dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0; + $dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$filedata['width']; + $dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$filedata['height']; + $size = min($dest_w, $dest_h); + $size = ($size > MAX_ORIGINAL) ? MAX_ORIGINAL:$size; - $cur = common_current_user(); - - $filename = common_avatar_filename($this->group->id, - image_type_to_extension($imagefile->type), - null, - 'group-'.common_timestamp()); - - $filepath = common_avatar_path($filename); - - switch ($filedata['type']) { - case IMAGETYPE_GIF: - imagegif($image_dest, $filepath); - break; - case IMAGETYPE_JPEG: - imagejpeg($image_dest, $filepath); - break; - case IMAGETYPE_PNG: - imagepng($image_dest, $filepath); - break; - default: - $this->serverError(_('Unknown file type')); - return; - } + $imagefile = new ImageFile($this->group->id, $filedata['filepath']); + $filename = $imagefile->resize($size, $dest_x, $dest_y, $dest_w, $dest_h); - if ($this->group->setOriginal($filename, $filedata['type'])) { - @unlink(common_avatar_path($filedata['filename'])); + if ($this->group->setOriginal($filename)) { + @unlink($filedata['filepath']); unset($_SESSION['FILEDATA']); $this->mode = 'upload'; $this->showForm(_('Logo updated.'), true); @@ -494,13 +447,15 @@ class GrouplogoAction extends Action { parent::showScripts(); - $jcropPack = common_path('js/jcrop/jquery.Jcrop.pack.js'); - $jcropGo = common_path('js/jcrop/jquery.Jcrop.go.js'); + if ($this->mode == 'crop') { + $jcropPack = common_path('js/jcrop/jquery.Jcrop.pack.js'); + $jcropGo = common_path('js/jcrop/jquery.Jcrop.go.js'); - $this->element('script', array('type' => 'text/javascript', - 'src' => $jcropPack)); - $this->element('script', array('type' => 'text/javascript', - 'src' => $jcropGo)); + $this->element('script', array('type' => 'text/javascript', + 'src' => $jcropPack)); + $this->element('script', array('type' => 'text/javascript', + 'src' => $jcropGo)); + } } function showLocalNav() diff --git a/actions/groupmembers.php b/actions/groupmembers.php index 53395c418..00f43a9f5 100644 --- a/actions/groupmembers.php +++ b/actions/groupmembers.php @@ -122,11 +122,13 @@ class GroupmembersAction extends Action $offset = ($this->page-1) * PROFILES_PER_PAGE; $limit = PROFILES_PER_PAGE + 1; + $cnt = 0; + $members = $this->group->getMembers($offset, $limit); if ($members) { $member_list = new ProfileList($members, null, $this); - $member_list->show(); + $cnt = $member_list->show(); } $members->free(); diff --git a/actions/grouprss.php b/actions/grouprss.php new file mode 100644 index 000000000..de76a5960 --- /dev/null +++ b/actions/grouprss.php @@ -0,0 +1,144 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Group main page + * + * PHP version 5 + * + * LICENCE: 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/>. + * + * @category Group + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Sarven Capadisli <csarven@controlyourself.ca> + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/rssaction.php'; + +define('MEMBERS_PER_SECTION', 81); + +/** + * Group RSS feed + * + * @category Group + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class groupRssAction extends Rss10Action +{ + /** group we're viewing. */ + var $group = null; + + /** + * Is this page read-only? + * + * @return boolean true + */ + + function isReadOnly() + { + return true; + } + + /** + * Prepare the action + * + * Reads and validates arguments and instantiates the attributes. + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + */ + + function prepare($args) + { + parent::prepare($args); + + if (!common_config('inboxes','enabled')) { + $this->serverError(_('Inboxes must be enabled for groups to work')); + return false; + } + + $nickname_arg = $this->arg('nickname'); + $nickname = common_canonical_nickname($nickname_arg); + + // Permanent redirect on non-canonical nickname + + if ($nickname_arg != $nickname) { + $args = array('nickname' => $nickname); + common_redirect(common_local_url('showgroup', $args), 301); + return false; + } + + if (!$nickname) { + $this->clientError(_('No nickname'), 404); + return false; + } + + $this->group = User_group::staticGet('nickname', $nickname); + + if (!$this->group) { + $this->clientError(_('No such group'), 404); + return false; + } + + return true; + } + + function getNotices($limit=0) + { + + $group = $this->group; + + if (is_null($group)) { + return null; + } + + $notice = $group->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit); + + while ($notice->fetch()) { + $notices[] = clone($notice); + } + + return $notices; + } + + function getChannel() + { + $group = $this->group; + $c = array('url' => common_local_url('grouprss', + array('nickname' => + $group->nickname)), + 'title' => $group->nickname, + 'link' => common_local_url('showgroup', array('nickname' => $group->nickname)), + 'description' => sprintf(_('Microblog by %s group'), $group->nickname)); + return $c; + } + + function getImage() + { + return $this->group->homepage_logo; + } +} diff --git a/actions/login.php b/actions/login.php index 11cf1f02a..b049791fb 100644 --- a/actions/login.php +++ b/actions/login.php @@ -78,6 +78,7 @@ class LoginAction extends Action } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { $this->checkLogin(); } else { + common_ensure_session(); $this->showForm(); } } @@ -96,7 +97,7 @@ class LoginAction extends Action { // XXX: login throttle - // CSRF protection - token set in common_notice_form() + // CSRF protection - token set in NoticeForm $token = $this->trimmed('token'); if (!$token || $token != common_session_token()) { $this->clientError(_('There was a problem with your session token. '. @@ -106,29 +107,10 @@ class LoginAction extends Action $nickname = common_canonical_nickname($this->trimmed('nickname')); $password = $this->arg('password'); - if (common_check_user($nickname, $password)) { - // success! - if (!common_set_user($nickname)) { - $this->serverError(_('Error setting user.')); - return; - } - common_real_login(true); - if ($this->boolean('rememberme')) { - common_debug('Adding rememberme cookie for ' . $nickname); - common_rememberme(); - } - // success! - $url = common_get_returnto(); - if ($url) { - // We don't have to return to it again - common_set_returnto(null); - } else { - $url = common_local_url('all', - array('nickname' => - $nickname)); - } - common_redirect($url); - } else { + + $user = common_check_user($nickname, $password); + + if (!$user) { $this->showForm(_('Incorrect username or password.')); return; } @@ -142,11 +124,11 @@ class LoginAction extends Action common_real_login(true); if ($this->boolean('rememberme')) { - common_debug('Adding rememberme cookie for ' . $nickname); common_rememberme($user); } - // success! + $url = common_get_returnto(); + if ($url) { // We don't have to return to it again common_set_returnto(null); @@ -155,6 +137,7 @@ class LoginAction extends Action array('nickname' => $nickname)); } + common_redirect($url); } diff --git a/actions/newgroup.php b/actions/newgroup.php index 42fd380df..cbd8dfeec 100644 --- a/actions/newgroup.php +++ b/actions/newgroup.php @@ -142,13 +142,13 @@ class NewgroupAction extends Action array('http', 'https')))) { $this->showForm(_('Homepage is not a valid URL.')); return; - } else if (!is_null($fullname) && strlen($fullname) > 255) { + } else if (!is_null($fullname) && mb_strlen($fullname) > 255) { $this->showForm(_('Full name is too long (max 255 chars).')); return; - } else if (!is_null($description) && strlen($description) > 140) { + } else if (!is_null($description) && mb_strlen($description) > 140) { $this->showForm(_('description is too long (max 140 chars).')); return; - } else if (!is_null($location) && strlen($location) > 255) { + } else if (!is_null($location) && mb_strlen($location) > 255) { $this->showForm(_('Location is too long (max 255 chars).')); return; } diff --git a/actions/newmessage.php b/actions/newmessage.php index aa94f8c4f..82276ff34 100644 --- a/actions/newmessage.php +++ b/actions/newmessage.php @@ -2,7 +2,7 @@ /** * Laconica, the distributed open-source microblogging tool * - * Handler for posting new notices + * Handler for posting new messages * * PHP version 5 * @@ -28,9 +28,9 @@ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://laconi.ca/ */ - -if (!defined('LACONICA')) { - exit(1); + +if (!defined('LACONICA')) { + exit(1); } /** @@ -47,13 +47,17 @@ if (!defined('LACONICA')) { class NewmessageAction extends Action { - + /** * Error message, if any */ var $msg = null; + var $content = null; + var $to = null; + var $other = null; + /** * Title of the page * @@ -61,12 +65,12 @@ class NewmessageAction extends Action * * @return string page title */ - + function title() { return _('New message'); } - + /** * Handle input, produce output * @@ -74,7 +78,7 @@ class NewmessageAction extends Action * * @return void */ - + function handle($args) { parent::handle($args); @@ -88,62 +92,85 @@ class NewmessageAction extends Action } } - function saveNewMessage() + function prepare($args) { + parent::prepare($args); + $user = common_current_user(); - assert($user); // XXX: maybe an error instead... + if (!$user) { + $this->clientError(_('Only logged-in users can send direct messages.'), 403); + return false; + } + + $this->content = $this->trimmed('content'); + $this->to = $this->trimmed('to'); + + if ($this->to) { + + $this->other = User::staticGet('id', $this->to); + + if (!$this->other) { + $this->clientError(_('No such user'), 404); + return false; + } + + if (!$user->mutuallySubscribed($this->other)) { + $this->clientError(_('You can\'t send a message to this user.'), 404); + return false; + } + } + + return true; + } + + function saveNewMessage() + { // CSRF protection - + $token = $this->trimmed('token'); if (!$token || $token != common_session_token()) { $this->showForm(_('There was a problem with your session token. ' . 'Try again, please.')); return; } - - $content = $this->trimmed('content'); - $to = $this->trimmed('to'); - - if (!$content) { + + $user = common_current_user(); + assert($user); // XXX: maybe an error instead... + + if (!$this->content) { $this->showForm(_('No content!')); return; } else { - $content_shortened = common_shorten_links($content); + $content_shortened = common_shorten_links($this->content); if (mb_strlen($content_shortened) > 140) { - common_debug("Content = '$content_shortened'", __FILE__); - common_debug("mb_strlen(\$content) = " . - mb_strlen($content_shortened), - __FILE__); $this->showForm(_('That\'s too long. ' . 'Max message size is 140 chars.')); return; } } - $other = User::staticGet('id', $to); - - if (!$other) { + if (!$this->other) { $this->showForm(_('No recipient specified.')); return; - } else if (!$user->mutuallySubscribed($other)) { + } else if (!$user->mutuallySubscribed($this->other)) { $this->clientError(_('You can\'t send a message to this user.'), 404); return; - } else if ($user->id == $other->id) { + } else if ($user->id == $this->other->id) { $this->clientError(_('Don\'t send a message to yourself; ' . 'just say it to yourself quietly instead.'), 403); return; } - - $message = Message::saveNew($user->id, $other->id, $content, 'web'); - + + $message = Message::saveNew($user->id, $this->other->id, $this->content, 'web'); + if (is_string($message)) { $this->showForm($message); return; } - $this->notify($user, $other, $message); + $this->notify($user, $this->other, $message); $url = common_local_url('outbox', array('nickname' => $user->nickname)); @@ -152,31 +179,29 @@ class NewmessageAction extends Action function showForm($msg = null) { - $content = $this->trimmed('content'); - $user = common_current_user(); + $this->msg = $msg; - $to = $this->trimmed('to'); - - $other = User::staticGet('id', $to); + $this->showPage(); + } - if (!$other) { - $this->clientError(_('No such user'), 404); - return; + function showPageNotice() + { + if ($this->msg) { + $this->element('p', 'error', $this->msg); } - - if (!$user->mutuallySubscribed($other)) { - $this->clientError(_('You can\'t send a message to this user.'), 404); - return; - } - - $this->msg = $msg; - - $this->showPage(); } - + function notify($from, $to, $message) { mail_notify_message($message, $from, $to); // XXX: Jabber, SMS notifications... probably queued } + + // Do nothing (override) + + function showNoticeForm() + { + $message_form = new MessageForm($this, $this->other, $this->content); + $message_form->show(); + } } diff --git a/actions/newnotice.php b/actions/newnotice.php index 572adbb23..cbd04c58b 100644 --- a/actions/newnotice.php +++ b/actions/newnotice.php @@ -90,7 +90,7 @@ class NewnoticeAction extends Action $this->clientError(_('Not logged in.')); } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { - // CSRF protection - token set in common_notice_form() + // CSRF protection $token = $this->trimmed('token'); if (!$token || $token != common_session_token()) { $this->clientError(_('There was a problem with your session token. '. @@ -98,7 +98,12 @@ class NewnoticeAction extends Action return; } - $this->saveNewNotice(); + try { + $this->saveNewNotice(); + } catch (Exception $e) { + $this->showForm($e->getMessage()); + return; + } } else { $this->showForm(); } @@ -123,15 +128,13 @@ class NewnoticeAction extends Action $content = $this->trimmed('status_textarea'); if (!$content) { - $this->showForm(_('No content!')); - return; + $this->clientError(_('No content!')); } else { $content_shortened = common_shorten_links($content); if (mb_strlen($content_shortened) > 140) { - $this->showForm(_('That\'s too long. '. - 'Max notice size is 140 chars.')); - return; + $this->clientError(_('That\'s too long. '. + 'Max notice size is 140 chars.')); } } @@ -141,27 +144,32 @@ class NewnoticeAction extends Action if ($cmd) { if ($this->boolean('ajax')) { - $cmd->execute(new AjaxWebChannel()); + $cmd->execute(new AjaxWebChannel($this)); } else { - $cmd->execute(new WebChannel()); + $cmd->execute(new WebChannel($this)); } return; } $replyto = $this->trimmed('inreplyto'); + #If an ID of 0 is wrongly passed here, it will cause a database error, + #so override it... + if ($replyto == 0) { + $replyto = 'false'; + } $notice = Notice::saveNew($user->id, $content, 'web', 1, ($replyto == 'false') ? null : $replyto); if (is_string($notice)) { - $this->showForm($notice); + $this->clientError($notice); return; } common_broadcast_notice($notice); if ($this->boolean('ajax')) { - $this->startHTML('text/xml;charset=utf-8', true); + $this->startHTML('text/xml;charset=utf-8'); $this->elementStart('head'); $this->element('title', null, _('Notice posted')); $this->elementEnd('head'); @@ -195,7 +203,7 @@ class NewnoticeAction extends Action function ajaxErrorMsg($msg) { - common_start_html('text/xml;charset=utf-8', true); + $this->startHTML('text/xml;charset=utf-8', true); $this->elementStart('head'); $this->element('title', null, _('Ajax Error')); $this->elementEnd('head'); @@ -250,7 +258,7 @@ class NewnoticeAction extends Action } } - $notice_form = new NoticeForm($this, $content); + $notice_form = new NoticeForm($this, '', $content); $notice_form->show(); } diff --git a/actions/noticesearch.php b/actions/noticesearch.php index a0d723b12..83e59dd9a 100644 --- a/actions/noticesearch.php +++ b/actions/noticesearch.php @@ -48,10 +48,20 @@ require_once INSTALLDIR.'/lib/searchaction.php'; */ class NoticesearchAction extends SearchAction { + + function prepare($args) + { + parent::prepare($args); + + common_set_returnto($this->selfUrl()); + + return true; + } + /** * Get instructions - * - * @return string instruction text + * + * @return string instruction text */ function getInstructions() { @@ -60,7 +70,7 @@ class NoticesearchAction extends SearchAction /** * Get title - * + * * @return string title */ function title() @@ -68,6 +78,20 @@ class NoticesearchAction extends SearchAction return _('Text search'); } + function getFeeds() + { + $q = $this->trimmed('q'); + + if (!$q) { + return null; + } + + return array(new Feed(Feed::RSS1, common_local_url('noticesearchrss', + array('q' => $q)), + sprintf(_('Search results for "%s" on %s'), + $q, common_config('site', 'name')))); + } + /** * Show results * @@ -89,151 +113,58 @@ class NoticesearchAction extends SearchAction } else { $cnt = $notice->find(); } - if ($cnt > 0) { - $terms = preg_split('/[\s,]+/', $q); - $this->elementStart('ul', array('class' => 'notices')); - for ($i = 0; $i < min($cnt, NOTICES_PER_PAGE); $i++) { - if ($notice->fetch()) { - $this->showNotice($notice, $terms); - } else { - // shouldn't happen! - break; - } - } - $this->elementEnd('ul'); - } else { + if ($cnt === 0) { $this->element('p', 'error', _('No results')); + return; } + $terms = preg_split('/[\s,]+/', $q); + $nl = new SearchNoticeList($notice, $this, $terms); - $this->pagination($page > 1, $cnt > NOTICES_PER_PAGE, - $page, 'noticesearch', array('q' => $q)); - } + $cnt = $nl->show(); - /** - * Show header - * - * @param array $arr array containing the query - * - * @return void - */ + $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, + $this->page, 'noticesearch', array('q' => $q)); + } + function isReadOnly() + { + return true; + } +} - function extraHead() +class SearchNoticeList extends NoticeList { + function __construct($notice, $out=null, $terms) { - $q = $this->trimmed('q'); - if ($q) { - $this->element('link', array('rel' => 'alternate', - 'href' => common_local_url('noticesearchrss', - array('q' => $q)), - 'type' => 'application/rss+xml', - 'title' => _('Search Stream Feed'))); - } + parent::__construct($notice, $out); + $this->terms = $terms; } - /** - * Show notice - * - * @param class $notice notice - * @param array $terms terms to highlight - * - * @return void - * - * @todo refactor and combine with StreamAction::showNotice() - */ - function showNotice($notice, $terms) + function newListItem($notice) { - $profile = $notice->getProfile(); - if (!$profile) { - common_log_db_error($notice, 'SELECT', __FILE__); - $this->serverError(_('Notice without matching profile')); - return; - } - // XXX: RDFa - $this->elementStart('li', array('class' => 'hentry notice', - 'id' => 'notice-' . $notice->id)); + return new SearchNoticeListItem($notice, $this->out, $this->terms); + } +} - $this->elementStart('div', 'entry-title'); - $this->elementStart('span', 'vcard author'); - $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); - $this->elementStart('a', array('href' => $profile->profileurl)); - $this->element('img', array('src' => ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_STREAM_SIZE), - 'class' => 'avatar photo', - 'width' => AVATAR_STREAM_SIZE, - 'height' => AVATAR_STREAM_SIZE, - 'alt' => - ($profile->fullname) ? $profile->fullname : - $profile->nickname)); - $this->element('span', 'nickname fn', $profile->nickname); - $this->elementEnd('a'); - $this->elementEnd('span'); +class SearchNoticeListItem extends NoticeListItem { + function __construct($notice, $out=null, $terms) + { + parent::__construct($notice, $out); + $this->terms = $terms; + } + function showContent() + { // FIXME: URL, image, video, audio - $this->elementStart('p', array('class' => 'entry-content')); - if ($notice->rendered) { - $this->raw($this->highlight($notice->rendered, $terms)); + $this->out->elementStart('p', array('class' => 'entry-content')); + if ($this->notice->rendered) { + $this->out->raw($this->highlight($this->notice->rendered, $this->terms)); } else { // XXX: may be some uncooked notices in the DB, // we cook them right now. This should probably disappear in future // versions (>> 0.4.x) - $this->raw($this->highlight(common_render_content($notice->content, $notice), $terms)); + $this->out->raw($this->highlight(common_render_content($this->notice->content, $this->notice), $this->terms)); } - $this->elementEnd('p'); - $this->elementEnd('div'); - - $noticeurl = common_local_url('shownotice', array('notice' => $notice->id)); - $this->elementStart('div', 'entry-content'); - $this->elementStart('dl', 'timestamp'); - $this->element('dt', null, _('Published')); - $this->elementStart('dd', null); - $this->elementStart('a', array('rel' => 'bookmark', - 'href' => $noticeurl)); - $dt = common_date_iso8601($notice->created); - $this->element('abbr', array('class' => 'published', - 'title' => $dt), - common_date_string($notice->created)); - $this->elementEnd('a'); - $this->elementEnd('dd'); - $this->elementEnd('dl'); - - if ($notice->reply_to) { - $replyurl = common_local_url('shownotice', - array('notice' => $this->notice->reply_to)); - $this->elementStart('dl', 'response'); - $this->element('dt', null, _('To')); - $this->elementStart('dd'); - $this->element('a', array('href' => $replyurl, - 'rel' => 'in-reply-to'), - _('in reply to')); - $this->elementEnd('dd'); - $this->elementEnd('dl'); - } - $this->elementEnd('div'); - - $this->elementStart('div', 'notice-options'); + $this->out->elementEnd('p'); - $reply_url = common_local_url('newnotice', - array('replyto' => $profile->nickname)); - - $this->elementStart('dl', 'notice_reply'); - $this->element('dt', null, _('Reply to this notice')); - $this->elementStart('dd'); - $this->elementStart('a', array('href' => $reply_url, - 'title' => _('Reply to this notice'))); - $this->text(_('Reply')); - $this->element('span', 'notice_id', $notice->id); - $this->elementEnd('a'); - $this->elementEnd('dd'); - $this->elementEnd('dl'); - - $this->elementStart('a', - array('href' => common_local_url('newnotice', - array('replyto' => $profile->nickname)), - 'onclick' => 'doreply("'.$profile->nickname.'"); return false', - 'title' => _('reply'), - 'class' => 'replybutton')); - $this->hidden('posttoken', common_session_token()); - $this->elementEnd('a'); - $this->elementEnd('div'); - $this->elementEnd('li'); } /** @@ -246,7 +177,7 @@ class NoticesearchAction extends SearchAction */ function highlight($text, $terms) { - /* Highligh serach terms */ + /* Highligh search terms */ $pattern = '/('.implode('|', array_map('htmlspecialchars', $terms)).')/i'; $result = preg_replace($pattern, '<strong>\\1</strong>', $text); @@ -257,10 +188,5 @@ class NoticesearchAction extends SearchAction } while ($count); return $result; } - - function isReadOnly() - { - return true; - } } diff --git a/actions/nudge.php b/actions/nudge.php index ca7947f5a..bc3d48478 100644 --- a/actions/nudge.php +++ b/actions/nudge.php @@ -89,7 +89,7 @@ class NudgeAction extends Action $this->notify($user, $other); if ($this->boolean('ajax')) { - $this->startHTML('text/xml;charset=utf-8', true); + $this->startHTML('text/xml;charset=utf-8'); $this->elementStart('head'); $this->element('title', null, _('Nudge sent')); $this->elementEnd('head'); diff --git a/actions/openidlogin.php b/actions/openidlogin.php index 7a267a2bd..1a4372d73 100644 --- a/actions/openidlogin.php +++ b/actions/openidlogin.php @@ -26,7 +26,7 @@ class OpenidloginAction extends Action function handle($args) { parent::handle($args); - if (common_logged_in()) { + if (common_is_real_login()) { $this->clientError(_('Already logged in.')); } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { $openid_url = $this->trimmed('openid_url'); @@ -59,7 +59,16 @@ class OpenidloginAction extends Action function getInstructions() { - return _('Login with an [OpenID](%%doc.openid%%) account.'); + if (common_logged_in() && !common_is_real_login() && + common_get_returnto()) { + // rememberme logins have to reauthenticate before + // changing any profile settings (cookie-stealing protection) + return _('For security reasons, please re-login with your ' . + '[OpenID](%%doc.openid%%) ' . + 'before changing your settings.'); + } else { + return _('Login with an [OpenID](%%doc.openid%%) account.'); + } } function showPageNotice() diff --git a/actions/opensearch.php b/actions/opensearch.php index 7709249bb..2eb818306 100644 --- a/actions/opensearch.php +++ b/actions/opensearch.php @@ -67,7 +67,7 @@ class OpensearchAction extends Action $short_name = _('Notice Search'); } header('Content-Type: text/html'); - common_start_xml(); + $this->startXML(); $this->elementStart('OpenSearchDescription', array('xmlns' => 'http://a9.com/-/spec/opensearch/1.1/')); $short_name = common_config('site', 'name').' '.$short_name; $this->element('ShortName', null, $short_name); @@ -81,7 +81,7 @@ class OpensearchAction extends Action $this->element('OutputEncoding', null, 'UTF-8'); $this->element('InputEncoding', null, 'UTF-8'); $this->elementEnd('OpenSearchDescription'); - common_end_xml(); + $this->endXML(); } function isReadOnly() diff --git a/actions/passwordsettings.php b/actions/passwordsettings.php index 17b2de4e8..bdce61035 100644 --- a/actions/passwordsettings.php +++ b/actions/passwordsettings.php @@ -84,7 +84,7 @@ class PasswordsettingsAction extends AccountSettingsAction 'id' => 'form_password', 'class' => 'form_settings', 'action' => - common_local_url('profilesettings'))); + common_local_url('passwordsettings'))); $this->elementStart('fieldset'); $this->element('legend', null, _('Password change')); $this->hidden('token', common_session_token()); @@ -141,7 +141,12 @@ class PasswordsettingsAction extends AccountSettingsAction $newpassword = $this->arg('newpassword'); $confirm = $this->arg('confirm'); - if (0 != strcmp($newpassword, $confirm)) { + # Some validation + + if (strlen($newpassword) < 6) { + $this->showForm(_('Password must be 6 or more characters.')); + return; + } else if (0 != strcmp($newpassword, $confirm)) { $this->showForm(_('Passwords don\'t match.')); return; } diff --git a/actions/peoplesearch.php b/actions/peoplesearch.php index 615201c46..14177fcf0 100644 --- a/actions/peoplesearch.php +++ b/actions/peoplesearch.php @@ -86,33 +86,9 @@ class PeoplesearchAction extends SearchAction } $profile->free(); - + $this->pagination($page > 1, $cnt > PROFILES_PER_PAGE, $page, 'peoplesearch', array('q' => $q)); } } -class PeopleSearchResults extends ProfileList -{ - var $terms = null; - var $pattern = null; - - function __construct($profile, $terms, $action) - { - parent::__construct($profile, $terms, $action); - $this->terms = array_map('preg_quote', - array_map('htmlspecialchars', $terms)); - $this->pattern = '/('.implode('|',$terms).')/i'; - } - - function highlight($text) - { - return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text)); - } - - function isReadOnly() - { - return true; - } -} - diff --git a/actions/peopletag.php b/actions/peopletag.php index 7bcfcb93e..6b1e34f1a 100644 --- a/actions/peopletag.php +++ b/actions/peopletag.php @@ -1,9 +1,12 @@ <?php -/* - * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. +/** + * Laconica, the distributed open-source microblogging tool * - * This program is free software: you can redistribute it and/or modify + * Action for showing profiles self-tagged with a given tag + * + * PHP version 5 + * + * LICENCE: 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. @@ -15,95 +18,131 @@ * * 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/>. + * + * @category Action + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Zach Copley <zach@controlyourself.ca> + * @copyright 2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ */ -if (!defined('LACONICA')) { exit(1); } +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/profilelist.php'; -require_once(INSTALLDIR.'/lib/profilelist.php'); +/** + * This class outputs a paginated list of profiles self-tagged with a given tag + * + * @category Output + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Zach Copley <zach@controlyourself.ca> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + * + * @see Action + */ class PeopletagAction extends Action { - - function handle($args) + + var $tag = null; + var $page = null; + + /** + * For initializing members of the class. + * + * @param array $argarray misc. arguments + * + * @return boolean true + */ + function prepare($argarray) { + parent::prepare($argarray); - parent::handle($args); + $this->tag = $this->trimmed('tag'); - $tag = $this->trimmed('tag'); - - if (!common_valid_profile_tag($tag)) { - $this->clientError(sprintf(_('Not a valid people tag: %s'), $tag)); + if (!common_valid_profile_tag($this->tag)) { + $this->clientError(sprintf(_('Not a valid people tag: %s'), + $this->tag)); return; } - $page = $this->trimmed('page'); - - if (!$page) { - $page = 1; - } - - # Looks like we're good; show the header + $this->page = ($this->arg('page')) ? $this->arg('page') : 1; - common_show_header(sprintf(_('Users self-tagged with %s - page %d'), $tag, $page), - null, $tag, array($this, 'show_top')); + common_set_returnto($this->selfUrl()); - $this->show_people($tag, $page); + return true; + } - common_show_footer(); + /** + * Handler method + * + * @param array $argarray is ignored since it's now passed in in prepare() + * + * @return boolean is read only action? + */ + function handle($argarray) + { + parent::handle($argarray); + $this->showPage(); } - function show_people($tag, $page) + /** + * Whips up a query to get a list of profiles based on the provided + * people tag and page, initalizes a ProfileList widget, and displays + * it to the user. + * + * @return nothing + */ + function showContent() { - + $profile = new Profile(); - $offset = ($page-1)*PROFILES_PER_PAGE; - $limit = PROFILES_PER_PAGE + 1; - - if (common_config('db','type') == 'pgsql') { + $offset = ($this->page - 1) * PROFILES_PER_PAGE; + $limit = PROFILES_PER_PAGE + 1; + + if (common_config('db', 'type') == 'pgsql') { $lim = ' LIMIT ' . $limit . ' OFFSET ' . $offset; } else { $lim = ' LIMIT ' . $offset . ', ' . $limit; } - # XXX: memcached this - - $profile->query(sprintf('SELECT profile.* ' . - 'FROM profile JOIN profile_tag ' . - 'ON profile.id = profile_tag.tagger ' . - 'WHERE profile_tag.tagger = profile_tag.tagged ' . - 'AND tag = "%s" ' . - 'ORDER BY profile_tag.modified DESC ' . - $lim, $tag)); - - $pl = new ProfileList($profile); - $cnt = $pl->show_list(); - - common_pagination($page > 1, + // XXX: memcached this + + $qry = 'SELECT profile.* ' . + 'FROM profile JOIN profile_tag ' . + 'ON profile.id = profile_tag.tagger ' . + 'WHERE profile_tag.tagger = profile_tag.tagged ' . + 'AND tag = "%s" ' . + 'ORDER BY profile_tag.modified DESC%s'; + + $profile->query(sprintf($qry, $this->tag, $lim)); + + $pl = new ProfileList($profile, null, $this); + $cnt = $pl->show(); + + $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE, - $page, - $this->trimmed('action'), - array('tag' => $tag)); - } - - function show_top($tag) - { - $instr = sprintf(_('These are users who have tagged themselves "%s" ' . - 'to show a common interest, characteristic, hobby or job.'), $tag); - $this->elementStart('div', 'instructions'); - $this->elementStart('p'); - $this->text($instr); - $this->elementEnd('p'); - $this->elementEnd('div'); + $this->page, + 'peopletag', + array('tag' => $this->tag)); } - function get_title() + /** + * Returns the page title + * + * @return string page title + */ + function title() { - return null; + return sprintf(_('Users self-tagged with %s - page %d'), + $this->tag, $this->page); } - function show_header($arr) - { - return; - } } diff --git a/actions/profilesettings.php b/actions/profilesettings.php index 6dd4775e5..60f7c0796 100644 --- a/actions/profilesettings.php +++ b/actions/profilesettings.php @@ -83,7 +83,7 @@ class ProfilesettingsAction extends AccountSettingsAction $user = common_current_user(); $profile = $user->getProfile(); - $this->elementStart('form', array('method' => 'POST', + $this->elementStart('form', array('method' => 'post', 'id' => 'form_settings_profile', 'class' => 'form_settings', 'action' => common_local_url('profilesettings'))); @@ -198,13 +198,13 @@ class ProfilesettingsAction extends AccountSettingsAction !Validate::uri($homepage, array('allowed_schemes' => array('http', 'https')))) { $this->showForm(_('Homepage is not a valid URL.')); return; - } else if (!is_null($fullname) && strlen($fullname) > 255) { + } else if (!is_null($fullname) && mb_strlen($fullname) > 255) { $this->showForm(_('Full name is too long (max 255 chars).')); return; - } else if (!is_null($bio) && strlen($bio) > 140) { + } else if (!is_null($bio) && mb_strlen($bio) > 140) { $this->showForm(_('Bio is too long (max 140 chars).')); return; - } else if (!is_null($location) && strlen($location) > 255) { + } else if (!is_null($location) && mb_strlen($location) > 255) { $this->showForm(_('Location is too long (max 255 chars).')); return; } else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) { diff --git a/actions/public.php b/actions/public.php index cfdc99bb3..a20ae4032 100644 --- a/actions/public.php +++ b/actions/public.php @@ -73,6 +73,9 @@ class PublicAction extends Action { parent::prepare($args); $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + + common_set_returnto($this->selfUrl()); + return true; } @@ -116,12 +119,20 @@ class PublicAction extends Action * @return void */ - function showFeeds() + function getFeeds() { - $this->element('link', array('rel' => 'alternate', - 'href' => common_local_url('publicrss'), - 'type' => 'application/rss+xml', - 'title' => _('Public Stream Feed'))); + return array(new Feed(Feed::RSS1, common_local_url('publicrss'), + _('Public Stream Feed (RSS 1.0)')), + new Feed(Feed::RSS2, + common_local_url('api', + array('apiaction' => 'statuses', + 'method' => 'public_timeline.rss')), + _('Public Stream Feed (RSS 2.0)')), + new Feed(Feed::ATOM, + common_local_url('api', + array('apiaction' => 'statuses', + 'method' => 'public_timeline.atom')), + _('Public Stream Feed (Atom)'))); } /** @@ -182,27 +193,6 @@ class PublicAction extends Action $this->page, 'public'); } - /** - * Makes a list of exported feeds for this page - * - * @return void - * - * @todo I18N - */ - - function showExportData() - { - $fl = new FeedList($this); - $fl->show(array(0 => array('href' => common_local_url('publicrss'), - 'type' => 'rss', - 'version' => 'RSS 1.0', - 'item' => 'publicrss'), - 1 => array('href' => common_local_url('publicatom'), - 'type' => 'atom', - 'version' => 'Atom 1.0', - 'item' => 'publicatom'))); - } - function showSections() { // $top = new TopPostersSection($this); diff --git a/actions/register.php b/actions/register.php index a63da2e0f..5d7a8ce69 100644 --- a/actions/register.php +++ b/actions/register.php @@ -167,13 +167,13 @@ class RegisterAction extends Action array('http', 'https')))) { $this->showForm(_('Homepage is not a valid URL.')); return; - } else if (!is_null($fullname) && strlen($fullname) > 255) { + } else if (!is_null($fullname) && mb_strlen($fullname) > 255) { $this->showForm(_('Full name is too long (max 255 chars).')); return; - } else if (!is_null($bio) && strlen($bio) > 140) { + } else if (!is_null($bio) && mb_strlen($bio) > 140) { $this->showForm(_('Bio is too long (max 140 chars).')); return; - } else if (!is_null($location) && strlen($location) > 255) { + } else if (!is_null($location) && mb_strlen($location) > 255) { $this->showForm(_('Location is too long (max 255 chars).')); return; } else if (strlen($password) < 6) { @@ -252,14 +252,14 @@ class RegisterAction extends Action function showPageTitle() { $this->element('h1', array('class' => 'entry-title'), $this->title()); } - + // overrided to add hentry, and content-inner class function showContentBlock() { $this->elementStart('div', array('id' => 'content', 'class' => 'hentry')); $this->showPageTitle(); $this->showPageNoticeBlock(); - $this->elementStart('div', array('id' => 'content_inner', + $this->elementStart('div', array('id' => 'content_inner', 'class' => 'entry-content')); // show the actual content (forms, lists, whatever) $this->showContent(); @@ -426,11 +426,13 @@ class RegisterAction extends Action } $this->elementStart('li'); $this->element('input', $attrs); + $this->elementStart('label', array('class' => 'checkbox', 'for' => 'license')); $this->text(_('My text and files are available under ')); $this->element('a', array('href' => common_config('license', 'url')), - $config['license']['title']); + common_config('license', 'title'), _("Creative Commons Attribution 3.0")); $this->text(_(' except this private data: password, '. - 'email address, IM address, phone number.')); + 'email address, IM address, and phone number.')); + $this->elementEnd('label'); $this->elementEnd('li'); $this->elementEnd('ul'); $this->submit('submit', _('Register')); diff --git a/actions/remotesubscribe.php b/actions/remotesubscribe.php index f727a63b8..7ea7acd6d 100644 --- a/actions/remotesubscribe.php +++ b/actions/remotesubscribe.php @@ -321,8 +321,7 @@ class RemotesubscribeAction extends Action $result = $fetcher->post($req->get_normalized_http_url(), $req->to_postdata(), - array('User-Agent' => 'Laconica/' . LACONICA_VERSION)); - + array('User-Agent: Laconica/' . LACONICA_VERSION)); if ($result->status != 200) { return null; } diff --git a/actions/replies.php b/actions/replies.php index 9ec373a96..4ab9b14ed 100644 --- a/actions/replies.php +++ b/actions/replies.php @@ -83,6 +83,8 @@ class RepliesAction extends Action $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + common_set_returnto($this->selfUrl()); + return true; } @@ -127,16 +129,13 @@ class RepliesAction extends Action * @return void */ - function showFeeds() + function getFeeds() { $rssurl = common_local_url('repliesrss', array('nickname' => $this->user->nickname)); $rsstitle = sprintf(_('Feed for replies to %s'), $this->user->nickname); - $this->element('link', array('rel' => 'alternate', - 'href' => $rssurl, - 'type' => 'application/rss+xml', - 'title' => $rsstitle)); + return array(new Feed(Feed::RSS1, $rssurl, $rsstitle)); } /** @@ -152,25 +151,6 @@ class RepliesAction extends Action } /** - * Show the replies feed links - * - * @return void - */ - - function showExportData() - { - $fl = new FeedList($this); - - $rssurl = common_local_url('repliesrss', - array('nickname' => $this->user->nickname)); - - $fl->show(array(0=>array('href'=> $rssurl, - 'type' => 'rss', - 'version' => 'RSS 1.0', - 'item' => 'repliesrss'))); - } - - /** * Show the content * * A list of notices that are replies to the user, plus pagination. diff --git a/actions/showfavorites.php b/actions/showfavorites.php index bb68f8d94..d1c9283f0 100644 --- a/actions/showfavorites.php +++ b/actions/showfavorites.php @@ -112,6 +112,8 @@ class ShowfavoritesAction extends Action $this->page = 1; } + common_set_returnto($this->selfUrl()); + return true; } @@ -134,10 +136,10 @@ class ShowfavoritesAction extends Action /** * Feeds for the <head> section * - * @return void + * @return array Feed objects to show */ - function showFeeds() + function getFeeds() { $feedurl = common_local_url('favoritesrss', array('nickname' => @@ -145,10 +147,7 @@ class ShowfavoritesAction extends Action $feedtitle = sprintf(_('Feed for favorites of %s'), $this->user->nickname); - $this->element('link', array('rel' => 'alternate', - 'href' => $feedurl, - 'type' => 'application/rss+xml', - 'title' => $feedtitle)); + return array(new Feed(Feed::RSS1, $feedurl, $feedtitle)); } /** @@ -164,28 +163,6 @@ class ShowfavoritesAction extends Action } /** - * Show the replies feed links - * - * @return void - */ - - function showExportData() - { - $feedurl = common_local_url('favoritesrss', - array('nickname' => - $this->user->nickname)); - - $fl = new FeedList($this); - - // XXX: I18N - - $fl->show(array(0=>array('href'=> $feedurl, - 'type' => 'rss', - 'version' => 'RSS 1.0', - 'item' => 'Favorites'))); - } - - /** * Show the content * * A list of notices that this user has marked as a favorite diff --git a/actions/showgroup.php b/actions/showgroup.php index 99367a97c..c20941a35 100644 --- a/actions/showgroup.php +++ b/actions/showgroup.php @@ -129,6 +129,8 @@ class ShowgroupAction extends Action return false; } + common_set_returnto($this->selfUrl()); + return true; } @@ -242,7 +244,7 @@ class ShowgroupAction extends Action if ($this->group->location) { $this->elementStart('dl', 'entity_location'); $this->element('dt', null, _('Location')); - $this->element('dd', 'location', $this->group->location); + $this->element('dd', 'label', $this->group->location); $this->elementEnd('dl'); } @@ -269,7 +271,7 @@ class ShowgroupAction extends Action $this->elementStart('div', 'entity_actions'); $this->element('h2', null, _('Group actions')); $this->elementStart('ul'); - $this->elementStart('li', array('id' => 'entity_subscribe')); + $this->elementStart('li', 'entity_subscribe'); $cur = common_current_user(); if ($cur) { if ($cur->isMember($this->group)) { @@ -290,37 +292,18 @@ class ShowgroupAction extends Action } /** - * Show a list of links to feeds this page produces - * - * @return void - */ - - function showExportData() - { - $fl = new FeedList($this); - $fl->show(array(0=>array('href'=>common_local_url('grouprss', - array('nickname' => $this->group->nickname)), - 'type' => 'rss', - 'version' => 'RSS 1.0', - 'item' => 'notices'))); - } - - /** - * Show a list of links to feeds this page produces + * Get a list of the feeds for this page * * @return void */ - function showFeeds() + function getFeeds() { $url = common_local_url('grouprss', array('nickname' => $this->group->nickname)); - $this->element('link', array('rel' => 'alternate', - 'href' => $url, - 'type' => 'application/rss+xml', - 'title' => sprintf(_('Notice feed for %s group'), + return array(new Feed(Feed::RSS1, $url, sprintf(_('Notice feed for %s group'), $this->group->nickname))); } @@ -333,6 +316,7 @@ class ShowgroupAction extends Action function showSections() { $this->showMembers(); + $this->showStatistics(); $cloud = new GroupTagCloudSection($this, $this->group); $cloud->show(); } @@ -356,12 +340,10 @@ class ShowgroupAction extends Action $this->element('h2', null, _('Members')); - if ($member) { - $pml = new ProfileMiniList($member, null, $this); - $cnt = $pml->show(); - if ($cnt == 0) { - $this->element('p', null, _('(None)')); - } + $pml = new ProfileMiniList($member, null, $this); + $cnt = $pml->show(); + if ($cnt == 0) { + $this->element('p', null, _('(None)')); } if ($cnt == MEMBERS_PER_SECTION) { @@ -373,6 +355,41 @@ class ShowgroupAction extends Action $this->elementEnd('div'); } + /** + * Show some statistics + * + * @return void + */ + + function showStatistics() + { + // XXX: WORM cache this + $members = $this->group->getMembers(); + $members_count = 0; + /** $member->count() doesn't work. */ + while ($members->fetch()) { + $members_count++; + } + + $this->elementStart('div', array('id' => 'entity_statistics', + 'class' => 'section')); + + $this->element('h2', null, _('Statistics')); + + $this->elementStart('dl', 'entity_created'); + $this->element('dt', null, _('Created')); + $this->element('dd', null, date('j M Y', + strtotime($this->group->created))); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_members'); + $this->element('dt', null, _('Members')); + $this->element('dd', null, (is_int($members_count)) ? $members_count : '0'); + $this->elementEnd('dl'); + + $this->elementEnd('div'); + } + function showAnonymousMessage() { $m = sprintf(_('**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' . diff --git a/actions/showstream.php b/actions/showstream.php index eab1fc0a2..65482167e 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -110,6 +110,8 @@ class ShowstreamAction extends Action $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + common_set_returnto($this->selfUrl()); + return true; } @@ -140,7 +142,12 @@ class ShowstreamAction extends Action function showPageTitle() { - $this->element('h1', NULL, $this->profile->nickname._("'s profile")); + $user =& common_current_user(); + if ($user && ($user->id == $this->profile->id)) { + $this->element('h1', NULL, _("Your profile")); + } else { + $this->element('h1', NULL, sprintf(_('%s\'s profile'), $this->profile->nickname)); + } } function showPageNoticeBlock() @@ -148,62 +155,39 @@ class ShowstreamAction extends Action return; } - function showExportData() - { - $fl = new FeedList($this); - $fl->show(array(0=>array('href'=>common_local_url('userrss', - array('nickname' => $this->user->nickname)), - 'type' => 'rss', - 'version' => 'RSS 1.0', - 'item' => 'notices'), - 1=>array('href'=>common_local_url('usertimeline', - array('nickname' => $this->user->nickname)), - 'type' => 'atom', - 'version' => 'Atom 1.0', - 'item' => 'usertimeline'), - 2=>array('href'=>common_local_url('foaf', - array('nickname' => $this->user->nickname)), - 'type' => 'rdf', - 'version' => 'FOAF', - 'item' => 'foaf'))); - } - - function showFeeds() + function getFeeds() { - // Feeds - $this->element('link', array('rel' => 'alternate', - 'href' => common_local_url('api', - array('apiaction' => 'statuses', - 'method' => 'entity_timeline.rss', - 'argument' => $this->user->nickname)), - 'type' => 'application/rss+xml', - 'title' => sprintf(_('Notice feed for %s'), $this->user->nickname))); - $this->element('link', array('rel' => 'alternate feed', - 'href' => common_local_url('api', - array('apiaction' => 'statuses', - 'method' => 'entity_timeline.atom', - 'argument' => $this->user->nickname)), - 'type' => 'application/atom+xml', - 'title' => sprintf(_('Notice feed for %s'), $this->user->nickname))); - $this->element('link', array('rel' => 'alternate', - 'href' => common_local_url('userrss', array('nickname' => - $this->user->nickname)), - 'type' => 'application/rdf+xml', - 'title' => sprintf(_('Notice feed for %s'), $this->user->nickname))); + return array(new Feed(Feed::RSS1, + common_local_url('userrss', + array('nickname' => $this->user->nickname)), + sprintf(_('Notice feed for %s (RSS 1.0)'), + $this->user->nickname)), + new Feed(Feed::RSS2, + common_local_url('api', + array('apiaction' => 'statuses', + 'method' => 'user_timeline', + 'argument' => $this->user->nickname.'.rss')), + sprintf(_('Notice feed for %s (RSS 2.0)'), + $this->user->nickname)), + new Feed(Feed::ATOM, + common_local_url('api', + array('apiaction' => 'statuses', + 'method' => 'user_timeline', + 'argument' => $this->user->nickname.'.atom')), + sprintf(_('Notice feed for %s (Atom)'), + $this->user->nickname)), + new Feed(Feed::FOAF, + common_local_url('foaf', array('nickname' => + $this->user->nickname)), + sprintf(_('FOAF for %s'), $this->user->nickname))); } function extraHead() { - // FOAF - $this->element('link', array('rel' => 'meta', - 'href' => common_local_url('foaf', array('nickname' => - $this->user->nickname)), - 'type' => 'application/rdf+xml', - 'title' => 'FOAF')); // for remote subscriptions etc. $this->element('meta', array('http-equiv' => 'X-XRDS-Location', 'content' => common_local_url('xrds', array('nickname' => - $this->user->nickname)))); + $this->user->nickname)))); if ($this->profile->bio) { $this->element('meta', array('name' => 'description', @@ -239,12 +223,21 @@ class ShowstreamAction extends Action $this->elementStart('dl', 'entity_depiction'); $this->element('dt', null, _('Photo')); $this->elementStart('dd'); - $this->element('img', array('src' => ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_PROFILE_SIZE), + $this->element('img', array('src' => ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE), 'class' => 'photo avatar', 'width' => AVATAR_PROFILE_SIZE, 'height' => AVATAR_PROFILE_SIZE, 'alt' => $this->profile->nickname)); $this->elementEnd('dd'); + + $user = User::staticGet('id', $this->profile->id); + $cur = common_current_user(); + if ($cur && $cur->id == $user->id) { + $this->elementStart('dd'); + $this->element('a', array('href' => common_local_url('avatarsettings')), _('Edit Avatar')); + $this->elementEnd('dd'); + } + $this->elementEnd('dl'); $this->elementStart('dl', 'entity_nickname'); @@ -253,7 +246,7 @@ class ShowstreamAction extends Action $hasFN = ($this->profile->fullname) ? 'nickname url uid' : 'fn nickname url uid'; $this->element('a', array('href' => $this->profile->profileurl, 'rel' => 'me', 'class' => $hasFN), - $this->profile->nickname); + $this->profile->nickname); $this->elementEnd('dd'); $this->elementEnd('dl'); @@ -269,7 +262,7 @@ class ShowstreamAction extends Action if ($this->profile->location) { $this->elementStart('dl', 'entity_location'); $this->element('dt', null, _('Location')); - $this->element('dd', 'location', $this->profile->location); + $this->element('dd', 'label', $this->profile->location); $this->elementEnd('dl'); } @@ -299,11 +292,11 @@ class ShowstreamAction extends Action $this->elementStart('ul', 'tags xoxo'); foreach ($tags as $tag) { $this->elementStart('li'); - $this->element('span', 'mark_hash', '#'); - $this->element('a', array('rel' => 'tag', - 'href' => common_local_url('peopletag', - array('tag' => $tag))), - $tag); + // Avoid space by using raw output. + $pt = '<span class="mark_hash">#</span><a rel="tag" href="' . + common_local_url('peopletag', array('tag' => $tag)) . + '">' . $tag . '</a>'; + $this->raw($pt); $this->elementEnd('li'); } $this->elementEnd('ul'); @@ -312,14 +305,22 @@ class ShowstreamAction extends Action } $this->elementEnd('div'); - //XXX: entity_actions doesn't need to be outputted if entity is looking at their own profile $this->elementStart('div', 'entity_actions'); $this->element('h2', null, _('User actions')); $this->elementStart('ul'); - $this->elementStart('li', array('class' => 'entity_subscribe')); $cur = common_current_user(); + + if ($cur && $cur->id == $this->profile->id) { + $this->elementStart('li', 'entity_edit'); + $this->element('a', array('href' => common_local_url('profilesettings'), + 'title' => _('Edit profile settings')), + _('Edit')); + $this->elementEnd('li'); + } + if ($cur) { if ($cur->id != $this->profile->id) { + $this->elementStart('li', 'entity_subscribe'); if ($cur->isSubscribed($this->profile)) { $usf = new UnsubscribeForm($this, $this->profile); $usf->show(); @@ -327,24 +328,23 @@ class ShowstreamAction extends Action $sf = new SubscribeForm($this, $this->profile); $sf->show(); } + $this->elementEnd('li'); } } else { + $this->elementStart('li', 'entity_subscribe'); $this->showRemoteSubscribeLink(); + $this->elementEnd('li'); } - $this->elementEnd('li'); -// common_profile_new_message_nudge($cur, $this->user, $this->profile); - - $user = User::staticGet('id', $this->profile->id); if ($cur && $cur->id != $user->id && $cur->mutuallySubscribed($user)) { - $this->elementStart('li', array('class' => 'entity_send-a-message')); + $this->elementStart('li', 'entity_send-a-message'); $this->element('a', array('href' => common_local_url('newmessage', array('to' => $user->id)), 'title' => _('Send a direct message to this user')), _('Message')); $this->elementEnd('li'); if ($user->email && $user->emailnotifynudge) { - $this->elementStart('li', array('class' => 'entity_nudge')); + $this->elementStart('li', 'entity_nudge'); $nf = new NudgeForm($this, $user); $nf->show(); $this->elementEnd('li'); @@ -353,7 +353,7 @@ class ShowstreamAction extends Action if ($cur && $cur->id != $this->profile->id) { $blocked = $cur->hasBlocked($this->profile); - $this->elementStart('li', array('class' => 'entity_block')); + $this->elementStart('li', 'entity_block'); if ($blocked) { $ubf = new UnblockForm($this, $this->profile); $ubf->show(); @@ -479,7 +479,7 @@ class ShowstreamAction extends Action $this->elementStart('dl', 'entity_member-since'); $this->element('dt', null, _('Member since')); $this->element('dd', null, date('j M Y', - strtotime($this->profile->created))); + strtotime($this->profile->created))); $this->elementEnd('dl'); $this->elementStart('dl', 'entity_subscriptions'); diff --git a/actions/smssettings.php b/actions/smssettings.php index f89cbe1ab..a5f75d266 100644 --- a/actions/smssettings.php +++ b/actions/smssettings.php @@ -490,4 +490,55 @@ class SmssettingsAction extends ConnectSettingsAction common_redirect(common_local_url('confirmaddress', array('code' => $code))); } + + /** + * Handle a request to remove an incoming email address + * + * @return void + */ + + function removeIncoming() + { + $user = common_current_user(); + + if (!$user->incomingemail) { + $this->showForm(_('No incoming email address.')); + return; + } + + $orig = clone($user); + + $user->incomingemail = null; + + if (!$user->updateKeys($orig)) { + common_log_db_error($user, 'UPDATE', __FILE__); + $this->serverError(_("Couldn't update user record.")); + } + + $this->showForm(_('Incoming email address removed.'), true); + } + + /** + * Generate a new incoming email address + * + * @return void + * + * @see Emailsettings::newIncoming + */ + + function newIncoming() + { + $user = common_current_user(); + + $orig = clone($user); + + $user->incomingemail = mail_new_incoming_address(); + + if (!$user->updateKeys($orig)) { + common_log_db_error($user, 'UPDATE', __FILE__); + $this->serverError(_("Couldn't update user record.")); + } + + $this->showForm(_('New incoming email address added.'), true); + } } diff --git a/actions/subscribe.php b/actions/subscribe.php index 171332734..f761992de 100644 --- a/actions/subscribe.php +++ b/actions/subscribe.php @@ -64,7 +64,7 @@ class SubscribeAction extends Action } if ($this->boolean('ajax')) { - $this->startHTML('text/xml;charset=utf-8', true); + $this->startHTML('text/xml;charset=utf-8'); $this->elementStart('head'); $this->element('title', null, _('Subscribed')); $this->elementEnd('head'); diff --git a/actions/subscribers.php b/actions/subscribers.php index fb8733c77..22faafaef 100644 --- a/actions/subscribers.php +++ b/actions/subscribers.php @@ -72,9 +72,13 @@ class SubscribersAction extends GalleryAction function showContent() { + parent::showContent(); + $offset = ($this->page-1) * PROFILES_PER_PAGE; $limit = PROFILES_PER_PAGE + 1; + $cnt = 0; + if ($this->tag) { $subscribers = $this->user->getTaggedSubscribers($this->tag, $offset, $limit); } else { @@ -83,7 +87,7 @@ class SubscribersAction extends GalleryAction if ($subscribers) { $subscribers_list = new SubscribersList($subscribers, $this->user, $this); - $subscribers_list->show(); + $cnt = $subscribers_list->show(); } $subscribers->free(); @@ -96,9 +100,9 @@ class SubscribersAction extends GalleryAction class SubscribersList extends ProfileList { - function showOwnerControls($profile) + function showBlockForm() { - $bf = new BlockForm($this->out, $profile, + $bf = new BlockForm($this->out, $this->profile, array('action' => 'subscribers', 'nickname' => $this->owner->nickname)); $bf->show(); diff --git a/actions/subscriptions.php b/actions/subscriptions.php index d7ba0d624..3fbea2039 100644 --- a/actions/subscriptions.php +++ b/actions/subscriptions.php @@ -84,6 +84,8 @@ class SubscriptionsAction extends GalleryAction $offset = ($this->page-1) * PROFILES_PER_PAGE; $limit = PROFILES_PER_PAGE + 1; + $cnt = 0; + if ($this->tag) { $subscriptions = $this->user->getTaggedSubscriptions($this->tag, $offset, $limit); } else { @@ -92,7 +94,7 @@ class SubscriptionsAction extends GalleryAction if ($subscriptions) { $subscriptions_list = new SubscriptionsList($subscriptions, $this->user, $this); - $subscriptions_list->show(); + $cnt = $subscriptions_list->show(); } $subscriptions->free(); diff --git a/actions/tag.php b/actions/tag.php index 039cd9660..231f2c299 100644 --- a/actions/tag.php +++ b/actions/tag.php @@ -24,14 +24,22 @@ class TagAction extends Action function prepare($args) { parent::prepare($args); - $this->tag = $this->trimmed('tag'); + $taginput = $this->trimmed('tag'); + $this->tag = common_canonical_tag($taginput); if (!$this->tag) { common_redirect(common_local_url('publictagcloud'), 301); return false; } + if ($this->tag != $taginput) { + common_redirect(common_local_url('tag', array('tag' => $this->tag))); + } + $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + + common_set_returnto($this->selfUrl()); + return true; } @@ -53,12 +61,11 @@ class TagAction extends Action $this->showPage(); } - function showFeeds() + function getFeeds() { - $this->element('link', array('rel' => 'alternate', - 'href' => common_local_url('tagrss', array('tag' => $this->tag)), - 'type' => 'application/rss+xml', - 'title' => sprintf(_('Feed for tag %s'), $this->tag))); + return array(new Feed(Feed::RSS1, + common_local_url('tagrss', array('tag' => $this->tag)), + sprintf(_('Feed for tag %s'), $this->tag))); } function showPageNotice() @@ -66,15 +73,6 @@ class TagAction extends Action return sprintf(_('Messages tagged "%s", most recent first'), $this->tag); } - function showExportData() - { - $fl = new FeedList($this); - $fl->show(array(0=>array('href'=>common_local_url('tagrss', array('tag' => $this->tag)), - 'type' => 'rss', - 'version' => 'RSS 1.0', - 'item' => 'tagrss'))); - } - function showContent() { $notice = Notice_tag::getStream($this->tag, (($this->page-1)*NOTICES_PER_PAGE), NOTICES_PER_PAGE + 1); diff --git a/actions/tagother.php b/actions/tagother.php index 9a63fc438..0d18945a0 100644 --- a/actions/tagother.php +++ b/actions/tagother.php @@ -80,7 +80,7 @@ class TagotherAction extends Action $this->elementStart('dl', 'entity_depiction'); $this->element('dt', null, _('Photo')); $this->elementStart('dd'); - $this->element('img', array('src' => ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_PROFILE_SIZE), + $this->element('img', array('src' => ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE), 'class' => 'photo avatar', 'width' => AVATAR_PROFILE_SIZE, 'height' => AVATAR_PROFILE_SIZE, @@ -90,7 +90,6 @@ class TagotherAction extends Action $this->elementEnd('dd'); $this->elementEnd('dl'); - $this->elementStart('dl', 'entity_nickname'); $this->element('dt', null, _('Nickname')); $this->elementStart('dd'); @@ -111,7 +110,7 @@ class TagotherAction extends Action if ($this->profile->location) { $this->elementStart('dl', 'entity_location'); $this->element('dt', null, _('Location')); - $this->element('dd', 'location', $this->profile->location); + $this->element('dd', 'label', $this->profile->location); $this->elementEnd('dl'); } if ($this->profile->homepage) { @@ -136,12 +135,15 @@ class TagotherAction extends Action 'id' => 'form_tag_user', 'class' => 'form_settings', 'name' => 'tagother', - 'action' => $this->selfUrl())); + 'action' => common_local_url('tagother', array('id' => $this->profile->id)))); + $this->elementStart('fieldset'); $this->element('legend', null, _('Tag user')); $this->hidden('token', common_session_token()); $this->hidden('id', $this->profile->id); + $user = common_current_user(); + $this->elementStart('ul', 'form_data'); $this->elementStart('li'); $this->input('tags', _('Tags'), @@ -202,7 +204,7 @@ class TagotherAction extends Action $action = $user->isSubscribed($this->profile) ? 'subscriptions' : 'subscribers'; if ($this->boolean('ajax')) { - $this->startHTML('text/xml'); + $this->startHTML('text/xml;charset=utf-8'); $this->elementStart('head'); $this->element('title', null, _('Tags')); $this->elementEnd('head'); diff --git a/actions/tagrss.php b/actions/tagrss.php index b4c2dcdff..a77fa12c9 100644 --- a/actions/tagrss.php +++ b/actions/tagrss.php @@ -25,12 +25,12 @@ require_once(INSTALLDIR.'/lib/rssaction.php'); class TagrssAction extends Rss10Action { + var $tag; - function init() - { - $tag = $this->trimmed('tag'); + function prepare($args) { + parent::prepare($args); + $tag = common_canonical_tag($this->trimmed('tag')); $this->tag = Notice_tag::staticGet('tag', $tag); - if (!$this->tag) { $this->clientError(_('No such tag.')); return false; @@ -39,7 +39,7 @@ class TagrssAction extends Rss10Action } } - function get_notices($limit=0) + function getNotices($limit=0) { $tag = $this->tag; @@ -48,7 +48,6 @@ class TagrssAction extends Rss10Action } $notice = Notice_tag::getStream($tag->tag, 0, ($limit == 0) ? NOTICES_PER_PAGE : $limit); - while ($notice->fetch()) { $notices[] = clone($notice); } @@ -56,10 +55,9 @@ class TagrssAction extends Rss10Action return $notices; } - function get_channel() + function getChannel() { - $tag = $this->tag->tag; - + $tagname = $this->tag->tag; $c = array('url' => common_local_url('tagrss', array('tag' => $tagname)), 'title' => $tagname, 'link' => common_local_url('tagrss', array('tag' => $tagname)), diff --git a/actions/twitapiaccount.php b/actions/twitapiaccount.php index dc8e2e798..68a18cb57 100644 --- a/actions/twitapiaccount.php +++ b/actions/twitapiaccount.php @@ -23,23 +23,24 @@ require_once(INSTALLDIR.'/lib/twitterapi.php'); class TwitapiaccountAction extends TwitterapiAction { - - function verify_credentials($args, $apidata) + function verify_credentials($args, $apidata) { + parent::handle($args); - if ($apidata['content-type'] == 'xml') { - header('Content-Type: application/xml; charset=utf-8'); - print '<authorized>true</authorized>'; - } elseif ($apidata['content-type'] == 'json') { - header('Content-Type: application/json; charset=utf-8'); - print '{"authorized":true}'; - } else { - common_user_error(_('API method not found!'), $code=404); - } - - } + switch ($apidata['content-type']) { + case 'xml': + case 'json': + $action_obj = new TwitapiusersAction(); + $action_obj->prepare($args); + call_user_func(array($action_obj, 'show'), $args, $apidata); + break; + default: + header('Content-Type: text/html; charset=utf-8'); + print 'Authorized'; + } + } - function end_session($args, $apidata) + function end_session($args, $apidata) { parent::handle($args); $this->serverError(_('API method under construction.'), $code=501); @@ -56,7 +57,7 @@ class TwitapiaccountAction extends TwitterapiAction $location = trim($this->arg('location')); - if (!is_null($location) && strlen($location) > 255) { + if (!is_null($location) && mb_strlen($location) > 255) { // XXX: But Twitter just truncates and runs with it. -- Zach $this->clientError(_('That\'s too long. Max notice size is 255 chars.'), 406, $apidate['content-type']); diff --git a/actions/twitapisearch.php b/actions/twitapisearch.php new file mode 100644 index 000000000..822ee77e1 --- /dev/null +++ b/actions/twitapisearch.php @@ -0,0 +1,97 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * List of replies + * + * PHP version 5 + * + * LICENCE: 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/>. + * + * @category Search + * @package Laconica + * @author Zach Copley <zach@controlyourself.ca> + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/twitterapi.php'; + +/** + * Action handler for Twitter-compatible API search + * + * @category Search + * @package Laconica + * @author Zach Copley <zach@controlyourself.ca> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + * @see TwitterapiAction + */ + +class TwitapisearchAction extends TwitterapiAction +{ + + var $query; + var $limit; + var $callback; + + /** + * Initialization. + * + * @param array $args Web and URL arguments + * + * @return boolean false if user doesn't exist + */ + + function prepare($args) + { + parent::prepare($args); + $qeury = $this->trimmed('query'); + + return true; + } + + /** + * Handle a request + * + * @param array $args Arguments from $_REQUEST + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + $this->showResults($this->limit); + } + + /** + * Show search results + * + * @param int $limit Number of notices to show + * + * @return void + */ + + function showResults($limit) + { + $this->serverError(_('API method under construction.'), $code = 501); + } + +} diff --git a/actions/twitapistatuses.php b/actions/twitapistatuses.php index a35f4b12e..216835026 100644 --- a/actions/twitapistatuses.php +++ b/actions/twitapistatuses.php @@ -204,7 +204,7 @@ class TwitapistatusesAction extends TwitterapiAction # FriendFeed's SUP protocol # Also added RSS and Atom feeds - $suplink = common_local_url('sup', null, $user->id); + $suplink = common_local_url('sup', null, null, $user->id); header('X-SUP-ID: '.$suplink); # XXX: since @@ -450,7 +450,6 @@ class TwitapistatusesAction extends TwitterapiAction if ($user->id == $notice->profile_id) { $replies = new Reply; $replies->get('notice_id', $notice_id); - common_dequeue_notice($notice); $replies->delete(); $notice->delete(); @@ -471,19 +470,28 @@ class TwitapistatusesAction extends TwitterapiAction return $this->subscriptions($apidata, 'subscribed', 'subscriber'); } - function followers($args, $apidata) + function friendsIDs($args, $apidata) { parent::handle($args); + return $this->subscriptions($apidata, 'subscribed', 'subscriber', true); + } + function followers($args, $apidata) + { + parent::handle($args); return $this->subscriptions($apidata, 'subscriber', 'subscribed'); } - function subscriptions($apidata, $other_attr, $user_attr) + function followersIDs($args, $apidata) { + parent::handle($args); + return $this->subscriptions($apidata, 'subscriber', 'subscribed', true); + } - # XXX: lite + function subscriptions($apidata, $other_attr, $user_attr, $onlyIDs=false) + { - $this->auth_user = $apidate['user']; + $this->auth_user = $apidata['user']; $user = $this->get_user($apidata['api_arg'], $apidata); if (!$user) { @@ -515,7 +523,10 @@ class TwitapistatusesAction extends TwitterapiAction } $sub->orderBy('created DESC'); - $sub->limit(($page-1)*100, 100); + + if (!$onlyIDs) { + $sub->limit(($page-1)*100, 100); + } $others = array(); @@ -530,7 +541,13 @@ class TwitapistatusesAction extends TwitterapiAction $type = $apidata['content-type']; $this->init_document($type); - $this->show_profiles($others, $type); + + if ($onlyIDs) { + $this->showIDs($others, $type); + } else { + $this->show_profiles($others, $type); + } + $this->end_document($type); } @@ -556,6 +573,28 @@ class TwitapistatusesAction extends TwitterapiAction } } + function showIDs($profiles, $type) + { + switch ($type) { + case 'xml': + $this->elementStart('ids'); + foreach ($profiles as $profile) { + $this->element('id', null, $profile->id); + } + $this->elementEnd('ids'); + break; + case 'json': + $ids = array(); + foreach ($profiles as $profile) { + $ids[] = (int)$profile->id; + } + print json_encode($ids); + break; + default: + $this->clientError(_('unsupported file type')); + } + } + function featured($args, $apidata) { parent::handle($args); diff --git a/actions/twitapitrends.php b/actions/twitapitrends.php new file mode 100644 index 000000000..c73d89446 --- /dev/null +++ b/actions/twitapitrends.php @@ -0,0 +1,90 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * List of replies + * + * PHP version 5 + * + * LICENCE: 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/>. + * + * @category Search + * @package Laconica + * @author Zach Copley <zach@controlyourself.ca> + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/twitterapi.php'; + +/** + * Returns the top ten queries that are currently trending + * + * @category Search + * @package Laconica + * @author Zach Copley <zach@controlyourself.ca> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + * + * @see TwitterapiAction + */ + +class TwitapitrendsAction extends TwitterapiAction +{ + + var $callback; + + /** + * Initialization. + * + * @param array $args Web and URL arguments + * + * @return boolean false if user doesn't exist + */ + function prepare($args) + { + parent::prepare($args); + return true; + } + + /** + * Handle a request + * + * @param array $args Arguments from $_REQUEST + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + $this->showTrends(); + } + + /** + * Output the trends + * + * @return void + */ + function showTrends() + { + $this->serverError(_('API method under construction.'), $code = 501); + } + +}
\ No newline at end of file diff --git a/actions/twitapiusers.php b/actions/twitapiusers.php index 8f16e5613..2894b7486 100644 --- a/actions/twitapiusers.php +++ b/actions/twitapiusers.php @@ -25,25 +25,29 @@ class TwitapiusersAction extends TwitterapiAction { function show($args, $apidata) - { + { parent::handle($args); - if (!in_array($apidata['content-type'], array('xml', 'json'))) { + if (!in_array($apidata['content-type'], array('xml', 'json'))) { $this->clientError(_('API method not found!'), $code = 404); return; } - - $this->auth_user = $apidata['user']; + $user = null; $email = $this->arg('email'); + $user_id = $this->arg('user_id'); if ($email) { $user = User::staticGet('email', $email); + } elseif ($user_id) { + $user = $this->get_user($user_id); } elseif (isset($apidata['api_arg'])) { $user = $this->get_user($apidata['api_arg']); - } - - if (!$user) { + } elseif (isset($apidata['user'])) { + $user = $apidata['user']; + } + + if (!$user) { // XXX: Twitter returns a random(?) user instead of throwing and err! -- Zach $this->client_error(_('Not found.'), 404, $apidata['content-type']); return; @@ -74,9 +78,12 @@ class TwitapiusersAction extends TwitterapiAction // Other fields Twitter sends... $twitter_user['profile_background_color'] = ''; + $twitter_user['profile_background_image_url'] = ''; $twitter_user['profile_text_color'] = ''; $twitter_user['profile_link_color'] = ''; $twitter_user['profile_sidebar_fill_color'] = ''; + $twitter_user['profile_sidebar_border_color'] = ''; + $twitter_user['profile_background_tile'] = 'false'; $faves = DB_DataObject::factory('fave'); $faves->user_id = $user->id; @@ -94,18 +101,27 @@ class TwitapiusersAction extends TwitterapiAction $twitter_user['utc_offset'] = $t->format('Z'); $twitter_user['time_zone'] = $timezone; - if (isset($this->auth_user)) { + if (isset($apidata['user'])) { - if ($this->auth_user->isSubscribed($profile)) { + if ($apidata['user']->isSubscribed($profile)) { $twitter_user['following'] = 'true'; } else { $twitter_user['following'] = 'false'; } - - // Not implemented yet - $twitter_user['notifications'] = 'false'; - } - + + // Notifications on? + $sub = Subscription::pkeyGet(array('subscriber' => + $apidata['user']->id, 'subscribed' => $profile->id)); + + if ($sub) { + if ($sub->jabber || $sub->sms) { + $twitter_user['notifications'] = 'true'; + } else { + $twitter_user['notifications'] = 'false'; + } + } + } + if ($apidata['content-type'] == 'xml') { $this->init_document('xml'); $this->show_twitter_xml_user($twitter_user); @@ -114,7 +130,13 @@ class TwitapiusersAction extends TwitterapiAction $this->init_document('json'); $this->show_json_objects($twitter_user); $this->end_document('json'); - } + } else { + + // This is in case 'show' was called via /account/verify_credentials + // without a format (xml or json). + header('Content-Type: text/html; charset=utf-8'); + print 'Authorized'; + } } } diff --git a/actions/twittersettings.php b/actions/twittersettings.php index bb0bc5946..a79859bbf 100644 --- a/actions/twittersettings.php +++ b/actions/twittersettings.php @@ -32,6 +32,7 @@ if (!defined('LACONICA')) { } require_once INSTALLDIR.'/lib/connectsettingsaction.php'; +require_once INSTALLDIR.'/lib/twitter.php'; define('SUBSCRIPTIONS', 80); @@ -57,7 +58,7 @@ class TwittersettingsAction extends ConnectSettingsAction function title() { - _('Twitter settings'); + return _('Twitter settings'); } /** @@ -90,7 +91,7 @@ class TwittersettingsAction extends ConnectSettingsAction $fuser = null; - $flink = Foreign_link::getByUserID($user->id, 1); // 1 == Twitter + $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE); if ($flink) { $fuser = $flink->getForeignUser(); @@ -109,7 +110,7 @@ class TwittersettingsAction extends ConnectSettingsAction $this->elementStart('li', array('id' => 'settings_twitter_remove')); $this->element('span', 'twitter_user', $fuser->nickname); $this->element('a', array('href' => $fuser->uri), $fuser->uri); - $this->element('p', 'form_guide', + $this->element('p', 'form_note', _('Current verified Twitter account.')); $this->hidden('flink_foreign_id', $flink->foreign_id); $this->elementEnd('li'); @@ -250,8 +251,8 @@ class TwittersettingsAction extends ConnectSettingsAction $avatar = $other->getAvatar(AVATAR_MINI_SIZE); $avatar_url = ($avatar) ? - common_avatar_display_url($avatar) : - common_default_avatar(AVATAR_MINI_SIZE); + $avatar->displayUrl() : + Avatar::defaultImage(AVATAR_MINI_SIZE); $this->element('img', array('src' => $avatar_url, 'width' => AVATAR_MINI_SIZE, @@ -358,7 +359,7 @@ class TwittersettingsAction extends ConnectSettingsAction $flink->user_id = $user->id; $flink->foreign_id = $twit_user->id; - $flink->service = 1; // Twitter + $flink->service = TWITTER_SERVICE; $flink->credentials = $password; $flink->created = common_sql_now(); diff --git a/actions/unsubscribe.php b/actions/unsubscribe.php index f9dd6f821..b1e2b6425 100644 --- a/actions/unsubscribe.php +++ b/actions/unsubscribe.php @@ -66,7 +66,7 @@ class UnsubscribeAction extends Action } if ($this->boolean('ajax')) { - $this->startHTML('text/xml;charset=utf-8', true); + $this->startHTML('text/xml;charset=utf-8'); $this->elementStart('head'); $this->element('title', null, _('Unsubscribed')); $this->elementEnd('head'); diff --git a/actions/updateprofile.php b/actions/updateprofile.php index c79112dac..4751a04ff 100644 --- a/actions/updateprofile.php +++ b/actions/updateprofile.php @@ -93,22 +93,22 @@ class UpdateprofileAction extends Action } # optional stuff $fullname = $req->get_parameter('omb_listenee_fullname'); - if ($fullname && strlen($fullname) > 255) { + if ($fullname && mb_strlen($fullname) > 255) { $this->clientError(_("Full name is too long (max 255 chars).")); return false; } $homepage = $req->get_parameter('omb_listenee_homepage'); - if ($homepage && (!common_valid_http_url($homepage) || strlen($homepage) > 255)) { + if ($homepage && (!common_valid_http_url($homepage) || mb_strlen($homepage) > 255)) { $this->clientError(sprintf(_("Invalid homepage '%s'"), $homepage)); return false; } $bio = $req->get_parameter('omb_listenee_bio'); - if ($bio && strlen($bio) > 140) { + if ($bio && mb_strlen($bio) > 140) { $this->clientError(_("Bio is too long (max 140 chars).")); return false; } $location = $req->get_parameter('omb_listenee_location'); - if ($location && strlen($location) > 255) { + if ($location && mb_strlen($location) > 255) { $this->clientError(_("Location is too long (max 255 chars).")); return false; } @@ -162,7 +162,13 @@ class UpdateprofileAction extends Action if ($avatar) { $temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar'); copy($avatar, $temp_filename); - if (!$profile->setOriginal($temp_filename)) { + $imagefile = new ImageFile($profile->id, $temp_filename); + $filename = Avatar::filename($profile->id, + image_type_to_extension($imagefile->type), + null, + common_timestamp()); + rename($temp_filename, Avatar::path($filename)); + if (!$profile->setOriginal($filename)) { $this->serverError(_('Could not save avatar info'), 500); return false; } diff --git a/actions/userauthorization.php b/actions/userauthorization.php index ed62f640c..0dc1841d4 100644 --- a/actions/userauthorization.php +++ b/actions/userauthorization.php @@ -86,7 +86,7 @@ class UserauthorizationAction extends Action $this->element('p', null, _('Please check these details to make sure '. 'that you want to subscribe to this user\'s notices. '. 'If you didn\'t just ask to subscribe to someone\'s notices, '. - 'click "Cancel".')); + 'click "Reject".')); } function showContent() @@ -105,7 +105,7 @@ class UserauthorizationAction extends Action $this->elementStart('div', 'profile'); if ($avatar) { $this->element('img', array('src' => $avatar, - 'class' => 'avatar profile', + 'class' => 'avatar', 'width' => AVATAR_PROFILE_SIZE, 'height' => AVATAR_PROFILE_SIZE, 'alt' => $nickname)); @@ -330,7 +330,13 @@ class UserauthorizationAction extends Action { $temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar'); copy($url, $temp_filename); - return $profile->setOriginal($temp_filename); + $imagefile = new ImageFile($profile->id, $temp_filename); + $filename = Avatar::filename($profile->id, + image_type_to_extension($imagefile->type), + null, + common_timestamp()); + rename($temp_filename, Avatar::path($filename)); + return $profile->setOriginal($filename); } function showAcceptMessage($tok) @@ -469,19 +475,19 @@ class UserauthorizationAction extends Action } # optional stuff $fullname = $req->get_parameter('omb_listenee_fullname'); - if ($fullname && strlen($fullname) > 255) { + if ($fullname && mb_strlen($fullname) > 255) { throw new OAuthException("Full name '$fullname' too long."); } $homepage = $req->get_parameter('omb_listenee_homepage'); - if ($homepage && (!common_valid_http_url($homepage) || strlen($homepage) > 255)) { + if ($homepage && (!common_valid_http_url($homepage) || mb_strlen($homepage) > 255)) { throw new OAuthException("Invalid homepage '$homepage'"); } $bio = $req->get_parameter('omb_listenee_bio'); - if ($bio && strlen($bio) > 140) { + if ($bio && mb_strlen($bio) > 140) { throw new OAuthException("Bio too long '$bio'"); } $location = $req->get_parameter('omb_listenee_location'); - if ($location && strlen($location) > 255) { + if ($location && mb_strlen($location) > 255) { throw new OAuthException("Location too long '$location'"); } $avatar = $req->get_parameter('omb_listenee_avatar'); diff --git a/actions/usergroups.php b/actions/usergroups.php index 58cf7eeb6..ded4ba76b 100644 --- a/actions/usergroups.php +++ b/actions/usergroups.php @@ -125,6 +125,12 @@ class UsergroupsAction extends Action _('Create a new group')); $this->elementEnd('p'); + $this->elementStart('p', array('id' => 'group_search')); + $this->element('a', array('href' => common_local_url('groupsearch'), + 'class' => 'more'), + _('Search for more groups')); + $this->elementEnd('p'); + $offset = ($this->page-1) * GROUPS_PER_PAGE; $limit = GROUPS_PER_PAGE + 1; diff --git a/actions/userrss.php b/actions/userrss.php index 04855ccca..a3e5a3aab 100644 --- a/actions/userrss.php +++ b/actions/userrss.php @@ -46,13 +46,13 @@ class UserrssAction extends Rss10Action { $user = $this->user; - + if (is_null($user)) { return null; } - + $notice = $user->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit); - + while ($notice->fetch()) { $notices[] = clone($notice); } @@ -87,10 +87,10 @@ class UserrssAction extends Rss10Action } # override parent to add X-SUP-ID URL - + function initRss($limit=0) { - $url = common_local_url('sup', null, $this->user->id); + $url = common_local_url('sup', null, null, $this->user->id); header('X-SUP-ID: '.$url); parent::initRss($limit); } @@ -100,4 +100,3 @@ class UserrssAction extends Rss10Action return true; } } - |