Merge branch '0.8.x' of git://gitorious.org/laconica/dev into dev/0.8.x

This commit is contained in:
Evan Prodromou 2009-05-19 17:43:14 +00:00
commit 003c63e587
531 changed files with 35333 additions and 11077 deletions

6
.gitignore vendored
View File

@ -1,8 +1,10 @@
avatar/* avatar/*
files/* files/*
_darcs/* _darcs/*
logs/*
config.php config.php
.htaccess .htaccess
httpd.conf
*.tmproj *.tmproj
dataobject.ini dataobject.ini
*~ *~
@ -10,3 +12,7 @@ dataobject.ini
*.orig *.orig
*.rej *.rej
.#* .#*
*.swp
.buildpath
.project
.settings

View File

@ -82,3 +82,38 @@ StartNoticeSave: before inserting a notice (good place for content filters)
EndNoticeSave: after inserting a notice and related code EndNoticeSave: after inserting a notice and related code
- $notice: notice that was saved (with ID and URI) - $notice: notice that was saved (with ID and URI)
StartShowLocalNavBlock: Showing the local nav menu
- $action: the current action
EndShowLocalNavBlock: At the end of the local nav menu
- $action: the current action
StartShowHTML: Chance to set document headers (e.g., content type, charset, language), DOCTYPE and html element properties
- $action: the current action
EndShowHTML: Showing after the html element
- $action: the current action
StartPublicGroupNav: Showing the public group nav menu
- $action: the current action
EndPublicGroupNav: At the end of the public group nav menu
- $action: the current action
StartSubGroupNav: Showing the subscriptions group nav menu
- $action: the current action
EndSubGroupNav: At the end of the subscriptions group nav menu
- $action: the current action
RouterInitialized: After the router instance has been initialized
- $m: the Net_URL_Mapper that has just been set up
StartLogout: Before logging out
- $action: the logout action
EndLogout: After logging out
- $action: the logout action
ArgsInitialized: After the argument array has been initialized
- $args: associative array of arguments, can be modified

286
README
View File

@ -2,8 +2,8 @@
README README
------ ------
Laconica 0.7.1 ("West of the Fields") Laconica 0.7.3 ("You Are The Everything")
6 February 2009 7 April 2009
This is the README file for Laconica, the Open Source microblogging This is the README file for Laconica, the Open Source microblogging
platform. It includes installation instructions, descriptions of platform. It includes installation instructions, descriptions of
@ -71,54 +71,29 @@ for additional terms.
New this version New this version
================ ================
This is a minor bug-fix release since version 0.7.0, released Jan 29 This is a minor bug-fix and feature release since version 0.7.2.1,
2009. Notable changes this version: released Mar 11 2009. Notable changes this version:
- Vast improvement in auto-linking to URLs. - A plugin to allow a templating language for customization
- Link to group search from user's group page - A plugin for Piwik Analytics engine
- Improved interface in Facebook application - A bookmarklet for posting a notice about a Web page you're reading
- Fix bad redirects in delete notice - A welcome notice ('welcomebot') and default subscription for new users
- Updated PostgreSQL database creation script - Support for SSL for some or all pages on the site
- Show filesize in avatar/logo upload - Better handling of empty notice lists on many pages
- Vastly improved avatar/logo upload - Major improvements to the Twitter friend-sync offline processing
- Allow re-authentication with OpenID - subscribers, subscriptions, groups are listed on the Personal page.
- Correctly link hashtabs inside parens and brackets - "Invite" link restored to main menu
- Group and avatar image transparency works - Better memory handling in FOAF output
- Better handling of commands through the Web and Ajax channels - Fix for SUP support (FriendFeed)
- Fix links for profile page feeds - Correct and intelligent redirect HTTP status codes
- Fixed destroy method in API - Fix DB collations for search and sort
- Fix endpoint of Connect menu when XMPP disabled - Better H1s and Titles using user full names
- Show number of group members - Fixes to make the linkback plugin operational
- Enable configuration files in /etc/laconica/ - Better indication that a notice is being published by Ajax (spinner)
- Better and unified Atom output
Changes in version 0.7.0: - Hiding "register" and "join now" messages when site is closed
- ping, twitter and facebook queuehandlers working better
- Support for groups. Users can join groups and send themed notices - Updated RPM spec
to those groups. All other members of the group receive the notices.
- Laconica-specific extensions to the Twitter API.
- A Facebook application.
- A massive UI redesign. The HTML generated by Laconica has changed
significantly, to make theming easier and to give a more open look
by default. Also, sidebar.
- Massive code hygiene changes to move towards compliance with the PEAR
coding standards and to support the new UI redesign.
- Began the breakup of util.php -- moved about 30% of code to a views
hierarchy.
- UI elements for statistical information (like top posters or most
popular groups) added in a sidebar.
- include Javascript badge by Kent Brewster.
- Updated online documentation.
- Cropping of user avatars using Jcrop.
- fix for Twitter bridge to not send "Expect:" headers.
- add 'dm' as a synonym for 'd' in commands.
- Upgrade upstream version of jQuery to 1.3.
- Upgrade upstream version of PHP-OpenID to 2.1.2.
- Move OpenMicroBlogging specification to its own repository.
- Make tag-based RSS streams work.
- Additional locales: Bulgarian, Catalan, Greek, Hebrew, simplified
Chinese, Telugu, Taiwanese Chinese, Vietnamese,
- PostgreSQL updates.
- Nasty bug in Twitter bridge that wouldn't verify with Twitter
Prerequisites Prerequisites
============= =============
@ -222,9 +197,9 @@ especially if you've previously installed PHP/MySQL packages.
1. Unpack the tarball you downloaded on your Web server. Usually a 1. Unpack the tarball you downloaded on your Web server. Usually a
command like this will work: command like this will work:
tar zxf laconica-0.7.1.tar.gz tar zxf laconica-0.7.3.tar.gz
...which will make a laconica-0.7.1 subdirectory in your current ...which will make a laconica-0.7.3 subdirectory in your current
directory. (If you don't have shell access on your Web server, you directory. (If you don't have shell access on your Web server, you
may have to unpack the tarball on your local computer and FTP the may have to unpack the tarball on your local computer and FTP the
files to the server.) files to the server.)
@ -232,7 +207,7 @@ especially if you've previously installed PHP/MySQL packages.
2. Move the tarball to a directory of your choosing in your Web root 2. Move the tarball to a directory of your choosing in your Web root
directory. Usually something like this will work: directory. Usually something like this will work:
mv laconica-0.7.1 /var/www/mublog mv laconica-0.7.3 /var/www/mublog
This will make your Laconica instance available in the mublog path of This will make your Laconica instance available in the mublog path of
your server, like "http://example.net/mublog". "microblog" or your server, like "http://example.net/mublog". "microblog" or
@ -240,21 +215,28 @@ especially if you've previously installed PHP/MySQL packages.
configure virtual hosts on your web server, you can try setting up configure virtual hosts on your web server, you can try setting up
"http://micro.example.net/" or the like. "http://micro.example.net/" or the like.
3. You should also take this moment to make your avatar subdirectory 3. Make your target directory writeable by the Web server.
chmod a+w /var/www/mublog/
On some systems, this will probably work:
chgrp www-data /var/www/mublog/
chmod g+w /var/www/mublog/
If your Web server runs as another user besides "www-data", try
that user's default group instead. As a last resort, you can create
a new group like "mublog" and add the Web server's user to the group.
4. You should also take this moment to make your avatar subdirectory
writeable by the Web server. An insecure way to do this is: writeable by the Web server. An insecure way to do this is:
chmod a+w /var/www/mublog/avatar chmod a+w /var/www/mublog/avatar
On some systems, this will probably work: You can also make the avatar directory writeable by the Web server
group, as noted above.
chgrp www-data /var/www/mublog/avatar 5. Create a database to hold your microblog data. Something like this
chmod g+w /var/www/mublog/avatar
If your Web server runs as another user besides "www-data", try
that user's default group instead. As a last resort, you can create
a new group like "avatar" and add the Web server's user to the group.
4. Create a database to hold your microblog data. Something like this
should work: should work:
mysqladmin -u "username" --password="password" create laconica mysqladmin -u "username" --password="password" create laconica
@ -267,63 +249,55 @@ especially if you've previously installed PHP/MySQL packages.
a tool like PHPAdmin to create a database. Check your hosting a tool like PHPAdmin to create a database. Check your hosting
service's documentation for how to create a new MySQL database.) service's documentation for how to create a new MySQL database.)
5. Run the laconica.sql SQL script in the db subdirectory to create
the database tables in the database. A typical system would work
like this:
mysql -u "username" --password="password" laconica < /var/www/mublog/db/laconica.sql
You may want to test by logging into the database and checking that
the tables were created. Here's an example:
SHOW TABLES;
6. Create a new database account that Laconica will use to access the 6. Create a new database account that Laconica will use to access the
database. If you have shell access, this will probably work from the database. If you have shell access, this will probably work from the
MySQL shell: MySQL shell:
GRANT SELECT,INSERT,DELETE,UPDATE on laconica.* GRANT ALL on laconica.*
TO 'lacuser'@'localhost' TO 'lacuser'@'localhost'
IDENTIFIED BY 'lacpassword'; IDENTIFIED BY 'lacpassword';
You should change 'lacuser' and 'lacpassword' to your preferred new You should change 'lacuser' and 'lacpassword' to your preferred new
username and password. You may want to test logging in as this new username and password. You may want to test logging in to MySQL as
user and testing that you can SELECT from some of the tables in the this new user.
DB (use SHOW TABLES to see which ones are there).
7. Copy the config.php.sample in the Laconica directory to config.php. 7. In a browser, navigate to the Laconica install script; something like:
8. Edit config.php to set the basic configuration for your system. http://yourserver.example.com/mublog/install.php
(See descriptions below for basic config options.) Note that there
are lots of options and if you try to do them all at once, you will
have a hard time making sure what's working and what's not. So,
stick with the basics at first. In particular, customizing the
'site' and 'db' settings will almost definitely be needed.
9. At this point, you should be able to navigate in a browser to your Enter the database connection information and your site name. The
microblog's main directory and see the "Public Timeline", which install program will configure your site and install the initial,
will be empty. If not, magic has happened! You can now register a almost-empty database.
new user, post some notices, edit your profile, etc. However, you
may want to wait to do that stuff if you think you can set up 8. You should now be able to navigate to your microblog's main directory
"fancy URLs" (see below), since some URLs are stored in the database. and see the "Public Timeline", which will be empty. If not, magic
has happened! You can now register a new user, post some notices,
edit your profile, etc. However, you may want to wait to do that stuff
if you think you can set up "fancy URLs" (see below), since some
URLs are stored in the database.
Fancy URLs Fancy URLs
---------- ----------
By default, Laconica will have big long sloppy URLs that are hard for By default, Laconica will use URLs that include the main PHP program's
people to remember or use. For example, a user's home profile might be name in them. For example, a user's home profile might be
found at: found at:
http://example.org/mublog/index.php?action=showstream&nickname=fred http://example.org/mublog/index.php/mublog/fred
On certain systems that don't support this kind of syntax, they'll
look like this:
http://example.org/mublog/index.php?p=mublog/fred
It's possible to configure the software so it looks like this instead: It's possible to configure the software so it looks like this instead:
http://example.org/mublog/fred http://example.org/mublog/fred
These "fancy URLs" are more readable and memorable for users. To use These "fancy URLs" are more readable and memorable for users. To use
fancy URLs, you must either have Apache 2.2.x with .htaccess enabled fancy URLs, you must either have Apache 2.x with .htaccess enabled and
and mod_redirect enabled, -OR- know how to configure "url redirection" mod_redirect enabled, -OR- know how to configure "url redirection" in
in your server. your server.
1. Copy the htaccess.sample file to .htaccess in your Laconica 1. Copy the htaccess.sample file to .htaccess in your Laconica
directory. Note: if you have control of your server's httpd.conf or directory. Note: if you have control of your server's httpd.conf or
@ -348,10 +322,6 @@ like:
If you changed your HTTP server configuration, you may need to restart If you changed your HTTP server configuration, you may need to restart
the server first. the server first.
If you have problems with the .htaccess file on versions of Apache
earlier than 2.2.x, try changing the regular expressions in the
htaccess.sample file that use "\w" to just use ".".
Sphinx Sphinx
------ ------
@ -511,7 +481,7 @@ server is probably a good idea for high-volume sites.
needs as a parameter the install path; if you run it from the needs as a parameter the install path; if you run it from the
Laconica dir, "." should suffice. Laconica dir, "." should suffice.
This will run six (for now) queue handlers: This will run eight (for now) queue handlers:
* xmppdaemon.php - listens for new XMPP messages from users and stores * xmppdaemon.php - listens for new XMPP messages from users and stores
them as notices in the database. them as notices in the database.
@ -525,6 +495,10 @@ This will run six (for now) queue handlers:
of registered users. of registered users.
* xmppconfirmhandler.php - sends confirmation messages to registered * xmppconfirmhandler.php - sends confirmation messages to registered
users. users.
* twitterqueuehandler.php - sends queued notices to Twitter for user
who have opted to set up Twitter bridging.
* facebookqueuehandler.php - sends queued notices to Facebook for users
of the built-in Facebook application.
Note that these queue daemons are pretty raw, and need your care. In Note that these queue daemons are pretty raw, and need your care. In
particular, they leak memory, and you may want to restart them on a particular, they leak memory, and you may want to restart them on a
@ -557,6 +531,53 @@ Sample cron job:
# Update Twitter friends subscriptions every half hour # Update Twitter friends subscriptions every half hour
0,30 * * * * /path/to/php /path/to/laconica/scripts/synctwitterfriends.php>&/dev/null 0,30 * * * * /path/to/php /path/to/laconica/scripts/synctwitterfriends.php>&/dev/null
Built-in Facebook Application
-----------------------------
Laconica's Facebook application allows your users to automatically
update their Facebook statuses with their latest notices, invite
their friends to use the app (and thus your site), view their notice
timelines, and post notices -- all from within Facebook. The application
is built into Laconica and runs on your host. For automatic Facebook
status updating to work you will need to enable queuing and run the
facebookqueuehandler.php daemon (see the "Queues and daemons" section
above).
Quick setup instructions*:
Install the Facebook Developer application on Facebook:
http://www.facebook.com/developers/
Use it to create a new application and generate an API key and secret.
Uncomment the Facebook app section of your config.php and copy in the
key and secret, e.g.:
# Config section for the built-in Facebook application
$config['facebook']['apikey'] = 'APIKEY';
$config['facebook']['secret'] = 'SECRET';
In Facebook's application editor, specify the following URLs for your app:
- Callback URL: http://example.net/mublog/facebook/
- Post-Remove URL: http://example.net/mublog/facebook/remove
- Post-Add Redirect URL: http://apps.facebook.com/yourapp/
- Canvas URL: http://apps.facebook.com/yourapp/
(Replace 'example.net' with your host's URL, 'mublog' with the path
to your Laconica installation, and 'yourapp' with the name of the
Facebook application you created.)
Additionally, Choose "Web" for Application type in the Advanced tab.
In the "Canvas setting" section, choose the "FBML" for Render Method,
"Smart Size" for IFrame size, and "Full width (760px)" for Canvas Width.
Everything else can be left with default values.
*For more detailed instructions please see the installation guide on the
Laconica wiki:
http://laconi.ca/trac/wiki/FacebookApplication
Sitemaps Sitemaps
-------- --------
@ -597,7 +618,7 @@ to these resources.
Themes Themes
------ ------
There are two themes shipped with this version of Laconica: "stoica", There are two themes shipped with this version of Laconica: "identica",
which is what the Identi.ca site uses, and "default", which is a good which is what the Identi.ca site uses, and "default", which is a good
basis for other sites. basis for other sites.
@ -676,7 +697,7 @@ Upgrading
If you've been using Laconica 0.6, 0.5 or lower, or if you've been If you've been using Laconica 0.6, 0.5 or lower, or if you've been
tracking the "git" version of the software, you will probably want tracking the "git" version of the software, you will probably want
to upgrade and keep your existing data. There is no automated upgrade to upgrade and keep your existing data. There is no automated upgrade
procedure in Laconica 0.7.1. Try these step-by-step instructions; read procedure in Laconica 0.7.3. Try these step-by-step instructions; read
to the end first before trying them. to the end first before trying them.
0. Download Laconica and set up all the prerequisites as if you were 0. Download Laconica and set up all the prerequisites as if you were
@ -703,16 +724,19 @@ to the end first before trying them.
directory to your new directory. directory to your new directory.
9. Copy htaccess.sample to .htaccess in the new directory. Change the 9. Copy htaccess.sample to .htaccess in the new directory. Change the
RewriteBase to use the correct path. RewriteBase to use the correct path.
10. Rebuild the database. Go to your Laconica directory and run the 10. Rebuild the database. For MySQL, go to your Laconica directory and
rebuilddb.sh script like this: run the rebuilddb.sh script like this:
./scripts/rebuilddb.sh rootuser rootpassword database db/laconica.sql ./scripts/rebuilddb.sh rootuser rootpassword database db/laconica.sql
Here, rootuser and rootpassword are the username and password for a Here, rootuser and rootpassword are the username and password for a
user who can drop and create databases as well as tables; typically user who can drop and create databases as well as tables; typically
that's _not_ the user Laconica runs as. that's _not_ the user Laconica runs as.
11. Use mysql client to log into your database and make sure that the For PostgreSQL databases there is an equivalent, rebuilddb_psql.sh,
notice, user, profile, subscription etc. tables are non-empty. which operates slightly differently. Read the documentation in that
script before running it.
11. Use mysql or psql client to log into your database and make sure that
the notice, user, profile, subscription etc. tables are non-empty.
12. Turn back on the Web server, and check that things still work. 12. Turn back on the Web server, and check that things still work.
13. Turn back on XMPP bots and email maildaemon. Note that the XMPP 13. Turn back on XMPP bots and email maildaemon. Note that the XMPP
bots have changed since version 0.5; see above for details. bots have changed since version 0.5; see above for details.
@ -793,7 +817,7 @@ This section is a catch-all for site-wide variables.
name: the name of your site, like 'YourCompany Microblog'. name: the name of your site, like 'YourCompany Microblog'.
server: the server part of your site's URLs, like 'example.net'. server: the server part of your site's URLs, like 'example.net'.
path: The path part of your site's URLs, like 'mublog' or '/' path: The path part of your site's URLs, like 'mublog' or ''
(installed in root). (installed in root).
fancy: whether or not your site uses fancy URLs (see Fancy URLs fancy: whether or not your site uses fancy URLs (see Fancy URLs
section above). Default is false. section above). Default is false.
@ -837,6 +861,20 @@ notice: A plain string that will appear on every page. A good place
to put introductory information about your service, or info about to put introductory information about your service, or info about
upgrades and outages, or other community info. Any HTML will upgrades and outages, or other community info. Any HTML will
be escaped. be escaped.
dupelimit: Time in which it's not OK for the same person to post the
same notice; default = 60 seconds.
logo: URL of an image file to use as the logo for the site. Overrides
the logo in the theme, if any.
ssl: Whether to use SSL and https:// URLs for some or all pages.
Possible values are 'always' (use it for all pages), 'never'
(don't use it for any pages), or 'sometimes' (use it for
sensitive pages that include passwords like login and registration,
but not for regular pages). Default to 'never'.
sslserver: use an alternate server name for SSL URLs, like
'secure.example.org'. You should be careful to set cookie
parameters correctly so that both the SSL server and the
"normal" server can access the session cookie and
preferably other cookies as well.
db db
-- --
@ -1081,6 +1119,23 @@ banned: an array of usernames and/or profile IDs of 'banned' profiles.
not be accepted at all. (Compare with blacklisted users above, not be accepted at all. (Compare with blacklisted users above,
whose posts just won't show up in the public stream.) whose posts just won't show up in the public stream.)
newuser
-------
Options with new users.
default: nickname of a user account to automatically subscribe new
users to. Typically this would be system account for e.g.
service updates or announcements. Users are able to unsub
if they want. Default is null; no auto subscribe.
welcome: nickname of a user account that sends welcome messages to new
users. Can be the same as 'default' account, although on
busy servers it may be a good idea to keep that one just for
'urgent' messages. Default is null; no message.
If either of these special user accounts are specified, the users should
be created before the configuration is updated.
Troubleshooting Troubleshooting
=============== ===============
@ -1093,7 +1148,7 @@ repository (see below), and you get a compilation error ("unexpected
T_STRING") in the browser, check to see that you don't have any T_STRING") in the browser, check to see that you don't have any
conflicts in your code. conflicts in your code.
If you upgraded to Laconica 0.7.1 without reading the "Notice inboxes" If you upgraded to Laconica 0.7.3 without reading the "Notice inboxes"
section above, and all your users' 'Personal' tabs are empty, read the section above, and all your users' 'Personal' tabs are empty, read the
"Notice inboxes" section above. "Notice inboxes" section above.
@ -1179,6 +1234,11 @@ if anyone's been overlooked in error.
* Ken Sheppardson (Trac server, man-about-town) * Ken Sheppardson (Trac server, man-about-town)
* Tiago 'gouki' Faria (i18n managerx) * Tiago 'gouki' Faria (i18n managerx)
* Sean Murphy * Sean Murphy
* Leslie Michael Orchard
* Eric Helgeson
* Ken Sedgwick
* Brian Hendrickson
* Tobias Diekershoff
Thanks also to the developers of our upstream library code and to the Thanks also to the developers of our upstream library code and to the
thousands of people who have tried out Identi.ca, installed Laconi.ca, thousands of people who have tried out Identi.ca, installed Laconi.ca,

View File

@ -59,7 +59,7 @@ class AccesstokenAction extends Action
try { try {
common_debug('getting request from env variables', __FILE__); common_debug('getting request from env variables', __FILE__);
common_remove_magic_from_request(); common_remove_magic_from_request();
$req = OAuthRequest::from_request(); $req = OAuthRequest::from_request('POST', common_local_url('accesstoken'));
common_debug('getting a server', __FILE__); common_debug('getting a server', __FILE__);
$server = omb_oauth_server(); $server = omb_oauth_server();
common_debug('fetching the access token', __FILE__); common_debug('fetching the access token', __FILE__);

View File

@ -23,31 +23,13 @@ require_once INSTALLDIR.'/lib/personalgroupnav.php';
require_once INSTALLDIR.'/lib/noticelist.php'; require_once INSTALLDIR.'/lib/noticelist.php';
require_once INSTALLDIR.'/lib/feedlist.php'; require_once INSTALLDIR.'/lib/feedlist.php';
class AllAction extends Action class AllAction extends ProfileAction
{ {
var $user = null; function isReadOnly($args)
var $page = null;
function isReadOnly()
{ {
return true; return true;
} }
function prepare($args)
{
parent::prepare($args);
$nickname = common_canonical_nickname($this->arg('nickname'));
$this->user = User::staticGet('nickname', $nickname);
$this->page = $this->trimmed('page');
if (!$this->page) {
$this->page = 1;
}
common_set_returnto($this->selfUrl());
return true;
}
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -77,22 +59,54 @@ class AllAction extends Action
sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)), sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)),
new Feed(Feed::RSS2, new Feed(Feed::RSS2,
common_local_url('api', array('apiaction' => 'statuses', common_local_url('api', array('apiaction' => 'statuses',
'method' => 'friends', 'method' => 'friends_timeline',
'argument' => $this->user->nickname.'.rss')), 'argument' => $this->user->nickname.'.rss')),
sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)), sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)),
new Feed(Feed::ATOM, new Feed(Feed::ATOM,
common_local_url('api', array('apiaction' => 'statuses', common_local_url('api', array('apiaction' => 'statuses',
'method' => 'friends', 'method' => 'friends_timeline',
'argument' => $this->user->nickname.'.atom')), 'argument' => $this->user->nickname.'.atom')),
sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname))); sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname)));
} }
/**
* Output document relationship links
*
* @return void
*/
function showRelationshipLinks()
{
$this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME
$this->page, 'all', array('nickname' => $this->user->nickname));
}
function showLocalNav() function showLocalNav()
{ {
$nav = new PersonalGroupNav($this); $nav = new PersonalGroupNav($this);
$nav->show(); $nav->show();
} }
function showEmptyListMessage()
{
$message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->user->nickname) . ' ';
if (common_logged_in()) {
$current_user = common_current_user();
if ($this->user->id === $current_user->id) {
$message .= _('Try subscribing to more people, [join a group](%%action.groups%%) or post something yourself.');
} else {
$message .= sprintf(_('You can try to [nudge %s](../%s) from his profile or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname);
}
}
else {
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname);
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
}
function showContent() function showContent()
{ {
$notice = $this->user->noticesWithFriends(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); $notice = $this->user->noticesWithFriends(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
@ -101,6 +115,10 @@ class AllAction extends Action
$cnt = $nl->show(); $cnt = $nl->show();
if (0 == $cnt) {
$this->showEmptyListMessage();
}
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'all', array('nickname' => $this->user->nickname)); $this->page, 'all', array('nickname' => $this->user->nickname));
} }

View File

@ -54,6 +54,8 @@ class AllrssAction extends Rss10Action
/** /**
* Initialization. * Initialization.
* *
* @param array $args Web and URL arguments
*
* @return boolean false if user doesn't exist * @return boolean false if user doesn't exist
*/ */
function prepare($args) function prepare($args)
@ -79,8 +81,9 @@ class AllrssAction extends Rss10Action
*/ */
function getNotices($limit=0) function getNotices($limit=0)
{ {
$user = $this->user; $user = $this->user;
$notice = $user->noticesWithFriends(0, $limit); $notice = $user->noticesWithFriends(0, $limit);
$notices = array();
while ($notice->fetch()) { while ($notice->fetch()) {
$notices[] = clone($notice); $notices[] = clone($notice);
@ -104,7 +107,8 @@ class AllrssAction extends Rss10Action
'link' => common_local_url('all', 'link' => common_local_url('all',
array('nickname' => array('nickname' =>
$user->nickname)), $user->nickname)),
'description' => sprintf(_('Feed for friends of %s'), $user->nickname)); 'description' => sprintf(_('Feed for friends of %s'),
$user->nickname));
return $c; return $c;
} }
@ -123,10 +127,5 @@ class AllrssAction extends Rss10Action
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
return $avatar ? $avatar->url : null; return $avatar ? $avatar->url : null;
} }
function isReadOnly()
{
return true;
}
} }

View File

@ -127,18 +127,21 @@ class ApiAction extends Action
'laconica/wadl'); 'laconica/wadl');
static $bareauth = array('statuses/user_timeline', static $bareauth = array('statuses/user_timeline',
'statuses/friends_timeline',
'statuses/friends', 'statuses/friends',
'statuses/replies',
'statuses/mentions',
'statuses/followers', 'statuses/followers',
'favorites/favorites'); 'favorites/favorites');
# If the site is "private", all API methods need authentication
if (common_config('site', 'private')) {
return true;
}
$fullname = "$this->api_action/$this->api_method"; $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 $fullname != 'laconica/config' || false;
}
if (in_array($fullname, $bareauth)) { if (in_array($fullname, $bareauth)) {
# bareauth: only needs auth if without an argument # bareauth: only needs auth if without an argument
if ($this->api_arg) { if ($this->api_arg) {
@ -178,11 +181,11 @@ class ApiAction extends Action
} }
} }
function isReadOnly() function isReadOnly($args)
{ {
# NOTE: before handle(), can't use $this->arg $apiaction = $args['apiaction'];
$apiaction = $_REQUEST['apiaction']; $method = $args['method'];
$method = $_REQUEST['method'];
list($cmdtext, $fmt) = explode('.', $method); list($cmdtext, $fmt) = explode('.', $method);
static $write_methods = array( static $write_methods = array(
@ -205,5 +208,4 @@ class ApiAction extends Action
return false; return false;
} }
} }

209
actions/attachment.php Normal file
View File

@ -0,0 +1,209 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* Show notice attachments
*
* 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 Personal
* @package Laconica
* @author Evan Prodromou <evan@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/personalgroupnav.php';
//require_once INSTALLDIR.'/lib/feedlist.php';
require_once INSTALLDIR.'/lib/attachmentlist.php';
/**
* Show notice attachments
*
* @category Personal
* @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 AttachmentAction extends Action
{
/**
* Attachment object to show
*/
var $attachment = null;
/**
* Load attributes based on database arguments
*
* Loads all the DB stuff
*
* @param array $args $_REQUEST array
*
* @return success flag
*/
function prepare($args)
{
parent::prepare($args);
$id = $this->arg('attachment');
$this->attachment = File::staticGet($id);
if (!$this->attachment) {
$this->clientError(_('No such attachment.'), 404);
return false;
}
return true;
}
/**
* Is this action read-only?
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* Title of the page
*
* @return string title of the page
*/
function title()
{
$a = new Attachment($this->attachment);
return $a->title();
}
/**
* Last-modified date for page
*
* When was the content of this page last modified? Based on notice,
* profile, avatar.
*
* @return int last-modified date as unix timestamp
*/
/*
function lastModified()
{
return max(strtotime($this->notice->created),
strtotime($this->profile->modified),
($this->avatar) ? strtotime($this->avatar->modified) : 0);
}
*/
/**
* An entity tag for this page
*
* Shows the ETag for the page, based on the notice ID and timestamps
* for the notice, profile, and avatar. It's weak, since we change
* the date text "one hour ago", etc.
*
* @return string etag
*/
/*
function etag()
{
$avtime = ($this->avatar) ?
strtotime($this->avatar->modified) : 0;
return 'W/"' . implode(':', array($this->arg('action'),
common_language(),
$this->notice->id,
strtotime($this->notice->created),
strtotime($this->profile->modified),
$avtime)) . '"';
}
*/
/**
* Handle input
*
* Only handles get, so just show the page.
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$this->showPage();
}
/**
* Don't show local navigation
*
* @return void
*/
function showLocalNavBlock()
{
}
/**
* Fill the content area of the page
*
* Shows a single notice list item.
*
* @return void
*/
function showContent()
{
$this->elementStart('ul', array('class' => 'attachments'));
$ali = new Attachment($this->attachment, $this);
$cnt = $ali->show();
$this->elementEnd('ul');
}
/**
* Don't show page notice
*
* @return void
*/
function showPageNoticeBlock()
{
}
/**
* Show aside: this attachments appears in what notices
*
* @return void
*/
function showSections() {
$ns = new AttachmentNoticeSection($this);
$ns->show();
$atcs = new AttachmentTagCloudSection($this);
$atcs->show();
}
}

141
actions/attachment_ajax.php Normal file
View File

@ -0,0 +1,141 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* Show notice attachments
*
* 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 Personal
* @package Laconica
* @author Evan Prodromou <evan@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.'/actions/attachment.php';
/**
* Show notice attachments
*
* @category Personal
* @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 Attachment_ajaxAction extends AttachmentAction
{
/**
* Load attributes based on database arguments
*
* Loads all the DB stuff
*
* @param array $args $_REQUEST array
*
* @return success flag
*/
function prepare($args)
{
parent::prepare($args);
if (!$this->attachment) {
$this->clientError(_('No such attachment.'), 404);
return false;
}
return true;
}
/**
* Show page, a template method.
*
* @return nothing
*/
function showPage()
{
if (Event::handle('StartShowBody', array($this))) {
$this->showCore();
Event::handle('EndShowBody', array($this));
}
}
/**
* Show core.
*
* Shows local navigation, content block and aside.
*
* @return nothing
*/
function showCore()
{
$this->elementStart('div', array('id' => 'core'));
if (Event::handle('StartShowContentBlock', array($this))) {
$this->showContentBlock();
Event::handle('EndShowContentBlock', array($this));
}
$this->elementEnd('div');
}
/**
* Last-modified date for page
*
* When was the content of this page last modified? Based on notice,
* profile, avatar.
*
* @return int last-modified date as unix timestamp
*/
/*
function lastModified()
{
return max(strtotime($this->notice->created),
strtotime($this->profile->modified),
($this->avatar) ? strtotime($this->avatar->modified) : 0);
}
*/
/**
* An entity tag for this page
*
* Shows the ETag for the page, based on the notice ID and timestamps
* for the notice, profile, and avatar. It's weak, since we change
* the date text "one hour ago", etc.
*
* @return string etag
*/
/*
function etag()
{
$avtime = ($this->avatar) ?
strtotime($this->avatar->modified) : 0;
return 'W/"' . implode(':', array($this->arg('action'),
common_language(),
$this->notice->id,
strtotime($this->notice->created),
strtotime($this->profile->modified),
$avtime)) . '"';
}
*/
}

292
actions/attachments.php Normal file
View File

@ -0,0 +1,292 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* Show notice attachments
*
* 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 Personal
* @package Laconica
* @author Evan Prodromou <evan@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/personalgroupnav.php';
//require_once INSTALLDIR.'/lib/feedlist.php';
require_once INSTALLDIR.'/lib/attachmentlist.php';
/**
* Show notice attachments
*
* @category Personal
* @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 AttachmentsAction extends Action
{
/**
* Notice object to show
*/
var $notice = null;
/**
* Profile of the notice object
*/
var $profile = null;
/**
* Avatar of the profile of the notice object
*/
var $avatar = null;
/**
* Is this action read-only?
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* Last-modified date for page
*
* When was the content of this page last modified? Based on notice,
* profile, avatar.
*
* @return int last-modified date as unix timestamp
*/
function lastModified()
{
return max(strtotime($this->notice->created),
strtotime($this->profile->modified),
($this->avatar) ? strtotime($this->avatar->modified) : 0);
}
/**
* An entity tag for this page
*
* Shows the ETag for the page, based on the notice ID and timestamps
* for the notice, profile, and avatar. It's weak, since we change
* the date text "one hour ago", etc.
*
* @return string etag
*/
function etag()
{
$avtime = ($this->avatar) ?
strtotime($this->avatar->modified) : 0;
return 'W/"' . implode(':', array($this->arg('action'),
common_language(),
$this->notice->id,
strtotime($this->notice->created),
strtotime($this->profile->modified),
$avtime)) . '"';
}
/**
* Title of the page
*
* @return string title of the page
*/
function title()
{
return sprintf(_('%1$s\'s status on %2$s'),
$this->profile->nickname,
common_exact_date($this->notice->created));
}
/**
* Load attributes based on database arguments
*
* Loads all the DB stuff
*
* @param array $args $_REQUEST array
*
* @return success flag
*/
function prepare($args)
{
parent::prepare($args);
$id = $this->arg('notice');
$this->notice = Notice::staticGet($id);
if (!$this->notice) {
$this->clientError(_('No such notice.'), 404);
return false;
}
/*
// STOP if there are no attachments
// maybe even redirect if there's a single one
// RYM FIXME TODO
$this->clientError(_('No such attachment.'), 404);
return false;
*/
$this->profile = $this->notice->getProfile();
if (!$this->profile) {
$this->serverError(_('Notice has no profile'), 500);
return false;
}
$this->avatar = $this->profile->getAvatar(AVATAR_PROFILE_SIZE);
return true;
}
/**
* Handle input
*
* Only handles get, so just show the page.
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
function handle($args)
{
parent::handle($args);
if ($this->notice->is_local == 0) {
if (!empty($this->notice->url)) {
common_redirect($this->notice->url, 301);
} else if (!empty($this->notice->uri) && preg_match('/^https?:/', $this->notice->uri)) {
common_redirect($this->notice->uri, 301);
}
} else {
$f2p = new File_to_post;
$f2p->post_id = $this->notice->id;
$file = new File;
$file->joinAdd($f2p);
$file->selectAdd();
$file->selectAdd('file.id as id');
$count = $file->find(true);
if (!$count) return;
if (1 === $count) {
common_redirect(common_local_url('attachment', array('attachment' => $file->id)), 301);
} else {
$this->showPage();
}
}
}
/**
* Don't show local navigation
*
* @return void
*/
function showLocalNavBlock()
{
}
/**
* Fill the content area of the page
*
* Shows a single notice list item.
*
* @return void
*/
function showContent()
{
$al = new AttachmentList($this->notice, $this);
$cnt = $al->show();
}
/**
* Don't show page notice
*
* @return void
*/
function showPageNoticeBlock()
{
}
/**
* Don't show aside
*
* @return void
*/
function showAside() {
}
/**
* Extra <head> content
*
* We show the microid(s) for the author, if any.
*
* @return void
*/
function extraHead()
{
$user = User::staticGet($this->profile->id);
if (!$user) {
return;
}
if ($user->emailmicroid && $user->email && $this->notice->uri) {
$id = new Microid('mailto:'. $user->email,
$this->notice->uri);
$this->element('meta', array('name' => 'microid',
'content' => $id->toString()));
}
if ($user->jabbermicroid && $user->jabber && $this->notice->uri) {
$id = new Microid('xmpp:', $user->jabber,
$this->notice->uri);
$this->element('meta', array('name' => 'microid',
'content' => $id->toString()));
}
}
}

View File

@ -0,0 +1,115 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* Show notice attachments
*
* 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 Personal
* @package Laconica
* @author Evan Prodromou <evan@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/personalgroupnav.php';
//require_once INSTALLDIR.'/lib/feedlist.php';
require_once INSTALLDIR.'/actions/attachments.php';
/**
* Show notice attachments
*
* @category Personal
* @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 Attachments_ajaxAction extends AttachmentsAction
{
function showContent()
{
}
/**
* Fill the content area of the page
*
* Shows a single notice list item.
*
* @return void
*/
function showContentBlock()
{
$al = new AttachmentList($this->notice, $this);
$cnt = $al->show();
}
/**
* Extra <head> content
*
* We show the microid(s) for the author, if any.
*
* @return void
*/
function extraHead()
{
}
/**
* Show page, a template method.
*
* @return nothing
*/
function showPage()
{
if (Event::handle('StartShowBody', array($this))) {
$this->showCore();
Event::handle('EndShowBody', array($this));
}
}
/**
* Show core.
*
* Shows local navigation, content block and aside.
*
* @return nothing
*/
function showCore()
{
$this->elementStart('div', array('id' => 'core'));
if (Event::handle('StartShowContentBlock', array($this))) {
$this->showContentBlock();
Event::handle('EndShowContentBlock', array($this));
}
$this->elementEnd('div');
}
}

View File

@ -98,7 +98,7 @@ class AvatarbynicknameAction extends Action
common_redirect($url, 302); common_redirect($url, 302);
} }
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -324,13 +324,14 @@ class AvatarsettingsAction extends AccountSettingsAction
return; return;
} }
// If image is not being cropped assume pos & dimentions of original $file_d = ($filedata['width'] > $filedata['height'])
? $filedata['height'] : $filedata['width'];
$dest_x = $this->arg('avatar_crop_x') ? $this->arg('avatar_crop_x'):0; $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_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_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'):$filedata['height']; $dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$file_d;
$size = min($dest_w, $dest_h); $size = min($dest_w, $dest_h, MAX_ORIGINAL);
$size = ($size > MAX_ORIGINAL) ? MAX_ORIGINAL:$size;
$user = common_current_user(); $user = common_current_user();
$profile = $user->getProfile(); $profile = $user->getProfile();
@ -343,6 +344,7 @@ class AvatarsettingsAction extends AccountSettingsAction
unset($_SESSION['FILEDATA']); unset($_SESSION['FILEDATA']);
$this->mode = 'upload'; $this->mode = 'upload';
$this->showForm(_('Avatar updated.'), true); $this->showForm(_('Avatar updated.'), true);
common_broadcast_profile($profile);
} else { } else {
$this->showForm(_('Failed updating avatar.')); $this->showForm(_('Failed updating avatar.'));
} }

View File

@ -93,7 +93,8 @@ class BlockAction extends Action
if ($this->arg('no')) { if ($this->arg('no')) {
$cur = common_current_user(); $cur = common_current_user();
$other = Profile::staticGet('id', $this->arg('blockto')); $other = Profile::staticGet('id', $this->arg('blockto'));
common_redirect(common_local_url('showstream', array('nickname' => $other->nickname))); common_redirect(common_local_url('showstream', array('nickname' => $other->nickname)),
303);
} elseif ($this->arg('yes')) { } elseif ($this->arg('yes')) {
$this->blockProfile(); $this->blockProfile();
} elseif ($this->arg('blockto')) { } elseif ($this->arg('blockto')) {
@ -102,7 +103,6 @@ class BlockAction extends Action
} }
} }
function showContent() { function showContent() {
$this->areYouSureForm(); $this->areYouSureForm();
} }
@ -178,10 +178,11 @@ class BlockAction extends Action
} }
if ($action) { if ($action) {
common_redirect(common_local_url($action, $args)); common_redirect(common_local_url($action, $args), 303);
} else { } else {
common_redirect(common_local_url('subscriptions', common_redirect(common_local_url('subscriptions',
array('nickname' => $cur->nickname))); array('nickname' => $cur->nickname)),
303);
} }
} }
} }

107
actions/conversation.php Normal file
View File

@ -0,0 +1,107 @@
<?php
/**
* Display a conversation in the browser
*
* PHP version 5
*
* @category Action
* @package Laconica
* @author Evan Prodromou <evan@controlyourself.ca>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://laconi.ca/
*
* Laconica - a distributed open-source microblogging tool
* Copyright (C) 2008, Controlez-Vous, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('LACONICA')) {
exit(1);
}
require_once(INSTALLDIR.'/lib/noticelist.php');
/**
* Conversation tree in the browser
*
* @category Action
* @package Laconica
* @author Evan Prodromou <evan@controlyourself.ca>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://laconi.ca/
*/
class ConversationAction extends Action
{
var $id = null;
var $page = null;
/**
* Initialization.
*
* @param array $args Web and URL arguments
*
* @return boolean false if id not passed in
*/
function prepare($args)
{
parent::prepare($args);
$this->id = $this->trimmed('id');
if (empty($this->id)) {
return false;
}
$this->page = $this->trimmed('page');
if (empty($this->page)) {
$this->page = 1;
}
return true;
}
function handle($args)
{
parent::handle($args);
$this->showPage();
}
function title()
{
return _("Conversation");
}
function showContent()
{
// FIXME this needs to be a tree, not a list
$qry = 'SELECT * FROM notice WHERE conversation = %s ';
$offset = ($this->page-1)*NOTICES_PER_PAGE;
$limit = NOTICES_PER_PAGE + 1;
$txt = sprintf($qry, $this->id);
$notices = Notice::getStream($txt,
'notice:conversation:'.$this->id,
$offset, $limit);
$nl = new NoticeList($notices, $this);
$cnt = $nl->show();
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'conversation', array('id' => $this->id));
}
}

View File

@ -141,6 +141,6 @@ class DeletenoticeAction extends DeleteAction
$url = common_local_url('public'); $url = common_local_url('public');
} }
common_redirect($url); common_redirect($url, 303);
} }
} }

262
actions/designsettings.php Normal file
View File

@ -0,0 +1,262 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* Change user password
*
* 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 Settings
* @package Laconica
* @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/accountsettingsaction.php';
class DesignsettingsAction extends AccountSettingsAction
{
/**
* Title of the page
*
* @return string Title of the page
*/
function title()
{
return _('Profile design');
}
/**
* Instructions for use
*
* @return instructions for use
*/
function getInstructions()
{
return _('Customize the way your profile looks with a background image and a colour palette of your choice.');
}
/**
* Content area of the page
*
* Shows a form for changing the password
*
* @return void
*/
function showContent()
{
$user = common_current_user();
$this->elementStart('form', array('method' => 'POST',
'id' => 'form_settings_design',
'class' => 'form_settings',
'action' =>
common_local_url('designsettings')));
$this->elementStart('fieldset');
$this->hidden('token', common_session_token());
$this->elementStart('fieldset', array('id' => 'settings_design_background-image'));
$this->element('legend', null, _('Change background image'));
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
$this->element('label', array('for' => 'design_ background-image_file'),
_('Upload file'));
$this->element('input', array('name' => 'design_background-image_file',
'type' => 'file',
'id' => 'design_background-image_file'));
$this->element('p', 'form_guide', _('You can upload your personal background image. The maximum file size is 2Mb.'));
$this->element('input', array('name' => 'MAX_FILE_SIZE',
'type' => 'hidden',
'id' => 'MAX_FILE_SIZE',
'value' => ImageFile::maxFileSizeInt()));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->elementEnd('fieldset');
$this->elementStart('fieldset', array('id' => 'settings_design_color'));
$this->element('legend', null, _('Change colours'));
$this->elementStart('ul', 'form_data');
//This is a JSON object in the DB field. Here for testing. Remove later.
$userSwatch = '{"body":{"background-color":"#F0F2F5"},
"#content":{"background-color":"#FFFFFF"},
"#aside_primary":{"background-color":"#CEE1E9"},
"html body":{"color":"#000000"},
"a":{"color":"#002E6E"}}';
//Default theme swatch -- Where should this be stored?
$defaultSwatch = array('body' => array('background-color' => '#F0F2F5'),
'#content' => array('background-color' => '#FFFFFF'),
'#aside_primary' => array('background-color' => '#CEE1E9'),
'html body' => array('color' => '#000000'),
'a' => array('color' => '#002E6E'));
$userSwatch = ($userSwatch) ? json_decode($userSwatch, true) : $defaultSwatch;
$s = 0;
$labelSwatch = array('Background',
'Content',
'Sidebar',
'Text',
'Links');
foreach($userSwatch as $propertyvalue => $value) {
$foo = array_values($value);
$this->elementStart('li');
$this->element('label', array('for' => 'swatch-'.$s), _($labelSwatch[$s]));
$this->element('input', array('name' => 'swatch-'.$s, //prefer swatch[$s] ?
'type' => 'text',
'id' => 'swatch-'.$s,
'class' => 'swatch',
'maxlength' => '7',
'size' => '7',
'value' => $foo[0]));
$this->elementEnd('li');
$s++;
}
$this->elementEnd('ul');
$this->elementEnd('fieldset');
$this->submit('save', _('Save'));
$this->element('input', array('type' => 'reset',
'value' => 'Reset',
'class' => 'form_action-secondary'));
/*TODO: Check submitted form values:
json_encode(form values)
if submitted Swatch == DefaultSwatch, don't store in DB.
else store in BD
*/
$this->elementEnd('fieldset');
$this->elementEnd('form');
}
/**
* Handle a post
*
* Validate input and save changes. Reload the form with a success
* or error message.
*
* @return void
*/
function handlePost()
{
/*
// 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;
}
$user = common_current_user();
assert(!is_null($user)); // should already be checked
// FIXME: scrub input
$newpassword = $this->arg('newpassword');
$confirm = $this->arg('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;
}
if ($user->password) {
$oldpassword = $this->arg('oldpassword');
if (!common_check_user($user->nickname, $oldpassword)) {
$this->showForm(_('Incorrect old password'));
return;
}
}
$original = clone($user);
$user->password = common_munge_password($newpassword, $user->id);
$val = $user->validate();
if ($val !== true) {
$this->showForm(_('Error saving user; invalid.'));
return;
}
if (!$user->update($original)) {
$this->serverError(_('Can\'t save new password.'));
return;
}
$this->showForm(_('Password saved.'), true);
*/
}
/**
* Add the Farbtastic stylesheet
*
* @return void
*/
function showStylesheets()
{
parent::showStylesheets();
$farbtasticStyle =
common_path('theme/base/css/farbtastic.css?version='.LACONICA_VERSION);
$this->element('link', array('rel' => 'stylesheet',
'type' => 'text/css',
'href' => $farbtasticStyle,
'media' => 'screen, projection, tv'));
}
/**
* Add the Farbtastic scripts
*
* @return void
*/
function showScripts()
{
parent::showScripts();
$farbtasticPack = common_path('js/farbtastic/farbtastic.js');
$farbtasticGo = common_path('js/farbtastic/farbtastic.go.js');
$this->element('script', array('type' => 'text/javascript',
'src' => $farbtasticPack));
$this->element('script', array('type' => 'text/javascript',
'src' => $farbtasticGo));
}
}

View File

@ -100,7 +100,8 @@ class DisfavorAction extends Action
$this->elementEnd('html'); $this->elementEnd('html');
} else { } else {
common_redirect(common_local_url('showfavorites', common_redirect(common_local_url('showfavorites',
array('nickname' => $user->nickname))); array('nickname' => $user->nickname)),
303);
} }
} }
} }

View File

@ -108,7 +108,7 @@ class DocAction extends Action
return ucfirst($this->title); return ucfirst($this->title);
} }
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -166,7 +166,6 @@ class EditgroupAction extends Action
return; return;
} }
$nickname = common_canonical_nickname($this->trimmed('nickname')); $nickname = common_canonical_nickname($this->trimmed('nickname'));
$fullname = $this->trimmed('fullname'); $fullname = $this->trimmed('fullname');
$homepage = $this->trimmed('homepage'); $homepage = $this->trimmed('homepage');
@ -221,7 +220,7 @@ class EditgroupAction extends Action
if ($this->group->nickname != $orig->nickname) { if ($this->group->nickname != $orig->nickname) {
common_redirect(common_local_url('editgroup', common_redirect(common_local_url('editgroup',
array('nickname' => $nickname)), array('nickname' => $nickname)),
307); 303);
} else { } else {
$this->showForm(_('Options saved.')); $this->showForm(_('Options saved.'));
} }

View File

@ -100,7 +100,8 @@ class FavorAction extends Action
$this->elementEnd('html'); $this->elementEnd('html');
} else { } else {
common_redirect(common_local_url('showfavorites', common_redirect(common_local_url('showfavorites',
array('nickname' => $user->nickname))); array('nickname' => $user->nickname)),
303);
} }
} }

View File

@ -85,7 +85,7 @@ class FavoritedAction extends Action
* @return boolean true * @return boolean true
*/ */
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
@ -145,6 +145,22 @@ class FavoritedAction extends Action
$this->elementEnd('div'); $this->elementEnd('div');
} }
function showEmptyList()
{
$message = _('Favorite notices appear on this page but no one has favorited one yet.') . ' ';
if (common_logged_in()) {
$message .= _('Be the first to add a notice to your favorites by clicking the fave button next to any notice you like.');
}
else {
$message .= _('Why not [register an account](%%action.register%%) and be the first to add a notice to your favorites!');
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
}
/** /**
* Local navigation * Local navigation
* *
@ -169,10 +185,16 @@ class FavoritedAction extends Action
function showContent() 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.*, '. $qry = 'SELECT notice.*, '.
'sum(exp(-(now() - fave.modified) / %s)) as weight ' . $weightexpr . ' as weight ' .
'FROM notice JOIN fave ON notice.id = fave.notice_id ' . 'FROM notice JOIN fave ON notice.id = fave.notice_id ' .
'GROUP BY fave.notice_id ' . 'GROUP BY id,profile_id,uri,content,rendered,url,created,notice.modified,reply_to,is_local,source ' .
'ORDER BY weight DESC'; 'ORDER BY weight DESC';
$offset = ($this->page - 1) * NOTICES_PER_PAGE; $offset = ($this->page - 1) * NOTICES_PER_PAGE;
@ -192,7 +214,22 @@ class FavoritedAction extends Action
$cnt = $nl->show(); $cnt = $nl->show();
if ($cnt == 0) {
$this->showEmptyList();
}
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'favorited'); $this->page, 'favorited');
} }
/**
* Output document relationship links
*
* @return void
*/
function showRelationshipLinks()
{
$this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME
$this->page, 'favorited');
}
} }

View File

@ -107,7 +107,7 @@ class FavoritesrssAction extends Rss10Action
$c = array('url' => common_local_url('favoritesrss', $c = array('url' => common_local_url('favoritesrss',
array('nickname' => array('nickname' =>
$user->nickname)), $user->nickname)),
'title' => sprintf(_("%s favorite notices"), $user->nickname), 'title' => sprintf(_("%s's favorite notices"), $user->nickname),
'link' => common_local_url('showfavorites', 'link' => common_local_url('showfavorites',
array('nickname' => array('nickname' =>
$user->nickname)), $user->nickname)),

View File

@ -50,7 +50,7 @@ class FeaturedAction extends Action
{ {
var $page = null; var $page = null;
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
@ -107,6 +107,7 @@ class FeaturedAction extends Action
$featured_nicks = common_config('nickname', 'featured'); $featured_nicks = common_config('nickname', 'featured');
if (count($featured_nicks) > 0) { if (count($featured_nicks) > 0) {
$quoted = array(); $quoted = array();
@ -118,7 +119,7 @@ class FeaturedAction extends Action
$user = new User; $user = new User;
$user->whereAdd(sprintf('nickname IN (%s)', implode(',', $quoted))); $user->whereAdd(sprintf('nickname IN (%s)', implode(',', $quoted)));
$user->limit(($this->page - 1) * PROFILES_PER_PAGE, PROFILES_PER_PAGE + 1); $user->limit(($this->page - 1) * PROFILES_PER_PAGE, PROFILES_PER_PAGE + 1);
$user->orderBy('user.nickname ASC'); $user->orderBy(common_database_tablename('user') .'.nickname ASC');
$user->find(); $user->find();

View File

@ -139,7 +139,7 @@ class FinishaddopenidAction extends Action
oid_set_last($display); oid_set_last($display);
common_redirect(common_local_url('openidsettings')); common_redirect(common_local_url('openidsettings'), 303);
} }
} }

View File

@ -62,9 +62,8 @@ class FinishopenidloginAction extends Action
if ($this->error) { if ($this->error) {
$this->element('div', array('class' => 'error'), $this->error); $this->element('div', array('class' => 'error'), $this->error);
} else { } else {
global $config;
$this->element('div', 'instructions', $this->element('div', 'instructions',
sprintf(_('This is the first time you\'ve logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), $config['site']['name'])); sprintf(_('This is the first time you\'ve logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name')));
} }
} }
@ -83,7 +82,7 @@ class FinishopenidloginAction extends Action
function showContent() function showContent()
{ {
if ($this->message_text) { if (!empty($this->message_text)) {
$this->element('p', null, $this->message); $this->element('p', null, $this->message);
return; return;
} }
@ -192,11 +191,28 @@ class FinishopenidloginAction extends Action
{ {
# FIXME: save invite code before redirect, and check here # FIXME: save invite code before redirect, and check here
if (common_config('site', 'closed') || common_config('site', 'inviteonly')) { if (common_config('site', 'closed')) {
$this->clientError(_('Registration not allowed.')); $this->clientError(_('Registration not allowed.'));
return; return;
} }
$invite = null;
if (common_config('site', 'inviteonly')) {
$code = $_SESSION['invitecode'];
if (empty($code)) {
$this->clientError(_('Registration not allowed.'));
return;
}
$invite = Invitation::staticGet($code);
if (empty($invite)) {
$this->clientError(_('Not a valid invitation code.'));
return;
}
}
$nickname = $this->trimmed('newname'); $nickname = $this->trimmed('newname');
if (!Validate::string($nickname, array('min_length' => 1, if (!Validate::string($nickname, array('min_length' => 1,
@ -232,7 +248,8 @@ class FinishopenidloginAction extends Action
return; return;
} }
if ($sreg['country']) { $location = '';
if (!empty($sreg['country'])) {
if ($sreg['postcode']) { if ($sreg['postcode']) {
# XXX: use postcode to get city and region # XXX: use postcode to get city and region
# XXX: also, store postcode somewhere -- it's valuable! # XXX: also, store postcode somewhere -- it's valuable!
@ -242,21 +259,31 @@ class FinishopenidloginAction extends Action
} }
} }
if ($sreg['fullname'] && mb_strlen($sreg['fullname']) <= 255) { if (!empty($sreg['fullname']) && mb_strlen($sreg['fullname']) <= 255) {
$fullname = $sreg['fullname']; $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']; $email = $sreg['email'];
} else {
$email = '';
} }
# XXX: add language # XXX: add language
# XXX: add timezone # XXX: add timezone
$user = User::register(array('nickname' => $nickname, $args = array('nickname' => $nickname,
'email' => $email, 'email' => $email,
'fullname' => $fullname, 'fullname' => $fullname,
'location' => $location)); 'location' => $location);
if (!empty($invite)) {
$args['code'] = $invite->code;
}
$user = User::register($args);
$result = oid_link_user($user->id, $canonical, $display); $result = oid_link_user($user->id, $canonical, $display);
@ -267,7 +294,8 @@ class FinishopenidloginAction extends Action
common_rememberme($user); common_rememberme($user);
} }
unset($_SESSION['openid_rememberme']); unset($_SESSION['openid_rememberme']);
common_redirect(common_local_url('showstream', array('nickname' => $user->nickname))); common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)),
303);
} }
function connectUser() function connectUser()
@ -320,7 +348,7 @@ class FinishopenidloginAction extends Action
array('nickname' => array('nickname' =>
$nickname)); $nickname));
} }
common_redirect($url); common_redirect($url, 303);
} }
function bestNewNickname($display, $sreg) function bestNewNickname($display, $sreg)
@ -328,7 +356,7 @@ class FinishopenidloginAction extends Action
# Try the passed-in nickname # Try the passed-in nickname
if ($sreg['nickname']) { if (!empty($sreg['nickname'])) {
$nickname = $this->nicknamize($sreg['nickname']); $nickname = $this->nicknamize($sreg['nickname']);
if ($this->isNewNickname($nickname)) { if ($this->isNewNickname($nickname)) {
return $nickname; return $nickname;
@ -337,7 +365,7 @@ class FinishopenidloginAction extends Action
# Try the full name # Try the full name
if ($sreg['fullname']) { if (!empty($sreg['fullname'])) {
$fullname = $this->nicknamize($sreg['fullname']); $fullname = $this->nicknamize($sreg['fullname']);
if ($this->isNewNickname($fullname)) { if ($this->isNewNickname($fullname)) {
return $fullname; return $fullname;

View File

@ -44,7 +44,7 @@ class FinishremotesubscribeAction extends Action
common_debug('stored request: '.print_r($omb,true), __FILE__); common_debug('stored request: '.print_r($omb,true), __FILE__);
common_remove_magic_from_request(); common_remove_magic_from_request();
$req = OAuthRequest::from_request(); $req = OAuthRequest::from_request('POST', common_local_url('finishuserauthorization'));
$token = $req->get_parameter('oauth_token'); $token = $req->get_parameter('oauth_token');
@ -136,16 +136,16 @@ class FinishremotesubscribeAction extends Action
$profile->nickname = $nickname; $profile->nickname = $nickname;
$profile->profileurl = $profile_url; $profile->profileurl = $profile_url;
if ($fullname) { if (!is_null($fullname)) {
$profile->fullname = $fullname; $profile->fullname = $fullname;
} }
if ($homepage) { if (!is_null($homepage)) {
$profile->homepage = $homepage; $profile->homepage = $homepage;
} }
if ($bio) { if (!is_null($bio)) {
$profile->bio = $bio; $profile->bio = $bio;
} }
if ($location) { if (!is_null($location)) {
$profile->location = $location; $profile->location = $location;
} }
@ -230,7 +230,8 @@ class FinishremotesubscribeAction extends Action
# show up close to the top of the page # show up close to the top of the page
common_redirect(common_local_url('subscribers', array('nickname' => common_redirect(common_local_url('subscribers', array('nickname' =>
$user->nickname))); $user->nickname)),
303);
} }
function add_avatar($profile, $url) function add_avatar($profile, $url)
@ -283,7 +284,7 @@ class FinishremotesubscribeAction extends Action
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
$result = $fetcher->post($req->get_normalized_http_url(), $result = $fetcher->post($req->get_normalized_http_url(),
$req->to_postdata(), $req->to_postdata(),
array('User-Agent' => 'Laconica/' . LACONICA_VERSION)); array('User-Agent: Laconica/' . LACONICA_VERSION));
common_debug('got result: "'.print_r($result,true).'"', __FILE__); common_debug('got result: "'.print_r($result,true).'"', __FILE__);

View File

@ -25,7 +25,7 @@ define('BOTH', 0);
class FoafAction extends Action class FoafAction extends Action
{ {
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
@ -33,7 +33,24 @@ class FoafAction extends Action
function prepare($args) function prepare($args)
{ {
parent::prepare($args); parent::prepare($args);
$this->nickname = $this->trimmed('nickname');
$nickname_arg = $this->arg('nickname');
if (empty($nickname_arg)) {
$this->clientError(_('No such user.'), 404);
return false;
}
$this->nickname = common_canonical_nickname($nickname_arg);
// Permanent redirect on non-canonical nickname
if ($nickname_arg != $this->nickname) {
common_redirect(common_local_url('foaf',
array('nickname' => $this->nickname)),
301);
return false;
}
$this->user = User::staticGet('nickname', $this->nickname); $this->user = User::staticGet('nickname', $this->nickname);
@ -122,20 +139,30 @@ class FoafAction extends Action
if ($sub->find()) { if ($sub->find()) {
while ($sub->fetch()) { while ($sub->fetch()) {
if ($sub->token) { if (!empty($sub->token)) {
$other = Remote_profile::staticGet('id', $sub->subscribed); $other = Remote_profile::staticGet('id', $sub->subscribed);
} else { } else {
$other = User::staticGet('id', $sub->subscribed); $other = User::staticGet('id', $sub->subscribed);
} }
if (!$other) { if (empty($other)) {
common_debug('Got a bad subscription: '.print_r($sub,true)); common_debug('Got a bad subscription: '.print_r($sub,true));
continue; continue;
} }
$this->element('knows', array('rdf:resource' => $other->uri)); $this->element('knows', array('rdf:resource' => $other->uri));
$person[$other->uri] = array(LISTENEE, $other); $person[$other->uri] = array(LISTENEE,
$other->id,
$other->nickname,
(empty($sub->token)) ? 'User' : 'Remote_profile');
$other->free();
$other = null;
unset($other);
} }
} }
$sub->free();
$sub = null;
unset($sub);
// Get people who subscribe to user // Get people who subscribe to user
$sub = new Subscription(); $sub = new Subscription();
@ -156,25 +183,36 @@ class FoafAction extends Action
if (array_key_exists($other->uri, $person)) { if (array_key_exists($other->uri, $person)) {
$person[$other->uri][0] = BOTH; $person[$other->uri][0] = BOTH;
} else { } else {
$person[$other->uri] = array(LISTENER, $other); $person[$other->uri] = array(LISTENER,
$other->id,
$other->nickname,
(empty($sub->token)) ? 'User' : 'Remote_profile');
} }
$other->free();
$other = null;
unset($other);
} }
} }
$sub->free();
$sub = null;
unset($sub);
$this->elementEnd('Person'); $this->elementEnd('Person');
foreach ($person as $uri => $p) { foreach ($person as $uri => $p) {
$foaf_url = null; $foaf_url = null;
if ($p[1] instanceof User) { list($type, $id, $nickname, $cls) = $p;
$foaf_url = common_local_url('foaf', array('nickname' => $p[1]->nickname)); if ($cls == 'User') {
$foaf_url = common_local_url('foaf', array('nickname' => $nickname));
} }
$this->profile = Profile::staticGet($p[1]->id); $profile = Profile::staticGet($id);
$this->elementStart('Person', array('rdf:about' => $uri)); $this->elementStart('Person', array('rdf:about' => $uri));
if ($p[0] == LISTENER || $p[0] == BOTH) { if ($type == LISTENER || $type == BOTH) {
$this->element('knows', array('rdf:resource' => $this->user->uri)); $this->element('knows', array('rdf:resource' => $this->user->uri));
} }
$this->showMicrobloggingAccount($this->profile, ($p[1] instanceof User) ? $this->showMicrobloggingAccount($profile, ($cls == 'User') ?
common_root_url() : null); common_root_url() : null);
if ($foaf_url) { if ($foaf_url) {
$this->element('rdfs:seeAlso', array('rdf:resource' => $foaf_url)); $this->element('rdfs:seeAlso', array('rdf:resource' => $foaf_url));
} }
@ -182,6 +220,9 @@ class FoafAction extends Action
if ($foaf_url) { if ($foaf_url) {
$this->showPpd($foaf_url, $uri); $this->showPpd($foaf_url, $uri);
} }
$profile->free();
$profile = null;
unset($profile);
} }
$this->elementEnd('rdf:RDF'); $this->elementEnd('rdf:RDF');

View File

@ -59,7 +59,7 @@ class GroupbyidAction extends Action
* @return boolean true * @return boolean true
*/ */
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -83,7 +83,7 @@ class GrouplogoAction extends Action
if ($nickname_arg != $nickname) { if ($nickname_arg != $nickname) {
$args = array('nickname' => $nickname); $args = array('nickname' => $nickname);
common_redirect(common_local_url('editgroup', $args), 301); common_redirect(common_local_url('grouplogo', $args), 301);
return false; return false;
} }

View File

@ -48,7 +48,7 @@ class GroupmembersAction extends Action
{ {
var $page = null; var $page = null;
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
@ -137,4 +137,15 @@ class GroupmembersAction extends Action
$this->page, 'groupmembers', $this->page, 'groupmembers',
array('nickname' => $this->group->nickname)); array('nickname' => $this->group->nickname));
} }
/**
* Output document relationship links
*
* @return void
*/
function showRelationshipLinks()
{
$this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME
$this->page, 'groupmembers', array('nickname' => $this->group->nickname));
}
} }

View File

@ -34,7 +34,7 @@ if (!defined('LACONICA')) {
require_once INSTALLDIR.'/lib/rssaction.php'; require_once INSTALLDIR.'/lib/rssaction.php';
define('MEMBERS_PER_SECTION', 81); define('MEMBERS_PER_SECTION', 27);
/** /**
* Group RSS feed * Group RSS feed
@ -57,7 +57,7 @@ class groupRssAction extends Rss10Action
* @return boolean true * @return boolean true
*/ */
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
@ -141,13 +141,4 @@ class groupRssAction extends Rss10Action
{ {
return $this->group->homepage_logo; return $this->group->homepage_logo;
} }
# override parent to add X-SUP-ID URL
function initRss($limit=0)
{
$url = common_local_url('sup', null, $this->group->id);
header('X-SUP-ID: '.$url);
parent::initRss($limit);
}
} }

View File

@ -51,7 +51,7 @@ class GroupsAction extends Action
var $page = null; var $page = null;
var $profile = null; var $profile = null;
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
@ -129,4 +129,15 @@ class GroupsAction extends Action
$gbm = new GroupsByMembersSection($this); $gbm = new GroupsByMembersSection($this);
$gbm->show(); $gbm->show();
} }
/**
* Output document relationship links
*
* @return void
*/
function showRelationshipLinks()
{
$this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME
$this->page, 'groups', array('nickname' => $this->group->nickname));
}
} }

View File

@ -1,9 +1,4 @@
<?php <?php
// define('GROUPS_PER_PAGE', 20);
/** /**
* Group search action class. * Group search action class.
* *
@ -77,12 +72,23 @@ class GroupsearchAction extends SearchAction
$terms = preg_split('/[\s,]+/', $q); $terms = preg_split('/[\s,]+/', $q);
$results = new GroupSearchResults($user_group, $terms, $this); $results = new GroupSearchResults($user_group, $terms, $this);
$results->show(); $results->show();
} else { $user_group->free();
$this->element('p', 'error', _('No results')); $this->pagination($page > 1, $cnt > GROUPS_PER_PAGE,
}
$user_group->free();
$this->pagination($page > 1, $cnt > GROUPS_PER_PAGE,
$page, 'groupsearch', array('q' => $q)); $page, 'groupsearch', array('q' => $q));
} else {
$this->element('p', 'error', _('No results.'));
$this->searchSuggestions($q);
if (common_logged_in()) {
$message = _('If you can\'t find the group you\'re looking for, you can [create it](%%action.newgroup%%) yourself.');
}
else {
$message = _('Why not [register an account](%%action.register%%) and [create the group](%%action.newgroup%%) yourself!');
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
$user_group->free();
}
} }
} }
@ -103,10 +109,5 @@ class GroupSearchResults extends GroupList
{ {
return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text)); return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text));
} }
function isReadOnly()
{
return true;
}
} }

View File

@ -63,6 +63,17 @@ class InboxAction extends MailboxAction
} }
} }
/**
* Output document relationship links
*
* @return void
*/
function showRelationshipLinks()
{
$this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME
$this->page, 'inbox', array('nickname' => $this->user->nickname));
}
/** /**
* Retrieve the messages for this user and this page * Retrieve the messages for this user and this page
* *

View File

@ -27,7 +27,7 @@ class InviteAction extends Action
var $subbed = null; var $subbed = null;
var $sent = null; var $sent = null;
function isReadOnly() function isReadOnly($args)
{ {
return false; return false;
} }

View File

@ -73,7 +73,7 @@ class JoingroupAction extends Action
if ($nickname_arg != $nickname) { if ($nickname_arg != $nickname) {
$args = array('nickname' => $nickname); $args = array('nickname' => $nickname);
common_redirect(common_local_url('editgroup', $args), 301); common_redirect(common_local_url('joingroup', $args), 301);
return false; return false;
} }
@ -143,7 +143,8 @@ class JoingroupAction extends Action
$this->elementEnd('html'); $this->elementEnd('html');
} else { } else {
common_redirect(common_local_url('groupmembers', array('nickname' => common_redirect(common_local_url('groupmembers', array('nickname' =>
$this->group->nickname))); $this->group->nickname)),
303);
} }
} }
} }

View File

@ -73,7 +73,7 @@ class LeavegroupAction extends Action
if ($nickname_arg != $nickname) { if ($nickname_arg != $nickname) {
$args = array('nickname' => $nickname); $args = array('nickname' => $nickname);
common_redirect(common_local_url('editgroup', $args), 301); common_redirect(common_local_url('leavegroup', $args), 301);
return false; return false;
} }
@ -96,12 +96,6 @@ class LeavegroupAction extends Action
return false; return false;
} }
if ($cur->isAdmin($this->group)) {
$this->clientError(_('You may not leave a group while you are its administrator.'), 403);
return false;
}
return true; return true;
} }
@ -153,7 +147,8 @@ class LeavegroupAction extends Action
$this->elementEnd('html'); $this->elementEnd('html');
} else { } else {
common_redirect(common_local_url('groupmembers', array('nickname' => common_redirect(common_local_url('groupmembers', array('nickname' =>
$this->group->nickname))); $this->group->nickname)),
303);
} }
} }
} }

View File

@ -55,7 +55,7 @@ class LoginAction extends Action
* @return boolean false * @return boolean false
*/ */
function isReadOnly() function isReadOnly($args)
{ {
return false; return false;
} }
@ -108,13 +108,15 @@ class LoginAction extends Action
$nickname = common_canonical_nickname($this->trimmed('nickname')); $nickname = common_canonical_nickname($this->trimmed('nickname'));
$password = $this->arg('password'); $password = $this->arg('password');
if (!common_check_user($nickname, $password)) { $user = common_check_user($nickname, $password);
if (!$user) {
$this->showForm(_('Incorrect username or password.')); $this->showForm(_('Incorrect username or password.'));
return; return;
} }
// success! // success!
if (!common_set_user($nickname)) { if (!common_set_user($user)) {
$this->serverError(_('Error setting user.')); $this->serverError(_('Error setting user.'));
return; return;
} }
@ -136,7 +138,7 @@ class LoginAction extends Action
$nickname)); $nickname));
} }
common_redirect($url); common_redirect($url, 303);
} }
/** /**

View File

@ -52,7 +52,7 @@ class LogoutAction extends Action
* *
* @return boolean true * @return boolean true
*/ */
function isReadOnly() function isReadOnly($args)
{ {
return false; return false;
} }
@ -70,10 +70,20 @@ class LogoutAction extends Action
if (!common_logged_in()) { if (!common_logged_in()) {
$this->clientError(_('Not logged in.')); $this->clientError(_('Not logged in.'));
} else { } else {
common_set_user(null); if (Event::handle('StartLogout', array($this))) {
common_real_login(false); // not logged in $this->logout();
common_forgetme(); // don't log back in! }
common_redirect(common_local_url('public')); Event::handle('EndLogout', array($this));
common_redirect(common_local_url('public'), 303);
} }
} }
function logout()
{
common_set_user(null);
common_real_login(false); // not logged in
common_forgetme(); // don't log back in!
}
} }

View File

@ -74,7 +74,7 @@ class MicrosummaryAction extends Action
print $user->nickname . ': ' . $notice->content; print $user->nickname . ': ' . $notice->content;
} }
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -193,7 +193,7 @@ class NewgroupAction extends Action
$group->query('COMMIT'); $group->query('COMMIT');
common_redirect($group->homeUrl(), 307); common_redirect($group->homeUrl(), 303);
} }
function nicknameExists($nickname) function nicknameExists($nickname)

View File

@ -172,15 +172,54 @@ class NewmessageAction extends Action
$this->notify($user, $this->other, $message); $this->notify($user, $this->other, $message);
$url = common_local_url('outbox', array('nickname' => $user->nickname)); if ($this->boolean('ajax')) {
$this->startHTML('text/xml;charset=utf-8');
$this->elementStart('head');
$this->element('title', null, _('Message sent'));
$this->elementEnd('head');
$this->elementStart('body');
$this->element('p', array('id' => 'command_result'),
sprintf(_('Direct message to %s sent'),
$this->other->nickname));
$this->elementEnd('body');
$this->elementEnd('html');
} else {
$url = common_local_url('outbox',
array('nickname' => $user->nickname));
common_redirect($url, 303);
}
}
common_redirect($url, 303); /**
* Show an Ajax-y error message
*
* Goes back to the browser, where it's shown in a popup.
*
* @param string $msg Message to show
*
* @return void
*/
function ajaxErrorMsg($msg)
{
$this->startHTML('text/xml;charset=utf-8', true);
$this->elementStart('head');
$this->element('title', null, _('Ajax Error'));
$this->elementEnd('head');
$this->elementStart('body');
$this->element('p', array('id' => 'error'), $msg);
$this->elementEnd('body');
$this->elementEnd('html');
} }
function showForm($msg = null) function showForm($msg = null)
{ {
$this->msg = $msg; if ($msg && $this->boolean('ajax')) {
$this->ajaxErrorMsg($msg);
return;
}
$this->msg = $msg;
$this->showPage(); $this->showPage();
} }

View File

@ -152,8 +152,14 @@ class NewnoticeAction extends Action
} }
$replyto = $this->trimmed('inreplyto'); $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, // $notice = Notice::saveNew($user->id, $content_shortened, 'web', 1,
$notice = Notice::saveNew($user->id, $content_shortened, 'web', 1,
($replyto == 'false') ? null : $replyto); ($replyto == 'false') ? null : $replyto);
if (is_string($notice)) { if (is_string($notice)) {
@ -161,6 +167,8 @@ class NewnoticeAction extends Action
return; return;
} }
$this->saveUrls($notice);
common_broadcast_notice($notice); common_broadcast_notice($notice);
if ($this->boolean('ajax')) { if ($this->boolean('ajax')) {
@ -186,6 +194,24 @@ class NewnoticeAction extends Action
} }
} }
/** save all urls in the notice to the db
*
* follow redirects and save all available file information
* (mimetype, date, size, oembed, etc.)
*
* @param class $notice Notice to pull URLs from
*
* @return void
*/
function saveUrls($notice) {
common_replace_urls_callback($notice->content, array($this, 'saveUrl'), $notice->id);
}
function saveUrl($data) {
list($url, $notice_id) = $data;
$zzz = File::processNew($url, $notice_id);
}
/** /**
* Show an Ajax-y error message * Show an Ajax-y error message
* *
@ -253,7 +279,7 @@ class NewnoticeAction extends Action
} }
} }
$notice_form = new NoticeForm($this, $content); $notice_form = new NoticeForm($this, '', $content);
$notice_form->show(); $notice_form->show();
} }

View File

@ -103,7 +103,7 @@ class NoticesearchAction extends SearchAction
function showResults($q, $page) function showResults($q, $page)
{ {
$notice = new Notice(); $notice = new Notice();
$q = strtolower($q);
$search_engine = $notice->getSearchEngine('identica_notices'); $search_engine = $notice->getSearchEngine('identica_notices');
$search_engine->set_sort_mode('chron'); $search_engine->set_sort_mode('chron');
// Ask for an extra to see if there's more. // Ask for an extra to see if there's more.
@ -113,123 +113,64 @@ class NoticesearchAction extends SearchAction
} else { } else {
$cnt = $notice->find(); $cnt = $notice->find();
} }
if ($cnt > 0) { if ($cnt === 0) {
$terms = preg_split('/[\s,]+/', $q); $this->element('p', 'error', _('No results.'));
$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 {
$this->element('p', 'error', _('No results'));
}
$this->searchSuggestions($q);
if (common_logged_in()) {
$message = sprintf(_('Be the first to [post on this topic](%%%%action.newnotice%%%%?status_textarea=%s)!'), urlencode($q));
}
else {
$message = sprintf(_('Why not [register an account](%%%%action.register%%%%) and be the first to [post on this topic](%%%%action.newnotice%%%%?status_textarea=%s)!'), urlencode($q));
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
return;
}
$terms = preg_split('/[\s,]+/', $q);
$nl = new SearchNoticeList($notice, $this, $terms);
$cnt = $nl->show();
$this->pagination($page > 1, $cnt > NOTICES_PER_PAGE, $this->pagination($page > 1, $cnt > NOTICES_PER_PAGE,
$page, 'noticesearch', array('q' => $q)); $page, 'noticesearch', array('q' => $q));
} }
}
/** class SearchNoticeList extends NoticeList {
* Show notice function __construct($notice, $out=null, $terms)
*
* @param class $notice notice
* @param array $terms terms to highlight
*
* @return void
*
* @todo refactor and combine with StreamAction::showNotice()
*/
function showNotice($notice, $terms)
{ {
$profile = $notice->getProfile(); parent::__construct($notice, $out);
if (!$profile) { $this->terms = $terms;
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));
$this->elementStart('div', 'entry-title'); function newListItem($notice)
$this->elementStart('span', 'vcard author'); {
$avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); return new SearchNoticeListItem($notice, $this->out, $this->terms);
$this->elementStart('a', array('href' => $profile->profileurl, }
'class' => 'url')); }
$this->element('img', array('src' => ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(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 // FIXME: URL, image, video, audio
$this->elementStart('p', array('class' => 'entry-content')); $this->out->elementStart('p', array('class' => 'entry-content'));
if ($notice->rendered) { if ($this->notice->rendered) {
$this->raw($this->highlight($notice->rendered, $terms)); $this->out->raw($this->highlight($this->notice->rendered, $this->terms));
} else { } else {
// XXX: may be some uncooked notices in the DB, // XXX: may be some uncooked notices in the DB,
// we cook them right now. This should probably disappear in future // we cook them right now. This should probably disappear in future
// versions (>> 0.4.x) // 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->out->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');
$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->elementEnd('div');
$this->elementEnd('li');
} }
/** /**
@ -242,21 +183,18 @@ class NoticesearchAction extends SearchAction
*/ */
function highlight($text, $terms) function highlight($text, $terms)
{ {
/* Highligh serach terms */ /* Highligh search terms */
$pattern = '/('.implode('|', array_map('htmlspecialchars', $terms)).')/i'; $options = implode('|', array_map('preg_quote', array_map('htmlspecialchars', $terms),
array_fill(0, sizeof($terms), '/')));
$pattern = "/($options)/i";
$result = preg_replace($pattern, '<strong>\\1</strong>', $text); $result = preg_replace($pattern, '<strong>\\1</strong>', $text);
/* Remove highlighting from inside links, loop incase multiple highlights in links */ /* Remove highlighting from inside links, loop incase multiple highlights in links */
$pattern = '/(href="[^"]*)<strong>('.implode('|', array_map('htmlspecialchars', $terms)).')<\/strong>([^"]*")/iU'; $pattern = '/(href="[^"]*)<strong>('.$options.')<\/strong>([^"]*")/iU';
do { do {
$result = preg_replace($pattern, '\\1\\2\\3', $result, -1, $count); $result = preg_replace($pattern, '\\1\\2\\3', $result, -1, $count);
} while ($count); } while ($count);
return $result; return $result;
} }
function isReadOnly()
{
return true;
}
} }

View File

@ -62,9 +62,6 @@ class NoticesearchrssAction extends Rss10Action
$notice = new Notice(); $notice = new Notice();
# lcase it for comparison
$q = strtolower($q);
$search_engine = $notice->getSearchEngine('identica_notices'); $search_engine = $notice->getSearchEngine('identica_notices');
$search_engine->set_sort_mode('chron'); $search_engine->set_sort_mode('chron');
@ -82,10 +79,9 @@ class NoticesearchrssAction extends Rss10Action
function getChannel() function getChannel()
{ {
global $config;
$q = $this->trimmed('q'); $q = $this->trimmed('q');
$c = array('url' => common_local_url('noticesearchrss', array('q' => $q)), $c = array('url' => common_local_url('noticesearchrss', array('q' => $q)),
'title' => $config['site']['name'] . sprintf(_(' Search Stream for "%s"'), $q), 'title' => common_config('site', 'name') . sprintf(_(' Search Stream for "%s"'), $q),
'link' => common_local_url('noticesearch', array('q' => $q)), 'link' => common_local_url('noticesearch', array('q' => $q)),
'description' => sprintf(_('All updates matching search term "%s"'), $q)); 'description' => sprintf(_('All updates matching search term "%s"'), $q));
return $c; return $c;
@ -96,7 +92,7 @@ class NoticesearchrssAction extends Rss10Action
return null; return null;
} }
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -100,7 +100,8 @@ class NudgeAction extends Action
} else { } else {
// display a confirmation to the user // display a confirmation to the user
common_redirect(common_local_url('showstream', common_redirect(common_local_url('showstream',
array('nickname' => $other->nickname))); array('nickname' => $other->nickname)),
303);
} }
} }
@ -123,7 +124,7 @@ class NudgeAction extends Action
} }
} }
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -67,8 +67,8 @@ class OpenidsettingsAction extends AccountSettingsAction
function getInstructions() function getInstructions()
{ {
return _('[OpenID](%%doc.openid%%) lets you log into many sites ' . return _('[OpenID](%%doc.openid%%) lets you log into many sites' .
' with the same user account. '. ' with the same user account.'.
' Manage your associated OpenIDs from here.'); ' Manage your associated OpenIDs from here.');
} }

View File

@ -84,7 +84,7 @@ class OpensearchAction extends Action
$this->endXML(); $this->endXML();
} }
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -62,6 +62,17 @@ class OutboxAction extends MailboxAction
} }
} }
/**
* Output document relationship links
*
* @return void
*/
function showRelationshipLinks()
{
$this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME
$this->page, 'outbox', array('nickname' => $this->user->nickname));
}
/** /**
* retrieve the messages for this user and this page * retrieve the messages for this user and this page
* *

View File

@ -60,16 +60,10 @@ class PeoplesearchAction extends SearchAction
function showResults($q, $page) function showResults($q, $page)
{ {
$profile = new Profile(); $profile = new Profile();
# lcase it for comparison
$q = strtolower($q);
$search_engine = $profile->getSearchEngine('identica_people'); $search_engine = $profile->getSearchEngine('identica_people');
$search_engine->set_sort_mode('chron'); $search_engine->set_sort_mode('chron');
# Ask for an extra to see if there's more. // Ask for an extra to see if there's more.
$search_engine->limit((($page-1)*PROFILES_PER_PAGE), PROFILES_PER_PAGE + 1); $search_engine->limit((($page-1)*PROFILES_PER_PAGE), PROFILES_PER_PAGE + 1);
if (false === $search_engine->query($q)) { if (false === $search_engine->query($q)) {
$cnt = 0; $cnt = 0;
@ -81,38 +75,15 @@ class PeoplesearchAction extends SearchAction
$terms = preg_split('/[\s,]+/', $q); $terms = preg_split('/[\s,]+/', $q);
$results = new PeopleSearchResults($profile, $terms, $this); $results = new PeopleSearchResults($profile, $terms, $this);
$results->show(); $results->show();
} else { $profile->free();
$this->element('p', 'error', _('No results')); $this->pagination($page > 1, $cnt > PROFILES_PER_PAGE,
}
$profile->free();
$this->pagination($page > 1, $cnt > PROFILES_PER_PAGE,
$page, 'peoplesearch', array('q' => $q)); $page, 'peoplesearch', array('q' => $q));
}
} } else {
$this->element('p', 'error', _('No results.'));
class PeopleSearchResults extends ProfileList $this->searchSuggestions($q);
{ $profile->free();
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;
} }
} }

View File

@ -119,7 +119,7 @@ class PeopletagAction extends Action
'FROM profile JOIN profile_tag ' . 'FROM profile JOIN profile_tag ' .
'ON profile.id = profile_tag.tagger ' . 'ON profile.id = profile_tag.tagger ' .
'WHERE profile_tag.tagger = profile_tag.tagged ' . 'WHERE profile_tag.tagger = profile_tag.tagged ' .
'AND tag = "%s" ' . "AND tag = '%s' " .
'ORDER BY profile_tag.modified DESC%s'; 'ORDER BY profile_tag.modified DESC%s';
$profile->query(sprintf($qry, $this->tag, $lim)); $profile->query(sprintf($qry, $this->tag, $lim));

View File

@ -28,7 +28,7 @@ class PostnoticeAction extends Action
parent::handle($args); parent::handle($args);
try { try {
common_remove_magic_from_request(); common_remove_magic_from_request();
$req = OAuthRequest::from_request(); $req = OAuthRequest::from_request('POST', common_local_url('postnotice'));
# Note: server-to-server function! # Note: server-to-server function!
$server = omb_oauth_server(); $server = omb_oauth_server();
list($consumer, $token) = $server->verify_request($req); list($consumer, $token) = $server->verify_request($req);
@ -79,7 +79,7 @@ class PostnoticeAction extends Action
} }
$notice = Notice::staticGet('uri', $notice_uri); $notice = Notice::staticGet('uri', $notice_uri);
if (!$notice) { if (!$notice) {
$notice = Notice::saveNew($remote_profile->id, $content, 'omb', false, 0, $notice_uri); $notice = Notice::saveNew($remote_profile->id, $content, 'omb', false, null, $notice_uri);
if (is_string($notice)) { if (is_string($notice)) {
common_server_serror($notice, 500); common_server_serror($notice, 500);
return false; return false;

View File

@ -91,67 +91,68 @@ class ProfilesettingsAction extends AccountSettingsAction
$this->element('legend', null, _('Profile information')); $this->element('legend', null, _('Profile information'));
$this->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
# too much common patterns here... abstractable? // too much common patterns here... abstractable?
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
$this->elementStart('li'); if (Event::handle('StartProfileFormData', array($this))) {
$this->input('nickname', _('Nickname'), $this->elementStart('li');
($this->arg('nickname')) ? $this->arg('nickname') : $profile->nickname, $this->input('nickname', _('Nickname'),
_('1-64 lowercase letters or numbers, no punctuation or spaces')); ($this->arg('nickname')) ? $this->arg('nickname') : $profile->nickname,
$this->elementEnd('li'); _('1-64 lowercase letters or numbers, no punctuation or spaces'));
$this->elementStart('li'); $this->elementEnd('li');
$this->input('fullname', _('Full name'), $this->elementStart('li');
($this->arg('fullname')) ? $this->arg('fullname') : $profile->fullname); $this->input('fullname', _('Full name'),
$this->elementEnd('li'); ($this->arg('fullname')) ? $this->arg('fullname') : $profile->fullname);
$this->elementStart('li'); $this->elementEnd('li');
$this->input('homepage', _('Homepage'), $this->elementStart('li');
($this->arg('homepage')) ? $this->arg('homepage') : $profile->homepage, $this->input('homepage', _('Homepage'),
_('URL of your homepage, blog, or profile on another site')); ($this->arg('homepage')) ? $this->arg('homepage') : $profile->homepage,
$this->elementEnd('li'); _('URL of your homepage, blog, or profile on another site'));
$this->elementStart('li'); $this->elementEnd('li');
$this->textarea('bio', _('Bio'), $this->elementStart('li');
($this->arg('bio')) ? $this->arg('bio') : $profile->bio, $this->textarea('bio', _('Bio'),
_('Describe yourself and your interests in 140 chars')); ($this->arg('bio')) ? $this->arg('bio') : $profile->bio,
$this->elementEnd('li'); _('Describe yourself and your interests in 140 chars'));
$this->elementStart('li'); $this->elementEnd('li');
$this->input('location', _('Location'), $this->elementStart('li');
($this->arg('location')) ? $this->arg('location') : $profile->location, $this->input('location', _('Location'),
_('Where you are, like "City, State (or Region), Country"')); ($this->arg('location')) ? $this->arg('location') : $profile->location,
$this->elementEnd('li'); _('Where you are, like "City, State (or Region), Country"'));
$this->elementStart('li'); $this->elementEnd('li');
$this->input('tags', _('Tags'), Event::handle('EndProfileFormData', array($this));
($this->arg('tags')) ? $this->arg('tags') : implode(' ', $user->getSelfTags()), $this->elementStart('li');
_('Tags for yourself (letters, numbers, -, ., and _), comma- or space- separated')); $this->input('tags', _('Tags'),
$this->elementEnd('li'); ($this->arg('tags')) ? $this->arg('tags') : implode(' ', $user->getSelfTags()),
$this->elementStart('li'); _('Tags for yourself (letters, numbers, -, ., and _), comma- or space- separated'));
$language = common_language(); $this->elementEnd('li');
$this->dropdown('language', _('Language'), $this->elementStart('li');
get_nice_language_list(), _('Preferred language'), $language = common_language();
true, $language); $this->dropdown('language', _('Language'),
$this->elementEnd('li'); get_nice_language_list(), _('Preferred language'),
$timezone = common_timezone(); false, $language);
$timezones = array(); $this->elementEnd('li');
foreach(DateTimeZone::listIdentifiers() as $k => $v) { $timezone = common_timezone();
$timezones[$v] = $v; $timezones = array();
foreach(DateTimeZone::listIdentifiers() as $k => $v) {
$timezones[$v] = $v;
}
$this->elementStart('li');
$this->dropdown('timezone', _('Timezone'),
$timezones, _('What timezone are you normally in?'),
true, $timezone);
$this->elementEnd('li');
$this->elementStart('li');
$this->checkbox('autosubscribe',
_('Automatically subscribe to whoever '.
'subscribes to me (best for non-humans)'),
($this->arg('autosubscribe')) ?
$this->boolean('autosubscribe') : $user->autosubscribe);
$this->elementEnd('li');
} }
$this->elementStart('li');
$this->dropdown('timezone', _('Timezone'),
$timezones, _('What timezone are you normally in?'),
true, $timezone);
$this->elementEnd('li');
$this->elementStart('li');
$this->checkbox('autosubscribe',
_('Automatically subscribe to whoever '.
'subscribes to me (best for non-humans)'),
($this->arg('autosubscribe')) ?
$this->boolean('autosubscribe') : $user->autosubscribe);
$this->elementEnd('li');
$this->elementEnd('ul'); $this->elementEnd('ul');
$this->submit('save', _('Save')); $this->submit('save', _('Save'));
$this->elementEnd('fieldset'); $this->elementEnd('fieldset');
$this->elementEnd('form'); $this->elementEnd('form');
} }
/** /**
@ -165,158 +166,158 @@ class ProfilesettingsAction extends AccountSettingsAction
function handlePost() function handlePost()
{ {
# CSRF protection // CSRF protection
$token = $this->trimmed('token'); $token = $this->trimmed('token');
if (!$token || $token != common_session_token()) { if (!$token || $token != common_session_token()) {
$this->showForm(_('There was a problem with your session token. '. $this->showForm(_('There was a problem with your session token. '.
'Try again, please.')); 'Try again, please.'));
return; return;
} }
$nickname = $this->trimmed('nickname'); if (Event::handle('StartProfileSaveForm', array($this))) {
$fullname = $this->trimmed('fullname');
$homepage = $this->trimmed('homepage');
$bio = $this->trimmed('bio');
$location = $this->trimmed('location');
$autosubscribe = $this->boolean('autosubscribe');
$language = $this->trimmed('language');
$timezone = $this->trimmed('timezone');
$tagstring = $this->trimmed('tags');
# Some validation $nickname = $this->trimmed('nickname');
$fullname = $this->trimmed('fullname');
$homepage = $this->trimmed('homepage');
$bio = $this->trimmed('bio');
$location = $this->trimmed('location');
$autosubscribe = $this->boolean('autosubscribe');
$language = $this->trimmed('language');
$timezone = $this->trimmed('timezone');
$tagstring = $this->trimmed('tags');
if (!Validate::string($nickname, array('min_length' => 1, // Some validation
'max_length' => 64, if (!Validate::string($nickname, array('min_length' => 1,
'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { 'max_length' => 64,
$this->showForm(_('Nickname must have only lowercase letters and numbers and no spaces.')); 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) {
return; $this->showForm(_('Nickname must have only lowercase letters and numbers and no spaces.'));
} else if (!User::allowed_nickname($nickname)) { return;
$this->showForm(_('Not a valid nickname.')); } else if (!User::allowed_nickname($nickname)) {
return; $this->showForm(_('Not a valid nickname.'));
} else if (!is_null($homepage) && (strlen($homepage) > 0) && return;
!Validate::uri($homepage, array('allowed_schemes' => array('http', 'https')))) { } else if (!is_null($homepage) && (strlen($homepage) > 0) &&
$this->showForm(_('Homepage is not a valid URL.')); !Validate::uri($homepage, array('allowed_schemes' => array('http', 'https')))) {
return; $this->showForm(_('Homepage is not a valid URL.'));
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) { return;
$this->showForm(_('Full name is too long (max 255 chars).')); } else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
return; $this->showForm(_('Full name is too long (max 255 chars).'));
} else if (!is_null($bio) && mb_strlen($bio) > 140) { return;
$this->showForm(_('Bio is too long (max 140 chars).')); } else if (!is_null($bio) && mb_strlen($bio) > 140) {
return; $this->showForm(_('Bio is too long (max 140 chars).'));
} else if (!is_null($location) && mb_strlen($location) > 255) { return;
$this->showForm(_('Location is too long (max 255 chars).')); } else if (!is_null($location) && mb_strlen($location) > 255) {
return; $this->showForm(_('Location is too long (max 255 chars).'));
} else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) { return;
$this->showForm(_('Timezone not selected.')); } else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) {
return; $this->showForm(_('Timezone not selected.'));
} else if ($this->nicknameExists($nickname)) { return;
$this->showForm(_('Nickname already in use. Try another one.')); } else if ($this->nicknameExists($nickname)) {
return; $this->showForm(_('Nickname already in use. Try another one.'));
} else if (!is_null($language) && strlen($language) > 50) { return;
$this->showForm(_('Language is too long (max 50 chars).')); } else if (!is_null($language) && strlen($language) > 50) {
return; $this->showForm(_('Language is too long (max 50 chars).'));
}
if ($tagstring) {
$tags = array_map('common_canonical_tag', preg_split('/[\s,]+/', $tagstring));
} else {
$tags = array();
}
foreach ($tags as $tag) {
if (!common_valid_profile_tag($tag)) {
$this->showForm(sprintf(_('Invalid tag: "%s"'), $tag));
return; return;
} }
}
$user = common_current_user(); if ($tagstring) {
$tags = array_map('common_canonical_tag', preg_split('/[\s,]+/', $tagstring));
$user->query('BEGIN');
if ($user->nickname != $nickname ||
$user->language != $language ||
$user->timezone != $timezone) {
common_debug('Updating user nickname from ' . $user->nickname . ' to ' . $nickname,
__FILE__);
common_debug('Updating user language from ' . $user->language . ' to ' . $language,
__FILE__);
common_debug('Updating user timezone from ' . $user->timezone . ' to ' . $timezone,
__FILE__);
$original = clone($user);
$user->nickname = $nickname;
$user->language = $language;
$user->timezone = $timezone;
$result = $user->updateKeys($original);
if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__);
$this->serverError(_('Couldn\'t update user.'));
return;
} else { } else {
# Re-initialize language environment if it changed $tags = array();
common_init_language();
} }
}
# XXX: XOR foreach ($tags as $tag) {
if (!common_valid_profile_tag($tag)) {
$this->showForm(sprintf(_('Invalid tag: "%s"'), $tag));
return;
}
}
if ($user->autosubscribe ^ $autosubscribe) { $user = common_current_user();
$original = clone($user); $user->query('BEGIN');
$user->autosubscribe = $autosubscribe; if ($user->nickname != $nickname ||
$user->language != $language ||
$user->timezone != $timezone) {
$result = $user->update($original); common_debug('Updating user nickname from ' . $user->nickname . ' to ' . $nickname,
__FILE__);
common_debug('Updating user language from ' . $user->language . ' to ' . $language,
__FILE__);
common_debug('Updating user timezone from ' . $user->timezone . ' to ' . $timezone,
__FILE__);
if ($result === false) { $original = clone($user);
common_log_db_error($user, 'UPDATE', __FILE__);
$this->serverError(_('Couldn\'t update user for autosubscribe.')); $user->nickname = $nickname;
$user->language = $language;
$user->timezone = $timezone;
$result = $user->updateKeys($original);
if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__);
$this->serverError(_('Couldn\'t update user.'));
return;
} else {
// Re-initialize language environment if it changed
common_init_language();
}
}
// XXX: XOR
if ($user->autosubscribe ^ $autosubscribe) {
$original = clone($user);
$user->autosubscribe = $autosubscribe;
$result = $user->update($original);
if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__);
$this->serverError(_('Couldn\'t update user for autosubscribe.'));
return;
}
}
$profile = $user->getProfile();
$orig_profile = clone($profile);
$profile->nickname = $user->nickname;
$profile->fullname = $fullname;
$profile->homepage = $homepage;
$profile->bio = $bio;
$profile->location = $location;
$profile->profileurl = common_profile_url($nickname);
common_debug('Old profile: ' . common_log_objstring($orig_profile), __FILE__);
common_debug('New profile: ' . common_log_objstring($profile), __FILE__);
$result = $profile->update($orig_profile);
if (!$result) {
common_log_db_error($profile, 'UPDATE', __FILE__);
$this->serverError(_('Couldn\'t save profile.'));
return; return;
} }
// Set the user tags
$result = $user->setSelfTags($tags);
if (!$result) {
$this->serverError(_('Couldn\'t save tags.'));
return;
}
$user->query('COMMIT');
Event::handle('EndProfileSaveForm', array($this));
common_broadcast_profile($profile);
$this->showForm(_('Settings saved.'), true);
} }
$profile = $user->getProfile();
$orig_profile = clone($profile);
$profile->nickname = $user->nickname;
$profile->fullname = $fullname;
$profile->homepage = $homepage;
$profile->bio = $bio;
$profile->location = $location;
$profile->profileurl = common_profile_url($nickname);
common_debug('Old profile: ' . common_log_objstring($orig_profile), __FILE__);
common_debug('New profile: ' . common_log_objstring($profile), __FILE__);
$result = $profile->update($orig_profile);
if (!$result) {
common_log_db_error($profile, 'UPDATE', __FILE__);
$this->serverError(_('Couldn\'t save profile.'));
return;
}
# Set the user tags
$result = $user->setSelfTags($tags);
if (!$result) {
$this->serverError(_('Couldn\'t save tags.'));
return;
}
$user->query('COMMIT');
common_broadcast_profile($profile);
$this->showForm(_('Settings saved.'), true);
} }
function nicknameExists($nickname) function nicknameExists($nickname)

View File

@ -56,7 +56,7 @@ class PublicAction extends Action
var $page = null; var $page = null;
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
@ -135,6 +135,17 @@ class PublicAction extends Action
_('Public Stream Feed (Atom)'))); _('Public Stream Feed (Atom)')));
} }
/**
* Output document relationship links
*
* @return void
*/
function showRelationshipLinks()
{
$this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME
$this->page, 'public');
}
/** /**
* Extra head elements * Extra head elements
* *
@ -166,6 +177,22 @@ class PublicAction extends Action
$nav->show(); $nav->show();
} }
function showEmptyList()
{
$message = _('This is the public timeline for %%site.name%% but no one has posted anything yet.') . ' ';
if (common_logged_in()) {
$message .= _('Be the first to post!');
}
else {
$message .= _('Why not [register an account](%%action.register%%) and be the first to post!');
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
}
/** /**
* Fill the content area * Fill the content area
* *
@ -189,6 +216,10 @@ class PublicAction extends Action
$cnt = $nl->show(); $cnt = $nl->show();
if ($cnt == 0) {
$this->showEmptyList();
}
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'public'); $this->page, 'public');
} }
@ -207,9 +238,14 @@ class PublicAction extends Action
function showAnonymousMessage() function showAnonymousMessage()
{ {
$m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' . if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
'based on the Free Software [Laconica](http://laconi.ca/) tool. ' . $m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
'[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ([Read more](%%doc.help%%))'); 'based on the Free Software [Laconica](http://laconi.ca/) tool. ' .
'[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ([Read more](%%doc.help%%))');
} else {
$m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
'based on the Free Software [Laconica](http://laconi.ca/) tool.');
}
$this->elementStart('div', array('id' => 'anon_notice')); $this->elementStart('div', array('id' => 'anon_notice'));
$this->raw(common_markup_to_html($m)); $this->raw(common_markup_to_html($m));
$this->elementEnd('div'); $this->elementEnd('div');

View File

@ -84,12 +84,11 @@ class PublicrssAction extends Rss10Action
*/ */
function getChannel() function getChannel()
{ {
global $config;
$c = array( $c = array(
'url' => common_local_url('publicrss') 'url' => common_local_url('publicrss')
, 'title' => sprintf(_('%s Public Stream'), $config['site']['name']) , 'title' => sprintf(_('%s Public Stream'), common_config('site', 'name'))
, 'link' => common_local_url('public') , 'link' => common_local_url('public')
, 'description' => sprintf(_('All updates for %s'), $config['site']['name'])); , 'description' => sprintf(_('All updates for %s'), common_config('site', 'name')));
return $c; return $c;
} }
@ -103,7 +102,7 @@ class PublicrssAction extends Rss10Action
// nop // nop
} }
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -47,7 +47,7 @@ define('TAGS_PER_PAGE', 100);
class PublictagcloudAction extends Action class PublictagcloudAction extends Action
{ {
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
@ -64,6 +64,22 @@ class PublictagcloudAction extends Action
common_config('site', 'name'))); common_config('site', 'name')));
} }
function showEmptyList()
{
$message = _('No one has posted a notice with a [hashtag](%%doc.tags%%) yet.') . ' ';
if (common_logged_in()) {
$message .= _('Be the first to post one!');
}
else {
$message .= _('Why not [register an account](%%action.register%%) and be the first to post one!');
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
}
function showLocalNav() function showLocalNav()
{ {
$nav = new PublicGroupNav($this); $nav = new PublicGroupNav($this);
@ -126,6 +142,8 @@ class PublictagcloudAction extends Action
$this->elementEnd('dd'); $this->elementEnd('dd');
$this->elementEnd('dl'); $this->elementEnd('dl');
$this->elementEnd('div'); $this->elementEnd('div');
} else {
$this->showEmptyList();
} }
} }

View File

@ -54,7 +54,7 @@ class PublicxrdsAction extends Action
* *
* @return boolean true * @return boolean true
*/ */
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
@ -70,19 +70,19 @@ class PublicxrdsAction extends Action
{ {
parent::handle($args); parent::handle($args);
header('Content-Type: application/xrds+xml'); header('Content-Type: application/xrds+xml');
common_start_xml(); $this->startXML();
$this->elementStart('XRDS', array('xmlns' => 'xri://$xrds')); $this->elementStart('XRDS', array('xmlns' => 'xri://$xrds'));
$this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', $this->elementStart('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)',
'xmlns:simple' => 'http://xrds-simple.net/core/1.0', 'xmlns:simple' => 'http://xrds-simple.net/core/1.0',
'version' => '2.0')); 'version' => '2.0'));
$this->element('Type', null, 'xri://$xrds*simple'); $this->element('Type', null, 'xri://$xrds*simple');
foreach (array('finishopenidlogin', 'finishaddopenid', 'finishimmediate') as $finish) { foreach (array('finishopenidlogin', 'finishaddopenid') as $finish) {
$this->showService(Auth_OpenID_RP_RETURN_TO_URL_TYPE, $this->showService(Auth_OpenID_RP_RETURN_TO_URL_TYPE,
common_local_url($finish)); common_local_url($finish));
} }
$this->elementEnd('XRD'); $this->elementEnd('XRD');
$this->elementEnd('XRDS'); $this->elementEnd('XRDS');
common_end_xml(); $this->endXML();
} }
/** /**

View File

@ -151,11 +151,11 @@ class RecoverpasswordAction extends Action
$this->element('p', null, $this->element('p', null,
_('If you\'ve forgotten or lost your' . _('If you\'ve forgotten or lost your' .
' password, you can get a new one sent to' . ' password, you can get a new one sent to' .
' the email address you have stored ' . ' the email address you have stored' .
' in your account.')); ' in your account.'));
} else if ($this->mode == 'reset') { } else if ($this->mode == 'reset') {
$this->element('p', null, $this->element('p', null,
_('You\'ve been identified. Enter a ' . _('You\'ve been identified. Enter a' .
' new password below. ')); ' new password below. '));
} }
$this->elementEnd('div'); $this->elementEnd('div');
@ -181,13 +181,21 @@ class RecoverpasswordAction extends Action
function showRecoverForm() function showRecoverForm()
{ {
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
'id' => 'recoverpassword', 'id' => 'form_password_recover',
'class' => 'form_settings',
'action' => common_local_url('recoverpassword'))); 'action' => common_local_url('recoverpassword')));
$this->elementStart('fieldset');
$this->element('legend', null, _('Password recover'));
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
$this->input('nicknameoremail', _('Nickname or email'), $this->input('nicknameoremail', _('Nickname or email'),
$this->trimmed('nicknameoremail'), $this->trimmed('nicknameoremail'),
_('Your nickname on this server, ' . _('Your nickname on this server, ' .
'or your registered email address.')); 'or your registered email address.'));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->submit('recover', _('Recover')); $this->submit('recover', _('Recover'));
$this->elementEnd('fieldset');
$this->elementEnd('form'); $this->elementEnd('form');
} }
@ -213,14 +221,24 @@ class RecoverpasswordAction extends Action
function showResetForm() function showResetForm()
{ {
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
'id' => 'recoverpassword', 'id' => 'form_password_change',
'class' => 'form_settings',
'action' => common_local_url('recoverpassword'))); 'action' => common_local_url('recoverpassword')));
$this->elementStart('fieldset');
$this->element('legend', null, _('Password change'));
$this->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
$this->password('newpassword', _('New password'), $this->password('newpassword', _('New password'),
_('6 or more characters, and don\'t forget it!')); _('6 or more characters, and don\'t forget it!'));
$this->elementEnd('li');
$this->elementStart('li');
$this->password('confirm', _('Confirm'), $this->password('confirm', _('Confirm'),
_('Same as password above')); _('Same as password above'));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->submit('reset', _('Reset')); $this->submit('reset', _('Reset'));
$this->elementEnd('fieldset');
$this->elementEnd('form'); $this->elementEnd('form');
} }

View File

@ -55,6 +55,45 @@ class RegisterAction extends Action
var $registered = false; var $registered = false;
/**
* Prepare page to run
*
*
* @param $args
* @return string title
*/
function prepare($args)
{
parent::prepare($args);
$this->code = $this->trimmed('code');
if (empty($this->code)) {
common_ensure_session();
if (array_key_exists('invitecode', $_SESSION)) {
$this->code = $_SESSION['invitecode'];
}
}
if (common_config('site', 'inviteonly') && empty($this->code)) {
$this->clientError(_('Sorry, only invited people can register.'));
return false;
}
if (!empty($this->code)) {
$this->invite = Invitation::staticGet('code', $this->code);
if (empty($this->invite)) {
$this->clientError(_('Sorry, invalid invitation code.'));
return false;
}
// Store this in case we need it
common_ensure_session();
$_SESSION['invitecode'] = $this->code;
}
return true;
}
/** /**
* Title of the page * Title of the page
* *
@ -108,107 +147,109 @@ class RegisterAction extends Action
function tryRegister() function tryRegister()
{ {
$token = $this->trimmed('token'); if (Event::handle('StartRegistrationTry', array($this))) {
if (!$token || $token != common_session_token()) { $token = $this->trimmed('token');
$this->showForm(_('There was a problem with your session token. '. if (!$token || $token != common_session_token()) {
'Try again, please.')); $this->showForm(_('There was a problem with your session token. '.
return; 'Try again, please.'));
} return;
}
$nickname = $this->trimmed('nickname'); $nickname = $this->trimmed('nickname');
$email = $this->trimmed('email'); $email = $this->trimmed('email');
$fullname = $this->trimmed('fullname'); $fullname = $this->trimmed('fullname');
$homepage = $this->trimmed('homepage'); $homepage = $this->trimmed('homepage');
$bio = $this->trimmed('bio'); $bio = $this->trimmed('bio');
$location = $this->trimmed('location'); $location = $this->trimmed('location');
// We don't trim these... whitespace is OK in a password! // We don't trim these... whitespace is OK in a password!
$password = $this->arg('password');
$confirm = $this->arg('confirm');
$password = $this->arg('password'); // invitation code, if any
$confirm = $this->arg('confirm'); $code = $this->trimmed('code');
// invitation code, if any if ($code) {
$invite = Invitation::staticGet($code);
}
$code = $this->trimmed('code'); if (common_config('site', 'inviteonly') && !($code && $invite)) {
$this->clientError(_('Sorry, only invited people can register.'));
return;
}
if ($code) { // Input scrubbing
$invite = Invitation::staticGet($code); $nickname = common_canonical_nickname($nickname);
} $email = common_canonical_email($email);
if (common_config('site', 'inviteonly') && !($code && $invite)) { if (!$this->boolean('license')) {
$this->clientError(_('Sorry, only invited people can register.')); $this->showForm(_('You can\'t register if you don\'t '.
return; 'agree to the license.'));
} } else if ($email && !Validate::email($email, true)) {
$this->showForm(_('Not a valid email address.'));
} else if (!Validate::string($nickname, array('min_length' => 1,
'max_length' => 64,
'format' => NICKNAME_FMT))) {
$this->showForm(_('Nickname must have only lowercase letters '.
'and numbers and no spaces.'));
} else if ($this->nicknameExists($nickname)) {
$this->showForm(_('Nickname already in use. Try another one.'));
} else if (!User::allowed_nickname($nickname)) {
$this->showForm(_('Not a valid nickname.'));
} else if ($this->emailExists($email)) {
$this->showForm(_('Email address already exists.'));
} else if (!is_null($homepage) && (strlen($homepage) > 0) &&
!Validate::uri($homepage,
array('allowed_schemes' =>
array('http', 'https')))) {
$this->showForm(_('Homepage is not a valid URL.'));
return;
} 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) && mb_strlen($bio) > 140) {
$this->showForm(_('Bio is too long (max 140 chars).'));
return;
} else if (!is_null($location) && mb_strlen($location) > 255) {
$this->showForm(_('Location is too long (max 255 chars).'));
return;
} else if (strlen($password) < 6) {
$this->showForm(_('Password must be 6 or more characters.'));
return;
} else if ($password != $confirm) {
$this->showForm(_('Passwords don\'t match.'));
} else if ($user = User::register(array('nickname' => $nickname,
'password' => $password,
'email' => $email,
'fullname' => $fullname,
'homepage' => $homepage,
'bio' => $bio,
'location' => $location,
'code' => $code))) {
if (!$user) {
$this->showForm(_('Invalid username or password.'));
return;
}
// success!
if (!common_set_user($user)) {
$this->serverError(_('Error setting user.'));
return;
}
// this is a real login
common_real_login(true);
if ($this->boolean('rememberme')) {
common_debug('Adding rememberme cookie for ' . $nickname);
common_rememberme($user);
}
// Input scrubbing Event::handle('EndRegistrationTry', array($this));
$nickname = common_canonical_nickname($nickname); // Re-init language env in case it changed (not yet, but soon)
$email = common_canonical_email($email); common_init_language();
$this->showSuccess();
if (!$this->boolean('license')) { } else {
$this->showForm(_('You can\'t register if you don\'t '.
'agree to the license.'));
} else if ($email && !Validate::email($email, true)) {
$this->showForm(_('Not a valid email address.'));
} else if (!Validate::string($nickname, array('min_length' => 1,
'max_length' => 64,
'format' => NICKNAME_FMT))) {
$this->showForm(_('Nickname must have only lowercase letters '.
'and numbers and no spaces.'));
} else if ($this->nicknameExists($nickname)) {
$this->showForm(_('Nickname already in use. Try another one.'));
} else if (!User::allowed_nickname($nickname)) {
$this->showForm(_('Not a valid nickname.'));
} else if ($this->emailExists($email)) {
$this->showForm(_('Email address already exists.'));
} else if (!is_null($homepage) && (strlen($homepage) > 0) &&
!Validate::uri($homepage,
array('allowed_schemes' =>
array('http', 'https')))) {
$this->showForm(_('Homepage is not a valid URL.'));
return;
} 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) && mb_strlen($bio) > 140) {
$this->showForm(_('Bio is too long (max 140 chars).'));
return;
} else if (!is_null($location) && mb_strlen($location) > 255) {
$this->showForm(_('Location is too long (max 255 chars).'));
return;
} else if (strlen($password) < 6) {
$this->showForm(_('Password must be 6 or more characters.'));
return;
} else if ($password != $confirm) {
$this->showForm(_('Passwords don\'t match.'));
} else if ($user = User::register(array('nickname' => $nickname,
'password' => $password,
'email' => $email,
'fullname' => $fullname,
'homepage' => $homepage,
'bio' => $bio,
'location' => $location,
'code' => $code))) {
if (!$user) {
$this->showForm(_('Invalid username or password.')); $this->showForm(_('Invalid username or password.'));
return;
} }
// success!
if (!common_set_user($user)) {
$this->serverError(_('Error setting user.'));
return;
}
// this is a real login
common_real_login(true);
if ($this->boolean('rememberme')) {
common_debug('Adding rememberme cookie for ' . $nickname);
common_rememberme($user);
}
// Re-init language env in case it changed (not yet, but soon)
common_init_language();
$this->showSuccess();
} else {
$this->showForm(_('Invalid username or password.'));
} }
} }
@ -250,22 +291,24 @@ class RegisterAction extends Action
// overrrided to add entry-title class // overrrided to add entry-title class
function showPageTitle() { function showPageTitle() {
$this->element('h1', array('class' => 'entry-title'), $this->title()); if (Event::handle('StartShowPageTitle', array($this))) {
$this->element('h1', array('class' => 'entry-title'), $this->title());
}
} }
// overrided to add hentry, and content-inner class // overrided to add hentry, and content-inner class
function showContentBlock() function showContentBlock()
{ {
$this->elementStart('div', array('id' => 'content', 'class' => 'hentry')); $this->elementStart('div', array('id' => 'content', 'class' => 'hentry'));
$this->showPageTitle(); $this->showPageTitle();
$this->showPageNoticeBlock(); $this->showPageNoticeBlock();
$this->elementStart('div', array('id' => 'content_inner', $this->elementStart('div', array('id' => 'content_inner',
'class' => 'entry-content')); 'class' => 'entry-content'));
// show the actual content (forms, lists, whatever) // show the actual content (forms, lists, whatever)
$this->showContent(); $this->showContent();
$this->elementEnd('div'); $this->elementEnd('div');
$this->elementEnd('div'); $this->elementEnd('div');
} }
/** /**
* Instructions or a notice for the page * Instructions or a notice for the page
@ -341,6 +384,8 @@ class RegisterAction extends Action
{ {
$code = $this->trimmed('code'); $code = $this->trimmed('code');
$invite = null;
if ($code) { if ($code) {
$invite = Invitation::staticGet($code); $invite = Invitation::staticGet($code);
} }
@ -358,82 +403,85 @@ class RegisterAction extends Action
$this->element('legend', null, 'Account settings'); $this->element('legend', null, 'Account settings');
$this->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
if ($code) { if ($this->code) {
$this->hidden('code', $code); $this->hidden('code', $this->code);
} }
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
$this->elementStart('li'); if (Event::handle('StartRegistrationFormData', array($this))) {
$this->input('nickname', _('Nickname'), $this->trimmed('nickname'), $this->elementStart('li');
_('1-64 lowercase letters or numbers, '. $this->input('nickname', _('Nickname'), $this->trimmed('nickname'),
'no punctuation or spaces. Required.')); _('1-64 lowercase letters or numbers, '.
$this->elementEnd('li'); 'no punctuation or spaces. Required.'));
$this->elementStart('li'); $this->elementEnd('li');
$this->password('password', _('Password'), $this->elementStart('li');
_('6 or more characters. Required.')); $this->password('password', _('Password'),
$this->elementEnd('li'); _('6 or more characters. Required.'));
$this->elementStart('li'); $this->elementEnd('li');
$this->password('confirm', _('Confirm'), $this->elementStart('li');
_('Same as password above. Required.')); $this->password('confirm', _('Confirm'),
$this->elementEnd('li'); _('Same as password above. Required.'));
$this->elementStart('li'); $this->elementEnd('li');
if ($invite && $invite->address_type == 'email') { $this->elementStart('li');
$this->input('email', _('Email'), $invite->address, if ($this->invite && $this->invite->address_type == 'email') {
_('Used only for updates, announcements, '. $this->input('email', _('Email'), $this->invite->address,
'and password recovery')); _('Used only for updates, announcements, '.
} else { 'and password recovery'));
$this->input('email', _('Email'), $this->trimmed('email'), } else {
_('Used only for updates, announcements, '. $this->input('email', _('Email'), $this->trimmed('email'),
'and password recovery')); _('Used only for updates, announcements, '.
'and password recovery'));
}
$this->elementEnd('li');
$this->elementStart('li');
$this->input('fullname', _('Full name'),
$this->trimmed('fullname'),
_('Longer name, preferably your "real" name'));
$this->elementEnd('li');
$this->elementStart('li');
$this->input('homepage', _('Homepage'),
$this->trimmed('homepage'),
_('URL of your homepage, blog, '.
'or profile on another site'));
$this->elementEnd('li');
$this->elementStart('li');
$this->textarea('bio', _('Bio'),
$this->trimmed('bio'),
_('Describe yourself and your '.
'interests in 140 chars'));
$this->elementEnd('li');
$this->elementStart('li');
$this->input('location', _('Location'),
$this->trimmed('location'),
_('Where you are, like "City, '.
'State (or Region), Country"'));
$this->elementEnd('li');
Event::handle('EndRegistrationFormData', array($this));
$this->elementStart('li', array('id' => 'settings_rememberme'));
$this->checkbox('rememberme', _('Remember me'),
$this->boolean('rememberme'),
_('Automatically login in the future; '.
'not for shared computers!'));
$this->elementEnd('li');
$attrs = array('type' => 'checkbox',
'id' => 'license',
'class' => 'checkbox',
'name' => 'license',
'value' => 'true');
if ($this->boolean('license')) {
$attrs['checked'] = 'checked';
}
$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')),
common_config('license', 'title'), _("Creative Commons Attribution 3.0"));
$this->text(_(' except this private data: password, '.
'email address, IM address, and phone number.'));
$this->elementEnd('label');
$this->elementEnd('li');
} }
$this->elementEnd('li');
$this->elementStart('li');
$this->input('fullname', _('Full name'),
$this->trimmed('fullname'),
_('Longer name, preferably your "real" name'));
$this->elementEnd('li');
$this->elementStart('li');
$this->input('homepage', _('Homepage'),
$this->trimmed('homepage'),
_('URL of your homepage, blog, '.
'or profile on another site'));
$this->elementEnd('li');
$this->elementStart('li');
$this->textarea('bio', _('Bio'),
$this->trimmed('bio'),
_('Describe yourself and your '.
'interests in 140 chars'));
$this->elementEnd('li');
$this->elementStart('li');
$this->input('location', _('Location'),
$this->trimmed('location'),
_('Where you are, like "City, '.
'State (or Region), Country"'));
$this->elementEnd('li');
$this->elementStart('li', array('id' => 'settings_rememberme'));
$this->checkbox('rememberme', _('Remember me'),
$this->boolean('rememberme'),
_('Automatically login in the future; '.
'not for shared computers!'));
$this->elementEnd('li');
$attrs = array('type' => 'checkbox',
'id' => 'license',
'class' => 'checkbox',
'name' => 'license',
'value' => 'true');
if ($this->boolean('license')) {
$attrs['checked'] = 'checked';
}
$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')),
common_config('license', 'title'), _("Creative Commons Attribution 3.0"));
$this->text(_(' except this private data: password, '.
'email address, IM address, and phone number.'));
$this->elementEnd('label');
$this->elementEnd('li');
$this->elementEnd('ul'); $this->elementEnd('ul');
$this->submit('submit', _('Register')); $this->submit('submit', _('Register'));
$this->elementEnd('fieldset'); $this->elementEnd('fieldset');
@ -515,3 +563,4 @@ class RegisterAction extends Action
$nav->show(); $nav->show();
} }
} }

View File

@ -97,7 +97,7 @@ class RemotesubscribeAction extends Action
'class' => 'form_settings', 'class' => 'form_settings',
'action' => common_local_url('remotesubscribe'))); 'action' => common_local_url('remotesubscribe')));
$this->elementStart('fieldset'); $this->elementStart('fieldset');
$this->element('legend', 'Subscribe to a remote user'); $this->element('legend', _('Subscribe to a remote user'));
$this->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
@ -321,8 +321,7 @@ class RemotesubscribeAction extends Action
$result = $fetcher->post($req->get_normalized_http_url(), $result = $fetcher->post($req->get_normalized_http_url(),
$req->to_postdata(), $req->to_postdata(),
array('User-Agent' => 'Laconica/' . LACONICA_VERSION)); array('User-Agent: Laconica/' . LACONICA_VERSION));
if ($result->status != 200) { if ($result->status != 200) {
return null; return null;
} }
@ -334,8 +333,6 @@ class RemotesubscribeAction extends Action
function requestAuthorization($user, $omb, $token, $secret) function requestAuthorization($user, $omb, $token, $secret)
{ {
global $config; # for license URL
$con = omb_oauth_consumer(); $con = omb_oauth_consumer();
$tok = new OAuthToken($token, $secret); $tok = new OAuthToken($token, $secret);
@ -359,7 +356,7 @@ class RemotesubscribeAction extends Action
$req->set_parameter('omb_listenee', $user->uri); $req->set_parameter('omb_listenee', $user->uri);
$req->set_parameter('omb_listenee_profile', common_profile_url($user->nickname)); $req->set_parameter('omb_listenee_profile', common_profile_url($user->nickname));
$req->set_parameter('omb_listenee_nickname', $user->nickname); $req->set_parameter('omb_listenee_nickname', $user->nickname);
$req->set_parameter('omb_listenee_license', $config['license']['url']); $req->set_parameter('omb_listenee_license', common_config('license', 'url'));
$profile = $user->getProfile(); $profile = $user->getProfile();
if (!$profile) { if (!$profile) {
@ -368,16 +365,16 @@ class RemotesubscribeAction extends Action
return; return;
} }
if ($profile->fullname) { if (!is_null($profile->fullname)) {
$req->set_parameter('omb_listenee_fullname', $profile->fullname); $req->set_parameter('omb_listenee_fullname', $profile->fullname);
} }
if ($profile->homepage) { if (!is_null($profile->homepage)) {
$req->set_parameter('omb_listenee_homepage', $profile->homepage); $req->set_parameter('omb_listenee_homepage', $profile->homepage);
} }
if ($profile->bio) { if (!is_null($profile->bio)) {
$req->set_parameter('omb_listenee_bio', $profile->bio); $req->set_parameter('omb_listenee_bio', $profile->bio);
} }
if ($profile->location) { if (!is_null($profile->location)) {
$req->set_parameter('omb_listenee_location', $profile->location); $req->set_parameter('omb_listenee_location', $profile->location);
} }
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
@ -410,7 +407,7 @@ class RemotesubscribeAction extends Action
# Redirect to authorization service # Redirect to authorization service
common_redirect($req->to_url()); common_redirect($req->to_url(), 303);
return; return;
} }
} }

View File

@ -138,6 +138,17 @@ class RepliesAction extends Action
return array(new Feed(Feed::RSS1, $rssurl, $rsstitle)); return array(new Feed(Feed::RSS1, $rssurl, $rsstitle));
} }
/**
* Output document relationship links
*
* @return void
*/
function showRelationshipLinks()
{
$this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME
$this->page, 'replies', array('nickname' => $this->user->nickname));
}
/** /**
* show the personal group nav * show the personal group nav
* *
@ -166,13 +177,37 @@ class RepliesAction extends Action
$nl = new NoticeList($notice, $this); $nl = new NoticeList($notice, $this);
$cnt = $nl->show(); $cnt = $nl->show();
if (0 === $cnt) {
$this->showEmptyListMessage();
}
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'replies', $this->page, 'replies',
array('nickname' => $this->user->nickname)); array('nickname' => $this->user->nickname));
} }
function isReadOnly() function showEmptyListMessage()
{
$message = sprintf(_('This is the timeline showing replies to %s but %s hasn\'t received a notice to his attention yet.'), $this->user->nickname, $this->user->nickname) . ' ';
if (common_logged_in()) {
$current_user = common_current_user();
if ($this->user->id === $current_user->id) {
$message .= _('You can engage other users in a conversation, subscribe to more people or [join groups](%%action.groups%%).');
} else {
$message .= sprintf(_('You can try to [nudge %s](../%s) or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname);
}
}
else {
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname);
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
}
function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -83,7 +83,7 @@ class RepliesrssAction extends Rss10Action
return ($avatar) ? $avatar->url : null; return ($avatar) ? $avatar->url : null;
} }
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -52,7 +52,7 @@ class RequesttokenAction extends Action
* *
* @return boolean false * @return boolean false
*/ */
function isReadOnly() function isReadOnly($args)
{ {
return false; return false;
} }
@ -69,7 +69,7 @@ class RequesttokenAction extends Action
parent::handle($args); parent::handle($args);
try { try {
common_remove_magic_from_request(); common_remove_magic_from_request();
$req = OAuthRequest::from_request(); $req = OAuthRequest::from_request('POST', common_local_url('requesttoken'));
$server = omb_oauth_server(); $server = omb_oauth_server();
$token = $server->fetch_request_token($req); $token = $server->fetch_request_token($req);
print $token; print $token;

View File

@ -58,7 +58,7 @@ class ShowfavoritesAction extends Action
* @return boolean true * @return boolean true
*/ */
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
@ -74,9 +74,9 @@ class ShowfavoritesAction extends Action
function title() function title()
{ {
if ($this->page == 1) { if ($this->page == 1) {
return sprintf(_("%s favorite notices"), $this->user->nickname); return sprintf(_("%s's favorite notices"), $this->user->nickname);
} else { } else {
return sprintf(_("%s favorite notices, page %d"), return sprintf(_("%s's favorite notices, page %d"),
$this->user->nickname, $this->user->nickname,
$this->page); $this->page);
} }
@ -150,6 +150,18 @@ class ShowfavoritesAction extends Action
return array(new Feed(Feed::RSS1, $feedurl, $feedtitle)); return array(new Feed(Feed::RSS1, $feedurl, $feedtitle));
} }
/**
* Output document relationship links
*
* @return void
*/
function showRelationshipLinks()
{
$this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME
$this->page, 'showfavorites', array('nickname' => $this->user->nickname));
}
/** /**
* show the personal group nav * show the personal group nav
* *
@ -162,6 +174,25 @@ class ShowfavoritesAction extends Action
$nav->show(); $nav->show();
} }
function showEmptyListMessage()
{
if (common_logged_in()) {
$current_user = common_current_user();
if ($this->user->id === $current_user->id) {
$message = _('You haven\'t chosen any favorite notices yet. Click the fave button on notices you like to bookmark them for later or shed a spotlight on them.');
} else {
$message = sprintf(_('%s hasn\'t added any notices to his favorites yet. Post something interesting they would add to their favorites :)'), $this->user->nickname);
}
}
else {
$message = sprintf(_('%s hasn\'t added any notices to his favorites yet. Why not [register an account](%%%%action.register%%%%) and then post something interesting they would add to thier favorites :)'), $this->user->nickname);
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
}
/** /**
* Show the content * Show the content
* *
@ -183,9 +214,17 @@ class ShowfavoritesAction extends Action
$nl = new NoticeList($notice, $this); $nl = new NoticeList($notice, $this);
$cnt = $nl->show(); $cnt = $nl->show();
if (0 == $cnt) {
$this->showEmptyListMessage();
}
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'showfavorites', $this->page, 'showfavorites',
array('nickname' => $this->user->nickname)); array('nickname' => $this->user->nickname));
} }
function showPageNotice() {
$this->element('p', 'instructions', _('This is a way to share what you like.'));
}
} }

View File

@ -35,7 +35,7 @@ if (!defined('LACONICA')) {
require_once INSTALLDIR.'/lib/noticelist.php'; require_once INSTALLDIR.'/lib/noticelist.php';
require_once INSTALLDIR.'/lib/feedlist.php'; require_once INSTALLDIR.'/lib/feedlist.php';
define('MEMBERS_PER_SECTION', 81); define('MEMBERS_PER_SECTION', 27);
/** /**
* Group main page * Group main page
@ -60,7 +60,7 @@ class ShowgroupAction extends Action
* @return boolean true * @return boolean true
*/ */
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
@ -73,11 +73,17 @@ class ShowgroupAction extends Action
function title() function title()
{ {
if (!empty($this->group->fullname)) {
$base = $this->group->fullname . ' (' . $this->group->nickname . ')';
} else {
$base = $this->group->nickname;
}
if ($this->page == 1) { if ($this->page == 1) {
return sprintf(_("%s group"), $this->group->nickname); return sprintf(_("%s group"), $base);
} else { } else {
return sprintf(_("%s group, page %d"), return sprintf(_("%s group, page %d"),
$this->group->nickname, $base,
$this->page); $this->page);
} }
} }
@ -275,10 +281,8 @@ class ShowgroupAction extends Action
$cur = common_current_user(); $cur = common_current_user();
if ($cur) { if ($cur) {
if ($cur->isMember($this->group)) { if ($cur->isMember($this->group)) {
if (!$cur->isAdmin($this->group)) { $lf = new LeaveForm($this, $this->group);
$lf = new LeaveForm($this, $this->group); $lf->show();
$lf->show();
}
} else { } else {
$jf = new JoinForm($this, $this->group); $jf = new JoinForm($this, $this->group);
$jf->show(); $jf->show();
@ -307,6 +311,17 @@ class ShowgroupAction extends Action
$this->group->nickname))); $this->group->nickname)));
} }
/**
* Output document relationship links
*
* @return void
*/
function showRelationshipLinks()
{
$this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME
$this->page, 'showgroup', array('nickname' => $this->group->nickname));
}
/** /**
* Fill in the sidebar. * Fill in the sidebar.
* *
@ -346,7 +361,7 @@ class ShowgroupAction extends Action
$this->element('p', null, _('(None)')); $this->element('p', null, _('(None)'));
} }
if ($cnt == MEMBERS_PER_SECTION) { if ($cnt > MEMBERS_PER_SECTION) {
$this->element('a', array('href' => common_local_url('groupmembers', $this->element('a', array('href' => common_local_url('groupmembers',
array('nickname' => $this->group->nickname))), array('nickname' => $this->group->nickname))),
_('All members')); _('All members'));
@ -392,11 +407,18 @@ class ShowgroupAction extends Action
function showAnonymousMessage() function showAnonymousMessage()
{ {
$m = sprintf(_('**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' . if (!(common_config('site','closed') || common_config('site','inviteonly'))) {
'based on the Free Software [Laconica](http://laconi.ca/) tool. Its members share ' . $m = sprintf(_('**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
'short messages about their life and interests. '. 'based on the Free Software [Laconica](http://laconi.ca/) tool. Its members share ' .
'[Join now](%%%%action.register%%%%) to become part of this group and many more! ([Read more](%%%%doc.help%%%%))'), 'short messages about their life and interests. '.
'[Join now](%%%%action.register%%%%) to become part of this group and many more! ([Read more](%%%%doc.help%%%%))'),
$this->group->nickname); $this->group->nickname);
} else {
$m = sprintf(_('**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
'based on the Free Software [Laconica](http://laconi.ca/) tool. Its members share ' .
'short messages about their life and interests. '),
$this->group->nickname);
}
$this->elementStart('div', array('id' => 'anon_notice')); $this->elementStart('div', array('id' => 'anon_notice'));
$this->raw(common_markup_to_html($m)); $this->raw(common_markup_to_html($m));
$this->elementEnd('div'); $this->elementEnd('div');

View File

@ -177,7 +177,7 @@ class ShowmessageAction extends MailboxAction
return ''; return '';
} }
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -106,7 +106,7 @@ class ShownoticeAction extends Action
* @return boolean true * @return boolean true
*/ */
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
@ -177,10 +177,17 @@ class ShownoticeAction extends Action
{ {
parent::handle($args); parent::handle($args);
$this->showPage(); if ($this->notice->is_local == 0) {
if (!empty($this->notice->url)) {
common_redirect($this->notice->url, 301);
} else if (!empty($this->notice->uri) && preg_match('/^https?:/', $this->notice->uri)) {
common_redirect($this->notice->uri, 301);
}
} else {
$this->showPage();
}
} }
/** /**
* Don't show local navigation * Don't show local navigation
* *
@ -191,7 +198,6 @@ class ShownoticeAction extends Action
{ {
} }
/** /**
* Fill the content area of the page * Fill the content area of the page
* *
@ -208,8 +214,6 @@ class ShownoticeAction extends Action
$this->elementEnd('ul'); $this->elementEnd('ul');
} }
/** /**
* Don't show page notice * Don't show page notice
* *
@ -220,7 +224,6 @@ class ShownoticeAction extends Action
{ {
} }
/** /**
* Don't show aside * Don't show aside
* *
@ -230,7 +233,6 @@ class ShownoticeAction extends Action
function showAside() { function showAside() {
} }
/** /**
* Extra <head> content * Extra <head> content
* *

View File

@ -54,67 +54,33 @@ require_once INSTALLDIR.'/lib/feedlist.php';
* @link http://laconi.ca/ * @link http://laconi.ca/
*/ */
class ShowstreamAction extends Action class ShowstreamAction extends ProfileAction
{ {
var $user = null; function isReadOnly($args)
var $page = null;
var $profile = null;
function isReadOnly()
{ {
return true; return true;
} }
function title() function title()
{ {
if (!empty($this->profile->fullname)) {
$base = $this->profile->fullname . ' (' . $this->user->nickname . ') ';
} else {
$base = $this->user->nickname;
}
if (!empty($this->tag)) {
$base .= sprintf(_(' tagged %s'), $this->tag);
}
if ($this->page == 1) { if ($this->page == 1) {
return $this->user->nickname; return $base;
} else { } else {
return sprintf(_("%s, page %d"), return sprintf(_("%s, page %d"),
$this->user->nickname, $base,
$this->page); $this->page);
} }
} }
function prepare($args)
{
parent::prepare($args);
$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);
if ($this->arg('page') && $this->arg('page') != 1) {
$args['page'] = $this->arg['page'];
}
common_redirect(common_local_url('showstream', $args), 301);
return false;
}
$this->user = User::staticGet('nickname', $nickname);
if (!$this->user) {
$this->clientError(_('No such user.'), 404);
return false;
}
$this->profile = $this->user->getProfile();
if (!$this->profile) {
$this->serverError(_('User has no profile.'));
return false;
}
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
common_set_returnto($this->selfUrl());
return true;
}
function handle($args) function handle($args)
{ {
@ -140,16 +106,6 @@ class ShowstreamAction extends Action
$nav->show(); $nav->show();
} }
function showPageTitle()
{
$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() function showPageNoticeBlock()
{ {
return; return;
@ -157,6 +113,15 @@ class ShowstreamAction extends Action
function getFeeds() function getFeeds()
{ {
if (!empty($this->tag)) {
return array(new Feed(Feed::RSS1,
common_local_url('userrss',
array('nickname' => $this->user->nickname,
'tag' => $this->tag)),
sprintf(_('Notice feed for %s tagged %s (RSS 1.0)'),
$this->user->nickname, $this->tag)));
}
return array(new Feed(Feed::RSS1, return array(new Feed(Feed::RSS1,
common_local_url('userrss', common_local_url('userrss',
array('nickname' => $this->user->nickname)), array('nickname' => $this->user->nickname)),
@ -182,6 +147,17 @@ class ShowstreamAction extends Action
sprintf(_('FOAF for %s'), $this->user->nickname))); sprintf(_('FOAF for %s'), $this->user->nickname)));
} }
/**
* Output document relationship links
*
* @return void
*/
function showRelationshipLinks()
{
$this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME
$this->page, 'showstream', array('nickname' => $this->user->nickname));
}
function extraHead() function extraHead()
{ {
// for remote subscriptions etc. // for remote subscriptions etc.
@ -292,11 +268,11 @@ class ShowstreamAction extends Action
$this->elementStart('ul', 'tags xoxo'); $this->elementStart('ul', 'tags xoxo');
foreach ($tags as $tag) { foreach ($tags as $tag) {
$this->elementStart('li'); $this->elementStart('li');
$this->element('span', 'mark_hash', '#'); // Avoid space by using raw output.
$this->element('a', array('rel' => 'tag', $pt = '<span class="mark_hash">#</span><a rel="tag" href="' .
'href' => common_local_url('peopletag', common_local_url('peopletag', array('tag' => $tag)) .
array('tag' => $tag))), '">' . $tag . '</a>';
$tag); $this->raw($pt);
$this->elementEnd('li'); $this->elementEnd('li');
} }
$this->elementEnd('ul'); $this->elementEnd('ul');
@ -376,178 +352,66 @@ class ShowstreamAction extends Action
_('Subscribe')); _('Subscribe'));
} }
function showEmptyListMessage()
{
$message = sprintf(_('This is the timeline for %s but %s hasn\'t posted anything yet.'), $this->user->nickname, $this->user->nickname) . ' ';
if (common_logged_in()) {
$current_user = common_current_user();
if ($this->user->id === $current_user->id) {
$message .= _('Seen anything interesting recently? You haven\'t posted any notices yet, now would be a good time to start :)');
} else {
$message .= sprintf(_('You can try to nudge %s or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%s).'), $this->user->nickname, '@' . $this->user->nickname);
}
}
else {
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname);
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
}
function showNotices() function showNotices()
{ {
$notice = $this->user->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); $notice = empty($this->tag)
? $this->user->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1)
: $this->user->getTaggedNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1, 0, 0, null, $this->tag);
$pnl = new ProfileNoticeList($notice, $this); $pnl = new ProfileNoticeList($notice, $this);
$cnt = $pnl->show(); $cnt = $pnl->show();
if (0 == $cnt) {
$this->showEmptyListMessage();
}
$this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page, $this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page,
'showstream', array('nickname' => $this->user->nickname)); 'showstream', array('nickname' => $this->user->nickname));
} }
function showSections()
{
$this->showSubscriptions();
$this->showSubscribers();
$this->showGroups();
$this->showStatistics();
$cloud = new PersonalTagCloudSection($this, $this->user);
$cloud->show();
}
function showSubscriptions()
{
$profile = $this->user->getSubscriptions(0, PROFILES_PER_MINILIST + 1);
$this->elementStart('div', array('id' => 'entity_subscriptions',
'class' => 'section'));
$this->element('h2', null, _('Subscriptions'));
if ($profile) {
$pml = new ProfileMiniList($profile, $this->user, $this);
$cnt = $pml->show();
if ($cnt == 0) {
$this->element('p', null, _('(None)'));
}
}
if ($cnt > PROFILES_PER_MINILIST) {
$this->elementStart('p');
$this->element('a', array('href' => common_local_url('subscriptions',
array('nickname' => $this->profile->nickname)),
'class' => 'more'),
_('All subscriptions'));
$this->elementEnd('p');
}
$this->elementEnd('div');
}
function showSubscribers()
{
$profile = $this->user->getSubscribers(0, PROFILES_PER_MINILIST + 1);
$this->elementStart('div', array('id' => 'entity_subscribers',
'class' => 'section'));
$this->element('h2', null, _('Subscribers'));
if ($profile) {
$pml = new ProfileMiniList($profile, $this->user, $this);
$cnt = $pml->show();
if ($cnt == 0) {
$this->element('p', null, _('(None)'));
}
}
if ($cnt > PROFILES_PER_MINILIST) {
$this->elementStart('p');
$this->element('a', array('href' => common_local_url('subscribers',
array('nickname' => $this->profile->nickname)),
'class' => 'more'),
_('All subscribers'));
$this->elementEnd('p');
}
$this->elementEnd('div');
}
function showStatistics()
{
// XXX: WORM cache this
$subs = new Subscription();
$subs->subscriber = $this->profile->id;
$subs_count = (int) $subs->count() - 1;
$subbed = new Subscription();
$subbed->subscribed = $this->profile->id;
$subbed_count = (int) $subbed->count() - 1;
$notices = new Notice();
$notices->profile_id = $this->profile->id;
$notice_count = (int) $notices->count();
$this->elementStart('div', array('id' => 'entity_statistics',
'class' => 'section'));
$this->element('h2', null, _('Statistics'));
// Other stats...?
$this->elementStart('dl', 'entity_member-since');
$this->element('dt', null, _('Member since'));
$this->element('dd', null, date('j M Y',
strtotime($this->profile->created)));
$this->elementEnd('dl');
$this->elementStart('dl', 'entity_subscriptions');
$this->elementStart('dt');
$this->element('a', array('href' => common_local_url('subscriptions',
array('nickname' => $this->profile->nickname))),
_('Subscriptions'));
$this->elementEnd('dt');
$this->element('dd', null, (is_int($subs_count)) ? $subs_count : '0');
$this->elementEnd('dl');
$this->elementStart('dl', 'entity_subscribers');
$this->elementStart('dt');
$this->element('a', array('href' => common_local_url('subscribers',
array('nickname' => $this->profile->nickname))),
_('Subscribers'));
$this->elementEnd('dt');
$this->element('dd', 'subscribers', (is_int($subbed_count)) ? $subbed_count : '0');
$this->elementEnd('dl');
$this->elementStart('dl', 'entity_notices');
$this->element('dt', null, _('Notices'));
$this->element('dd', null, (is_int($notice_count)) ? $notice_count : '0');
$this->elementEnd('dl');
$this->elementEnd('div');
}
function showGroups()
{
$groups = $this->user->getGroups(0, GROUPS_PER_MINILIST + 1);
$this->elementStart('div', array('id' => 'entity_groups',
'class' => 'section'));
$this->element('h2', null, _('Groups'));
if ($groups) {
$gml = new GroupMiniList($groups, $this->user, $this);
$cnt = $gml->show();
if ($cnt == 0) {
$this->element('p', null, _('(None)'));
}
}
if ($cnt > GROUPS_PER_MINILIST) {
$this->elementStart('p');
$this->element('a', array('href' => common_local_url('usergroups',
array('nickname' => $this->profile->nickname)),
'class' => 'more'),
_('All groups'));
$this->elementEnd('p');
}
$this->elementEnd('div');
}
function showAnonymousMessage() function showAnonymousMessage()
{ {
$m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' . if (!(common_config('site','closed') || common_config('site','inviteonly'))) {
'based on the Free Software [Laconica](http://laconi.ca/) tool. ' . $m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
'[Join now](%%%%action.register%%%%) to follow **%s**\'s notices and many more! ([Read more](%%%%doc.help%%%%))'), 'based on the Free Software [Laconica](http://laconi.ca/) tool. ' .
$this->user->nickname, $this->user->nickname); '[Join now](%%%%action.register%%%%) to follow **%s**\'s notices and many more! ([Read more](%%%%doc.help%%%%))'),
$this->user->nickname, $this->user->nickname);
} else {
$m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
'based on the Free Software [Laconica](http://laconi.ca/) tool. '),
$this->user->nickname, $this->user->nickname);
}
$this->elementStart('div', array('id' => 'anon_notice')); $this->elementStart('div', array('id' => 'anon_notice'));
$this->raw(common_markup_to_html($m)); $this->raw(common_markup_to_html($m));
$this->elementEnd('div'); $this->elementEnd('div');
} }
function showSections()
{
parent::showSections();
$cloud = new PersonalTagCloudSection($this, $this->user);
$cloud->show();
}
} }
// We don't show the author for a profile, since we already know who it is! // We don't show the author for a profile, since we already know who it is!

View File

@ -488,7 +488,8 @@ class SmssettingsAction extends ConnectSettingsAction
} }
common_redirect(common_local_url('confirmaddress', common_redirect(common_local_url('confirmaddress',
array('code' => $code))); array('code' => $code)),
303);
} }
/** /**

View File

@ -85,7 +85,8 @@ class SubeditAction extends Action
} }
common_redirect(common_local_url('subscriptions', common_redirect(common_local_url('subscriptions',
array('nickname' => $cur->nickname))); array('nickname' => $cur->nickname)),
303);
} }
} }
} }

View File

@ -75,7 +75,8 @@ class SubscribeAction extends Action
$this->elementEnd('html'); $this->elementEnd('html');
} else { } else {
common_redirect(common_local_url('subscriptions', array('nickname' => common_redirect(common_local_url('subscriptions', array('nickname' =>
$user->nickname))); $user->nickname)),
303);
} }
} }
} }

View File

@ -88,6 +88,9 @@ class SubscribersAction extends GalleryAction
if ($subscribers) { if ($subscribers) {
$subscribers_list = new SubscribersList($subscribers, $this->user, $this); $subscribers_list = new SubscribersList($subscribers, $this->user, $this);
$cnt = $subscribers_list->show(); $cnt = $subscribers_list->show();
if (0 == $cnt) {
$this->showEmptyListMessage();
}
} }
$subscribers->free(); $subscribers->free();
@ -96,6 +99,35 @@ class SubscribersAction extends GalleryAction
$this->page, 'subscribers', $this->page, 'subscribers',
array('nickname' => $this->user->nickname)); array('nickname' => $this->user->nickname));
} }
function showEmptyListMessage()
{
if (common_logged_in()) {
$current_user = common_current_user();
if ($this->user->id === $current_user->id) {
$message = _('You have no subscribers. Try subscribing to people you know and they might return the favor');
} else {
$message = sprintf(_('%s has no subscribers. Want to be the first?'), $this->user->nickname);
}
}
else {
$message = sprintf(_('%s has no subscribers. Why not [register an account](%%%%action.register%%%%) and be the first?'), $this->user->nickname);
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
}
function showSections()
{
parent::showSections();
$cloud = new SubscribersPeopleTagCloudSection($this);
$cloud->show();
$cloud2 = new SubscribersPeopleSelfTagCloudSection($this);
$cloud2->show();
}
} }
class SubscribersList extends ProfileList class SubscribersList extends ProfileList
@ -108,7 +140,7 @@ class SubscribersList extends ProfileList
$bf->show(); $bf->show();
} }
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -95,6 +95,9 @@ class SubscriptionsAction extends GalleryAction
if ($subscriptions) { if ($subscriptions) {
$subscriptions_list = new SubscriptionsList($subscriptions, $this->user, $this); $subscriptions_list = new SubscriptionsList($subscriptions, $this->user, $this);
$cnt = $subscriptions_list->show(); $cnt = $subscriptions_list->show();
if (0 == $cnt) {
$this->showEmptyListMessage();
}
} }
$subscriptions->free(); $subscriptions->free();
@ -103,6 +106,35 @@ class SubscriptionsAction extends GalleryAction
$this->page, 'subscriptions', $this->page, 'subscriptions',
array('nickname' => $this->user->nickname)); array('nickname' => $this->user->nickname));
} }
function showEmptyListMessage()
{
if (common_logged_in()) {
$current_user = common_current_user();
if ($this->user->id === $current_user->id) {
$message = _('You\'re not listening to anyone\'s notices right now, try subscribing to people you know. Try [people search](%%action.peoplesearch%%), look for members in groups you\'re interested in and in our [featured users](%%action.featured%%). If you\'re a [Twitter user](%%action.twittersettings%%), you can automatically subscribe to people you already follow there.');
} else {
$message = sprintf(_('%s is not listening to anyone.'), $this->user->nickname);
}
}
else {
$message = sprintf(_('%s is not listening to anyone.'), $this->user->nickname);
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
}
function showSections()
{
parent::showSections();
$cloud = new SubscriptionsPeopleTagCloudSection($this);
$cloud->show();
$cloud2 = new SubscriptionsPeopleSelfTagCloudSection($this);
$cloud2->show();
}
} }
class SubscriptionsList extends ProfileList class SubscriptionsList extends ProfileList
@ -117,7 +149,7 @@ class SubscriptionsList extends ProfileList
$this->out->elementStart('form', array('id' => 'subedit-' . $profile->id, $this->out->elementStart('form', array('id' => 'subedit-' . $profile->id,
'method' => 'post', 'method' => 'post',
'class' => 'form_subcription_edit', 'class' => 'form_subscription_edit',
'action' => common_local_url('subedit'))); 'action' => common_local_url('subedit')));
$this->out->hidden('token', common_session_token()); $this->out->hidden('token', common_session_token());
$this->out->hidden('profile', $profile->id); $this->out->hidden('profile', $profile->id);

View File

@ -45,7 +45,7 @@ class SupAction extends Action
function availablePeriods() function availablePeriods()
{ {
static $periods = array(86400, 43200, 21600, 7200, static $periods = array(86400, 43200, 21600, 7200,
3600, 1800, 600, 300, 120, 3600, 1800, 600, 300, 120,
60, 30, 15); 60, 30, 15);
$available = array(); $available = array();
foreach ($periods as $period) { foreach ($periods as $period) {
@ -65,7 +65,9 @@ class SupAction extends Action
$notice->query('SELECT profile_id, max(id) AS max_id ' . $notice->query('SELECT profile_id, max(id) AS max_id ' .
'FROM notice ' . 'FROM notice ' .
'WHERE created > (now() - ' . $seconds . ') ' . ((common_config('db','type') == 'pgsql') ?
'WHERE extract(epoch from created) > (extract(epoch from now()) - ' . $seconds . ') ' :
'WHERE created > (now() - ' . $seconds . ') ' ) .
'GROUP BY profile_id'); 'GROUP BY profile_id');
$updates = array(); $updates = array();
@ -77,7 +79,7 @@ class SupAction extends Action
return $updates; return $updates;
} }
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -33,7 +33,9 @@ class TagAction extends Action
} }
if ($this->tag != $taginput) { if ($this->tag != $taginput) {
common_redirect(common_local_url('tag', array('tag' => $this->tag))); common_redirect(common_local_url('tag', array('tag' => $this->tag)),
301);
return false;
} }
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
@ -43,6 +45,14 @@ class TagAction extends Action
return true; return true;
} }
function showSections()
{
$pop = new PopularNoticeSection($this);
$pop->show();
$freqatt = new FrequentAttachmentSection($this);
$freqatt->show();
}
function title() function title()
{ {
if ($this->page == 1) { if ($this->page == 1) {
@ -68,6 +78,17 @@ class TagAction extends Action
sprintf(_('Feed for tag %s'), $this->tag))); sprintf(_('Feed for tag %s'), $this->tag)));
} }
/**
* Output document relationship links
*
* @return void
*/
function showRelationshipLinks()
{
$this->sequenceRelationships($this->page > 1, $this->count > NOTICES_PER_PAGE, // FIXME
$this->page, 'tag', array('tag' => $this->tag));
}
function showPageNotice() function showPageNotice()
{ {
return sprintf(_('Messages tagged "%s", most recent first'), $this->tag); return sprintf(_('Messages tagged "%s", most recent first'), $this->tag);
@ -84,4 +105,9 @@ class TagAction extends Action
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'tag', array('tag' => $this->tag)); $this->page, 'tag', array('tag' => $this->tag));
} }
function isReadOnly($args)
{
return true;
}
} }

View File

@ -135,7 +135,8 @@ class TagotherAction extends Action
'id' => 'form_tag_user', 'id' => 'form_tag_user',
'class' => 'form_settings', 'class' => 'form_settings',
'name' => 'tagother', 'name' => 'tagother',
'action' => $this->selfUrl())); 'action' => common_local_url('tagother', array('id' => $this->profile->id))));
$this->elementStart('fieldset'); $this->elementStart('fieldset');
$this->element('legend', null, _('Tag user')); $this->element('legend', null, _('Tag user'));
$this->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
@ -220,7 +221,8 @@ class TagotherAction extends Action
$this->elementEnd('html'); $this->elementEnd('html');
} else { } else {
common_redirect(common_local_url($action, array('nickname' => common_redirect(common_local_url($action, array('nickname' =>
$user->nickname))); $user->nickname)),
303);
} }
} }

View File

@ -65,7 +65,7 @@ class TagrssAction extends Rss10Action
return $c; return $c;
} }
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -23,23 +23,24 @@ require_once(INSTALLDIR.'/lib/twitterapi.php');
class TwitapiaccountAction extends TwitterapiAction class TwitapiaccountAction extends TwitterapiAction
{ {
function verify_credentials($args, $apidata)
function verify_credentials($args, $apidata)
{ {
parent::handle($args);
if ($apidata['content-type'] == 'xml') { switch ($apidata['content-type']) {
header('Content-Type: application/xml; charset=utf-8'); case 'xml':
print '<authorized>true</authorized>'; case 'json':
} elseif ($apidata['content-type'] == 'json') { $action_obj = new TwitapiusersAction();
header('Content-Type: application/json; charset=utf-8'); $action_obj->prepare($args);
print '{"authorized":true}'; call_user_func(array($action_obj, 'show'), $args, $apidata);
} else { break;
common_user_error(_('API method not found!'), $code=404); default:
} header('Content-Type: text/html; charset=utf-8');
print 'Authorized';
}
}
} function end_session($args, $apidata)
function end_session($args, $apidata)
{ {
parent::handle($args); parent::handle($args);
$this->serverError(_('API method under construction.'), $code=501); $this->serverError(_('API method under construction.'), $code=501);

View File

@ -38,7 +38,6 @@ class Twitapidirect_messagesAction extends TwitterapiAction
function show_messages($args, $apidata, $type) function show_messages($args, $apidata, $type)
{ {
$user = $apidata['user']; $user = $apidata['user'];
$count = $this->arg('count'); $count = $this->arg('count');
@ -102,7 +101,17 @@ class Twitapidirect_messagesAction extends TwitterapiAction
$this->show_rss_dmsgs($message, $title, $link, $subtitle); $this->show_rss_dmsgs($message, $title, $link, $subtitle);
break; break;
case 'atom': case 'atom':
$this->show_atom_dmsgs($message, $title, $link, $subtitle); $selfuri = common_root_url() . 'api/direct_messages';
$selfuri .= ($type == 'received') ? '.atom' : '/sent.atom';
$taguribase = common_config('integration', 'taguri');
if ($type == 'sent') {
$id = "tag:$taguribase:SentDirectMessages:" . $user->id;
} else {
$id = "tag:$taguribase:DirectMessages:" . $user->id;
}
$this->show_atom_dmsgs($message, $title, $link, $subtitle, $selfuri, $id);
break; break;
case 'json': case 'json':
$this->show_json_dmsgs($message); $this->show_json_dmsgs($message);
@ -190,7 +199,7 @@ class Twitapidirect_messagesAction extends TwitterapiAction
$this->init_document('xml'); $this->init_document('xml');
$this->elementStart('direct-messages', array('type' => 'array')); $this->elementStart('direct-messages', array('type' => 'array'));
if (is_array($messages)) { if (is_array($message)) {
foreach ($message as $m) { foreach ($message as $m) {
$twitter_dm = $this->twitter_dmsg_array($m); $twitter_dm = $this->twitter_dmsg_array($m);
$this->show_twitter_xml_dmsg($twitter_dm); $this->show_twitter_xml_dmsg($twitter_dm);
@ -261,16 +270,17 @@ class Twitapidirect_messagesAction extends TwitterapiAction
} }
function show_atom_dmsgs($message, $title, $link, $subtitle) function show_atom_dmsgs($message, $title, $link, $subtitle, $selfuri, $id)
{ {
$this->init_document('atom'); $this->init_document('atom');
$this->element('title', null, $title); $this->element('title', null, $title);
$siteserver = common_config('site', 'server'); $this->element('id', null, $id);
$this->element('id', null, "tag:$siteserver,2008:DirectMessage");
$this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null); $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
$this->element('updated', null, common_date_iso8601(strftime('%c'))); $this->element('link', array('href' => $selfuri, 'rel' => 'self',
'type' => 'application/atom+xml'), null);
$this->element('updated', null, common_date_iso8601('now'));
$this->element('subtitle', null, $subtitle); $this->element('subtitle', null, $subtitle);
if (is_array($message)) { if (is_array($message)) {

View File

@ -61,10 +61,9 @@ class TwitapifavoritesAction extends TwitterapiAction
} }
$sitename = common_config('site', 'name'); $sitename = common_config('site', 'name');
$siteserver = common_config('site', 'server');
$title = sprintf(_('%s / Favorites from %s'), $sitename, $user->nickname); $title = sprintf(_('%s / Favorites from %s'), $sitename, $user->nickname);
$id = "tag:$siteserver:favorites:".$user->id; $taguribase = common_config('integration', 'taguri');
$id = "tag:$taguribase:Favorites:".$user->id;
$link = common_local_url('favorites', array('nickname' => $user->nickname)); $link = common_local_url('favorites', array('nickname' => $user->nickname));
$subtitle = sprintf(_('%s updates favorited by %s / %s.'), $sitename, $profile->getBestName(), $user->nickname); $subtitle = sprintf(_('%s updates favorited by %s / %s.'), $sitename, $profile->getBestName(), $user->nickname);
@ -76,7 +75,14 @@ class TwitapifavoritesAction extends TwitterapiAction
$this->show_rss_timeline($notice, $title, $link, $subtitle); $this->show_rss_timeline($notice, $title, $link, $subtitle);
break; break;
case 'atom': case 'atom':
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle); if (isset($apidata['api_arg'])) {
$selfuri = $selfuri = common_root_url() .
'api/favorites/' . $apidata['api_arg'] . '.atom';
} else {
$selfuri = $selfuri = common_root_url() .
'api/favorites.atom';
}
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle, null, $selfuri);
break; break;
case 'json': case 'json':
$this->show_json_timeline($notice); $this->show_json_timeline($notice);

View File

@ -133,11 +133,7 @@ class TwitapifriendshipsAction extends TwitterapiAction
return; return;
} }
if ($user_a->isSubscribed($user_b)) { $result = $user_a->isSubscribed($user_b);
$result = 'true';
} else {
$result = 'false';
}
switch ($apidata['content-type']) { switch ($apidata['content-type']) {
case 'xml': case 'xml':

View File

@ -0,0 +1,377 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* Action for showing Twitter-like Atom search results
*
* 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 for outputting search results in Twitter compatible Atom
* format.
*
* TODO: abstract Atom stuff into a ruseable base class like
* RSS10Action.
*
* @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 TwitapisearchatomAction extends TwitterapiAction
{
var $cnt;
var $query;
var $lang;
var $rpp;
var $page;
var $since_id;
var $geocode;
/**
* Constructor
*
* Just wraps the Action constructor.
*
* @param string $output URI to output to, default = stdout
* @param boolean $indent Whether to indent output, default true
*
* @see Action::__construct
*/
function __construct($output='php://output', $indent=true)
{
parent::__construct($output, $indent);
}
/**
* Do we need to write to the database?
*
* @return boolean true
*/
function isReadonly()
{
return true;
}
/**
* Read arguments and initialize members
*
* @param array $args Arguments from $_REQUEST
*
* @return boolean success
*
*/
function prepare($args)
{
parent::prepare($args);
$this->query = $this->trimmed('q');
$this->lang = $this->trimmed('lang');
$this->rpp = $this->trimmed('rpp');
if (!$this->rpp) {
$this->rpp = 15;
}
if ($this->rpp > 100) {
$this->rpp = 100;
}
$this->page = $this->trimmed('page');
if (!$this->page) {
$this->page = 1;
}
// TODO: Suppport since_id -- we need to tweak the backend
// Search classes to support it.
$this->since_id = $this->trimmed('since_id');
$this->geocode = $this->trimmed('geocode');
// TODO: Also, language and geocode
return true;
}
/**
* Handle a request
*
* @param array $args Arguments from $_REQUEST
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$this->showAtom();
}
/**
* Get the notices to output as results. This also sets some class
* attrs so we can use them to calculate pagination, and output
* since_id and max_id.
*
* @return array an array of Notice objects sorted in reverse chron
*/
function getNotices()
{
// TODO: Support search operators like from: and to:, boolean, etc.
$notices = array();
$notice = new Notice();
// lcase it for comparison
$q = strtolower($this->query);
$search_engine = $notice->getSearchEngine('identica_notices');
$search_engine->set_sort_mode('chron');
$search_engine->limit(($this->page - 1) * $this->rpp,
$this->rpp + 1, true);
$search_engine->query($q);
$this->cnt = $notice->find();
$cnt = 0;
while ($notice->fetch()) {
++$cnt;
if (!$this->max_id) {
$this->max_id = $notice->id;
}
if ($cnt > $this->rpp) {
break;
}
$notices[] = clone($notice);
}
return $notices;
}
/**
* Output search results as an Atom feed
*
* @return void
*/
function showAtom()
{
$notices = $this->getNotices();
$this->initAtom();
$this->showFeed();
foreach ($notices as $n) {
$this->showEntry($n);
}
$this->endAtom();
}
/**
* Show feed specific Atom elements
*
* @return void
*/
function showFeed()
{
// TODO: A9 OpenSearch stuff like search.twitter.com?
$server = common_config('site', 'server');
$sitename = common_config('site', 'name');
// XXX: Use xmlns:laconica instead?
$this->elementStart('feed',
array('xmlns' => 'http://www.w3.org/2005/Atom',
// XXX: xmlns:twitter causes Atom validation to fail
// It's used for the source attr on notices
'xmlns:twitter' => 'http://api.twitter.com/',
'xml:lang' => 'en-US')); // XXX Other locales ?
$taguribase = common_config('integration', 'taguri');
$this->element('id', null, "tag:$taguribase:search/$server");
$site_uri = common_path(false);
$search_uri = $site_uri . 'api/search.atom?q=' . urlencode($this->query);
if ($this->rpp != 15) {
$search_uri .= '&rpp=' . $this->rpp;
}
// FIXME: this alternate link is not quite right because our
// web-based notice search doesn't support a rpp (responses per
// page) param yet
$this->element('link', array('type' => 'text/html',
'rel' => 'alternate',
'href' => $site_uri . 'search/notice?q=' .
urlencode($this->query)));
// self link
$self_uri = $search_uri;
$self_uri .= ($this->page > 1) ? '&page=' . $this->page : '';
$this->element('link', array('type' => 'application/atom+xml',
'rel' => 'self',
'href' => $self_uri));
$this->element('title', null, "$this->query - $sitename Search");
$this->element('updated', null, common_date_iso8601('now'));
// XXX: The below "rel" links are not valid Atom, but it's what
// Twitter does...
// refresh link
$refresh_uri = $search_uri . "&since_id=" . $this->max_id;
$this->element('link', array('type' => 'application/atom+xml',
'rel' => 'refresh',
'href' => $refresh_uri));
// pagination links
if ($this->cnt > $this->rpp) {
$next_uri = $search_uri . "&max_id=" . $this->max_id .
'&page=' . ($this->page + 1);
$this->element('link', array('type' => 'application/atom+xml',
'rel' => 'next',
'href' => $next_uri));
}
if ($this->page > 1) {
$previous_uri = $search_uri . "&max_id=" . $this->max_id .
'&page=' . ($this->page - 1);
$this->element('link', array('type' => 'application/atom+xml',
'rel' => 'previous',
'href' => $previous_uri));
}
}
/**
* Build an Atom entry similar to search.twitter.com's based on
* a given notice
*
* @param Notice $notice the notice to use
*
* @return void
*/
function showEntry($notice)
{
$server = common_config('site', 'server');
$profile = $notice->getProfile();
$nurl = common_local_url('shownotice', array('notice' => $notice->id));
$this->elementStart('entry');
$taguribase = common_config('integration', 'taguri');
$this->element('id', null, "tag:$taguribase:$notice->id");
$this->element('published', null, common_date_w3dtf($notice->created));
$this->element('link', array('type' => 'text/html',
'rel' => 'alternate',
'href' => $nurl));
$this->element('title', null, common_xml_safe_str(trim($notice->content)));
$this->element('content', array('type' => 'html'), $notice->rendered);
$this->element('updated', null, common_date_w3dtf($notice->created));
$this->element('link', array('type' => 'image/png',
// XXX: Twitter uses rel="image" (not valid)
'rel' => 'related',
'href' => $profile->avatarUrl()));
// TODO: Here is where we'd put in a link to an atom feed for threads
$this->element("twitter:source", null,
htmlentities($this->source_link($notice->source)));
$this->elementStart('author');
$name = $profile->nickname;
if ($profile->fullname) {
$name .= ' (' . $profile->fullname . ')';
}
$this->element('name', null, $name);
$this->element('uri', null, common_profile_uri($profile));
$this->elementEnd('author');
$this->elementEnd('entry');
}
/**
* Initialize the Atom output, send headers
*
* @return void
*/
function initAtom()
{
header('Content-Type: application/atom+xml; charset=utf-8');
$this->startXml();
}
/**
* End the Atom feed
*
* @return void
*/
function endAtom()
{
$this->elementEnd('feed');
}
}

View File

@ -0,0 +1,149 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* Action for showing Twitter-like JSON search results
*
* 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';
require_once INSTALLDIR.'/lib/jsonsearchresultslist.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 TwitapisearchjsonAction extends TwitterapiAction
{
var $query;
var $lang;
var $rpp;
var $page;
var $since_id;
var $limit;
var $geocode;
/**
* Initialization.
*
* @param array $args Web and URL arguments
*
* @return boolean true if nothing goes wrong
*/
function prepare($args)
{
parent::prepare($args);
$this->query = $this->trimmed('q');
$this->lang = $this->trimmed('lang');
$this->rpp = $this->trimmed('rpp');
if (!$this->rpp) {
$this->rpp = 15;
}
if ($this->rpp > 100) {
$this->rpp = 100;
}
$this->page = $this->trimmed('page');
if (!$this->page) {
$this->page = 1;
}
$this->since_id = $this->trimmed('since_id');
$this->geocode = $this->trimmed('geocode');
return true;
}
/**
* Handle a request
*
* @param array $args Arguments from $_REQUEST
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$this->showResults();
}
/**
* Show search results
*
* @return void
*/
function showResults()
{
// TODO: Support search operators like from: and to:, boolean, etc.
$notice = new Notice();
// lcase it for comparison
$q = strtolower($this->query);
$search_engine = $notice->getSearchEngine('identica_notices');
$search_engine->set_sort_mode('chron');
$search_engine->limit(($this->page - 1) * $this->rpp, $this->rpp + 1, true);
$search_engine->query($q);
$cnt = $notice->find();
// TODO: since_id, lang, geocode
$results = new JSONSearchResultsList($notice, $q, $this->rpp, $this->page);
$this->init_document('json');
$results->show();
$this->end_document('json');
}
/**
* Do we need to write to the database?
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
}

View File

@ -29,10 +29,12 @@ class TwitapistatusesAction extends TwitterapiAction
parent::handle($args); parent::handle($args);
$sitename = common_config('site', 'name'); $sitename = common_config('site', 'name');
$siteserver = common_config('site', 'server');
$title = sprintf(_("%s public timeline"), $sitename); $title = sprintf(_("%s public timeline"), $sitename);
$id = "tag:$siteserver:Statuses";
$taguribase = common_config('integration', 'taguri');
$id = "tag:$taguribase:PublicTimeline";
$link = common_root_url(); $link = common_root_url();
$subtitle = sprintf(_("%s updates from everyone!"), $sitename); $subtitle = sprintf(_("%s updates from everyone!"), $sitename);
// Number of public statuses to return by default -- Twitter sends 20 // Number of public statuses to return by default -- Twitter sends 20
@ -70,7 +72,8 @@ class TwitapistatusesAction extends TwitterapiAction
$this->show_rss_timeline($notice, $title, $link, $subtitle); $this->show_rss_timeline($notice, $title, $link, $subtitle);
break; break;
case 'atom': case 'atom':
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle); $selfuri = common_root_url() . 'api/statuses/public_timeline.atom';
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle, null, $selfuri);
break; break;
case 'json': case 'json':
$this->show_json_timeline($notice); $this->show_json_timeline($notice);
@ -114,17 +117,19 @@ class TwitapistatusesAction extends TwitterapiAction
} }
$since = strtotime($this->arg('since')); $since = strtotime($this->arg('since'));
$user = $this->get_user($apidata['api_arg'], $apidata);
$user = $this->get_user(null, $apidata);
$this->auth_user = $user; $this->auth_user = $user;
if (empty($user)) {
$this->clientError(_('No such user!'), 404, $apidata['content-type']);
return;
}
$profile = $user->getProfile(); $profile = $user->getProfile();
$sitename = common_config('site', 'name'); $sitename = common_config('site', 'name');
$siteserver = common_config('site', 'server');
$title = sprintf(_("%s and friends"), $user->nickname); $title = sprintf(_("%s and friends"), $user->nickname);
$id = "tag:$siteserver:friends:" . $user->id; $taguribase = common_config('integration', 'taguri');
$id = "tag:$taguribase:FriendsTimeline:" . $user->id;
$link = common_local_url('all', array('nickname' => $user->nickname)); $link = common_local_url('all', array('nickname' => $user->nickname));
$subtitle = sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, $sitename); $subtitle = sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, $sitename);
@ -138,7 +143,14 @@ class TwitapistatusesAction extends TwitterapiAction
$this->show_rss_timeline($notice, $title, $link, $subtitle); $this->show_rss_timeline($notice, $title, $link, $subtitle);
break; break;
case 'atom': case 'atom':
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle); if (isset($apidata['api_arg'])) {
$selfuri = common_root_url() .
'api/statuses/friends_timeline/' . $apidata['api_arg'] . '.atom';
} else {
$selfuri = common_root_url() .
'api/statuses/friends_timeline.atom';
}
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle, null, $selfuri);
break; break;
case 'json': case 'json':
$this->show_json_timeline($notice); $this->show_json_timeline($notice);
@ -194,17 +206,16 @@ class TwitapistatusesAction extends TwitterapiAction
$since = strtotime($this->arg('since')); $since = strtotime($this->arg('since'));
$sitename = common_config('site', 'name'); $sitename = common_config('site', 'name');
$siteserver = common_config('site', 'server');
$title = sprintf(_("%s timeline"), $user->nickname); $title = sprintf(_("%s timeline"), $user->nickname);
$id = "tag:$siteserver:user:".$user->id; $taguribase = common_config('integration', 'taguri');
$id = "tag:$taguribase:UserTimeline:".$user->id;
$link = common_local_url('showstream', array('nickname' => $user->nickname)); $link = common_local_url('showstream', array('nickname' => $user->nickname));
$subtitle = sprintf(_('Updates from %1$s on %2$s!'), $user->nickname, $sitename); $subtitle = sprintf(_('Updates from %1$s on %2$s!'), $user->nickname, $sitename);
# FriendFeed's SUP protocol # FriendFeed's SUP protocol
# Also added RSS and Atom feeds # 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); header('X-SUP-ID: '.$suplink);
# XXX: since # XXX: since
@ -219,7 +230,14 @@ class TwitapistatusesAction extends TwitterapiAction
$this->show_rss_timeline($notice, $title, $link, $subtitle, $suplink); $this->show_rss_timeline($notice, $title, $link, $subtitle, $suplink);
break; break;
case 'atom': case 'atom':
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle, $suplink); if (isset($apidata['api_arg'])) {
$selfuri = common_root_url() .
'api/statuses/user_timeline/' . $apidata['api_arg'] . '.atom';
} else {
$selfuri = common_root_url() .
'api/statuses/user_timeline.atom';
}
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle, $suplink, $selfuri);
break; break;
case 'json': case 'json':
$this->show_json_timeline($notice); $this->show_json_timeline($notice);
@ -326,7 +344,7 @@ class TwitapistatusesAction extends TwitterapiAction
$this->show($args, $apidata); $this->show($args, $apidata);
} }
function replies($args, $apidata) function mentions($args, $apidata)
{ {
parent::handle($args); parent::handle($args);
@ -337,17 +355,18 @@ class TwitapistatusesAction extends TwitterapiAction
$since_id = $this->arg('since_id'); $since_id = $this->arg('since_id');
$before_id = $this->arg('before_id'); $before_id = $this->arg('before_id');
$user = $this->get_user($apidata['api_arg'], $apidata);
$this->auth_user = $apidata['user']; $this->auth_user = $apidata['user'];
$user = $this->auth_user;
$profile = $user->getProfile(); $profile = $user->getProfile();
$sitename = common_config('site', 'name'); $sitename = common_config('site', 'name');
$siteserver = common_config('site', 'server'); $title = sprintf(_('%1$s / Updates mentioning %2$s'),
$sitename, $user->nickname);
$title = sprintf(_('%1$s / Updates replying to %2$s'), $sitename, $user->nickname); $taguribase = common_config('integration', 'taguri');
$id = "tag:$siteserver:replies:".$user->id; $id = "tag:$taguribase:Mentions:".$user->id;
$link = common_local_url('replies', array('nickname' => $user->nickname)); $link = common_local_url('replies', array('nickname' => $user->nickname));
$subtitle = sprintf(_('%1$s updates that reply to updates from %2$s / %3$s.'), $sitename, $user->nickname, $profile->getBestName()); $subtitle = sprintf(_('%1$s updates that reply to updates from %2$s / %3$s.'),
$sitename, $user->nickname, $profile->getBestName());
if (!$page) { if (!$page) {
$page = 1; $page = 1;
@ -368,7 +387,8 @@ class TwitapistatusesAction extends TwitterapiAction
$since = strtotime($this->arg('since')); $since = strtotime($this->arg('since'));
$notice = $user->getReplies((($page-1)*20), $count, $since_id, $before_id, $since); $notice = $user->getReplies((($page-1)*20),
$count, $since_id, $before_id, $since);
$notices = array(); $notices = array();
while ($notice->fetch()) { while ($notice->fetch()) {
@ -383,7 +403,10 @@ class TwitapistatusesAction extends TwitterapiAction
$this->show_rss_timeline($notices, $title, $link, $subtitle); $this->show_rss_timeline($notices, $title, $link, $subtitle);
break; break;
case 'atom': case 'atom':
$this->show_atom_timeline($notices, $title, $id, $link, $subtitle); $selfuri = common_root_url() .
ltrim($_SERVER['QUERY_STRING'], 'p=');
$this->show_atom_timeline($notices, $title, $id, $link, $subtitle,
null, $selfuri);
break; break;
case 'json': case 'json':
$this->show_json_timeline($notices); $this->show_json_timeline($notices);
@ -394,6 +417,11 @@ class TwitapistatusesAction extends TwitterapiAction
} }
function replies($args, $apidata)
{
call_user_func(array($this, 'mentions'), $args, $apidata);
}
function show($args, $apidata) function show($args, $apidata)
{ {
parent::handle($args); parent::handle($args);
@ -470,19 +498,28 @@ class TwitapistatusesAction extends TwitterapiAction
return $this->subscriptions($apidata, 'subscribed', 'subscriber'); return $this->subscriptions($apidata, 'subscribed', 'subscriber');
} }
function friendsIDs($args, $apidata)
{
parent::handle($args);
return $this->subscriptions($apidata, 'subscribed', 'subscriber', true);
}
function followers($args, $apidata) function followers($args, $apidata)
{ {
parent::handle($args); parent::handle($args);
return $this->subscriptions($apidata, 'subscriber', 'subscribed'); 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);
}
function subscriptions($apidata, $other_attr, $user_attr, $onlyIDs=false)
{ {
# XXX: lite $this->auth_user = $apidata['user'];
$this->auth_user = $apidate['user'];
$user = $this->get_user($apidata['api_arg'], $apidata); $user = $this->get_user($apidata['api_arg'], $apidata);
if (!$user) { if (!$user) {
@ -514,7 +551,10 @@ class TwitapistatusesAction extends TwitterapiAction
} }
$sub->orderBy('created DESC'); $sub->orderBy('created DESC');
$sub->limit(($page-1)*100, 100);
if (!$onlyIDs) {
$sub->limit(($page-1)*100, 100);
}
$others = array(); $others = array();
@ -529,7 +569,13 @@ class TwitapistatusesAction extends TwitterapiAction
$type = $apidata['content-type']; $type = $apidata['content-type'];
$this->init_document($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); $this->end_document($type);
} }
@ -555,6 +601,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) function featured($args, $apidata)
{ {
parent::handle($args); parent::handle($args);

90
actions/twitapitrends.php Normal file
View File

@ -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);
}
}

View File

@ -33,15 +33,19 @@ class TwitapiusersAction extends TwitterapiAction
return; return;
} }
$this->auth_user = $apidata['user'];
$user = null; $user = null;
$email = $this->arg('email'); $email = $this->arg('email');
$user_id = $this->arg('user_id');
if ($email) { if ($email) {
$user = User::staticGet('email', $email); $user = User::staticGet('email', $email);
} elseif ($user_id) {
$user = $this->get_user($user_id);
} elseif (isset($apidata['api_arg'])) { } elseif (isset($apidata['api_arg'])) {
$user = $this->get_user($apidata['api_arg']); $user = $this->get_user($apidata['api_arg']);
} } elseif (isset($apidata['user'])) {
$user = $apidata['user'];
}
if (!$user) { if (!$user) {
// XXX: Twitter returns a random(?) user instead of throwing and err! -- Zach // XXX: Twitter returns a random(?) user instead of throwing and err! -- Zach
@ -74,9 +78,12 @@ class TwitapiusersAction extends TwitterapiAction
// Other fields Twitter sends... // Other fields Twitter sends...
$twitter_user['profile_background_color'] = ''; $twitter_user['profile_background_color'] = '';
$twitter_user['profile_background_image_url'] = '';
$twitter_user['profile_text_color'] = ''; $twitter_user['profile_text_color'] = '';
$twitter_user['profile_link_color'] = ''; $twitter_user['profile_link_color'] = '';
$twitter_user['profile_sidebar_fill_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 = DB_DataObject::factory('fave');
$faves->user_id = $user->id; $faves->user_id = $user->id;
@ -94,16 +101,17 @@ class TwitapiusersAction extends TwitterapiAction
$twitter_user['utc_offset'] = $t->format('Z'); $twitter_user['utc_offset'] = $t->format('Z');
$twitter_user['time_zone'] = $timezone; $twitter_user['time_zone'] = $timezone;
if (isset($this->auth_user)) { if (isset($apidata['user'])) {
if ($this->auth_user->isSubscribed($profile)) { $twitter_user['following'] = $apidata['user']->isSubscribed($profile);
$twitter_user['following'] = 'true';
} else { // Notifications on?
$twitter_user['following'] = 'false'; $sub = Subscription::pkeyGet(array('subscriber' =>
$apidata['user']->id, 'subscribed' => $profile->id));
if ($sub) {
$twitter_user['notifications'] = ($sub->jabber || $sub->sms);
} }
// Not implemented yet
$twitter_user['notifications'] = 'false';
} }
if ($apidata['content-type'] == 'xml') { if ($apidata['content-type'] == 'xml') {
@ -114,7 +122,13 @@ class TwitapiusersAction extends TwitterapiAction
$this->init_document('json'); $this->init_document('json');
$this->show_json_objects($twitter_user); $this->show_json_objects($twitter_user);
$this->end_document('json'); $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';
}
} }
} }

View File

@ -138,7 +138,7 @@ class TwittersettingsAction extends ConnectSettingsAction
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
$this->elementStart('li'); $this->elementStart('li');
$this->checkbox('noticesync', $this->checkbox('noticesend',
_('Automatically send my notices to Twitter.'), _('Automatically send my notices to Twitter.'),
($flink) ? ($flink) ?
($flink->noticesync & FOREIGN_NOTICE_SEND) : ($flink->noticesync & FOREIGN_NOTICE_SEND) :
@ -158,6 +158,22 @@ class TwittersettingsAction extends ConnectSettingsAction
($flink->friendsync & FOREIGN_FRIEND_RECV) : ($flink->friendsync & FOREIGN_FRIEND_RECV) :
false); false);
$this->elementEnd('li'); $this->elementEnd('li');
if (common_config('twitterbridge','enabled')) {
$this->elementStart('li');
$this->checkbox('noticerecv',
_('Import my Friends Timeline.'),
($flink) ?
($flink->noticesync & FOREIGN_NOTICE_RECV) :
false);
$this->elementEnd('li');
} else {
// preserve setting even if bidrection bridge toggled off
if ($flink && ($flink->noticesync & FOREIGN_NOTICE_RECV)) {
$this->hidden('noticerecv', true, 'noticerecv');
}
}
$this->elementEnd('ul'); $this->elementEnd('ul');
if ($flink) { if ($flink) {
@ -186,12 +202,12 @@ class TwittersettingsAction extends ConnectSettingsAction
$current_user = common_current_user(); $current_user = common_current_user();
$qry = 'SELECT user.* ' . $qry = 'SELECT "user".* ' .
'FROM subscription ' . 'FROM subscription ' .
'JOIN user ON subscription.subscribed = user.id ' . 'JOIN "user" ON subscription.subscribed = "user".id ' .
'JOIN foreign_link ON foreign_link.user_id = user.id ' . 'JOIN foreign_link ON foreign_link.user_id = "user".id ' .
'WHERE subscriber = %d ' . 'WHERE subscriber = %d ' .
'ORDER BY user.nickname'; 'ORDER BY "user".nickname';
$user = new User(); $user = new User();
@ -320,7 +336,8 @@ class TwittersettingsAction extends ConnectSettingsAction
{ {
$screen_name = $this->trimmed('twitter_username'); $screen_name = $this->trimmed('twitter_username');
$password = $this->trimmed('twitter_password'); $password = $this->trimmed('twitter_password');
$noticesync = $this->boolean('noticesync'); $noticesend = $this->boolean('noticesend');
$noticerecv = $this->boolean('noticerecv');
$replysync = $this->boolean('replysync'); $replysync = $this->boolean('replysync');
$friendsync = $this->boolean('friendsync'); $friendsync = $this->boolean('friendsync');
@ -363,7 +380,7 @@ class TwittersettingsAction extends ConnectSettingsAction
$flink->credentials = $password; $flink->credentials = $password;
$flink->created = common_sql_now(); $flink->created = common_sql_now();
$flink->set_flags($noticesync, $replysync, $friendsync); $flink->set_flags($noticesend, $noticerecv, $replysync, $friendsync);
$flink_id = $flink->insert(); $flink_id = $flink->insert();
@ -375,6 +392,8 @@ class TwittersettingsAction extends ConnectSettingsAction
if ($friendsync) { if ($friendsync) {
save_twitter_friends($user, $twit_user->id, $screen_name, $password); save_twitter_friends($user, $twit_user->id, $screen_name, $password);
$flink->last_friendsync = common_sql_now();
$flink->update();
} }
$this->showForm(_('Twitter settings saved.'), true); $this->showForm(_('Twitter settings saved.'), true);
@ -419,7 +438,8 @@ class TwittersettingsAction extends ConnectSettingsAction
function savePreferences() function savePreferences()
{ {
$noticesync = $this->boolean('noticesync'); $noticesend = $this->boolean('noticesend');
$noticerecv = $this->boolean('noticerecv');
$friendsync = $this->boolean('friendsync'); $friendsync = $this->boolean('friendsync');
$replysync = $this->boolean('replysync'); $replysync = $this->boolean('replysync');
@ -448,7 +468,7 @@ class TwittersettingsAction extends ConnectSettingsAction
$original = clone($flink); $original = clone($flink);
$flink->set_flags($noticesync, $replysync, $friendsync); $flink->set_flags($noticesend, $noticerecv, $replysync, $friendsync);
$result = $flink->update($original); $result = $flink->update($original);

View File

@ -116,10 +116,11 @@ class UnblockAction extends Action
} }
} }
if ($action) { if ($action) {
common_redirect(common_local_url($action, $args)); common_redirect(common_local_url($action, $args), 303);
} else { } else {
common_redirect(common_local_url('subscriptions', common_redirect(common_local_url('subscriptions',
array('nickname' => $cur->nickname))); array('nickname' => $cur->nickname)),
303);
} }
} }
} }

View File

@ -77,7 +77,8 @@ class UnsubscribeAction extends Action
$this->elementEnd('html'); $this->elementEnd('html');
} else { } else {
common_redirect(common_local_url('subscriptions', array('nickname' => common_redirect(common_local_url('subscriptions', array('nickname' =>
$user->nickname))); $user->nickname)),
303);
} }
} }
} }

View File

@ -29,11 +29,13 @@ class UpdateprofileAction extends Action
parent::handle($args); parent::handle($args);
try { try {
common_remove_magic_from_request(); common_remove_magic_from_request();
$req = OAuthRequest::from_request(); $req = OAuthRequest::from_request('POST', common_local_url('updateprofile'));
# Note: server-to-server function! # Note: server-to-server function!
$server = omb_oauth_server(); $server = omb_oauth_server();
list($consumer, $token) = $server->verify_request($req); list($consumer, $token) = $server->verify_request($req);
if ($this->update_profile($req, $consumer, $token)) { if ($this->update_profile($req, $consumer, $token)) {
header('HTTP/1.1 200 OK');
header('Content-type: text/plain');
print "omb_version=".OMB_VERSION_01; print "omb_version=".OMB_VERSION_01;
} }
} catch (OAuthException $e) { } catch (OAuthException $e) {
@ -136,22 +138,24 @@ class UpdateprofileAction extends Action
$orig_profile = clone($profile); $orig_profile = clone($profile);
if ($nickname) { /* Use values even if they are an empty string. Parsing an empty string in
updateProfile is the specified way of clearing a parameter in OMB. */
if (!is_null($nickname)) {
$profile->nickname = $nickname; $profile->nickname = $nickname;
} }
if ($profile_url) { if (!is_null($profile_url)) {
$profile->profileurl = $profile_url; $profile->profileurl = $profile_url;
} }
if ($fullname) { if (!is_null($fullname)) {
$profile->fullname = $fullname; $profile->fullname = $fullname;
} }
if ($homepage) { if (!is_null($homepage)) {
$profile->homepage = $homepage; $profile->homepage = $homepage;
} }
if ($bio) { if (!is_null($bio)) {
$profile->bio = $bio; $profile->bio = $bio;
} }
if ($location) { if (!is_null($location)) {
$profile->location = $location; $profile->location = $location;
} }
@ -162,15 +166,17 @@ class UpdateprofileAction extends Action
if ($avatar) { if ($avatar) {
$temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar'); $temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar');
copy($avatar, $temp_filename); 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); $this->serverError(_('Could not save avatar info'), 500);
return false; return false;
} }
} }
header('HTTP/1.1 200 OK');
header('Content-type: text/plain');
print 'Updated profile';
print "\n";
return true; return true;
} }
} }

View File

@ -25,7 +25,7 @@ define('TIMESTAMP_THRESHOLD', 300);
class UserauthorizationAction extends Action class UserauthorizationAction extends Action
{ {
var $error; var $error;
var $req; var $params;
function handle($args) function handle($args)
{ {
@ -35,8 +35,8 @@ class UserauthorizationAction extends Action
# CSRF protection # CSRF protection
$token = $this->trimmed('token'); $token = $this->trimmed('token');
if (!$token || $token != common_session_token()) { if (!$token || $token != common_session_token()) {
$req = $this->getStoredRequest(); $params = $this->getStoredParams();
$this->showForm($req, _('There was a problem with your session token. '. $this->showForm($params, _('There was a problem with your session token. '.
'Try again, please.')); 'Try again, please.'));
return; return;
} }
@ -50,18 +50,13 @@ class UserauthorizationAction extends Action
common_redirect(common_local_url('login')); common_redirect(common_local_url('login'));
return; return;
} }
try { try {
# this must be a new request $this->validateRequest();
$req = $this->getNewRequest(); $this->storeParams($_GET);
if (!$req) { $this->showForm($_GET);
$this->clientError(_('No request found!'));
}
# XXX: only validate new requests, since nonce is one-time use
$this->validateRequest($req);
$this->storeRequest($req);
$this->showForm($req);
} catch (OAuthException $e) { } catch (OAuthException $e) {
$this->clearRequest(); $this->clearParams();
$this->clientError($e->getMessage()); $this->clientError($e->getMessage());
return; return;
} }
@ -69,9 +64,9 @@ class UserauthorizationAction extends Action
} }
} }
function showForm($req, $error=null) function showForm($params, $error=null)
{ {
$this->req = $req; $this->params = $params;
$this->error = $error; $this->error = $error;
$this->showPage(); $this->showPage();
} }
@ -91,113 +86,157 @@ class UserauthorizationAction extends Action
function showContent() function showContent()
{ {
$req = $this->req; $params = $this->params;
$nickname = $req->get_parameter('omb_listenee_nickname'); $nickname = $params['omb_listenee_nickname'];
$profile = $req->get_parameter('omb_listenee_profile'); $profile = $params['omb_listenee_profile'];
$license = $req->get_parameter('omb_listenee_license'); $license = $params['omb_listenee_license'];
$fullname = $req->get_parameter('omb_listenee_fullname'); $fullname = $params['omb_listenee_fullname'];
$homepage = $req->get_parameter('omb_listenee_homepage'); $homepage = $params['omb_listenee_homepage'];
$bio = $req->get_parameter('omb_listenee_bio'); $bio = $params['omb_listenee_bio'];
$location = $req->get_parameter('omb_listenee_location'); $location = $params['omb_listenee_location'];
$avatar = $req->get_parameter('omb_listenee_avatar'); $avatar = $params['omb_listenee_avatar'];
$this->elementStart('div', 'profile'); $this->elementStart('div', array('class' => 'profile'));
$this->elementStart('div', 'entity_profile vcard');
$this->elementStart('a', array('href' => $profile,
'class' => 'url'));
if ($avatar) { if ($avatar) {
$this->element('img', array('src' => $avatar, $this->element('img', array('src' => $avatar,
'class' => 'avatar profile', 'class' => 'photo avatar',
'width' => AVATAR_PROFILE_SIZE, 'width' => AVATAR_PROFILE_SIZE,
'height' => AVATAR_PROFILE_SIZE, 'height' => AVATAR_PROFILE_SIZE,
'alt' => $nickname)); 'alt' => $nickname));
} }
$this->element('a', array('href' => $profile, $hasFN = ($fullname !== '') ? 'nickname' : 'fn nickname';
'class' => 'external profile nickname'), $this->elementStart('span', $hasFN);
$nickname); $this->raw($nickname);
if ($fullname) { $this->elementEnd('span');
$this->elementStart('div', 'fullname'); $this->elementEnd('a');
if ($homepage) {
$this->element('a', array('href' => $homepage), if (!is_null($fullname)) {
$fullname); $this->elementStart('dl', 'entity_fn');
} else { $this->elementStart('dd');
$this->text($fullname); $this->elementStart('span', 'fn');
} $this->raw($fullname);
$this->elementEnd('div'); $this->elementEnd('span');
$this->elementEnd('dd');
$this->elementEnd('dl');
} }
if ($location) { if (!is_null($location)) {
$this->element('div', 'location', $location); $this->elementStart('dl', 'entity_location');
$this->element('dt', null, _('Location'));
$this->elementStart('dd', 'label');
$this->raw($location);
$this->elementEnd('dd');
$this->elementEnd('dl');
} }
if ($bio) {
$this->element('div', 'bio', $bio); if (!is_null($homepage)) {
$this->elementStart('dl', 'entity_url');
$this->element('dt', null, _('URL'));
$this->elementStart('dd');
$this->elementStart('a', array('href' => $homepage,
'class' => 'url'));
$this->raw($homepage);
$this->elementEnd('a');
$this->elementEnd('dd');
$this->elementEnd('dl');
}
if (!is_null($bio)) {
$this->elementStart('dl', 'entity_note');
$this->element('dt', null, _('Note'));
$this->elementStart('dd', 'note');
$this->raw($bio);
$this->elementEnd('dd');
$this->elementEnd('dl');
}
if (!is_null($license)) {
$this->elementStart('dl', 'entity_license');
$this->element('dt', null, _('License'));
$this->elementStart('dd', 'license');
$this->element('a', array('href' => $license,
'class' => 'license'),
$license);
$this->elementEnd('dd');
$this->elementEnd('dl');
} }
$this->elementStart('div', 'license');
$this->element('a', array('href' => $license,
'class' => 'license'),
$license);
$this->elementEnd('div');
$this->elementEnd('div'); $this->elementEnd('div');
$this->elementStart('div', 'entity_actions');
$this->elementStart('ul');
$this->elementStart('li', 'entity_subscribe');
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
'id' => 'userauthorization', 'id' => 'userauthorization',
'class' => 'form_user_authorization',
'name' => 'userauthorization', 'name' => 'userauthorization',
'action' => common_local_url('userauthorization'))); 'action' => common_local_url('userauthorization')));
$this->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
$this->submit('accept', _('Accept'));
$this->submit('reject', _('Reject')); $this->submit('accept', _('Accept'), 'submit accept', null, _('Subscribe to this user'));
$this->submit('reject', _('Reject'), 'submit reject', null, _('Reject this subscription'));
$this->elementEnd('form'); $this->elementEnd('form');
$this->elementEnd('li');
$this->elementEnd('ul');
$this->elementEnd('div');
$this->elementEnd('div');
} }
function sendAuthorization() function sendAuthorization()
{ {
$req = $this->getStoredRequest(); $params = $this->getStoredParams();
if (!$req) { if (!$params) {
$this->clientError(_('No authorization request!')); $this->clientError(_('No authorization request!'));
return; return;
} }
$callback = $req->get_parameter('oauth_callback'); $callback = $params['oauth_callback'];
if ($this->arg('accept')) { if ($this->arg('accept')) {
if (!$this->authorizeToken($req)) { if (!$this->authorizeToken($params)) {
$this->clientError(_('Error authorizing token')); $this->clientError(_('Error authorizing token'));
} }
if (!$this->saveRemoteProfile($req)) { if (!$this->saveRemoteProfile($params)) {
$this->clientError(_('Error saving remote profile')); $this->clientError(_('Error saving remote profile'));
} }
if (!$callback) { if (!$callback) {
$this->showAcceptMessage($req->get_parameter('oauth_token')); $this->showAcceptMessage($params['oauth_token']);
} else { } else {
$params = array(); $newparams = array();
$params['oauth_token'] = $req->get_parameter('oauth_token'); $newparams['oauth_token'] = $params['oauth_token'];
$params['omb_version'] = OMB_VERSION_01; $newparams['omb_version'] = OMB_VERSION_01;
$user = User::staticGet('uri', $req->get_parameter('omb_listener')); $user = User::staticGet('uri', $params['omb_listener']);
$profile = $user->getProfile(); $profile = $user->getProfile();
if (!$profile) { if (!$profile) {
common_log_db_error($user, 'SELECT', __FILE__); common_log_db_error($user, 'SELECT', __FILE__);
$this->serverError(_('User without matching profile')); $this->serverError(_('User without matching profile'));
return; return;
} }
$params['omb_listener_nickname'] = $user->nickname; $newparams['omb_listener_nickname'] = $user->nickname;
$params['omb_listener_profile'] = common_local_url('showstream', $newparams['omb_listener_profile'] = common_local_url('showstream',
array('nickname' => $user->nickname)); array('nickname' => $user->nickname));
if ($profile->fullname) { if (!is_null($profile->fullname)) {
$params['omb_listener_fullname'] = $profile->fullname; $newparams['omb_listener_fullname'] = $profile->fullname;
} }
if ($profile->homepage) { if (!is_null($profile->homepage)) {
$params['omb_listener_homepage'] = $profile->homepage; $newparams['omb_listener_homepage'] = $profile->homepage;
} }
if ($profile->bio) { if (!is_null($profile->bio)) {
$params['omb_listener_bio'] = $profile->bio; $newparams['omb_listener_bio'] = $profile->bio;
} }
if ($profile->location) { if (!is_null($profile->location)) {
$params['omb_listener_location'] = $profile->location; $newparams['omb_listener_location'] = $profile->location;
} }
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
if ($avatar) { if ($avatar) {
$params['omb_listener_avatar'] = $avatar->url; $newparams['omb_listener_avatar'] = $avatar->url;
} }
$parts = array(); $parts = array();
foreach ($params as $k => $v) { foreach ($newparams as $k => $v) {
$parts[] = $k . '=' . OAuthUtil::urlencodeRFC3986($v); $parts[] = $k . '=' . OAuthUtil::urlencode_rfc3986($v);
} }
$query_string = implode('&', $parts); $query_string = implode('&', $parts);
$parsed = parse_url($callback); $parsed = parse_url($callback);
@ -214,12 +253,10 @@ class UserauthorizationAction extends Action
} }
} }
function authorizeToken(&$req) function authorizeToken(&$params)
{ {
$consumer_key = $req->get_parameter('oauth_consumer_key'); $token_field = $params['oauth_token'];
$token_field = $req->get_parameter('oauth_token');
$rt = new Token(); $rt = new Token();
$rt->consumer_key = $consumer_key;
$rt->tok = $token_field; $rt->tok = $token_field;
$rt->type = 0; $rt->type = 0;
$rt->state = 0; $rt->state = 0;
@ -235,21 +272,21 @@ class UserauthorizationAction extends Action
# XXX: refactor with similar code in finishremotesubscribe.php # XXX: refactor with similar code in finishremotesubscribe.php
function saveRemoteProfile(&$req) function saveRemoteProfile(&$params)
{ {
# FIXME: we should really do this when the consumer comes # FIXME: we should really do this when the consumer comes
# back for an access token. If they never do, we've got stuff in a # back for an access token. If they never do, we've got stuff in a
# weird state. # weird state.
$nickname = $req->get_parameter('omb_listenee_nickname'); $nickname = $params['omb_listenee_nickname'];
$fullname = $req->get_parameter('omb_listenee_fullname'); $fullname = $params['omb_listenee_fullname'];
$profile_url = $req->get_parameter('omb_listenee_profile'); $profile_url = $params['omb_listenee_profile'];
$homepage = $req->get_parameter('omb_listenee_homepage'); $homepage = $params['omb_listenee_homepage'];
$bio = $req->get_parameter('omb_listenee_bio'); $bio = $params['omb_listenee_bio'];
$location = $req->get_parameter('omb_listenee_location'); $location = $params['omb_listenee_location'];
$avatar_url = $req->get_parameter('omb_listenee_avatar'); $avatar_url = $params['omb_listenee_avatar'];
$listenee = $req->get_parameter('omb_listenee'); $listenee = $params['omb_listenee'];
$remote = Remote_profile::staticGet('uri', $listenee); $remote = Remote_profile::staticGet('uri', $listenee);
if ($remote) { if ($remote) {
@ -267,16 +304,16 @@ class UserauthorizationAction extends Action
$profile->nickname = $nickname; $profile->nickname = $nickname;
$profile->profileurl = $profile_url; $profile->profileurl = $profile_url;
if ($fullname) { if (!is_null($fullname)) {
$profile->fullname = $fullname; $profile->fullname = $fullname;
} }
if ($homepage) { if (!is_null($homepage)) {
$profile->homepage = $homepage; $profile->homepage = $homepage;
} }
if ($bio) { if (!is_null($bio)) {
$profile->bio = $bio; $profile->bio = $bio;
} }
if ($location) { if (!is_null($location)) {
$profile->location = $location; $profile->location = $location;
} }
@ -309,14 +346,11 @@ class UserauthorizationAction extends Action
} }
$user = common_current_user(); $user = common_current_user();
$datastore = omb_oauth_datastore();
$consumer = $this->getConsumer($datastore, $req);
$token = $this->getToken($datastore, $req, $consumer);
$sub = new Subscription(); $sub = new Subscription();
$sub->subscriber = $user->id; $sub->subscriber = $user->id;
$sub->subscribed = $remote->id; $sub->subscribed = $remote->id;
$sub->token = $token->key; # NOTE: request token, not valid for use! $sub->token = $params['oauth_token']; # NOTE: request token, not valid for use!
$sub->created = DB_DataObject_Cast::dateTime(); # current time $sub->created = DB_DataObject_Cast::dateTime(); # current time
if (!$sub->insert()) { if (!$sub->insert()) {
@ -360,65 +394,59 @@ class UserauthorizationAction extends Action
common_show_footer(); common_show_footer();
} }
function storeRequest($req) function storeParams($params)
{ {
common_ensure_session(); common_ensure_session();
$_SESSION['userauthorizationrequest'] = $req; $_SESSION['userauthorizationparams'] = $params;
} }
function clearRequest() function clearParams()
{ {
common_ensure_session(); common_ensure_session();
unset($_SESSION['userauthorizationrequest']); unset($_SESSION['userauthorizationparams']);
} }
function getStoredRequest() function getStoredParams()
{ {
common_ensure_session(); common_ensure_session();
$req = $_SESSION['userauthorizationrequest']; $params = $_SESSION['userauthorizationparams'];
return $req; return $params;
}
function getNewRequest()
{
common_remove_magic_from_request();
$req = OAuthRequest::from_request();
return $req;
} }
# Throws an OAuthException if anything goes wrong # Throws an OAuthException if anything goes wrong
function validateRequest(&$req) function validateRequest()
{ {
# OAuth stuff -- have to copy from OAuth.php since they're /* Find token.
# all private methods, and there's no user-authentication method TODO: If no token is passed the user should get a prompt to enter it
$this->checkVersion($req); according to OAuth Core 1.0 */
$datastore = omb_oauth_datastore(); $t = new Token();
$consumer = $this->getConsumer($datastore, $req); $t->tok = $_GET['oauth_token'];
$token = $this->getToken($datastore, $req, $consumer); $t->type = 0;
$this->checkTimestamp($req); if (!$t->find(true)) {
$this->checkNonce($datastore, $req, $consumer, $token); throw new OAuthException("Invalid request token: " . $_GET['oauth_token']);
$this->checkSignature($req, $consumer, $token); }
$this->validateOmb($req);
$this->validateOmb();
return true; return true;
} }
function validateOmb(&$req) function validateOmb()
{ {
foreach (array('omb_version', 'omb_listener', 'omb_listenee', foreach (array('omb_version', 'omb_listener', 'omb_listenee',
'omb_listenee_profile', 'omb_listenee_nickname', 'omb_listenee_profile', 'omb_listenee_nickname',
'omb_listenee_license') as $param) 'omb_listenee_license') as $param)
{ {
if (!$req->get_parameter($param)) { if (!isset($_GET[$param]) || is_null($_GET[$param])) {
throw new OAuthException("Required parameter '$param' not found"); throw new OAuthException("Required parameter '$param' not found");
} }
} }
# Now, OMB stuff # Now, OMB stuff
$version = $req->get_parameter('omb_version'); $version = $_GET['omb_version'];
if ($version != OMB_VERSION_01) { if ($version != OMB_VERSION_01) {
throw new OAuthException("OpenMicroBlogging version '$version' not supported"); throw new OAuthException("OpenMicroBlogging version '$version' not supported");
} }
$listener = $req->get_parameter('omb_listener'); $listener = $_GET['omb_listener'];
$user = User::staticGet('uri', $listener); $user = User::staticGet('uri', $listener);
if (!$user) { if (!$user) {
throw new OAuthException("Listener URI '$listener' not found here"); throw new OAuthException("Listener URI '$listener' not found here");
@ -427,7 +455,7 @@ class UserauthorizationAction extends Action
if ($cur->id != $user->id) { if ($cur->id != $user->id) {
throw new OAuthException("Can't add for another user!"); throw new OAuthException("Can't add for another user!");
} }
$listenee = $req->get_parameter('omb_listenee'); $listenee = $_GET['omb_listenee'];
if (!Validate::uri($listenee) && if (!Validate::uri($listenee) &&
!common_valid_tag($listenee)) { !common_valid_tag($listenee)) {
throw new OAuthException("Listenee URI '$listenee' not a recognizable URI"); throw new OAuthException("Listenee URI '$listenee' not a recognizable URI");
@ -450,13 +478,13 @@ class UserauthorizationAction extends Action
throw new OAuthException("Already subscribed to user!"); throw new OAuthException("Already subscribed to user!");
} }
} }
$nickname = $req->get_parameter('omb_listenee_nickname'); $nickname = $_GET['omb_listenee_nickname'];
if (!Validate::string($nickname, array('min_length' => 1, if (!Validate::string($nickname, array('min_length' => 1,
'max_length' => 64, 'max_length' => 64,
'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) {
throw new OAuthException('Nickname must have only letters and numbers and no spaces.'); throw new OAuthException('Nickname must have only letters and numbers and no spaces.');
} }
$profile = $req->get_parameter('omb_listenee_profile'); $profile = $_GET['omb_listenee_profile'];
if (!common_valid_http_url($profile)) { if (!common_valid_http_url($profile)) {
throw new OAuthException("Invalid profile URL '$profile'."); throw new OAuthException("Invalid profile URL '$profile'.");
} }
@ -465,7 +493,7 @@ class UserauthorizationAction extends Action
throw new OAuthException("Profile URL '$profile' is for a local user."); throw new OAuthException("Profile URL '$profile' is for a local user.");
} }
$license = $req->get_parameter('omb_listenee_license'); $license = $_GET['omb_listenee_license'];
if (!common_valid_http_url($license)) { if (!common_valid_http_url($license)) {
throw new OAuthException("Invalid license URL '$license'."); throw new OAuthException("Invalid license URL '$license'.");
} }
@ -474,23 +502,23 @@ class UserauthorizationAction extends Action
throw new OAuthException("Listenee stream license '$license' not compatible with site license '$site_license'."); throw new OAuthException("Listenee stream license '$license' not compatible with site license '$site_license'.");
} }
# optional stuff # optional stuff
$fullname = $req->get_parameter('omb_listenee_fullname'); $fullname = $_GET['omb_listenee_fullname'];
if ($fullname && mb_strlen($fullname) > 255) { if ($fullname && mb_strlen($fullname) > 255) {
throw new OAuthException("Full name '$fullname' too long."); throw new OAuthException("Full name '$fullname' too long.");
} }
$homepage = $req->get_parameter('omb_listenee_homepage'); $homepage = $_GET['omb_listenee_homepage'];
if ($homepage && (!common_valid_http_url($homepage) || mb_strlen($homepage) > 255)) { if ($homepage && (!common_valid_http_url($homepage) || mb_strlen($homepage) > 255)) {
throw new OAuthException("Invalid homepage '$homepage'"); throw new OAuthException("Invalid homepage '$homepage'");
} }
$bio = $req->get_parameter('omb_listenee_bio'); $bio = $_GET['omb_listenee_bio'];
if ($bio && mb_strlen($bio) > 140) { if ($bio && mb_strlen($bio) > 140) {
throw new OAuthException("Bio too long '$bio'"); throw new OAuthException("Bio too long '$bio'");
} }
$location = $req->get_parameter('omb_listenee_location'); $location = $_GET['omb_listenee_location'];
if ($location && mb_strlen($location) > 255) { if ($location && mb_strlen($location) > 255) {
throw new OAuthException("Location too long '$location'"); throw new OAuthException("Location too long '$location'");
} }
$avatar = $req->get_parameter('omb_listenee_avatar'); $avatar = $_GET['omb_listenee_avatar'];
if ($avatar) { if ($avatar) {
if (!common_valid_http_url($avatar) || strlen($avatar) > 255) { if (!common_valid_http_url($avatar) || strlen($avatar) > 255) {
throw new OAuthException("Invalid avatar URL '$avatar'"); throw new OAuthException("Invalid avatar URL '$avatar'");
@ -507,7 +535,7 @@ class UserauthorizationAction extends Action
throw new OAuthException("Wrong image type for '$avatar'"); throw new OAuthException("Wrong image type for '$avatar'");
} }
} }
$callback = $req->get_parameter('oauth_callback'); $callback = $_GET['oauth_callback'];
if ($callback && !common_valid_http_url($callback)) { if ($callback && !common_valid_http_url($callback)) {
throw new OAuthException("Invalid callback URL '$callback'"); throw new OAuthException("Invalid callback URL '$callback'");
} }
@ -515,92 +543,4 @@ class UserauthorizationAction extends Action
throw new OAuthException("Callback URL '$callback' is for local site."); throw new OAuthException("Callback URL '$callback' is for local site.");
} }
} }
# Snagged from OAuthServer
function checkVersion(&$req)
{
$version = $req->get_parameter("oauth_version");
if (!$version) {
$version = 1.0;
}
if ($version != 1.0) {
throw new OAuthException("OAuth version '$version' not supported");
}
return $version;
}
# Snagged from OAuthServer
function getConsumer($datastore, $req)
{
$consumer_key = @$req->get_parameter("oauth_consumer_key");
if (!$consumer_key) {
throw new OAuthException("Invalid consumer key");
}
$consumer = $datastore->lookup_consumer($consumer_key);
if (!$consumer) {
throw new OAuthException("Invalid consumer");
}
return $consumer;
}
# Mostly cadged from OAuthServer
function getToken($datastore, &$req, $consumer)
{/*{{{*/
$token_field = @$req->get_parameter('oauth_token');
$token = $datastore->lookup_token($consumer, 'request', $token_field);
if (!$token) {
throw new OAuthException("Invalid $token_type token: $token_field");
}
return $token;
}
function checkTimestamp(&$req)
{
$timestamp = @$req->get_parameter('oauth_timestamp');
$now = time();
if ($now - $timestamp > TIMESTAMP_THRESHOLD) {
throw new OAuthException("Expired timestamp, yours $timestamp, ours $now");
}
}
# NOTE: don't call twice on the same request; will fail!
function checkNonce(&$datastore, &$req, $consumer, $token)
{
$timestamp = @$req->get_parameter('oauth_timestamp');
$nonce = @$req->get_parameter('oauth_nonce');
$found = $datastore->lookup_nonce($consumer, $token, $nonce, $timestamp);
if ($found) {
throw new OAuthException("Nonce already used");
}
return true;
}
function checkSignature(&$req, $consumer, $token)
{
$signature_method = $this->getSignatureMethod($req);
$signature = $req->get_parameter('oauth_signature');
$valid_sig = $signature_method->check_signature($req,
$consumer,
$token,
$signature);
if (!$valid_sig) {
throw new OAuthException("Invalid signature");
}
}
function getSignatureMethod(&$req)
{
$signature_method = @$req->get_parameter("oauth_signature_method");
if (!$signature_method) {
$signature_method = "PLAINTEXT";
}
if ($signature_method != 'HMAC-SHA1') {
throw new OAuthException("Signature method '$signature_method' not supported.");
}
return omb_hmac_sha1();
}
} }

View File

@ -50,7 +50,7 @@ class UserbyidAction extends Action
* *
* @return boolean true * @return boolean true
*/ */
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

View File

@ -52,7 +52,7 @@ class UsergroupsAction extends Action
var $page = null; var $page = null;
var $profile = null; var $profile = null;
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
@ -139,10 +139,28 @@ class UsergroupsAction extends Action
if ($groups) { if ($groups) {
$gl = new GroupList($groups, $this->user, $this); $gl = new GroupList($groups, $this->user, $this);
$cnt = $gl->show(); $cnt = $gl->show();
if (0 == $cnt) {
$this->showEmptyListMessage();
}
} }
$this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE, $this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE,
$this->page, 'usergroups', $this->page, 'usergroups',
array('nickname' => $this->user->nickname)); array('nickname' => $this->user->nickname));
} }
function showEmptyListMessage()
{
$message = sprintf(_('%s is not a member of any group.'), $this->user->nickname) . ' ';
if (common_logged_in()) {
$current_user = common_current_user();
if ($this->user->id === $current_user->id) {
$message .= _('Try [searching for groups](%%action.groupsearch%%) and joining them.');
}
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
}
} }

View File

@ -25,14 +25,15 @@ require_once(INSTALLDIR.'/lib/rssaction.php');
class UserrssAction extends Rss10Action class UserrssAction extends Rss10Action
{ {
var $user = null; var $user = null;
var $tag = null;
function prepare($args) function prepare($args)
{ {
parent::prepare($args); parent::prepare($args);
$nickname = $this->trimmed('nickname'); $nickname = $this->trimmed('nickname');
$this->user = User::staticGet('nickname', $nickname); $this->user = User::staticGet('nickname', $nickname);
$this->tag = $this->trimmed('tag');
if (!$this->user) { if (!$this->user) {
$this->clientError(_('No such user.')); $this->clientError(_('No such user.'));
@ -42,6 +43,25 @@ class UserrssAction extends Rss10Action
} }
} }
function getTaggedNotices($tag = null, $limit=0)
{
$user = $this->user;
if (is_null($user)) {
return null;
}
$notice = $user->getTaggedNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit, 0, 0, null, $tag);
$notices = array();
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
}
function getNotices($limit=0) function getNotices($limit=0)
{ {
@ -53,6 +73,7 @@ class UserrssAction extends Rss10Action
$notice = $user->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit); $notice = $user->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit);
$notices = array();
while ($notice->fetch()) { while ($notice->fetch()) {
$notices[] = clone($notice); $notices[] = clone($notice);
} }
@ -90,14 +111,13 @@ class UserrssAction extends Rss10Action
function initRss($limit=0) 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); header('X-SUP-ID: '.$url);
parent::initRss($limit); parent::initRss($limit);
} }
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
} }

View File

@ -52,7 +52,7 @@ class XrdsAction extends Action
* *
* @return boolean true * @return boolean true
*/ */
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }

BIN
apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More