Merge branch '1.0.x' into shortcontrol10x
This commit is contained in:
commit
12eee30586
274
EVENTS.txt
274
EVENTS.txt
@ -551,6 +551,12 @@ EndPublicXRDS: End XRDS output (right before the closing XRDS tag)
|
||||
- $action: the current action
|
||||
- &$xrdsoutputter - XRDSOutputter object to write to
|
||||
|
||||
StartHostMetaLinks: Start /.well-known/host-meta links
|
||||
- &links: array containing the links elements to be written
|
||||
|
||||
EndHostMetaLinks: End /.well-known/host-meta links
|
||||
- &links: array containing the links elements to be written
|
||||
|
||||
StartCheckPassword: Check a username/password
|
||||
- $nickname: The nickname to check
|
||||
- $password: The password to check
|
||||
@ -836,3 +842,271 @@ EndDeleteUser: handling the post for deleting a user
|
||||
- $action: action being shown
|
||||
- $user: user being deleted
|
||||
|
||||
StartActivityStart: starting the output for a notice activity <event>
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$attrs: <entry> attributes (mostly namespace declarations, if any)
|
||||
|
||||
EndActivityStart: end the opening tag for an activity <event>
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $attrs: <entry> attributes (mostly namespace declarations, if any)
|
||||
|
||||
StartActivitySource: before outputting the <source> element for a notice activity
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
|
||||
EndActivitySource: after outputting the <source> element for a notice activity
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
|
||||
StartActivityTitle: before outputting notice activity title
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$title: title of the notice, mutable
|
||||
|
||||
EndActivityTitle: after outputting notice activity title
|
||||
- $notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $title: title of the notice
|
||||
|
||||
StartActivityAuthor: before outputting atom author
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$atomAuthor: string for XML representing atom author
|
||||
|
||||
EndActivityAuthor: after outputting atom author
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$atomAuthor: string for XML representing atom author
|
||||
|
||||
StartActivityActor: before outputting activity actor element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$actor: string for XML representing activity actor
|
||||
|
||||
EndActivityActor: after outputting activity actor element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$actor: string for XML representing activity actor
|
||||
|
||||
StartActivityLink: before outputting activity HTML link element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$url: URL for activity HTML link element for a notice activity entry
|
||||
|
||||
EndActivityLink: before outputting activity HTML link element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $url: URL for activity HTML link element for a notice activity entry
|
||||
|
||||
StartActivityId: before outputting atom:id element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$id: atom:id (notice URI by default)
|
||||
|
||||
EndActivityId: after outputting atom:id element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $id: atom:id (notice URI by default)
|
||||
|
||||
StartActivityPublished: before outputting atom:published element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$published: atom:published value (notice created by default)
|
||||
|
||||
EndActivityPublished: before outputting atom:published element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $published: atom:published value (notice created by default)
|
||||
|
||||
StartActivityUpdated: before outputting atom:updated element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$updated: atom:updated value (same as atom:published by default)
|
||||
|
||||
EndActivityUpdated: after outputting atom:updated element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $updated: atom:updated value (same as atom:published by default)
|
||||
|
||||
StartActivityContent: before outputting atom:content element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$content: atom:content value (notice rendered HTML by default)
|
||||
|
||||
EndActivityContent: after outputting atom:content element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $content: atom:content value (notice rendered HTML by default)
|
||||
|
||||
StartActivityVerb: before outputting activity:verb element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$verb: activity:verb URI ('http://activitystrea.ms/schema/1.0/post' by default)
|
||||
|
||||
EndActivityVerb: after outputting activity:verb element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $verb: activity:verb URI ('http://activitystrea.ms/schema/1.0/post' by default)
|
||||
|
||||
StartActivityDefaultObjectType: before outputting activity:object-type element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$type: activity:object-type URI for default object ('http://activitystrea.ms/schema/1.0/note' by default)
|
||||
|
||||
EndActivityDefaultObjectType: after outputting activity:verb element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $type: activity:object-type URI for default object ('http://activitystrea.ms/schema/1.0/note' by default)
|
||||
|
||||
StartActivityObjects: before outputting activity:object elements for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$objects: array of ActivityObject objects to output (empty by default)
|
||||
|
||||
EndActivityObjects: after outputting activity:object elements for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $objects: array of ActivityObject objects to output (empty by default)
|
||||
|
||||
StartActivityNoticeInfo: before outputting statusnet:notice-info element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$noticeInfoAttr: array of attributes for notice info element
|
||||
|
||||
EndActivityNoticeInfo: after outputting statusnet:notice-info element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $noticeInfoAttr: array of attributes for notice info element
|
||||
|
||||
StartActivityInReplyTo: before outputting thr:in-reply-to element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$replyNotice: Notice object the main notice is in-reply-to
|
||||
|
||||
EndActivityInReplyTo: after outputting thr:in-reply-to element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $replyNotice: Notice object the main notice is in-reply-to
|
||||
|
||||
StartActivityConversation: before outputting ostatus:conversation link element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$conv: Conversation object
|
||||
|
||||
EndActivityConversation: after outputting ostatus:conversation link element for a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $conv: Conversation object
|
||||
|
||||
StartActivityAttentionProfiles: before outputting ostatus:attention link element for people in a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$replyProfiles: array of profiles of people being replied to
|
||||
|
||||
EndActivityAttentionProfiles: after outputting ostatus:attention link element for people in a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $replyProfiles: array of Profile object of people being replied to
|
||||
|
||||
StartActivityAttentionGroups: before outputting ostatus:attention link element for groups in a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$groups: array of Group objects of groups being addressed
|
||||
|
||||
EndActivityAttentionGroups: after outputting ostatus:attention link element for groups in a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $groups: array of Group objects of groups being addressed
|
||||
|
||||
StartActivityForward: before outputting ostatus:forward link element in a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$repeat: Notice that was repeated
|
||||
|
||||
EndActivityForward: after outputting ostatus:forward link element in a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $repeat: Notice that was repeated
|
||||
|
||||
StartActivityCategories: before outputting atom:category elements in a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$tags: array of strings for tags on the notice (used for categories)
|
||||
|
||||
EndActivityCategories: after outputting atom:category elements in a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $tags: array of strings for tags on the notice (used for categories)
|
||||
|
||||
StartActivityEnclosures: before outputting enclosure link elements in a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$enclosures: array of enclosure objects (see File::getEnclosure() for details)
|
||||
|
||||
EndActivityEnclosures: after outputting enclosure link elements in a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $enclosures: array of enclosure objects (see File::getEnclosure() for details)
|
||||
|
||||
StartActivityGeo: before outputting geo:rss element in a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- &$lat: latitude
|
||||
- &$lon: longitude
|
||||
|
||||
EndActivityGeo: after outputting geo:rss element in a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
- $lat: latitude
|
||||
- $lon: longitude
|
||||
|
||||
StartActivityEnd: before the closing </entry> in a notice activity entry (last chance for data!)
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
|
||||
EndActivityEnd: after the closing </entry> in a notice activity entry
|
||||
- &$notice: notice being output
|
||||
- &$xs: XMLStringer for output
|
||||
|
||||
StartNoticeSaveWeb: before saving a notice through the Web interface
|
||||
- $action: action being executed (instance of NewNoticeAction)
|
||||
- &$authorId: integer ID of the author
|
||||
- &$text: text of the notice
|
||||
- &$options: additional options (location, replies, etc.)
|
||||
|
||||
EndNoticeSaveWeb: after saving a notice through the Web interface
|
||||
- $action: action being executed (instance of NewNoticeAction)
|
||||
- $notice: notice that was saved
|
||||
|
||||
StartRssEntryArray: at the start of copying a notice to an array
|
||||
- $notice: the notice being copied
|
||||
- &$entry: the entry (empty at beginning)
|
||||
|
||||
EndRssEntryArray: at the end of copying a notice to an array
|
||||
- $notice: the notice being copied
|
||||
- &$entry: the entry, with all the fields filled up
|
||||
|
||||
NoticeDeleteRelated: at the beginning of deleting related fields to a notice
|
||||
- $notice: notice being deleted
|
||||
|
||||
StartShowHeadTitle: when beginning to show the <title> element
|
||||
- $action: action being shown
|
||||
|
||||
EndShowHeadTitle: when done showing the <title>
|
||||
- $action: action being shown
|
||||
|
||||
StartShowPageTitle: when beginning to show the page title <h1>
|
||||
- $action: action being shown
|
||||
|
||||
EndShowPageTitle: when done showing the page title <h1>
|
||||
- $action: action being shown
|
||||
|
||||
StartDeleteOwnNotice: when a user starts to delete their own notice
|
||||
- $user: the user doing the delete
|
||||
- $notice: the notice being deleted
|
||||
|
||||
EndDeleteOwnNotice: when a user has deleted their own notice
|
||||
- $user: the user doing the delete
|
||||
- $notice: the notice being deleted
|
||||
|
89
README
89
README
@ -2,8 +2,8 @@
|
||||
README
|
||||
------
|
||||
|
||||
StatusNet 0.9.1 ("Everybody Hurts")
|
||||
28 Mar 2010
|
||||
StatusNet 0.9.5 "What's The Frequency, Kenneth?"
|
||||
10 September 2010
|
||||
|
||||
This is the README file for StatusNet, the Open Source microblogging
|
||||
platform. It includes installation instructions, descriptions of
|
||||
@ -38,11 +38,16 @@ more, please see the Open Software Service Definition 1.1:
|
||||
http://www.opendefinition.org/ossd
|
||||
|
||||
StatusNet, Inc. <http://status.net/> also offers this software as a
|
||||
Web service, requiring no installation on your part. The software run
|
||||
Web service, requiring no installation on your part. See
|
||||
<http://status.net/signup> for details. The software run
|
||||
on status.net is identical to the software available for download, so
|
||||
you can move back and forth between a hosted version or a version
|
||||
installed on your own servers.
|
||||
|
||||
A commercial software subscription is available from StatusNet Inc. It
|
||||
includes 24-hour technical support and developer support. More
|
||||
information at http://status.net/contact or email sales@status.net.
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
@ -68,6 +73,20 @@ License along with this program, in the file "COPYING". If not, see
|
||||
of using the software, and if you do not wish to share your
|
||||
modifications, *YOU MAY NOT INSTALL STATUSNET*.
|
||||
|
||||
Documentation in the /doc-src/ directory is available under the
|
||||
Creative Commons Attribution 3.0 Unported license, with attribution to
|
||||
"StatusNet". See http://creativecommons.org/licenses/by/3.0/ for details.
|
||||
|
||||
CSS and images in the /theme/ directory are available under the
|
||||
Creative Commons Attribution 3.0 Unported license, with attribution to
|
||||
"StatusNet". See http://creativecommons.org/licenses/by/3.0/ for details.
|
||||
|
||||
Our understanding and intention is that if you add your own theme that
|
||||
uses only CSS and images, those files are not subject to the copyleft
|
||||
requirements of the Affero General Public License 3.0. See
|
||||
http://wordpress.org/news/2009/07/themes-are-gpl-too/ . This is not
|
||||
legal advice; consult your lawyer.
|
||||
|
||||
Additional library software has been made available in the 'extlib'
|
||||
directory. All of it is Free Software and can be distributed under
|
||||
liberal terms, but those terms may differ in detail from the AGPL's
|
||||
@ -77,34 +96,33 @@ for additional terms.
|
||||
New this version
|
||||
================
|
||||
|
||||
This is a minor bug and feature release since version 0.9.0 released 4
|
||||
March 2010.
|
||||
This is a security, bug and feature release since version 0.9.4 released on
|
||||
16 August 2010.
|
||||
|
||||
Because of fixes to OStatus bugs, it is highly recommended that all
|
||||
public sites upgrade to the new version immediately.
|
||||
For best compatibility with client software and site federation, and a lot of
|
||||
bug fixes, it is highly recommended that all public sites upgrade to the new
|
||||
version.
|
||||
|
||||
Notable changes this version:
|
||||
|
||||
- Twitter bridge truncates and links back to original for long
|
||||
notices.
|
||||
- Changed "Home" link in main menu to "Personal".
|
||||
- A new memcached plugin (using pecl/memcached versus pecl/memcache)
|
||||
- Opt-in subscription to update@status.net
|
||||
- Script to run commands on behalf of a user.
|
||||
- Better Web UI for long notices.
|
||||
- A plugin to open external links in their own window or tab
|
||||
- Fixes to Salmon protocol for compatibility with other systems.
|
||||
- Updates to latest ActivityStreams definition.
|
||||
- Twitpic-compatible API for image upload.
|
||||
- Background deletion of user accounts.
|
||||
- Better support for HTTP basic authentication with CGI/FastCGI
|
||||
- Better discovery on OStatus
|
||||
- Support for PuSH-enabled RSS 2.0 feeds
|
||||
- OpenID-only mode
|
||||
- OpenID blacklist/whitelist
|
||||
- OStatus unit tests
|
||||
- Change of license for default themes and documentation from
|
||||
AGPLv3 to CC-By 3.0 Unported.
|
||||
- An experimental TinyMCE plugin to do in-browser rich editing of
|
||||
status updates. Does not support StatusNet syntax like @-replies or
|
||||
#hashtags very well.
|
||||
- An experimental plugin to add titles to notices.
|
||||
- A plugin to support the Echo <http://aboutecho.com/> commenting
|
||||
system.
|
||||
- A plugin to support the Disqus <http://disqus.com/> commenting system.
|
||||
- Changes to OStatus support to make StatusNet work for the Social Web
|
||||
Acid Test Level 0 <http://federatedsocialweb.net/wiki/SWAT0>.
|
||||
- Themes now support a theme.ini file for theme configuration, including
|
||||
defining a "base" theme.
|
||||
- Improved two-way Twitter integration, including support for
|
||||
repeats and retweets, replies, and faves going both ways across the
|
||||
bridge, as well as better parsing of Twitter statuses.
|
||||
|
||||
A full changelog is available at http://status.net/wiki/StatusNet_0.9.1.
|
||||
A full changelog is available at http://status.net/wiki/StatusNet_0.9.5.
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
@ -115,8 +133,8 @@ run correctly.
|
||||
- PHP 5.2.3+. It may be possible to run this software on earlier
|
||||
versions of PHP, but many of the functions used are only available
|
||||
in PHP 5.2 or above. 5.2.6 or later is needed for XMPP background
|
||||
daemons on 64-bit platforms. PHP 5.3.x should work but is known
|
||||
to cause some failures for OpenID.
|
||||
daemons on 64-bit platforms. PHP 5.3.x should work correctly in this
|
||||
release, but problems with some plugins are possible.
|
||||
- MySQL 5.x. The StatusNet database is stored, by default, in a MySQL
|
||||
server. It has been primarily tested on 5.x servers, although it may
|
||||
be possible to install on earlier (or later!) versions. The server
|
||||
@ -132,7 +150,6 @@ Your PHP installation must include the following PHP extensions:
|
||||
- MySQL. For accessing the database.
|
||||
- GD. For scaling down avatar images.
|
||||
- mbstring. For handling Unicode (UTF-8) encoded strings.
|
||||
- gettext. For multiple languages. Default on many PHP installs.
|
||||
|
||||
For some functionality, you will also need the following extensions:
|
||||
|
||||
@ -147,6 +164,8 @@ For some functionality, you will also need the following extensions:
|
||||
Sphinx server to serve the search queries.
|
||||
- bcmath or gmp. For Salmon signatures (part of OStatus). Needed
|
||||
if you have OStatus configured.
|
||||
- gettext. For multiple languages. Default on many PHP installs;
|
||||
will be emulated if not present.
|
||||
|
||||
You will almost definitely get 2-3 times better performance from your
|
||||
site if you install a PHP bytecode cache/accelerator. Some well-known
|
||||
@ -216,9 +235,9 @@ especially if you've previously installed PHP/MySQL packages.
|
||||
1. Unpack the tarball you downloaded on your Web server. Usually a
|
||||
command like this will work:
|
||||
|
||||
tar zxf statusnet-0.9.1.tar.gz
|
||||
tar zxf statusnet-0.9.5.tar.gz
|
||||
|
||||
...which will make a statusnet-0.9.1 subdirectory in your current
|
||||
...which will make a statusnet-0.9.5 subdirectory in your current
|
||||
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
|
||||
files to the server.)
|
||||
@ -226,7 +245,7 @@ especially if you've previously installed PHP/MySQL packages.
|
||||
2. Move the tarball to a directory of your choosing in your Web root
|
||||
directory. Usually something like this will work:
|
||||
|
||||
mv statusnet-0.9.1 /var/www/statusnet
|
||||
mv statusnet-0.9.5 /var/www/statusnet
|
||||
|
||||
This will make your StatusNet instance available in the statusnet path of
|
||||
your server, like "http://example.net/statusnet". "microblog" or
|
||||
@ -641,7 +660,7 @@ with this situation.
|
||||
If you've been using StatusNet 0.7, 0.6, 0.5 or lower, or if you've
|
||||
been tracking the "git" version of the software, you will probably
|
||||
want to upgrade and keep your existing data. There is no automated
|
||||
upgrade procedure in StatusNet 0.9.1. Try these step-by-step
|
||||
upgrade procedure in StatusNet 0.9.5. Try these step-by-step
|
||||
instructions; read to the end first before trying them.
|
||||
|
||||
0. Download StatusNet and set up all the prerequisites as if you were
|
||||
@ -662,7 +681,7 @@ instructions; read to the end first before trying them.
|
||||
5. Once all writing processes to your site are turned off, make a
|
||||
final backup of the Web directory and database.
|
||||
6. Move your StatusNet directory to a backup spot, like "statusnet.bak".
|
||||
7. Unpack your StatusNet 0.9.1 tarball and move it to "statusnet" or
|
||||
7. Unpack your StatusNet 0.9.5 tarball and move it to "statusnet" or
|
||||
wherever your code used to be.
|
||||
8. Copy the config.php file and avatar directory from your old
|
||||
directory to your new directory.
|
||||
@ -1539,7 +1558,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
|
||||
conflicts in your code.
|
||||
|
||||
If you upgraded to StatusNet 0.9.1 without reading the "Notice
|
||||
If you upgraded to StatusNet 0.9.5 without reading the "Notice
|
||||
inboxes" section above, and all your users' 'Personal' tabs are empty,
|
||||
read the "Notice inboxes" section above.
|
||||
|
||||
|
@ -18,15 +18,19 @@
|
||||
*
|
||||
* @category Actions
|
||||
* @package Actions
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Mike Cochrane <mikec@mikenz.geek.nz>
|
||||
* @author Robin Millette <millette@controlyourself.ca>
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @author Meitar Moscovitz <meitarm@gmail.com>
|
||||
* @author Sarven Capadisli <csarven@status.net>
|
||||
* @author Brenda Wallace <shiny@cpan.org>
|
||||
* @author Brion Vibber <brion@pobox.com>
|
||||
* @author Craig Andrews <candrews@integralblue.com>
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Jeffery To <jeffery.to@gmail.com>
|
||||
* @author Zach Copley <zach@controlyourself.ca>
|
||||
* @author Meitar Moscovitz <meitarm@gmail.com>
|
||||
* @author Mike Cochrane <mikec@mikenz.geek.nz>
|
||||
* @author Robin Millette <millette@status.net>
|
||||
* @author Sarven Capadisli <csarven@status.net>
|
||||
* @author Siebrand Mazeland <s.mazeland@xs4all.nl>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license GNU Affero General Public License http://www.gnu.org/licenses/
|
||||
* @link http://status.net
|
||||
*/
|
||||
@ -139,10 +143,10 @@ class AllAction extends ProfileAction
|
||||
$message .= _('Try subscribing to more people, [join a group](%%action.groups%%) or post something yourself.');
|
||||
} else {
|
||||
// TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@"
|
||||
$message .= sprintf(_('You can try to [nudge %1$s](../%2$s) from his profile or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname);
|
||||
$message .= sprintf(_('You can try to [nudge %1$s](../%2$s) from their profile or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$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);
|
||||
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->user->nickname);
|
||||
}
|
||||
|
||||
$this->elementStart('div', 'guide');
|
||||
|
@ -21,8 +21,10 @@
|
||||
*
|
||||
* @category API
|
||||
* @package StatusNet
|
||||
* @author Brion Vibber <brion@pobox.com>
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Robin Millette <robin@millette.info>
|
||||
* @author Siebrand Mazeland <s.mazeland@xs4all.nl>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
|
@ -21,6 +21,7 @@
|
||||
*
|
||||
* @category API
|
||||
* @package StatusNet
|
||||
* @author Siebrand Mazeland <s.mazeland@xs4all.nl>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
|
@ -22,7 +22,7 @@
|
||||
* @category API
|
||||
* @package StatusNet
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009-2010 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -131,7 +131,7 @@ class ApiAccountUpdateProfileColorsAction extends ApiAuthAction
|
||||
try {
|
||||
$this->setColors($design);
|
||||
} catch (WebColorException $e) {
|
||||
$this->clientError($e->getMessage());
|
||||
$this->clientError($e->getMessage(), 400, $this->format);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -153,7 +153,7 @@ class ApiAccountUpdateProfileColorsAction extends ApiAuthAction
|
||||
try {
|
||||
$this->setColors($design);
|
||||
} catch (WebColorException $e) {
|
||||
$this->clientError($e->getMessage());
|
||||
$this->clientError($e->getMessage(), 400, $this->format);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ class ApiAccountVerifyCredentialsAction extends ApiAuthAction
|
||||
|
||||
if ($this->format == 'xml') {
|
||||
$this->initDocument('xml');
|
||||
$this->showTwitterXmlUser($twitter_user);
|
||||
$this->showTwitterXmlUser($twitter_user, 'user', true);
|
||||
$this->endDocument('xml');
|
||||
} elseif ($this->format == 'json') {
|
||||
$this->initDocument('json');
|
||||
|
@ -23,7 +23,7 @@
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009-2010 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -65,7 +65,7 @@ class ApiBlockCreateAction extends ApiAuthAction
|
||||
parent::prepare($args);
|
||||
|
||||
$this->user = $this->auth_user;
|
||||
$this->other = $this->getTargetUser($this->arg('id'));
|
||||
$this->other = $this->getTargetProfile($this->arg('id'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009-2010 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -64,7 +64,7 @@ class ApiBlockDestroyAction extends ApiAuthAction
|
||||
parent::prepare($args);
|
||||
|
||||
$this->user = $this->auth_user;
|
||||
$this->other = $this->getTargetUser($this->arg('id'));
|
||||
$this->other = $this->getTargetProfile($this->arg('id'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -232,7 +232,8 @@ class ApiDirectMessageAction extends ApiAuthAction
|
||||
function showXmlDirectMessages()
|
||||
{
|
||||
$this->initDocument('xml');
|
||||
$this->elementStart('direct-messages', array('type' => 'array'));
|
||||
$this->elementStart('direct-messages', array('type' => 'array',
|
||||
'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
|
||||
|
||||
foreach ($this->messages as $m) {
|
||||
$dm_array = $this->directMessageArray($m);
|
||||
|
@ -52,7 +52,6 @@ require_once INSTALLDIR . '/lib/apiauth.php';
|
||||
|
||||
class ApiDirectMessageNewAction extends ApiAuthAction
|
||||
{
|
||||
var $source = null;
|
||||
var $other = null;
|
||||
var $content = null;
|
||||
|
||||
@ -76,13 +75,6 @@ class ApiDirectMessageNewAction extends ApiAuthAction
|
||||
return;
|
||||
}
|
||||
|
||||
$this->source = $this->trimmed('source'); // Not supported by Twitter.
|
||||
|
||||
$reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api');
|
||||
if (empty($this->source) || in_array($this->source, $reserved_sources)) {
|
||||
$source = 'api';
|
||||
}
|
||||
|
||||
$this->content = $this->trimmed('text');
|
||||
|
||||
$this->user = $this->auth_user;
|
||||
|
@ -25,6 +25,7 @@
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
@ -25,6 +25,7 @@
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
@ -24,7 +24,7 @@
|
||||
* @author Dan Moore <dan@moore.cx>
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009-2010 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -67,7 +67,7 @@ class ApiFriendshipsCreateAction extends ApiAuthAction
|
||||
parent::prepare($args);
|
||||
|
||||
$this->user = $this->auth_user;
|
||||
$this->other = $this->getTargetUser($id);
|
||||
$this->other = $this->getTargetProfile($this->arg('id'));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -106,7 +106,7 @@ class ApiFriendshipsCreateAction extends ApiAuthAction
|
||||
|
||||
if (empty($this->other)) {
|
||||
$this->clientError(
|
||||
_('Could not follow user: User not found.'),
|
||||
_('Could not follow user: profile not found.'),
|
||||
403,
|
||||
$this->format
|
||||
);
|
||||
|
@ -24,7 +24,7 @@
|
||||
* @author Dan Moore <dan@moore.cx>
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009-2010 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -67,7 +67,7 @@ class ApiFriendshipsDestroyAction extends ApiAuthAction
|
||||
parent::prepare($args);
|
||||
|
||||
$this->user = $this->auth_user;
|
||||
$this->other = $this->getTargetUser($id);
|
||||
$this->other = $this->getTargetProfile($this->arg('id'));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -125,8 +125,7 @@ class ApiFriendshipsDestroyAction extends ApiAuthAction
|
||||
}
|
||||
|
||||
// throws an exception on error
|
||||
Subscription::cancel($this->user->getProfile(),
|
||||
$this->other->getProfile());
|
||||
Subscription::cancel($this->user->getProfile(), $this->other);
|
||||
|
||||
$this->initDocument($this->format);
|
||||
$this->showProfile($this->other, $this->format);
|
||||
|
@ -24,7 +24,7 @@
|
||||
* @author Dan Moore <dan@moore.cx>
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009-2010 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -50,8 +50,8 @@ require_once INSTALLDIR . '/lib/apiprivateauth.php';
|
||||
|
||||
class ApiFriendshipsExistsAction extends ApiPrivateAuthAction
|
||||
{
|
||||
var $user_a = null;
|
||||
var $user_b = null;
|
||||
var $profile_a = null;
|
||||
var $profile_b = null;
|
||||
|
||||
/**
|
||||
* Take arguments for running
|
||||
@ -66,11 +66,8 @@ class ApiFriendshipsExistsAction extends ApiPrivateAuthAction
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
$user_a_id = $this->trimmed('user_a');
|
||||
$user_b_id = $this->trimmed('user_b');
|
||||
|
||||
$this->user_a = $this->getTargetUser($user_a_id);
|
||||
$this->user_b = $this->getTargetUser($user_b_id);
|
||||
$this->profile_a = $this->getTargetProfile($this->trimmed('user_a'));
|
||||
$this->profile_b = $this->getTargetProfile($this->trimmed('user_b'));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -89,16 +86,16 @@ class ApiFriendshipsExistsAction extends ApiPrivateAuthAction
|
||||
{
|
||||
parent::handle($args);
|
||||
|
||||
if (empty($this->user_a) || empty($this->user_b)) {
|
||||
if (empty($this->profile_a) || empty($this->profile_b)) {
|
||||
$this->clientError(
|
||||
_('Two user ids or screen_names must be supplied.'),
|
||||
_('Two valid IDs or screen_names must be supplied.'),
|
||||
400,
|
||||
$this->format
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $this->user_a->isSubscribed($this->user_b);
|
||||
$result = Subscription::exists($this->profile_a, $this->profile_b);
|
||||
|
||||
switch ($this->format) {
|
||||
case 'xml':
|
||||
|
@ -26,6 +26,7 @@
|
||||
* @author Jeffery To <jeffery.to@gmail.com>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
@ -26,6 +26,7 @@
|
||||
* @author Jeffery To <jeffery.to@gmail.com>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
@ -26,6 +26,7 @@
|
||||
* @author Jeffery To <jeffery.to@gmail.com>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
@ -26,6 +26,7 @@
|
||||
* @author Jeffery To <jeffery.to@gmail.com>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
@ -26,6 +26,7 @@
|
||||
* @author Jeffery To <jeffery.to@gmail.com>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
@ -26,6 +26,7 @@
|
||||
* @author Jeffery To <jeffery.to@gmail.com>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
@ -26,6 +26,7 @@
|
||||
* @author Jeffery To <jeffery.to@gmail.com>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
367
actions/apigroupprofileupdate.php
Normal file
367
actions/apigroupprofileupdate.php
Normal file
@ -0,0 +1,367 @@
|
||||
<?php
|
||||
/**
|
||||
* StatusNet, the distributed open-source microblogging tool
|
||||
*
|
||||
* Update a group's profile
|
||||
*
|
||||
* 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 API
|
||||
* @package StatusNet
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2010 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once INSTALLDIR . '/lib/apiauth.php';
|
||||
|
||||
/**
|
||||
* API analog to the group edit page
|
||||
*
|
||||
* @category API
|
||||
* @package StatusNet
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
class ApiGroupProfileUpdateAction extends ApiAuthAction
|
||||
{
|
||||
|
||||
/**
|
||||
* Take arguments for running
|
||||
*
|
||||
* @param array $args $_REQUEST args
|
||||
*
|
||||
* @return boolean success flag
|
||||
*
|
||||
*/
|
||||
|
||||
function prepare($args)
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
$this->nickname = common_canonical_nickname($this->trimmed('nickname'));
|
||||
|
||||
$this->fullname = $this->trimmed('fullname');
|
||||
$this->homepage = $this->trimmed('homepage');
|
||||
$this->description = $this->trimmed('description');
|
||||
$this->location = $this->trimmed('location');
|
||||
$this->aliasstring = $this->trimmed('aliases');
|
||||
|
||||
$this->user = $this->auth_user;
|
||||
$this->group = $this->getTargetGroup($this->arg('id'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the request
|
||||
*
|
||||
* See which request params have been set, and update the profile
|
||||
*
|
||||
* @param array $args $_REQUEST data (unused)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function handle($args)
|
||||
{
|
||||
parent::handle($args);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
|
||||
$this->clientError(
|
||||
_('This method requires a POST.'),
|
||||
400, $this->format
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!in_array($this->format, array('xml', 'json'))) {
|
||||
$this->clientError(
|
||||
_('API method not found.'),
|
||||
404,
|
||||
$this->format
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($this->user)) {
|
||||
$this->clientError(_('No such user.'), 404, $this->format);
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($this->group)) {
|
||||
$this->clientError(_('Group not found.'), 404, $this->format);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->user->isAdmin($this->group)) {
|
||||
$this->clientError(_('You must be an admin to edit the group.'), 403);
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->group->query('BEGIN');
|
||||
|
||||
$orig = clone($this->group);
|
||||
|
||||
try {
|
||||
|
||||
if (!empty($this->nickname)) {
|
||||
if ($this->validateNickname()) {
|
||||
$this->group->nickname = $this->nickname;
|
||||
$this->group->mainpage = common_local_url(
|
||||
'showgroup',
|
||||
array('nickname' => $this->nickname)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($this->fullname)) {
|
||||
$this->validateFullname();
|
||||
$this->group->fullname = $this->fullname;
|
||||
}
|
||||
|
||||
if (!empty($this->homepage)) {
|
||||
$this->validateHomepage();
|
||||
$this->group->homepage = $this->hompage;
|
||||
}
|
||||
|
||||
if (!empty($this->description)) {
|
||||
$this->validateDescription();
|
||||
$this->group->description = $this->decription;
|
||||
}
|
||||
|
||||
if (!empty($this->location)) {
|
||||
$this->validateLocation();
|
||||
$this->group->location = $this->location;
|
||||
}
|
||||
|
||||
} catch (ApiValidationException $ave) {
|
||||
$this->clientError(
|
||||
$ave->getMessage(),
|
||||
403,
|
||||
$this->format
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $this->group->update($orig);
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($this->group, 'UPDATE', __FILE__);
|
||||
$this->serverError(_('Could not update group.'));
|
||||
}
|
||||
|
||||
$aliases = array();
|
||||
|
||||
try {
|
||||
|
||||
if (!empty($this->aliasstring)) {
|
||||
$aliases = $this->validateAliases();
|
||||
}
|
||||
|
||||
} catch (ApiValidationException $ave) {
|
||||
$this->clientError(
|
||||
$ave->getMessage(),
|
||||
403,
|
||||
$this->format
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $this->group->setAliases($aliases);
|
||||
|
||||
if (!$result) {
|
||||
$this->serverError(_('Could not create aliases.'));
|
||||
}
|
||||
|
||||
if (!empty($this->nickname) && ($this->nickname != $orig->nickname)) {
|
||||
common_log(LOG_INFO, "Saving local group info.");
|
||||
$local = Local_group::staticGet('group_id', $this->group->id);
|
||||
$local->setNickname($this->nickname);
|
||||
}
|
||||
|
||||
$this->group->query('COMMIT');
|
||||
|
||||
switch($this->format) {
|
||||
case 'xml':
|
||||
$this->showSingleXmlGroup($this->group);
|
||||
break;
|
||||
case 'json':
|
||||
$this->showSingleJsonGroup($this->group);
|
||||
break;
|
||||
default:
|
||||
$this->clientError(_('API method not found.'), 404, $this->format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function nicknameExists($nickname)
|
||||
{
|
||||
$group = Local_group::staticGet('nickname', $nickname);
|
||||
|
||||
if (!empty($group) &&
|
||||
$group->group_id != $this->group->id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$alias = Group_alias::staticGet('alias', $nickname);
|
||||
|
||||
if (!empty($alias) &&
|
||||
$alias->group_id != $this->group->id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function validateNickname()
|
||||
{
|
||||
if (!Validate::string(
|
||||
$this->nickname, array(
|
||||
'min_length' => 1,
|
||||
'max_length' => 64,
|
||||
'format' => NICKNAME_FMT
|
||||
)
|
||||
)
|
||||
) {
|
||||
throw new ApiValidationException(
|
||||
_(
|
||||
'Nickname must have only lowercase letters ' .
|
||||
'and numbers and no spaces.'
|
||||
)
|
||||
);
|
||||
} else if ($this->nicknameExists($this->nickname)) {
|
||||
throw new ApiValidationException(
|
||||
_('Nickname already in use. Try another one.')
|
||||
);
|
||||
} else if (!User_group::allowedNickname($this->nickname)) {
|
||||
throw new ApiValidationException(
|
||||
_('Not a valid nickname.')
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function validateHomepage()
|
||||
{
|
||||
if (!is_null($this->homepage)
|
||||
&& (strlen($this->homepage) > 0)
|
||||
&& !Validate::uri(
|
||||
$this->homepage,
|
||||
array('allowed_schemes' => array('http', 'https')
|
||||
)
|
||||
)
|
||||
) {
|
||||
throw new ApiValidationException(
|
||||
_('Homepage is not a valid URL.')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function validateFullname()
|
||||
{
|
||||
if (!is_null($this->fullname) && mb_strlen($this->fullname) > 255) {
|
||||
throw new ApiValidationException(
|
||||
_('Full name is too long (max 255 chars).')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function validateDescription()
|
||||
{
|
||||
if (User_group::descriptionTooLong($this->description)) {
|
||||
throw new ApiValidationException(
|
||||
sprintf(
|
||||
_('description is too long (max %d chars).'),
|
||||
User_group::maxDescription()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function validateLocation()
|
||||
{
|
||||
if (!is_null($this->location) && mb_strlen($this->location) > 255) {
|
||||
throw new ApiValidationException(
|
||||
_('Location is too long (max 255 chars).')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function validateAliases()
|
||||
{
|
||||
$aliases = array_map(
|
||||
'common_canonical_nickname',
|
||||
array_unique(
|
||||
preg_split('/[\s,]+/',
|
||||
$this->aliasstring
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (count($aliases) > common_config('group', 'maxaliases')) {
|
||||
throw new ApiValidationException(
|
||||
sprintf(
|
||||
_('Too many aliases! Maximum %d.'),
|
||||
common_config('group', 'maxaliases')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($aliases as $alias) {
|
||||
if (!Validate::string(
|
||||
$alias, array(
|
||||
'min_length' => 1,
|
||||
'max_length' => 64,
|
||||
'format' => NICKNAME_FMT)
|
||||
)
|
||||
) {
|
||||
throw new ApiValidationException(
|
||||
sprintf(
|
||||
_('Invalid alias: "%s"'),
|
||||
$alias
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->nicknameExists($alias)) {
|
||||
throw new ApiValidationException(
|
||||
sprintf(
|
||||
_('Alias "%s" already in use. Try another one.'),
|
||||
$alias)
|
||||
);
|
||||
}
|
||||
|
||||
// XXX assumes alphanum nicknames
|
||||
if (strcmp($alias, $this->nickname) == 0) {
|
||||
throw new ApiValidationException(
|
||||
_('Alias can\'t be the same as nickname.')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $aliases;
|
||||
}
|
||||
|
||||
}
|
@ -26,6 +26,7 @@
|
||||
* @author Jeffery To <jeffery.to@gmail.com>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
@ -88,15 +88,15 @@ class ApiMediaUploadAction extends ApiAuthAction
|
||||
|
||||
try {
|
||||
$upload = MediaFile::fromUpload('media', $this->auth_user);
|
||||
} catch (ClientException $ce) {
|
||||
$this->clientError($ce->getMessage());
|
||||
} catch (Exception $e) {
|
||||
$this->clientError($e->getMessage(), $e->getCode());
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($upload)) {
|
||||
$this->showResponse($upload);
|
||||
} else {
|
||||
$this->clientError('Upload failed.');
|
||||
$this->clientError(_('Upload failed.'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
* @category Search
|
||||
* @package StatusNet
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2008-2009 StatusNet, Inc.
|
||||
* @copyright 2008-2010 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -31,6 +31,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once INSTALLDIR.'/lib/apiprivateauth.php';
|
||||
|
||||
/**
|
||||
* Action for outputting search results in Twitter compatible Atom
|
||||
* format.
|
||||
@ -44,10 +46,10 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*
|
||||
* @see ApiAction
|
||||
* @see ApiPrivateAuthAction
|
||||
*/
|
||||
|
||||
class TwitapisearchatomAction extends ApiAction
|
||||
class ApiSearchAtomAction extends ApiPrivateAuthAction
|
||||
{
|
||||
|
||||
var $cnt;
|
||||
@ -96,8 +98,11 @@ class TwitapisearchatomAction extends ApiAction
|
||||
|
||||
function prepare($args)
|
||||
{
|
||||
common_debug("in apisearchatom prepare()");
|
||||
|
||||
parent::prepare($args);
|
||||
|
||||
|
||||
$this->query = $this->trimmed('q');
|
||||
$this->lang = $this->trimmed('lang');
|
||||
$this->rpp = $this->trimmed('rpp');
|
||||
@ -138,6 +143,7 @@ class TwitapisearchatomAction extends ApiAction
|
||||
function handle($args)
|
||||
{
|
||||
parent::handle($args);
|
||||
common_debug("In apisearchatom handle()");
|
||||
$this->showAtom();
|
||||
}
|
||||
|
||||
@ -342,10 +348,24 @@ class TwitapisearchatomAction extends ApiAction
|
||||
'rel' => 'related',
|
||||
'href' => $profile->avatarUrl()));
|
||||
|
||||
// TODO: Here is where we'd put in a link to an atom feed for threads
|
||||
// @todo: Here is where we'd put in a link to an atom feed for threads
|
||||
|
||||
$this->element("twitter:source", null,
|
||||
htmlentities($this->sourceLink($notice->source)));
|
||||
$source = null;
|
||||
|
||||
$ns = $notice->getSource();
|
||||
if ($ns) {
|
||||
if (!empty($ns->name) && !empty($ns->url)) {
|
||||
$source = '<a href="'
|
||||
. htmlspecialchars($ns->url)
|
||||
. '" rel="nofollow">'
|
||||
. htmlspecialchars($ns->name)
|
||||
. '</a>';
|
||||
} else {
|
||||
$source = $ns->code;
|
||||
}
|
||||
}
|
||||
|
||||
$this->element("twitter:source", null, $source);
|
||||
|
||||
$this->elementStart('author');
|
||||
|
@ -22,7 +22,7 @@
|
||||
* @category Search
|
||||
* @package StatusNet
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2008-2009 StatusNet, Inc.
|
||||
* @copyright 2008-2010 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -31,6 +31,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once INSTALLDIR.'/lib/apiprivateauth.php';
|
||||
require_once INSTALLDIR.'/lib/jsonsearchresultslist.php';
|
||||
|
||||
/**
|
||||
@ -44,7 +45,7 @@ require_once INSTALLDIR.'/lib/jsonsearchresultslist.php';
|
||||
* @see ApiAction
|
||||
*/
|
||||
|
||||
class TwitapisearchjsonAction extends ApiAction
|
||||
class ApiSearchJSONAction extends ApiPrivateAuthAction
|
||||
{
|
||||
var $query;
|
||||
var $lang;
|
||||
@ -64,6 +65,8 @@ class TwitapisearchjsonAction extends ApiAction
|
||||
|
||||
function prepare($args)
|
||||
{
|
||||
common_debug("apisearchjson prepare()");
|
||||
|
||||
parent::prepare($args);
|
||||
|
||||
$this->query = $this->trimmed('q');
|
@ -29,6 +29,7 @@
|
||||
* @author Robin Millette <robin@millette.info>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -57,7 +58,7 @@ require_once INSTALLDIR . '/lib/apiauth.php';
|
||||
|
||||
class ApiStatusesDestroyAction extends ApiAuthAction
|
||||
{
|
||||
var $status = null;
|
||||
var $status = null;
|
||||
|
||||
/**
|
||||
* Take arguments for running
|
||||
@ -99,39 +100,43 @@ class ApiStatusesDestroyAction extends ApiAuthAction
|
||||
parent::handle($args);
|
||||
|
||||
if (!in_array($this->format, array('xml', 'json'))) {
|
||||
$this->clientError(_('API method not found.'), $code = 404);
|
||||
return;
|
||||
$this->clientError(
|
||||
_('API method not found.'),
|
||||
404
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
|
||||
$this->clientError(_('This method requires a POST or DELETE.'),
|
||||
400, $this->format);
|
||||
return;
|
||||
}
|
||||
if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
|
||||
$this->clientError(
|
||||
_('This method requires a POST or DELETE.'),
|
||||
400,
|
||||
$this->format
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($this->notice)) {
|
||||
$this->clientError(_('No status found with that ID.'),
|
||||
404, $this->format);
|
||||
return;
|
||||
}
|
||||
if (empty($this->notice)) {
|
||||
$this->clientError(
|
||||
_('No status found with that ID.'),
|
||||
404, $this->format
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->user->id == $this->notice->profile_id) {
|
||||
$replies = new Reply;
|
||||
$replies->get('notice_id', $this->notice_id);
|
||||
$replies->delete();
|
||||
$this->notice->delete();
|
||||
|
||||
if ($this->format == 'xml') {
|
||||
$this->showSingleXmlStatus($this->notice);
|
||||
} elseif ($this->format == 'json') {
|
||||
$this->show_single_json_status($this->notice);
|
||||
}
|
||||
} else {
|
||||
$this->clientError(_('You may not delete another user\'s status.'),
|
||||
403, $this->format);
|
||||
}
|
||||
|
||||
$this->showNotice();
|
||||
if ($this->user->id == $this->notice->profile_id) {
|
||||
if (Event::handle('StartDeleteOwnNotice', array($this->user, $this->notice))) {
|
||||
$this->notice->delete();
|
||||
Event::handle('EndDeleteOwnNotice', array($this->user, $this->notice));
|
||||
}
|
||||
$this->showNotice();
|
||||
} else {
|
||||
$this->clientError(
|
||||
_('You may not delete another user\'s status.'),
|
||||
403,
|
||||
$this->format
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,7 +79,7 @@ class ApiStatusesRetweetAction extends ApiAuthAction
|
||||
|
||||
$this->user = $this->auth_user;
|
||||
|
||||
if ($this->user->id == $notice->profile_id) {
|
||||
if ($this->user->id == $this->original->profile_id) {
|
||||
$this->clientError(_('Cannot repeat your own notice.'),
|
||||
400, $this->format);
|
||||
return false;
|
||||
|
@ -29,6 +29,7 @@
|
||||
* @author Robin Millette <robin@millette.info>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
@ -29,10 +29,102 @@
|
||||
* @author Robin Millette <robin@millette.info>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009-2010 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
/* External API usage documentation. Please update when you change how this method works. */
|
||||
|
||||
/*! @page statusesupdate statuses/update
|
||||
|
||||
@section Description
|
||||
Updates the authenticating user's status. Requires the status parameter specified below.
|
||||
Request must be a POST.
|
||||
|
||||
@par URL pattern
|
||||
/api/statuses/update.:format
|
||||
|
||||
@par Formats (:format)
|
||||
xml, json
|
||||
|
||||
@par HTTP Method(s)
|
||||
POST
|
||||
|
||||
@par Requires Authentication
|
||||
Yes
|
||||
|
||||
@param status (Required) The URL-encoded text of the status update.
|
||||
@param source (Optional) The source of the status.
|
||||
@param in_reply_to_status_id (Optional) The ID of an existing status that the update is in reply to.
|
||||
@param lat (Optional) The latitude the status refers to.
|
||||
@param long (Optional) The longitude the status refers to.
|
||||
@param media (Optional) a media upload, such as an image or movie file.
|
||||
|
||||
@sa @ref authentication
|
||||
@sa @ref apiroot
|
||||
|
||||
@subsection usagenotes Usage notes
|
||||
|
||||
@li The URL pattern is relative to the @ref apiroot.
|
||||
@li If the @e source parameter is not supplied the source of the status will default to 'api'.
|
||||
@li The XML response uses <a href="http://georss.org/Main_Page">GeoRSS</a>
|
||||
to encode the latitude and longitude (see example response below <georss:point>).
|
||||
@li Data uploaded via the @e media parameter should be multipart/form-data encoded.
|
||||
|
||||
@subsection exampleusage Example usage
|
||||
|
||||
@verbatim
|
||||
curl -u username:password http://example.com/api/statuses/update.xml -d status='Howdy!' -d lat='30.468' -d long='-94.743'
|
||||
@endverbatim
|
||||
|
||||
@subsection exampleresponse Example response
|
||||
|
||||
@verbatim
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<status>
|
||||
<text>Howdy!</text>
|
||||
<truncated>false</truncated>
|
||||
<created_at>Tue Mar 30 23:28:05 +0000 2010</created_at>
|
||||
<in_reply_to_status_id/>
|
||||
<source>api</source>
|
||||
<id>26668724</id>
|
||||
<in_reply_to_user_id/>
|
||||
<in_reply_to_screen_name/>
|
||||
<geo xmlns:georss="http://www.georss.org/georss">
|
||||
<georss:point>30.468 -94.743</georss:point>
|
||||
</geo>
|
||||
<favorited>false</favorited>
|
||||
<user>
|
||||
<id>25803</id>
|
||||
<name>Jed Sanders</name>
|
||||
<screen_name>jedsanders</screen_name>
|
||||
<location>Hoop and Holler, Texas</location>
|
||||
<description>I like to think of myself as America's Favorite.</description>
|
||||
<profile_image_url>http://avatar.example.com/25803-48-20080924200604.png</profile_image_url>
|
||||
<url>http://jedsanders.net</url>
|
||||
<protected>false</protected>
|
||||
<followers_count>5</followers_count>
|
||||
<profile_background_color/>
|
||||
<profile_text_color/>
|
||||
<profile_link_color/>
|
||||
<profile_sidebar_fill_color/>
|
||||
<profile_sidebar_border_color/>
|
||||
<friends_count>2</friends_count>
|
||||
<created_at>Wed Sep 24 20:04:00 +0000 2008</created_at>
|
||||
<favourites_count>0</favourites_count>
|
||||
<utc_offset>0</utc_offset>
|
||||
<time_zone>UTC</time_zone>
|
||||
<profile_background_image_url/>
|
||||
<profile_background_tile>false</profile_background_tile>
|
||||
<statuses_count>70</statuses_count>
|
||||
<following>true</following>
|
||||
<notifications>true</notifications>
|
||||
</user>
|
||||
</status>
|
||||
@endverbatim
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
@ -64,8 +156,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction
|
||||
var $lat = null;
|
||||
var $lon = null;
|
||||
|
||||
static $reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api');
|
||||
|
||||
/**
|
||||
* Take arguments for running
|
||||
*
|
||||
@ -80,19 +170,9 @@ class ApiStatusesUpdateAction extends ApiAuthAction
|
||||
parent::prepare($args);
|
||||
|
||||
$this->status = $this->trimmed('status');
|
||||
$this->source = $this->trimmed('source');
|
||||
$this->lat = $this->trimmed('lat');
|
||||
$this->lon = $this->trimmed('long');
|
||||
|
||||
// try to set the source attr from OAuth app
|
||||
if (empty($this->source)) {
|
||||
$this->source = $this->oauth_source;
|
||||
}
|
||||
|
||||
if (empty($this->source) || in_array($this->source, self::$reserved_sources)) {
|
||||
$this->source = 'api';
|
||||
}
|
||||
|
||||
$this->in_reply_to_status_id
|
||||
= intval($this->trimmed('in_reply_to_status_id'));
|
||||
|
||||
@ -116,7 +196,8 @@ class ApiStatusesUpdateAction extends ApiAuthAction
|
||||
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
|
||||
$this->clientError(
|
||||
_('This method requires a POST.'),
|
||||
400, $this->format
|
||||
400,
|
||||
$this->format
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -137,7 +218,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction
|
||||
|
||||
if (empty($this->status)) {
|
||||
$this->clientError(
|
||||
'Client must provide a \'status\' parameter with a value.',
|
||||
_('Client must provide a \'status\' parameter with a value.'),
|
||||
400,
|
||||
$this->format
|
||||
);
|
||||
@ -211,8 +292,8 @@ class ApiStatusesUpdateAction extends ApiAuthAction
|
||||
|
||||
try {
|
||||
$upload = MediaFile::fromUpload('media', $this->auth_user);
|
||||
} catch (ClientException $ce) {
|
||||
$this->clientError($ce->getMessage());
|
||||
} catch (Exception $e) {
|
||||
$this->clientError($e->getMessage(), $e->getCode(), $this->format);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -225,7 +306,11 @@ class ApiStatusesUpdateAction extends ApiAuthAction
|
||||
'Max notice size is %d chars, ' .
|
||||
'including attachment URL.'
|
||||
);
|
||||
$this->clientError(sprintf($msg, Notice::maxContent()));
|
||||
$this->clientError(
|
||||
sprintf($msg, Notice::maxContent()),
|
||||
400,
|
||||
$this->format
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,7 +337,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction
|
||||
$options
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
$this->clientError($e->getMessage());
|
||||
$this->clientError($e->getMessage(), $e->getCode(), $this->format);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -206,7 +206,8 @@ class ApiSubscriptionsAction extends ApiBareAuthAction
|
||||
{
|
||||
switch ($this->format) {
|
||||
case 'xml':
|
||||
$this->elementStart('users', array('type' => 'array'));
|
||||
$this->elementStart('users', array('type' => 'array',
|
||||
'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
|
||||
foreach ($this->profiles as $profile) {
|
||||
$this->showProfile(
|
||||
$profile,
|
||||
|
@ -25,6 +25,7 @@
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009-2010 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -150,7 +151,7 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction
|
||||
|
||||
header('Content-Type: application/atom+xml; charset=utf-8');
|
||||
|
||||
$atom = new AtomNoticeFeed();
|
||||
$atom = new AtomNoticeFeed($this->auth_user);
|
||||
|
||||
$atom->setId($id);
|
||||
$atom->setTitle($title);
|
||||
@ -185,17 +186,23 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction
|
||||
{
|
||||
$notices = array();
|
||||
|
||||
common_debug("since id = " . $this->since_id . " max id = " . $this->max_id);
|
||||
|
||||
if (!empty($this->auth_user) && $this->auth_user->id == $this->user->id) {
|
||||
$notice = $this->user->favoriteNotices(
|
||||
true,
|
||||
($this->page-1) * $this->count,
|
||||
$this->count,
|
||||
true
|
||||
$this->since_id,
|
||||
$this->max_id
|
||||
);
|
||||
} else {
|
||||
$notice = $this->user->favoriteNotices(
|
||||
false,
|
||||
($this->page-1) * $this->count,
|
||||
$this->count,
|
||||
false
|
||||
$this->since_id,
|
||||
$this->max_id
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -28,11 +28,107 @@
|
||||
* @author Mike Cochrane <mikec@mikenz.geek.nz>
|
||||
* @author Robin Millette <robin@millette.info>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009-2010 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
/* External API usage documentation. Please update when you change how this method works. */
|
||||
|
||||
/*! @page friendstimeline statuses/friends_timeline
|
||||
|
||||
@section Description
|
||||
Returns the 20 most recent statuses posted by the authenticating
|
||||
user and that user's friends. This is the equivalent of "You and
|
||||
friends" page in the web interface.
|
||||
|
||||
@par URL patterns
|
||||
@li /api/statuses/friends_timeline.:format
|
||||
@li /api/statuses/friends_timeline/:id.:format
|
||||
|
||||
@par Formats (:format)
|
||||
xml, json, rss, atom
|
||||
|
||||
@par ID (:id)
|
||||
username, user id
|
||||
|
||||
@par HTTP Method(s)
|
||||
GET
|
||||
|
||||
@par Requires Authentication
|
||||
Sometimes (see: @ref authentication)
|
||||
|
||||
@param user_id (Optional) Specifies a user by ID
|
||||
@param screen_name (Optional) Specifies a user by screename (nickname)
|
||||
@param since_id (Optional) Returns only statuses with an ID greater
|
||||
than (that is, more recent than) the specified ID.
|
||||
@param max_id (Optional) Returns only statuses with an ID less than
|
||||
(that is, older than) or equal to the specified ID.
|
||||
@param count (Optional) Specifies the number of statuses to retrieve.
|
||||
@param page (Optional) Specifies the page of results to retrieve.
|
||||
|
||||
@sa @ref authentication
|
||||
@sa @ref apiroot
|
||||
|
||||
@subsection usagenotes Usage notes
|
||||
@li The URL pattern is relative to the @ref apiroot.
|
||||
@li The XML response uses <a href="http://georss.org/Main_Page">GeoRSS</a>
|
||||
to encode the latitude and longitude (see example response below <georss:point>).
|
||||
|
||||
@subsection exampleusage Example usage
|
||||
|
||||
@verbatim
|
||||
curl http://identi.ca/api/statuses/friends_timeline/evan.xml?count=1&page=2
|
||||
@endverbatim
|
||||
|
||||
@subsection exampleresponse Example response
|
||||
|
||||
@verbatim
|
||||
<?xml version="1.0"?>
|
||||
<statuses type="array">
|
||||
<status>
|
||||
<text>back from the !yul !drupal meet with Evolving Web folk, @anarcat, @webchick and others, and an interesting refresher on SQL indexing</text>
|
||||
<truncated>false</truncated>
|
||||
<created_at>Wed Mar 31 01:33:02 +0000 2010</created_at>
|
||||
<in_reply_to_status_id/>
|
||||
<source><a href="http://code.google.com/p/microblog-purple/">mbpidgin</a></source>
|
||||
<id>26674201</id>
|
||||
<in_reply_to_user_id/>
|
||||
<in_reply_to_screen_name/>
|
||||
<geo/>
|
||||
<favorited>false</favorited>
|
||||
<user>
|
||||
<id>246</id>
|
||||
<name>Mark</name>
|
||||
<screen_name>lambic</screen_name>
|
||||
<location>Montreal, Canada</location>
|
||||
<description>Geek</description>
|
||||
<profile_image_url>http://avatar.identi.ca/246-48-20080702141545.png</profile_image_url>
|
||||
<url>http://lambic.co.uk</url>
|
||||
<protected>false</protected>
|
||||
<followers_count>73</followers_count>
|
||||
<profile_background_color>#F0F2F5</profile_background_color>
|
||||
<profile_text_color/>
|
||||
<profile_link_color>#002E6E</profile_link_color>
|
||||
<profile_sidebar_fill_color>#CEE1E9</profile_sidebar_fill_color>
|
||||
<profile_sidebar_border_color/>
|
||||
<friends_count>58</friends_count>
|
||||
<created_at>Wed Jul 02 14:12:15 +0000 2008</created_at>
|
||||
<favourites_count>2</favourites_count>
|
||||
<utc_offset>-14400</utc_offset>
|
||||
<time_zone>US/Eastern</time_zone>
|
||||
<profile_background_image_url/>
|
||||
<profile_background_tile>false</profile_background_tile>
|
||||
<statuses_count>933</statuses_count>
|
||||
<following>false</following>
|
||||
<notifications>false</notifications>
|
||||
</user>
|
||||
</status>
|
||||
</statuses>
|
||||
@endverbatim
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
@ -153,7 +249,7 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction
|
||||
|
||||
header('Content-Type: application/atom+xml; charset=utf-8');
|
||||
|
||||
$atom = new AtomNoticeFeed();
|
||||
$atom = new AtomNoticeFeed($this->auth_user);
|
||||
|
||||
$atom->setId($id);
|
||||
$atom->setTitle($title);
|
||||
|
@ -25,7 +25,8 @@
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Jeffery To <jeffery.to@gmail.com>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009-2010 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -105,7 +106,7 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
|
||||
function showTimeline()
|
||||
{
|
||||
// We'll pull common formatting out of this for other formats
|
||||
$atom = new AtomGroupNoticeFeed($this->group);
|
||||
$atom = new AtomGroupNoticeFeed($this->group, $this->auth_user);
|
||||
|
||||
$self = $this->getSelfUri();
|
||||
|
||||
@ -137,7 +138,9 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
|
||||
$this->raw($atom->getString());
|
||||
} catch (Atom10FeedException $e) {
|
||||
$this->serverError(
|
||||
'Could not generate feed for group - ' . $e->getMessage()
|
||||
'Could not generate feed for group - ' . $e->getMessage(),
|
||||
400,
|
||||
$this->format
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@
|
||||
* @author Robin Millette <robin@millette.info>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -152,7 +153,7 @@ class ApiTimelineHomeAction extends ApiBareAuthAction
|
||||
|
||||
header('Content-Type: application/atom+xml; charset=utf-8');
|
||||
|
||||
$atom = new AtomNoticeFeed();
|
||||
$atom = new AtomNoticeFeed($this->auth_user);
|
||||
|
||||
$atom->setId($id);
|
||||
$atom->setTitle($title);
|
||||
|
@ -29,6 +29,7 @@
|
||||
* @author Robin Millette <robin@millette.info>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -151,7 +152,7 @@ class ApiTimelineMentionsAction extends ApiBareAuthAction
|
||||
|
||||
header('Content-Type: application/atom+xml; charset=utf-8');
|
||||
|
||||
$atom = new AtomNoticeFeed();
|
||||
$atom = new AtomNoticeFeed($this->auth_user);
|
||||
|
||||
$atom->setId($id);
|
||||
$atom->setTitle($title);
|
||||
|
@ -29,6 +29,7 @@
|
||||
* @author Robin Millette <robin@millette.info>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -55,6 +56,95 @@ require_once INSTALLDIR . '/lib/apiprivateauth.php';
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
/* External API usage documentation. Please update when you change how this method works. */
|
||||
|
||||
/*! @page publictimeline statuses/public_timeline
|
||||
|
||||
@section Description
|
||||
Returns the 20 most recent notices from users throughout the system who have
|
||||
uploaded their own avatars. Depending on configuration, it may or may not
|
||||
not include notices from automatic posting services.
|
||||
|
||||
@par URL patterns
|
||||
@li /api/statuses/public_timeline.:format
|
||||
|
||||
@par Formats (:format)
|
||||
xml, json, rss, atom
|
||||
|
||||
@par HTTP Method(s)
|
||||
GET
|
||||
|
||||
@par Requires Authentication
|
||||
No
|
||||
|
||||
@param since_id (Optional) Returns only statuses with an ID greater
|
||||
than (that is, more recent than) the specified ID.
|
||||
@param max_id (Optional) Returns only statuses with an ID less than
|
||||
(that is, older than) or equal to the specified ID.
|
||||
@param count (Optional) Specifies the number of statuses to retrieve.
|
||||
@param page (Optional) Specifies the page of results to retrieve.
|
||||
|
||||
@sa @ref apiroot
|
||||
|
||||
@subsection usagenotes Usage notes
|
||||
@li The URL pattern is relative to the @ref apiroot.
|
||||
@li The XML response uses <a href="http://georss.org/Main_Page">GeoRSS</a>
|
||||
to encode the latitude and longitude (see example response below <georss:point>).
|
||||
|
||||
@subsection exampleusage Example usage
|
||||
|
||||
@verbatim
|
||||
curl http://identi.ca/api/statuses/friends_timeline/evan.xml?count=1&page=2
|
||||
@endverbatim
|
||||
|
||||
@subsection exampleresponse Example response
|
||||
|
||||
@verbatim
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<statuses type="array">
|
||||
<status>
|
||||
<text>@skwashd oh, commbank reenabled me super quick both times. but disconcerting when you don't expect it though</text>
|
||||
<truncated>false</truncated>
|
||||
<created_at>Sat Apr 17 00:49:12 +0000 2010</created_at>
|
||||
<in_reply_to_status_id>28838393</in_reply_to_status_id>
|
||||
<source>xmpp</source>
|
||||
<id>28838456</id>
|
||||
<in_reply_to_user_id>39303</in_reply_to_user_id>
|
||||
<in_reply_to_screen_name>skwashd</in_reply_to_screen_name>
|
||||
<geo></geo>
|
||||
<favorited>false</favorited>
|
||||
<user>
|
||||
<id>44517</id>
|
||||
<name>joshua may</name>
|
||||
<screen_name>notjosh</screen_name>
|
||||
<location></location>
|
||||
<description></description>
|
||||
<profile_image_url>http://avatar.identi.ca/44517-48-20090321004106.jpeg</profile_image_url>
|
||||
<url></url>
|
||||
<protected>false</protected>
|
||||
<followers_count>17</followers_count>
|
||||
<profile_background_color></profile_background_color>
|
||||
<profile_text_color></profile_text_color>
|
||||
<profile_link_color></profile_link_color>
|
||||
<profile_sidebar_fill_color></profile_sidebar_fill_color>
|
||||
<profile_sidebar_border_color></profile_sidebar_border_color>
|
||||
<friends_count>20</friends_count>
|
||||
<created_at>Sat Mar 21 00:40:25 +0000 2009</created_at>
|
||||
<favourites_count>0</favourites_count>
|
||||
<utc_offset>0</utc_offset>
|
||||
<time_zone>UTC</time_zone>
|
||||
<profile_background_image_url></profile_background_image_url>
|
||||
<profile_background_tile>false</profile_background_tile>
|
||||
<statuses_count>100</statuses_count>
|
||||
<following>false</following>
|
||||
<notifications>false</notifications>
|
||||
</user>
|
||||
</status>
|
||||
[....]
|
||||
</statuses>
|
||||
@endverbatim
|
||||
*/
|
||||
|
||||
class ApiTimelinePublicAction extends ApiPrivateAuthAction
|
||||
{
|
||||
|
||||
@ -130,7 +220,7 @@ class ApiTimelinePublicAction extends ApiPrivateAuthAction
|
||||
|
||||
header('Content-Type: application/atom+xml; charset=utf-8');
|
||||
|
||||
$atom = new AtomNoticeFeed();
|
||||
$atom = new AtomNoticeFeed($this->auth_user);
|
||||
|
||||
$atom->setId($id);
|
||||
$atom->setTitle($title);
|
||||
|
@ -117,7 +117,7 @@ class ApiTimelineRetweetsOfMeAction extends ApiAuthAction
|
||||
|
||||
header('Content-Type: application/atom+xml; charset=utf-8');
|
||||
|
||||
$atom = new AtomNoticeFeed();
|
||||
$atom = new AtomNoticeFeed($this->auth_user);
|
||||
|
||||
$atom->setId($id);
|
||||
$atom->setTitle($title);
|
||||
|
@ -26,6 +26,7 @@
|
||||
* @author Jeffery To <jeffery.to@gmail.com>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009-2010 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -138,7 +139,7 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction
|
||||
|
||||
header('Content-Type: application/atom+xml; charset=utf-8');
|
||||
|
||||
$atom = new AtomNoticeFeed();
|
||||
$atom = new AtomNoticeFeed($this->auth_user);
|
||||
|
||||
$atom->setId($id);
|
||||
$atom->setTitle($title);
|
||||
|
@ -29,6 +29,7 @@
|
||||
* @author Robin Millette <robin@millette.info>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2009 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -115,7 +116,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
|
||||
|
||||
// We'll use the shared params from the Atom stub
|
||||
// for other feed types.
|
||||
$atom = new AtomUserNoticeFeed($this->user);
|
||||
$atom = new AtomUserNoticeFeed($this->user, $this->auth_user);
|
||||
|
||||
$link = common_local_url(
|
||||
'showstream',
|
||||
|
@ -22,7 +22,7 @@
|
||||
* @category Search
|
||||
* @package StatusNet
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @copyright 2008-2009 StatusNet, Inc.
|
||||
* @copyright 2008-2010 StatusNet, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
@ -31,6 +31,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once INSTALLDIR.'/lib/apiprivateauth.php';
|
||||
|
||||
/**
|
||||
* Returns the top ten queries that are currently trending
|
||||
*
|
||||
@ -43,7 +45,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
||||
* @see ApiAction
|
||||
*/
|
||||
|
||||
class TwitapitrendsAction extends ApiAction
|
||||
class ApiTrendsAction extends ApiPrivateAuthAction
|
||||
{
|
||||
|
||||
var $callback;
|
||||
@ -82,7 +84,7 @@ class TwitapitrendsAction extends ApiAction
|
||||
*/
|
||||
function showTrends()
|
||||
{
|
||||
$this->serverError(_('API method under construction.'), $code = 501);
|
||||
$this->serverError(_('API method under construction.'), 501);
|
||||
}
|
||||
|
||||
}
|
@ -113,7 +113,7 @@ class ApiUserShowAction extends ApiPrivateAuthAction
|
||||
|
||||
if ($this->format == 'xml') {
|
||||
$this->initDocument('xml');
|
||||
$this->showTwitterXmlUser($twitter_user);
|
||||
$this->showTwitterXmlUser($twitter_user, 'user', true);
|
||||
$this->endDocument('xml');
|
||||
} elseif ($this->format == 'json') {
|
||||
$this->initDocument('json');
|
||||
|
@ -87,13 +87,15 @@ class BlockAction extends ProfileFormAction
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
if ($this->arg('no')) {
|
||||
$this->returnToArgs();
|
||||
$this->returnToPrevious();
|
||||
} elseif ($this->arg('yes')) {
|
||||
$this->handlePost();
|
||||
$this->returnToArgs();
|
||||
$this->returnToPrevious();
|
||||
} else {
|
||||
$this->showPage();
|
||||
}
|
||||
} else {
|
||||
$this->showPage();
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,6 +120,12 @@ class BlockAction extends ProfileFormAction
|
||||
*/
|
||||
function areYouSureForm()
|
||||
{
|
||||
// @fixme if we ajaxify the confirmation form, skip the preview on ajax hits
|
||||
$profile = new ArrayWrapper(array($this->profile));
|
||||
$preview = new ProfileList($profile, $this);
|
||||
$preview->show();
|
||||
|
||||
|
||||
$id = $this->profile->id;
|
||||
$this->elementStart('form', array('id' => 'block-' . $id,
|
||||
'method' => 'post',
|
||||
@ -187,4 +195,38 @@ class BlockAction extends ProfileFormAction
|
||||
$this->autofocus('form_action-yes');
|
||||
}
|
||||
|
||||
/**
|
||||
* Override for form session token checks; on our first hit we're just
|
||||
* requesting confirmation, which doesn't need a token. We need to be
|
||||
* able to take regular GET requests from email!
|
||||
*
|
||||
* @throws ClientException if token is bad on POST request or if we have
|
||||
* confirmation parameters which could trigger something.
|
||||
*/
|
||||
function checkSessionToken()
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST' ||
|
||||
$this->arg('yes') ||
|
||||
$this->arg('no')) {
|
||||
|
||||
return parent::checkSessionToken();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If we reached this form without returnto arguments, return to the
|
||||
* current user's subscription list.
|
||||
*
|
||||
* @return string URL
|
||||
*/
|
||||
function defaultReturnTo()
|
||||
{
|
||||
$user = common_current_user();
|
||||
if ($user) {
|
||||
return common_local_url('subscribers',
|
||||
array('nickname' => $user->nickname));
|
||||
} else {
|
||||
return common_local_url('public');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +172,10 @@ class DeletenoticeAction extends Action
|
||||
}
|
||||
|
||||
if ($this->arg('yes')) {
|
||||
$this->notice->delete();
|
||||
if (Event::handle('StartDeleteOwnNotice', array($this->user, $this->notice))) {
|
||||
$this->notice->delete();
|
||||
Event::handle('EndDeleteOwnNotice', array($this->user, $this->notice));
|
||||
}
|
||||
}
|
||||
|
||||
$url = common_get_returnto();
|
||||
|
@ -92,10 +92,10 @@ class DeleteuserAction extends ProfileFormAction
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
if ($this->arg('no')) {
|
||||
$this->returnToArgs();
|
||||
$this->returnToPrevious();
|
||||
} elseif ($this->arg('yes')) {
|
||||
$this->handlePost();
|
||||
$this->returnToArgs();
|
||||
$this->returnToPrevious();
|
||||
} else {
|
||||
$this->showPage();
|
||||
}
|
||||
|
@ -126,9 +126,19 @@ class DesignadminpanelAction extends AdminPanelAction
|
||||
return;
|
||||
}
|
||||
|
||||
// check for an image upload
|
||||
// check for file uploads
|
||||
|
||||
$bgimage = $this->saveBackgroundImage();
|
||||
$customTheme = $this->saveCustomTheme();
|
||||
|
||||
$oldtheme = common_config('site', 'theme');
|
||||
if ($customTheme) {
|
||||
// This feels pretty hacky :D
|
||||
$this->args['theme'] = $customTheme;
|
||||
$themeChanged = true;
|
||||
} else {
|
||||
$themeChanged = ($this->trimmed('theme') != $oldtheme);
|
||||
}
|
||||
|
||||
static $settings = array('theme', 'logo');
|
||||
|
||||
@ -140,15 +150,13 @@ class DesignadminpanelAction extends AdminPanelAction
|
||||
|
||||
$this->validate($values);
|
||||
|
||||
$oldtheme = common_config('site', 'theme');
|
||||
|
||||
$config = new Config();
|
||||
|
||||
$config->query('BEGIN');
|
||||
|
||||
// Only update colors if the theme has not changed.
|
||||
|
||||
if ($oldtheme == $values['theme']) {
|
||||
if (!$themeChanged) {
|
||||
|
||||
$bgcolor = new WebColor($this->trimmed('design_background'));
|
||||
$ccolor = new WebColor($this->trimmed('design_content'));
|
||||
@ -190,6 +198,13 @@ class DesignadminpanelAction extends AdminPanelAction
|
||||
Config::save('design', 'backgroundimage', $bgimage);
|
||||
}
|
||||
|
||||
if (common_config('custom_css', 'enabled')) {
|
||||
$css = $this->arg('css');
|
||||
if ($css != common_config('custom_css', 'css')) {
|
||||
Config::save('custom_css', 'css', $css);
|
||||
}
|
||||
}
|
||||
|
||||
$config->query('COMMIT');
|
||||
}
|
||||
|
||||
@ -263,6 +278,33 @@ class DesignadminpanelAction extends AdminPanelAction
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the custom theme if the user uploaded one.
|
||||
*
|
||||
* @return mixed custom theme name, if succesful, or null if no theme upload.
|
||||
* @throws ClientException for invalid theme archives
|
||||
* @throws ServerException if trouble saving the theme files
|
||||
*/
|
||||
|
||||
function saveCustomTheme()
|
||||
{
|
||||
if (common_config('theme_upload', 'enabled') &&
|
||||
$_FILES['design_upload_theme']['error'] == UPLOAD_ERR_OK) {
|
||||
|
||||
$upload = ThemeUploader::fromUpload('design_upload_theme');
|
||||
$basedir = common_config('local', 'dir');
|
||||
if (empty($basedir)) {
|
||||
$basedir = INSTALLDIR . '/local';
|
||||
}
|
||||
$name = 'custom'; // @todo allow multiples, custom naming?
|
||||
$outdir = $basedir . '/theme/' . $name;
|
||||
$upload->extract($outdir);
|
||||
return $name;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to validate setting values
|
||||
*
|
||||
@ -371,7 +413,15 @@ class DesignAdminPanelForm extends AdminForm
|
||||
|
||||
function formData()
|
||||
{
|
||||
$this->showLogo();
|
||||
$this->showTheme();
|
||||
$this->showBackground();
|
||||
$this->showColors();
|
||||
$this->showAdvanced();
|
||||
}
|
||||
|
||||
function showLogo()
|
||||
{
|
||||
$this->out->elementStart('fieldset', array('id' => 'settings_design_logo'));
|
||||
$this->out->element('legend', null, _('Change logo'));
|
||||
|
||||
@ -384,6 +434,11 @@ class DesignAdminPanelForm extends AdminForm
|
||||
$this->out->elementEnd('ul');
|
||||
|
||||
$this->out->elementEnd('fieldset');
|
||||
|
||||
}
|
||||
|
||||
function showTheme()
|
||||
{
|
||||
$this->out->elementStart('fieldset', array('id' => 'settings_design_theme'));
|
||||
$this->out->element('legend', null, _('Change theme'));
|
||||
|
||||
@ -407,10 +462,23 @@ class DesignAdminPanelForm extends AdminForm
|
||||
false, $this->value('theme'));
|
||||
$this->unli();
|
||||
|
||||
if (common_config('theme_upload', 'enabled')) {
|
||||
$this->li();
|
||||
$this->out->element('label', array('for' => 'design_upload_theme'), _('Custom theme'));
|
||||
$this->out->element('input', array('id' => 'design_upload_theme',
|
||||
'name' => 'design_upload_theme',
|
||||
'type' => 'file'));
|
||||
$this->out->element('p', 'form_guide', _('You can upload a custom StatusNet theme as a .ZIP archive.'));
|
||||
$this->unli();
|
||||
}
|
||||
|
||||
$this->out->elementEnd('ul');
|
||||
|
||||
$this->out->elementEnd('fieldset');
|
||||
}
|
||||
|
||||
function showBackground()
|
||||
{
|
||||
$design = $this->out->design;
|
||||
|
||||
$this->out->elementStart('fieldset', array('id' =>
|
||||
@ -486,6 +554,11 @@ class DesignAdminPanelForm extends AdminForm
|
||||
|
||||
$this->out->elementEnd('ul');
|
||||
$this->out->elementEnd('fieldset');
|
||||
}
|
||||
|
||||
function showColors()
|
||||
{
|
||||
$design = $this->out->design;
|
||||
|
||||
$this->out->elementStart('fieldset', array('id' => 'settings_design_color'));
|
||||
$this->out->element('legend', null, _('Change colours'));
|
||||
@ -493,6 +566,7 @@ class DesignAdminPanelForm extends AdminForm
|
||||
$this->out->elementStart('ul', 'form_data');
|
||||
|
||||
try {
|
||||
// @fixme avoid loop unrolling in non-performance-critical contexts like this
|
||||
|
||||
$bgcolor = new WebColor($design->backgroundcolor);
|
||||
|
||||
@ -560,6 +634,7 @@ class DesignAdminPanelForm extends AdminForm
|
||||
$this->unli();
|
||||
|
||||
} catch (WebColorException $e) {
|
||||
// @fixme normalize them individually!
|
||||
common_log(LOG_ERR, 'Bad color values in site design: ' .
|
||||
$e->getMessage());
|
||||
}
|
||||
@ -569,6 +644,27 @@ class DesignAdminPanelForm extends AdminForm
|
||||
$this->out->elementEnd('ul');
|
||||
}
|
||||
|
||||
function showAdvanced()
|
||||
{
|
||||
if (common_config('custom_css', 'enabled')) {
|
||||
$this->out->elementStart('fieldset', array('id' => 'settings_design_advanced'));
|
||||
$this->out->element('legend', null, _('Advanced'));
|
||||
$this->out->elementStart('ul', 'form_data');
|
||||
|
||||
$this->li();
|
||||
$this->out->element('label', array('for' => 'css'), _('Custom CSS'));
|
||||
$this->out->element('textarea', array('name' => 'css',
|
||||
'id' => 'css',
|
||||
'cols' => '50',
|
||||
'rows' => '10'),
|
||||
strval(common_config('custom_css', 'css')));
|
||||
$this->unli();
|
||||
|
||||
$this->out->elementEnd('fieldset');
|
||||
$this->out->elementEnd('ul');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action elements
|
||||
*
|
||||
|
@ -104,7 +104,7 @@ class FavorAction extends Action
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies a user when his notice is favorited.
|
||||
* Notifies a user when their notice is favorited.
|
||||
*
|
||||
* @param class $notice favorited notice
|
||||
* @param class $user user declaring a favorite
|
||||
|
@ -89,7 +89,7 @@ class FavoritesrssAction extends Rss10Action
|
||||
function getNotices($limit=0)
|
||||
{
|
||||
$user = $this->user;
|
||||
$notice = $user->favoriteNotices(0, $limit);
|
||||
$notice = $user->favoriteNotices(false, 0, $limit);
|
||||
$notices = array();
|
||||
while ($notice->fetch()) {
|
||||
$notices[] = clone($notice);
|
||||
|
@ -37,7 +37,7 @@ require_once INSTALLDIR.'/lib/omb.php';
|
||||
* Handler for remote subscription finish callback
|
||||
*
|
||||
* When a remote user subscribes a local user, a redirect to this action is
|
||||
* issued after the remote user authorized his service to subscribe.
|
||||
* issued after the remote user authorized their service to subscribe.
|
||||
*
|
||||
* @category Action
|
||||
* @package Laconica
|
||||
|
@ -95,7 +95,9 @@ class FoafAction extends Action
|
||||
// Would be nice to tell if they were a Person or not (e.g. a #person usertag?)
|
||||
$this->elementStart('Agent', array('rdf:about' =>
|
||||
$this->user->uri));
|
||||
$this->element('mbox_sha1sum', null, sha1('mailto:' . $this->user->email));
|
||||
if ($this->user->email) {
|
||||
$this->element('mbox_sha1sum', null, sha1('mailto:' . $this->user->email));
|
||||
}
|
||||
if ($this->profile->fullname) {
|
||||
$this->element('name', null, $this->profile->fullname);
|
||||
}
|
||||
@ -152,7 +154,9 @@ class FoafAction extends Action
|
||||
}
|
||||
|
||||
$person = $this->showMicrobloggingAccount($this->profile,
|
||||
common_root_url(), $this->user->uri, false);
|
||||
common_root_url(), $this->user->uri,
|
||||
/*$fetchSubscriptions*/true,
|
||||
/*$isSubscriber*/false);
|
||||
|
||||
// Get people who subscribe to user
|
||||
|
||||
@ -207,7 +211,8 @@ class FoafAction extends Action
|
||||
$this->showMicrobloggingAccount($profile,
|
||||
($local == 'local') ? common_root_url() : null,
|
||||
$uri,
|
||||
true);
|
||||
/*$fetchSubscriptions*/false,
|
||||
/*$isSubscriber*/($type == LISTENER || $type == BOTH));
|
||||
if ($foaf_url) {
|
||||
$this->element('rdfs:seeAlso', array('rdf:resource' => $foaf_url));
|
||||
}
|
||||
@ -232,7 +237,21 @@ class FoafAction extends Action
|
||||
$this->elementEnd('PersonalProfileDocument');
|
||||
}
|
||||
|
||||
function showMicrobloggingAccount($profile, $service=null, $useruri=null, $isSubscriber=false)
|
||||
/**
|
||||
* Output FOAF <account> bit for the given profile.
|
||||
*
|
||||
* @param Profile $profile
|
||||
* @param mixed $service Root URL of this StatusNet instance for a local
|
||||
* user, otherwise null.
|
||||
* @param mixed $useruri URI string for the referenced profile..
|
||||
* @param boolean $fetchSubscriptions Should we load and list all their subscriptions?
|
||||
* @param boolean $isSubscriber if not fetching subs, we can still mark the user as following the current page.
|
||||
*
|
||||
* @return array if $fetchSubscribers is set, return a list of info on those
|
||||
* subscriptions.
|
||||
*/
|
||||
|
||||
function showMicrobloggingAccount($profile, $service=null, $useruri=null, $fetchSubscriptions=false, $isSubscriber=false)
|
||||
{
|
||||
$attr = array();
|
||||
if ($useruri) {
|
||||
@ -254,9 +273,7 @@ class FoafAction extends Action
|
||||
|
||||
$person = array();
|
||||
|
||||
if ($isSubscriber) {
|
||||
$this->element('sioc:follows', array('rdf:resource'=>$this->user->uri . '#acct'));
|
||||
} else {
|
||||
if ($fetchSubscriptions) {
|
||||
// Get people user is subscribed to
|
||||
$sub = new Subscription();
|
||||
$sub->subscriber = $profile->id;
|
||||
@ -281,6 +298,9 @@ class FoafAction extends Action
|
||||
}
|
||||
|
||||
unset($sub);
|
||||
} else if ($isSubscriber) {
|
||||
// Just declare that they follow the user whose FOAF we're showing.
|
||||
$this->element('sioc:follows', array('rdf:resource' => $this->user->uri . '#acct'));
|
||||
}
|
||||
|
||||
$this->elementEnd('OnlineAccount');
|
||||
|
@ -37,6 +37,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
||||
* @category Action
|
||||
* @package StatusNet
|
||||
* @author Craig Andrews <candrews@integralblue.com>
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
@ -129,9 +129,9 @@ class GetfileAction extends Action
|
||||
return null;
|
||||
}
|
||||
|
||||
$cache = common_memcache();
|
||||
$cache = Cache::instance();
|
||||
if($cache) {
|
||||
$key = common_cache_key('attachments:etag:' . $this->path);
|
||||
$key = Cache::key('attachments:etag:' . $this->path);
|
||||
$etag = $cache->get($key);
|
||||
if($etag === false) {
|
||||
$etag = crc32(file_get_contents($this->path));
|
||||
|
@ -117,7 +117,7 @@ class GroupblockAction extends RedirectingAction
|
||||
parent::handle($args);
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
if ($this->arg('no')) {
|
||||
$this->returnToArgs();
|
||||
$this->returnToPrevious();
|
||||
} elseif ($this->arg('yes')) {
|
||||
$this->blockProfile();
|
||||
} elseif ($this->arg('blockto')) {
|
||||
@ -207,7 +207,7 @@ class GroupblockAction extends RedirectingAction
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->returnToArgs();
|
||||
$this->returnToPrevious();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,8 +18,10 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @package OStatusPlugin
|
||||
* @category Action
|
||||
* @package StatusNet
|
||||
* @maintainer James Walker <james@status.net>
|
||||
* @author Craig Andrews <candrews@integralblue.com>
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
|
||||
@ -27,22 +29,30 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
|
||||
class HostMetaAction extends Action
|
||||
{
|
||||
|
||||
/**
|
||||
* Is read only?
|
||||
*
|
||||
* @return boolean true
|
||||
*/
|
||||
function isReadOnly()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
function handle()
|
||||
{
|
||||
parent::handle();
|
||||
|
||||
$domain = common_config('site', 'server');
|
||||
$url = common_local_url('userxrd');
|
||||
$url.= '?uri={uri}';
|
||||
|
||||
$xrd = new XRD();
|
||||
|
||||
$xrd = new XRD();
|
||||
$xrd->host = $domain;
|
||||
$xrd->links[] = array('rel' => Discovery::LRDD_REL,
|
||||
'template' => $url,
|
||||
'title' => array('Resource Descriptor'));
|
||||
|
||||
if(Event::handle('StartHostMetaLinks', array(&$xrd->links))) {
|
||||
Event::handle('EndHostMetaLinks', array(&$xrd->links));
|
||||
}
|
||||
|
||||
header('Content-type: application/xrd+xml');
|
||||
print $xrd->toXML();
|
||||
}
|
||||
}
|
@ -133,8 +133,7 @@ class ImsettingsAction extends ConnectSettingsAction
|
||||
'message with further instructions. '.
|
||||
'(Did you add %s to your buddy list?)'),
|
||||
$transport_info['display'],
|
||||
$transport_info['daemon_screenname'],
|
||||
jabber_daemon_address()));
|
||||
$transport_info['daemonScreenname']));
|
||||
$this->hidden('screenname', $confirm->address);
|
||||
// TRANS: Button label to cancel an IM address confirmation procedure.
|
||||
$this->submit('cancel', _m('BUTTON','Cancel'));
|
||||
@ -163,12 +162,11 @@ class ImsettingsAction extends ConnectSettingsAction
|
||||
'action' =>
|
||||
common_local_url('imsettings')));
|
||||
$this->elementStart('fieldset', array('id' => 'settings_im_preferences'));
|
||||
$this->element('legend', null, _('Preferences'));
|
||||
// TRANS: Header for IM preferences form.
|
||||
$this->element('legend', null, _('IM Preferences'));
|
||||
$this->hidden('token', common_session_token());
|
||||
$this->elementStart('table');
|
||||
$this->elementStart('tr');
|
||||
// TRANS: Header for IM preferences form.
|
||||
$this->element('th', null, _('IM Preferences'));
|
||||
foreach($user_im_prefs_by_transport as $transport=>$user_im_prefs)
|
||||
{
|
||||
$this->element('th', null, $transports[$transport]['display']);
|
||||
@ -278,19 +276,20 @@ class ImsettingsAction extends ConnectSettingsAction
|
||||
$user = common_current_user();
|
||||
|
||||
$user_im_prefs = new User_im_prefs();
|
||||
$user_im_prefs->query('BEGIN');
|
||||
$user_im_prefs->user_id = $user->id;
|
||||
if($user_im_prefs->find() && $user_im_prefs->fetch())
|
||||
{
|
||||
$preferences = array('notify', 'updatefrompresence', 'replies', 'microid');
|
||||
$user_im_prefs->query('BEGIN');
|
||||
do
|
||||
{
|
||||
$original = clone($user_im_prefs);
|
||||
$new = clone($user_im_prefs);
|
||||
foreach($preferences as $preference)
|
||||
{
|
||||
$user_im_prefs->$preference = $this->boolean($user_im_prefs->transport . '_' . $preference);
|
||||
$new->$preference = $this->boolean($new->transport . '_' . $preference);
|
||||
}
|
||||
$result = $user_im_prefs->update($original);
|
||||
$result = $new->update($original);
|
||||
|
||||
if ($result === false) {
|
||||
common_log_db_error($user, 'UPDATE', __FILE__);
|
||||
@ -299,8 +298,8 @@ class ImsettingsAction extends ConnectSettingsAction
|
||||
return;
|
||||
}
|
||||
}while($user_im_prefs->fetch());
|
||||
$user_im_prefs->query('COMMIT');
|
||||
}
|
||||
$user_im_prefs->query('COMMIT');
|
||||
// TRANS: Confirmation message for successful IM preferences save.
|
||||
$this->showForm(_('Preferences saved.'), true);
|
||||
}
|
||||
|
@ -62,6 +62,28 @@ class LoginAction extends Action
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare page to run
|
||||
*
|
||||
*
|
||||
* @param $args
|
||||
* @return string title
|
||||
*/
|
||||
|
||||
function prepare($args)
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
// @todo this check should really be in index.php for all sensitive actions
|
||||
$ssl = common_config('site', 'ssl');
|
||||
if (empty($_SERVER['HTTPS']) && ($ssl == 'always' || $ssl == 'sometimes')) {
|
||||
common_redirect(common_local_url('login'));
|
||||
// exit
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle input, produce output
|
||||
*
|
||||
@ -96,27 +118,10 @@ class LoginAction extends Action
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function checkLogin($user_id=null, $token=null)
|
||||
function checkLogin($user_id=null)
|
||||
{
|
||||
// XXX: login throttle
|
||||
|
||||
// CSRF protection - token set in NoticeForm
|
||||
$token = $this->trimmed('token');
|
||||
if (!$token || $token != common_session_token()) {
|
||||
$st = common_session_token();
|
||||
if (empty($token)) {
|
||||
common_log(LOG_WARNING, 'No token provided by client.');
|
||||
} else if (empty($st)) {
|
||||
common_log(LOG_WARNING, 'No session token stored.');
|
||||
} else {
|
||||
common_log(LOG_WARNING, 'Token = ' . $token . ' and session token = ' . $st);
|
||||
}
|
||||
|
||||
$this->clientError(_('There was a problem with your session token. '.
|
||||
'Try again, please.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$nickname = $this->trimmed('nickname');
|
||||
$password = $this->arg('password');
|
||||
|
||||
@ -239,7 +244,6 @@ class LoginAction extends Action
|
||||
$this->elementEnd('li');
|
||||
$this->elementEnd('ul');
|
||||
$this->submit('submit', _('Login'));
|
||||
$this->hidden('token', common_session_token());
|
||||
$this->elementEnd('fieldset');
|
||||
$this->elementEnd('form');
|
||||
$this->elementStart('p');
|
||||
@ -267,9 +271,13 @@ class LoginAction extends Action
|
||||
'user name and password ' .
|
||||
'before changing your settings.');
|
||||
} else {
|
||||
return _('Login with your username and password. ' .
|
||||
'Don\'t have a username yet? ' .
|
||||
'[Register](%%action.register%%) a new account.');
|
||||
$prompt = _('Login with your username and password.');
|
||||
if (!common_config('site', 'closed') && !common_config('site', 'inviteonly')) {
|
||||
$prompt .= ' ';
|
||||
$prompt .= _('Don\'t have a username yet? ' .
|
||||
'[Register](%%action.register%%) a new account.');
|
||||
}
|
||||
return $prompt;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,6 +131,8 @@ class NewnoticeAction extends Action
|
||||
$user = common_current_user();
|
||||
assert($user); // XXX: maybe an error instead...
|
||||
$content = $this->trimmed('status_textarea');
|
||||
$options = array();
|
||||
Event::handle('StartSaveNewNoticeWeb', array($this, $user, &$content, &$options));
|
||||
|
||||
if (!$content) {
|
||||
$this->clientError(_('No content!'));
|
||||
@ -157,11 +159,9 @@ class NewnoticeAction extends Action
|
||||
Notice::maxContent()));
|
||||
}
|
||||
|
||||
$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';
|
||||
$replyto = intval($this->trimmed('inreplyto'));
|
||||
if ($replyto) {
|
||||
$options['reply_to'] = $replyto;
|
||||
}
|
||||
|
||||
$upload = null;
|
||||
@ -169,7 +169,10 @@ class NewnoticeAction extends Action
|
||||
|
||||
if (isset($upload)) {
|
||||
|
||||
$content_shortened .= ' ' . $upload->shortUrl();
|
||||
if (Event::handle('StartSaveNewNoticeAppendAttachment', array($this, $upload, &$content_shortened, &$options))) {
|
||||
$content_shortened .= ' ' . $upload->shortUrl();
|
||||
}
|
||||
Event::handle('EndSaveNewNoticeAppendAttachment', array($this, $upload, &$content_shortened, &$options));
|
||||
|
||||
if (Notice::contentTooLong($content_shortened)) {
|
||||
$upload->delete();
|
||||
@ -182,8 +185,6 @@ class NewnoticeAction extends Action
|
||||
}
|
||||
}
|
||||
|
||||
$options = array('reply_to' => ($replyto == 'false') ? null : $replyto);
|
||||
|
||||
if ($user->shareLocation()) {
|
||||
// use browser data if checked; otherwise profile data
|
||||
if ($this->arg('notice_data-geo')) {
|
||||
@ -203,11 +204,20 @@ class NewnoticeAction extends Action
|
||||
$options = array_merge($options, $locOptions);
|
||||
}
|
||||
|
||||
$notice = Notice::saveNew($user->id, $content_shortened, 'web', $options);
|
||||
$author_id = $user->id;
|
||||
$text = $content_shortened;
|
||||
|
||||
if (isset($upload)) {
|
||||
$upload->attachToNotice($notice);
|
||||
if (Event::handle('StartNoticeSaveWeb', array($this, &$author_id, &$text, &$options))) {
|
||||
|
||||
$notice = Notice::saveNew($user->id, $content_shortened, 'web', $options);
|
||||
|
||||
if (isset($upload)) {
|
||||
$upload->attachToNotice($notice);
|
||||
}
|
||||
|
||||
Event::handle('EndNoticeSaveWeb', array($this, $notice));
|
||||
}
|
||||
Event::handle('EndSaveNewNoticeWeb', array($this, $user, &$content_shortened, &$options));
|
||||
|
||||
if ($this->boolean('ajax')) {
|
||||
header('Content-Type: text/xml;charset=utf-8');
|
||||
|
@ -82,7 +82,7 @@ class NudgeAction extends Action
|
||||
}
|
||||
|
||||
if (!$other->email || !$other->emailnotifynudge) {
|
||||
$this->clientError(_('This user doesn\'t allow nudges or hasn\'t confirmed or set his email yet.'));
|
||||
$this->clientError(_('This user doesn\'t allow nudges or hasn\'t confirmed or set their email yet.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @copyright 2008 StatusNet, Inc.
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
@ -8,7 +8,9 @@
|
||||
* @category Action
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Craig Andrews <candrews@integralblue.com>
|
||||
* @author Robin Millette <millette@status.net>
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://status.net/
|
||||
*
|
||||
@ -44,6 +46,7 @@ require_once INSTALLDIR.'/lib/xrdsoutputter.php';
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Robin Millette <millette@status.net>
|
||||
* @author Craig Andrews <candrews@integralblue.com>
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://status.net/
|
||||
*
|
||||
|
@ -74,6 +74,13 @@ class RegisterAction extends Action
|
||||
parent::prepare($args);
|
||||
$this->code = $this->trimmed('code');
|
||||
|
||||
// @todo this check should really be in index.php for all sensitive actions
|
||||
$ssl = common_config('site', 'ssl');
|
||||
if (empty($_SERVER['HTTPS']) && ($ssl == 'always' || $ssl == 'sometimes')) {
|
||||
common_redirect(common_local_url('register'));
|
||||
// exit
|
||||
}
|
||||
|
||||
if (empty($this->code)) {
|
||||
common_ensure_session();
|
||||
if (array_key_exists('invitecode', $_SESSION)) {
|
||||
@ -491,6 +498,45 @@ class RegisterAction extends Action
|
||||
$this->elementStart('li');
|
||||
$this->element('input', $attrs);
|
||||
$this->elementStart('label', array('class' => 'checkbox', 'for' => 'license'));
|
||||
$this->raw($this->licenseCheckbox());
|
||||
$this->elementEnd('label');
|
||||
$this->elementEnd('li');
|
||||
}
|
||||
$this->elementEnd('ul');
|
||||
$this->submit('submit', _('Register'));
|
||||
$this->elementEnd('fieldset');
|
||||
$this->elementEnd('form');
|
||||
}
|
||||
|
||||
function licenseCheckbox()
|
||||
{
|
||||
$out = '';
|
||||
switch (common_config('license', 'type')) {
|
||||
case 'private':
|
||||
// TRANS: Copyright checkbox label in registration dialog, for private sites.
|
||||
$out .= htmlspecialchars(sprintf(
|
||||
_('I understand that content and data of %1$s are private and confidential.'),
|
||||
common_config('site', 'name')));
|
||||
// fall through
|
||||
case 'allrightsreserved':
|
||||
if ($out != '') {
|
||||
$out .= ' ';
|
||||
}
|
||||
if (common_config('license', 'owner')) {
|
||||
// TRANS: Copyright checkbox label in registration dialog, for all rights reserved with a specified copyright owner.
|
||||
$out .= htmlspecialchars(sprintf(
|
||||
_('My text and files are copyright by %1$s.'),
|
||||
common_config('license', 'owner')));
|
||||
} else {
|
||||
// TRANS: Copyright checkbox label in registration dialog, for all rights reserved with ownership left to contributors.
|
||||
$out .= htmlspecialchars(_('My text and files remain under my own copyright.'));
|
||||
}
|
||||
// TRANS: Copyright checkbox label in registration dialog, for all rights reserved.
|
||||
$out .= ' ' . _('All rights reserved.');
|
||||
break;
|
||||
case 'cc': // fall through
|
||||
default:
|
||||
// TRANS: Copyright checkbox label in registration dialog, for Creative Commons-style licenses.
|
||||
$message = _('My text and files are available under %s ' .
|
||||
'except this private data: password, ' .
|
||||
'email address, IM address, and phone number.');
|
||||
@ -499,14 +545,9 @@ class RegisterAction extends Action
|
||||
'">' .
|
||||
htmlspecialchars(common_config('license', 'title')) .
|
||||
'</a>';
|
||||
$this->raw(sprintf(htmlspecialchars($message), $link));
|
||||
$this->elementEnd('label');
|
||||
$this->elementEnd('li');
|
||||
$out .= sprintf(htmlspecialchars($message), $link);
|
||||
}
|
||||
$this->elementEnd('ul');
|
||||
$this->submit('submit', _('Register'));
|
||||
$this->elementEnd('fieldset');
|
||||
$this->elementEnd('form');
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,18 +196,18 @@ class RepliesAction extends OwnerDesignAction
|
||||
|
||||
function showEmptyListMessage()
|
||||
{
|
||||
$message = sprintf(_('This is the timeline showing replies to %1$s but %2$s hasn\'t received a notice to his attention yet.'), $this->user->nickname, $this->user->nickname) . ' ';
|
||||
$message = sprintf(_('This is the timeline showing replies to %1$s but %2$s hasn\'t received a notice to them 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 %1$s](../%2$s) or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname);
|
||||
$message .= sprintf(_('You can try to [nudge %1$s](../%2$s) or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$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);
|
||||
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->user->nickname);
|
||||
}
|
||||
|
||||
$this->elementStart('div', 'guide');
|
||||
|
@ -119,13 +119,13 @@ class ShowfavoritesAction extends OwnerDesignAction
|
||||
if (!empty($cur) && $cur->id == $this->user->id) {
|
||||
|
||||
// Show imported/gateway notices as well as local if
|
||||
// the user is looking at his own favorites
|
||||
// the user is looking at their own favorites
|
||||
|
||||
$this->notice = $this->user->favoriteNotices(($this->page-1)*NOTICES_PER_PAGE,
|
||||
NOTICES_PER_PAGE + 1, true);
|
||||
$this->notice = $this->user->favoriteNotices(true, ($this->page-1)*NOTICES_PER_PAGE,
|
||||
NOTICES_PER_PAGE + 1);
|
||||
} else {
|
||||
$this->notice = $this->user->favoriteNotices(($this->page-1)*NOTICES_PER_PAGE,
|
||||
NOTICES_PER_PAGE + 1, false);
|
||||
$this->notice = $this->user->favoriteNotices(false, ($this->page-1)*NOTICES_PER_PAGE,
|
||||
NOTICES_PER_PAGE + 1);
|
||||
}
|
||||
|
||||
if (empty($this->notice)) {
|
||||
@ -205,11 +205,11 @@ class ShowfavoritesAction extends OwnerDesignAction
|
||||
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);
|
||||
$message = sprintf(_('%s hasn\'t added any favorite notices 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 their favorites :)'), $this->user->nickname);
|
||||
$message = sprintf(_('%s hasn\'t added any favorite notices yet. Why not [register an account](%%%%action.register%%%%) and then post something interesting they would add to their favorites :)'), $this->user->nickname);
|
||||
}
|
||||
|
||||
$this->elementStart('div', 'guide');
|
||||
|
@ -430,14 +430,6 @@ class ShowgroupAction extends GroupDesignAction
|
||||
|
||||
function showStatistics()
|
||||
{
|
||||
// XXX: WORM cache this
|
||||
$members = $this->group->getMembers();
|
||||
$members_count = 0;
|
||||
/** $member->count() doesn't work. */
|
||||
while ($members->fetch()) {
|
||||
$members_count++;
|
||||
}
|
||||
|
||||
$this->elementStart('div', array('id' => 'entity_statistics',
|
||||
'class' => 'section'));
|
||||
|
||||
@ -451,7 +443,7 @@ class ShowgroupAction extends GroupDesignAction
|
||||
|
||||
$this->elementStart('dl', 'entity_members');
|
||||
$this->element('dt', null, _('Members'));
|
||||
$this->element('dd', null, (is_int($members_count)) ? $members_count : '0');
|
||||
$this->element('dd', null, $this->group->getMemberCount());
|
||||
$this->elementEnd('dl');
|
||||
|
||||
$this->elementEnd('div');
|
||||
|
@ -198,11 +198,11 @@ class ShowstreamAction extends ProfileAction
|
||||
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 %1$s or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%2$s).'), $this->user->nickname, '@' . $this->user->nickname);
|
||||
$message .= sprintf(_('You can try to nudge %1$s or [post something to them](%%%%action.newnotice%%%%?status_textarea=%2$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);
|
||||
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->user->nickname);
|
||||
}
|
||||
|
||||
$this->elementStart('div', 'guide');
|
||||
|
@ -185,7 +185,9 @@ class SubscriptionsListItem extends SubscriptionListItem
|
||||
return;
|
||||
}
|
||||
|
||||
if (!common_config('xmpp', 'enabled') && !common_config('sms', 'enabled')) {
|
||||
$transports = array();
|
||||
Event::handle('GetImTransports', array(&$transports));
|
||||
if (!$transports && !common_config('sms', 'enabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -195,7 +197,7 @@ class SubscriptionsListItem extends SubscriptionListItem
|
||||
'action' => common_local_url('subedit')));
|
||||
$this->out->hidden('token', common_session_token());
|
||||
$this->out->hidden('profile', $this->profile->id);
|
||||
if (common_config('xmpp', 'enabled')) {
|
||||
if ($transports) {
|
||||
$attrs = array('name' => 'jabber',
|
||||
'type' => 'checkbox',
|
||||
'class' => 'checkbox',
|
||||
@ -205,7 +207,7 @@ class SubscriptionsListItem extends SubscriptionListItem
|
||||
}
|
||||
|
||||
$this->out->element('input', $attrs);
|
||||
$this->out->element('label', array('for' => 'jabber-'.$this->profile->id), _('Jabber'));
|
||||
$this->out->element('label', array('for' => 'jabber-'.$this->profile->id), _('IM'));
|
||||
} else {
|
||||
$this->out->hidden('jabber', $sub->jabber);
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ if (!defined('STATUSNET')) {
|
||||
* @category Info
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Craig Andrews <candrews@integralblue.com>
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
0
avatar/.gitignore
vendored
Normal file → Executable file
0
avatar/.gitignore
vendored
Normal file → Executable file
@ -58,7 +58,7 @@ class Config extends Memcached_DataObject
|
||||
$c = self::memcache();
|
||||
|
||||
if (!empty($c)) {
|
||||
$settings = $c->get(common_cache_key(self::settingsKey));
|
||||
$settings = $c->get(Cache::key(self::settingsKey));
|
||||
if ($settings !== false) {
|
||||
return $settings;
|
||||
}
|
||||
@ -77,7 +77,7 @@ class Config extends Memcached_DataObject
|
||||
$config->free();
|
||||
|
||||
if (!empty($c)) {
|
||||
$c->set(common_cache_key(self::settingsKey), $settings);
|
||||
$c->set(Cache::key(self::settingsKey), $settings);
|
||||
}
|
||||
|
||||
return $settings;
|
||||
@ -154,7 +154,7 @@ class Config extends Memcached_DataObject
|
||||
$c = self::memcache();
|
||||
|
||||
if (!empty($c)) {
|
||||
$c->delete(common_cache_key(self::settingsKey));
|
||||
$c->delete(Cache::key(self::settingsKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,13 +75,13 @@ class Fave extends Memcached_DataObject
|
||||
return Memcached_DataObject::pkeyGet('Fave', $kv);
|
||||
}
|
||||
|
||||
function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $own=false)
|
||||
function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $own=false, $since_id=0, $max_id=0)
|
||||
{
|
||||
$ids = Notice::stream(array('Fave', '_streamDirect'),
|
||||
array($user_id, $own),
|
||||
($own) ? 'fave:ids_by_user_own:'.$user_id :
|
||||
'fave:ids_by_user:'.$user_id,
|
||||
$offset, $limit);
|
||||
$offset, $limit, $since_id, $max_id);
|
||||
return $ids;
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,11 @@ class File extends Memcached_DataObject
|
||||
return false;
|
||||
}
|
||||
|
||||
function processNew($given_url, $notice_id=null) {
|
||||
/**
|
||||
* @fixme refactor this mess, it's gotten pretty scary.
|
||||
* @param bool $followRedirects
|
||||
*/
|
||||
function processNew($given_url, $notice_id=null, $followRedirects=true) {
|
||||
if (empty($given_url)) return -1; // error, no url to process
|
||||
$given_url = File_redirection::_canonUrl($given_url);
|
||||
if (empty($given_url)) return -1; // error, no url to process
|
||||
@ -124,6 +128,10 @@ class File extends Memcached_DataObject
|
||||
if (empty($file)) {
|
||||
$file_redir = File_redirection::staticGet('url', $given_url);
|
||||
if (empty($file_redir)) {
|
||||
// @fixme for new URLs this also looks up non-redirect data
|
||||
// such as target content type, size, etc, which we need
|
||||
// for File::saveNew(); so we call it even if not following
|
||||
// new redirects.
|
||||
$redir_data = File_redirection::where($given_url);
|
||||
if (is_array($redir_data)) {
|
||||
$redir_url = $redir_data['url'];
|
||||
@ -131,14 +139,23 @@ class File extends Memcached_DataObject
|
||||
$redir_url = $redir_data;
|
||||
$redir_data = array();
|
||||
} else {
|
||||
throw new ServerException("Can't process url '$given_url'");
|
||||
// TRANS: Server exception thrown when a URL cannot be processed.
|
||||
throw new ServerException(sprintf(_("Cannot process URL '%s'"), $given_url));
|
||||
}
|
||||
// TODO: max field length
|
||||
if ($redir_url === $given_url || strlen($redir_url) > 255) {
|
||||
if ($redir_url === $given_url || strlen($redir_url) > 255 || !$followRedirects) {
|
||||
$x = File::saveNew($redir_data, $given_url);
|
||||
$file_id = $x->id;
|
||||
} else {
|
||||
$x = File::processNew($redir_url, $notice_id);
|
||||
// This seems kind of messed up... for now skipping this part
|
||||
// if we're already under a redirect, so we don't go into
|
||||
// horrible infinite loops if we've been given an unstable
|
||||
// redirect (where the final destination of the first request
|
||||
// doesn't match what we get when we ask for it again).
|
||||
//
|
||||
// Seen in the wild with clojure.org, which redirects through
|
||||
// wikispaces for auth and appends session data in the URL params.
|
||||
$x = File::processNew($redir_url, $notice_id, /*followRedirects*/false);
|
||||
$file_id = $x->id;
|
||||
File_redirection::saveNew($redir_data, $file_id, $given_url);
|
||||
}
|
||||
@ -153,7 +170,9 @@ class File extends Memcached_DataObject
|
||||
if (empty($x)) {
|
||||
$x = File::staticGet($file_id);
|
||||
if (empty($x)) {
|
||||
throw new ServerException("Robin thinks something is impossible.");
|
||||
// FIXME: This could possibly be a clearer message :)
|
||||
// TRANS: Server exception thrown when... Robin thinks something is impossible!
|
||||
throw new ServerException(_("Robin thinks something is impossible."));
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,8 +185,10 @@ class File extends Memcached_DataObject
|
||||
function isRespectsQuota($user,$fileSize) {
|
||||
|
||||
if ($fileSize > common_config('attachments', 'file_quota')) {
|
||||
return sprintf(_('No file may be larger than %d bytes ' .
|
||||
'and the file you sent was %d bytes. Try to upload a smaller version.'),
|
||||
// TRANS: Message given if an upload is larger than the configured maximum.
|
||||
// TRANS: %1$d is the byte limit for uploads, %2$d is the byte count for the uploaded file.
|
||||
return sprintf(_('No file may be larger than %1$d bytes ' .
|
||||
'and the file you sent was %2$d bytes. Try to upload a smaller version.'),
|
||||
common_config('attachments', 'file_quota'), $fileSize);
|
||||
}
|
||||
|
||||
@ -176,6 +197,8 @@ class File extends Memcached_DataObject
|
||||
$this->fetch();
|
||||
$total = $this->total + $fileSize;
|
||||
if ($total > common_config('attachments', 'user_quota')) {
|
||||
// TRANS: Message given if an upload would exceed user quota.
|
||||
// TRANS: %d (number) is the user quota in bytes.
|
||||
return sprintf(_('A file this large would exceed your user quota of %d bytes.'), common_config('attachments', 'user_quota'));
|
||||
}
|
||||
$query .= ' AND EXTRACT(month FROM file.modified) = EXTRACT(month FROM now()) and EXTRACT(year FROM file.modified) = EXTRACT(year FROM now())';
|
||||
@ -183,6 +206,8 @@ class File extends Memcached_DataObject
|
||||
$this->fetch();
|
||||
$total = $this->total + $fileSize;
|
||||
if ($total > common_config('attachments', 'monthly_quota')) {
|
||||
// TRANS: Message given id an upload would exceed a user's monthly quota.
|
||||
// TRANS: $d (number) is the monthly user quota in bytes.
|
||||
return sprintf(_('A file this large would exceed your monthly quota of %d bytes.'), common_config('attachments', 'monthly_quota'));
|
||||
}
|
||||
return true;
|
||||
@ -219,7 +244,8 @@ class File extends Memcached_DataObject
|
||||
static function path($filename)
|
||||
{
|
||||
if (!self::validFilename($filename)) {
|
||||
throw new ClientException("Invalid filename");
|
||||
// TRANS: Client exception thrown if a file upload does not have a valid name.
|
||||
throw new ClientException(_("Invalid filename."));
|
||||
}
|
||||
$dir = common_config('attachments', 'dir');
|
||||
|
||||
@ -233,7 +259,8 @@ class File extends Memcached_DataObject
|
||||
static function url($filename)
|
||||
{
|
||||
if (!self::validFilename($filename)) {
|
||||
throw new ClientException("Invalid filename");
|
||||
// TRANS: Client exception thrown if a file upload does not have a valid name.
|
||||
throw new ClientException(_("Invalid filename."));
|
||||
}
|
||||
if(common_config('site','private')) {
|
||||
|
||||
@ -286,6 +313,7 @@ class File extends Memcached_DataObject
|
||||
|
||||
if(! isset($this->filename)){
|
||||
$notEnclosureMimeTypes = array(null,'text/html','application/xhtml+xml');
|
||||
$mimetype = $this->mimetype;
|
||||
if($mimetype != null){
|
||||
$mimetype = strtolower($this->mimetype);
|
||||
}
|
||||
@ -325,4 +353,3 @@ class File extends Memcached_DataObject
|
||||
return !empty($enclosure);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,6 +240,14 @@ class File_redirection extends Memcached_DataObject
|
||||
} else if (is_string($redir_data)) {
|
||||
// The file is a known redirect target.
|
||||
$file = File::staticGet('url', $redir_data);
|
||||
if (empty($file)) {
|
||||
// @fixme should we save a new one?
|
||||
// this case was triggering sometimes for redirects
|
||||
// with unresolvable targets; found while fixing
|
||||
// "can't linkify" bugs with shortened links to
|
||||
// SSL sites with cert issues.
|
||||
return null;
|
||||
}
|
||||
$file_id = $file->id;
|
||||
}
|
||||
} else {
|
||||
|
@ -39,6 +39,22 @@ class Foreign_user extends Memcached_DataObject
|
||||
return null;
|
||||
}
|
||||
|
||||
static function getByNickname($nickname, $service)
|
||||
{
|
||||
if (empty($nickname) || empty($service)) {
|
||||
return null;
|
||||
} else {
|
||||
$fuser = new Foreign_user();
|
||||
$fuser->service = $service;
|
||||
$fuser->nickname = $nickname;
|
||||
$fuser->limit(1);
|
||||
|
||||
$result = $fuser->find(true);
|
||||
|
||||
return empty($result) ? null : $fuser;
|
||||
}
|
||||
}
|
||||
|
||||
function updateKeys(&$orig)
|
||||
{
|
||||
$this->_connect();
|
||||
|
@ -38,6 +38,7 @@ class Group_member extends Memcached_DataObject
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($member, 'INSERT', __FILE__);
|
||||
// TRANS: Exception thrown when joining a group fails.
|
||||
throw new Exception(_("Group join failed."));
|
||||
}
|
||||
|
||||
@ -50,6 +51,7 @@ class Group_member extends Memcached_DataObject
|
||||
'profile_id' => $profile_id));
|
||||
|
||||
if (empty($member)) {
|
||||
// TRANS: Exception thrown when trying to leave a group the user is not a member of.
|
||||
throw new Exception(_("Not part of group."));
|
||||
}
|
||||
|
||||
@ -57,6 +59,7 @@ class Group_member extends Memcached_DataObject
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($member, 'INSERT', __FILE__);
|
||||
// TRANS: Exception thrown when trying to leave a group fails.
|
||||
throw new Exception(_("Group leave failed."));
|
||||
}
|
||||
|
||||
|
@ -115,9 +115,12 @@ class Inbox extends Memcached_DataObject
|
||||
*/
|
||||
static function insertNotice($user_id, $notice_id)
|
||||
{
|
||||
$inbox = DB_DataObject::staticGet('inbox', 'user_id', $user_id);
|
||||
|
||||
if (empty($inbox)) {
|
||||
// Going straight to the DB rather than trusting our caching
|
||||
// during an update. Note: not using DB_DataObject::staticGet,
|
||||
// which is unsafe to use directly (in-process caching causes
|
||||
// memory leaks, which accumulate in queue processes).
|
||||
$inbox = new Inbox();
|
||||
if (!$inbox->get('user_id', $user_id)) {
|
||||
$inbox = Inbox::initialize($user_id);
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ class Local_group extends Memcached_DataObject
|
||||
$this->encache();
|
||||
} else {
|
||||
common_log_db_error($local, 'UPDATE', __FILE__);
|
||||
// TRANS: Server exception thrown when updating a local group fails.
|
||||
throw new ServerException(_('Could not update local group.'));
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,8 @@ class Login_token extends Memcached_DataObject
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($login_token, 'INSERT', __FILE__);
|
||||
// TRANS: Exception thrown when trying creating a login token failed.
|
||||
// TRANS: %s is the user nickname for which token creation failed.
|
||||
throw new Exception(sprintf(_('Could not create login token for %s'),
|
||||
$user->nickname));
|
||||
}
|
||||
|
@ -124,16 +124,17 @@ class Memcached_DataObject extends Safe_DataObject
|
||||
}
|
||||
|
||||
static function memcache() {
|
||||
return common_memcache();
|
||||
return Cache::instance();
|
||||
}
|
||||
|
||||
static function cacheKey($cls, $k, $v) {
|
||||
if (is_object($cls) || is_object($k) || is_object($v)) {
|
||||
if (is_object($cls) || is_object($k) || (is_object($v) && !($v instanceof DB_DataObject_Cast))) {
|
||||
$e = new Exception();
|
||||
common_log(LOG_ERR, __METHOD__ . ' object in param: ' .
|
||||
str_replace("\n", " ", $e->getTraceAsString()));
|
||||
}
|
||||
return common_cache_key(strtolower($cls).':'.$k.':'.$v);
|
||||
$vstr = self::valueString($v);
|
||||
return Cache::key(strtolower($cls).':'.$k.':'.$vstr);
|
||||
}
|
||||
|
||||
static function getcached($cls, $k, $v) {
|
||||
@ -229,11 +230,12 @@ class Memcached_DataObject extends Safe_DataObject
|
||||
if (empty($this->$key)) {
|
||||
continue;
|
||||
}
|
||||
$ckeys[] = $this->cacheKey($this->tableName(), $key, $this->$key);
|
||||
$ckeys[] = $this->cacheKey($this->tableName(), $key, self::valueString($this->$key));
|
||||
} else if ($type == 'K' || $type == 'N') {
|
||||
$pkey[] = $key;
|
||||
$pval[] = $this->$key;
|
||||
$pval[] = self::valueString($this->$key);
|
||||
} else {
|
||||
// Low level exception. No need for i18n as discussed with Brion.
|
||||
throw new Exception("Unknown key type $key => $type for " . $this->tableName());
|
||||
}
|
||||
}
|
||||
@ -281,6 +283,7 @@ class Memcached_DataObject extends Safe_DataObject
|
||||
} else if ($type == 'fulltext') {
|
||||
$search_engine = new MySQLSearch($this, $table);
|
||||
} else {
|
||||
// Low level exception. No need for i18n as discussed with Brion.
|
||||
throw new ServerException('Unknown search type: ' . $type);
|
||||
}
|
||||
} else {
|
||||
@ -299,8 +302,8 @@ class Memcached_DataObject extends Safe_DataObject
|
||||
$inst->query($qry);
|
||||
return $inst;
|
||||
}
|
||||
$key_part = common_keyize($cls).':'.md5($qry);
|
||||
$ckey = common_cache_key($key_part);
|
||||
$key_part = Cache::keyize($cls).':'.md5($qry);
|
||||
$ckey = Cache::key($key_part);
|
||||
$stored = $c->get($ckey);
|
||||
|
||||
if ($stored !== false) {
|
||||
@ -351,7 +354,7 @@ class Memcached_DataObject extends Safe_DataObject
|
||||
* low-level database function and add a comment to the
|
||||
* query string. This should then be visible in process lists
|
||||
* and slow query logs, to help identify problem areas.
|
||||
*
|
||||
*
|
||||
* Also marks whether this was a web GET/POST or which daemon
|
||||
* was running it.
|
||||
*
|
||||
@ -526,7 +529,8 @@ class Memcached_DataObject extends Safe_DataObject
|
||||
}
|
||||
|
||||
if (!$dsn) {
|
||||
throw new Exception("No database name / dsn found anywhere");
|
||||
// TRANS: Exception thrown when database name or Data Source Name could not be found.
|
||||
throw new Exception(_("No database name or DSN found anywhere."));
|
||||
}
|
||||
|
||||
return $dsn;
|
||||
@ -546,7 +550,7 @@ class Memcached_DataObject extends Safe_DataObject
|
||||
|
||||
$keyPart = vsprintf($format, $args);
|
||||
|
||||
$cacheKey = common_cache_key($keyPart);
|
||||
$cacheKey = Cache::key($keyPart);
|
||||
|
||||
return $c->delete($cacheKey);
|
||||
}
|
||||
@ -576,6 +580,7 @@ class Memcached_DataObject extends Safe_DataObject
|
||||
if ($message instanceof PEAR_Error) {
|
||||
$message = $message->getMessage();
|
||||
}
|
||||
// Low level exception. No need for i18n as discussed with Brion.
|
||||
throw new ServerException("[$id] DB_DataObject error [$type]: $message");
|
||||
}
|
||||
|
||||
@ -587,12 +592,12 @@ class Memcached_DataObject extends Safe_DataObject
|
||||
return false;
|
||||
}
|
||||
|
||||
$cacheKey = common_cache_key($keyPart);
|
||||
$cacheKey = Cache::key($keyPart);
|
||||
|
||||
return $c->get($cacheKey);
|
||||
}
|
||||
|
||||
static function cacheSet($keyPart, $value)
|
||||
static function cacheSet($keyPart, $value, $flag=null, $expiry=null)
|
||||
{
|
||||
$c = self::memcache();
|
||||
|
||||
@ -600,9 +605,36 @@ class Memcached_DataObject extends Safe_DataObject
|
||||
return false;
|
||||
}
|
||||
|
||||
$cacheKey = common_cache_key($keyPart);
|
||||
$cacheKey = Cache::key($keyPart);
|
||||
|
||||
return $c->set($cacheKey, $value);
|
||||
return $c->set($cacheKey, $value, $flag, $expiry);
|
||||
}
|
||||
|
||||
static function valueString($v)
|
||||
{
|
||||
$vstr = null;
|
||||
if (is_object($v) && $v instanceof DB_DataObject_Cast) {
|
||||
switch ($v->type) {
|
||||
case 'date':
|
||||
$vstr = $v->year . '-' . $v->month . '-' . $v->day;
|
||||
break;
|
||||
case 'blob':
|
||||
case 'string':
|
||||
case 'sql':
|
||||
case 'datetime':
|
||||
case 'time':
|
||||
// Low level exception. No need for i18n as discussed with Brion.
|
||||
throw new ServerException("Unhandled DB_DataObject_Cast type passed as cacheKey value: '$v->type'");
|
||||
break;
|
||||
default:
|
||||
// Low level exception. No need for i18n as discussed with Brion.
|
||||
throw new ServerException("Unknown DB_DataObject_Cast type passed as cacheKey value: '$v->type'");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$vstr = strval($v);
|
||||
}
|
||||
return $vstr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,7 @@ class Message extends Memcached_DataObject
|
||||
$sender = Profile::staticGet('id', $from);
|
||||
|
||||
if (!$sender->hasRight(Right::NEWMESSAGE)) {
|
||||
// TRANS: Client exception thrown when a user tries to send a direct message while being banned from sending them.
|
||||
throw new ClientException(_('You are banned from sending direct messages.'));
|
||||
}
|
||||
|
||||
@ -58,6 +59,7 @@ class Message extends Memcached_DataObject
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($msg, 'INSERT', __FILE__);
|
||||
// TRANS: Message given when a message could not be stored on the server.
|
||||
return _('Could not insert message.');
|
||||
}
|
||||
|
||||
@ -68,6 +70,7 @@ class Message extends Memcached_DataObject
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($msg, 'UPDATE', __FILE__);
|
||||
// TRANS: Message given when a message could not be updated on the server.
|
||||
return _('Could not update message with new URI.');
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
* @author Robin Millette <millette@controlyourself.ca>
|
||||
* @author Sarven Capadisli <csarven@controlyourself.ca>
|
||||
* @author Tom Adams <tom@holizz.com>
|
||||
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license GNU Affero General Public License http://www.gnu.org/licenses/
|
||||
*/
|
||||
|
||||
@ -41,10 +42,10 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
||||
*/
|
||||
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
|
||||
|
||||
/* We keep the first three 20-notice pages, plus one for pagination check,
|
||||
/* We keep 200 notices, the max number of notices available per API request,
|
||||
* in the memcached cache. */
|
||||
|
||||
define('NOTICE_CACHE_WINDOW', 61);
|
||||
define('NOTICE_CACHE_WINDOW', 200);
|
||||
|
||||
define('MAX_BOXCARS', 128);
|
||||
|
||||
@ -89,7 +90,15 @@ class Notice extends Memcached_DataObject
|
||||
|
||||
function getProfile()
|
||||
{
|
||||
return Profile::staticGet('id', $this->profile_id);
|
||||
$profile = Profile::staticGet('id', $this->profile_id);
|
||||
|
||||
if (empty($profile)) {
|
||||
// TRANS: Server exception thrown when a user profile for a notice cannot be found.
|
||||
// TRANS: %1$d is a profile ID (number), %2$d is a notice ID (number).
|
||||
throw new ServerException(sprintf(_('No such profile (%1$d) for notice (%2$d).'), $this->profile_id, $this->id));
|
||||
}
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
function delete()
|
||||
@ -97,26 +106,34 @@ class Notice extends Memcached_DataObject
|
||||
// For auditing purposes, save a record that the notice
|
||||
// was deleted.
|
||||
|
||||
$deleted = new Deleted_notice();
|
||||
// @fixme we have some cases where things get re-run and so the
|
||||
// insert fails.
|
||||
$deleted = Deleted_notice::staticGet('id', $this->id);
|
||||
if (!$deleted) {
|
||||
$deleted = new Deleted_notice();
|
||||
|
||||
$deleted->id = $this->id;
|
||||
$deleted->profile_id = $this->profile_id;
|
||||
$deleted->uri = $this->uri;
|
||||
$deleted->created = $this->created;
|
||||
$deleted->deleted = common_sql_now();
|
||||
$deleted->id = $this->id;
|
||||
$deleted->profile_id = $this->profile_id;
|
||||
$deleted->uri = $this->uri;
|
||||
$deleted->created = $this->created;
|
||||
$deleted->deleted = common_sql_now();
|
||||
|
||||
$deleted->insert();
|
||||
$deleted->insert();
|
||||
}
|
||||
|
||||
// Clear related records
|
||||
if (Event::handle('NoticeDeleteRelated', array($this))) {
|
||||
|
||||
$this->clearReplies();
|
||||
$this->clearRepeats();
|
||||
$this->clearFaves();
|
||||
$this->clearTags();
|
||||
$this->clearGroupInboxes();
|
||||
// Clear related records
|
||||
|
||||
// NOTE: we don't clear inboxes
|
||||
// NOTE: we don't clear queue items
|
||||
$this->clearReplies();
|
||||
$this->clearRepeats();
|
||||
$this->clearFaves();
|
||||
$this->clearTags();
|
||||
$this->clearGroupInboxes();
|
||||
|
||||
// NOTE: we don't clear inboxes
|
||||
// NOTE: we don't clear queue items
|
||||
}
|
||||
|
||||
$result = parent::delete();
|
||||
|
||||
@ -231,6 +248,8 @@ class Notice extends Memcached_DataObject
|
||||
if (!empty($options)) {
|
||||
$options = $options + $defaults;
|
||||
extract($options);
|
||||
} else {
|
||||
extract($defaults);
|
||||
}
|
||||
|
||||
if (!isset($is_local)) {
|
||||
@ -242,28 +261,34 @@ class Notice extends Memcached_DataObject
|
||||
$final = common_shorten_links($content);
|
||||
|
||||
if (Notice::contentTooLong($final)) {
|
||||
// TRANS: Client exception thrown if a notice contains too many characters.
|
||||
throw new ClientException(_('Problem saving notice. Too long.'));
|
||||
}
|
||||
|
||||
if (empty($profile)) {
|
||||
// TRANS: Client exception thrown when trying to save a notice for an unknown user.
|
||||
throw new ClientException(_('Problem saving notice. Unknown user.'));
|
||||
}
|
||||
|
||||
if (common_config('throttle', 'enabled') && !Notice::checkEditThrottle($profile_id)) {
|
||||
common_log(LOG_WARNING, 'Excessive posting by profile #' . $profile_id . '; throttled.');
|
||||
// TRANS: Client exception thrown when a user tries to post too many notices in a given time frame.
|
||||
throw new ClientException(_('Too many notices too fast; take a breather '.
|
||||
'and post again in a few minutes.'));
|
||||
}
|
||||
|
||||
if (common_config('site', 'dupelimit') > 0 && !Notice::checkDupes($profile_id, $final)) {
|
||||
common_log(LOG_WARNING, 'Dupe posting by profile #' . $profile_id . '; throttled.');
|
||||
// TRANS: Client exception thrown when a user tries to post too many duplicate notices in a given time frame.
|
||||
throw new ClientException(_('Too many duplicate messages too quickly;'.
|
||||
' take a breather and post again in a few minutes.'));
|
||||
}
|
||||
|
||||
if (!$profile->hasRight(Right::NEWNOTICE)) {
|
||||
common_log(LOG_WARNING, "Attempted post from user disallowed to post: " . $profile->nickname);
|
||||
throw new ClientException(_('You are banned from posting notices on this site.'));
|
||||
|
||||
// TRANS: Client exception thrown when a user tries to post while being banned.
|
||||
throw new ClientException(_('You are banned from posting notices on this site.'), 403);
|
||||
}
|
||||
|
||||
$notice = new Notice();
|
||||
@ -329,6 +354,7 @@ class Notice extends Memcached_DataObject
|
||||
|
||||
if (!$id) {
|
||||
common_log_db_error($notice, 'INSERT', __FILE__);
|
||||
// TRANS: Server exception thrown when a notice cannot be saved.
|
||||
throw new ServerException(_('Problem saving notice.'));
|
||||
}
|
||||
|
||||
@ -355,6 +381,7 @@ class Notice extends Memcached_DataObject
|
||||
if ($changed) {
|
||||
if (!$notice->update($orig)) {
|
||||
common_log_db_error($notice, 'UPDATE', __FILE__);
|
||||
// TRANS: Server exception thrown when a notice cannot be updated.
|
||||
throw new ServerException(_('Problem saving notice.'));
|
||||
}
|
||||
}
|
||||
@ -463,7 +490,7 @@ class Notice extends Memcached_DataObject
|
||||
function saveKnownUrls($urls)
|
||||
{
|
||||
// @fixme validation?
|
||||
foreach ($urls as $url) {
|
||||
foreach (array_unique($urls) as $url) {
|
||||
File::processNew($url, $this->id);
|
||||
}
|
||||
}
|
||||
@ -556,7 +583,9 @@ class Notice extends Memcached_DataObject
|
||||
if ($f2p->find()) {
|
||||
while ($f2p->fetch()) {
|
||||
$f = File::staticGet($f2p->file_id);
|
||||
$att[] = clone($f);
|
||||
if ($f) {
|
||||
$att[] = clone($f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $att;
|
||||
@ -564,7 +593,7 @@ class Notice extends Memcached_DataObject
|
||||
|
||||
function getStreamByIds($ids)
|
||||
{
|
||||
$cache = common_memcache();
|
||||
$cache = Cache::instance();
|
||||
|
||||
if (!empty($cache)) {
|
||||
$notices = array();
|
||||
@ -704,7 +733,7 @@ class Notice extends Memcached_DataObject
|
||||
|
||||
/**
|
||||
* Is this notice part of an active conversation?
|
||||
*
|
||||
*
|
||||
* @return boolean true if other messages exist in the same
|
||||
* conversation, false if this is the only one
|
||||
*/
|
||||
@ -732,7 +761,7 @@ class Notice extends Memcached_DataObject
|
||||
$c = self::memcache();
|
||||
|
||||
if (!empty($c)) {
|
||||
$ni = $c->get(common_cache_key('notice:who_gets:'.$this->id));
|
||||
$ni = $c->get(Cache::key('notice:who_gets:'.$this->id));
|
||||
if ($ni !== false) {
|
||||
return $ni;
|
||||
}
|
||||
@ -784,7 +813,7 @@ class Notice extends Memcached_DataObject
|
||||
|
||||
if (!empty($c)) {
|
||||
// XXX: pack this data better
|
||||
$c->set(common_cache_key('notice:who_gets:'.$this->id), $ni);
|
||||
$c->set(Cache::key('notice:who_gets:'.$this->id), $ni);
|
||||
}
|
||||
|
||||
return $ni;
|
||||
@ -866,11 +895,12 @@ class Notice extends Memcached_DataObject
|
||||
function saveKnownGroups($group_ids)
|
||||
{
|
||||
if (!is_array($group_ids)) {
|
||||
throw new ServerException("Bad type provided to saveKnownGroups");
|
||||
// TRANS: Server exception thrown when no array is provided to the method saveKnownGroups().
|
||||
throw new ServerException(_("Bad type provided to saveKnownGroups"));
|
||||
}
|
||||
|
||||
$groups = array();
|
||||
foreach ($group_ids as $id) {
|
||||
foreach (array_unique($group_ids) as $id) {
|
||||
$group = User_group::staticGet('id', $id);
|
||||
if ($group) {
|
||||
common_log(LOG_ERR, "Local delivery to group id $id, $group->nickname");
|
||||
@ -964,6 +994,7 @@ class Notice extends Memcached_DataObject
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($gi, 'INSERT', __FILE__);
|
||||
// TRANS: Server exception thrown when an update for a group inbox fails.
|
||||
throw new ServerException(_('Problem saving group inbox.'));
|
||||
}
|
||||
|
||||
@ -981,8 +1012,7 @@ class Notice extends Memcached_DataObject
|
||||
* messages, we won't deliver to any remote targets as that's the
|
||||
* source service's responsibility.
|
||||
*
|
||||
* @fixme Unlike saveReplies() there's no mail notification here.
|
||||
* Move that to distrib queue handler?
|
||||
* Mail notifications etc will be handled later.
|
||||
*
|
||||
* @param array of unique identifier URIs for recipients
|
||||
*/
|
||||
@ -991,26 +1021,31 @@ class Notice extends Memcached_DataObject
|
||||
if (empty($uris)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sender = Profile::staticGet($this->profile_id);
|
||||
|
||||
foreach ($uris as $uri) {
|
||||
foreach (array_unique($uris) as $uri) {
|
||||
|
||||
$user = User::staticGet('uri', $uri);
|
||||
$profile = Profile::fromURI($uri);
|
||||
|
||||
if (!empty($user)) {
|
||||
if ($user->hasBlocked($sender)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$reply = new Reply();
|
||||
|
||||
$reply->notice_id = $this->id;
|
||||
$reply->profile_id = $user->id;
|
||||
|
||||
$id = $reply->insert();
|
||||
|
||||
self::blow('reply:stream:%d', $user->id);
|
||||
if (empty($profile)) {
|
||||
common_log(LOG_WARNING, "Unable to determine profile for URI '$uri'");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($profile->hasBlocked($sender)) {
|
||||
common_log(LOG_INFO, "Not saving reply to profile {$profile->id} ($uri) from sender {$sender->id} because of a block.");
|
||||
continue;
|
||||
}
|
||||
|
||||
$reply = new Reply();
|
||||
|
||||
$reply->notice_id = $this->id;
|
||||
$reply->profile_id = $profile->id;
|
||||
|
||||
common_log(LOG_INFO, __METHOD__ . ": saving reply: notice $this->id to profile $profile->id");
|
||||
|
||||
$id = $reply->insert();
|
||||
}
|
||||
|
||||
return;
|
||||
@ -1021,8 +1056,7 @@ class Notice extends Memcached_DataObject
|
||||
* and save reply records indicating that this message needs to be
|
||||
* delivered to those users.
|
||||
*
|
||||
* Side effect: local recipients get e-mail notifications here.
|
||||
* @fixme move mail notifications to distrib?
|
||||
* Mail notifications to local profiles will be sent later.
|
||||
*
|
||||
* @return array of integer profile IDs
|
||||
*/
|
||||
@ -1073,26 +1107,26 @@ class Notice extends Memcached_DataObject
|
||||
|
||||
if (!$id) {
|
||||
common_log_db_error($reply, 'INSERT', __FILE__);
|
||||
throw new ServerException("Couldn't save reply for {$this->id}, {$mentioned->id}");
|
||||
// TRANS: Server exception thrown when a reply cannot be saved.
|
||||
// TRANS: %1$d is a notice ID, %2$d is the ID of the mentioned user.
|
||||
throw new ServerException(sprintf(_("Could not save reply for %1$d, %2$d."), $this->id, $mentioned->id));
|
||||
} else {
|
||||
$replied[$mentioned->id] = 1;
|
||||
self::blow('reply:stream:%d', $mentioned->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$recipientIds = array_keys($replied);
|
||||
|
||||
foreach ($recipientIds as $recipientId) {
|
||||
$user = User::staticGet('id', $recipientId);
|
||||
if (!empty($user)) {
|
||||
self::blow('reply:stream:%d', $reply->profile_id);
|
||||
mail_notify_attn($user, $this);
|
||||
}
|
||||
}
|
||||
|
||||
return $recipientIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull the complete list of @-reply targets for this notice.
|
||||
*
|
||||
* @return array of integer profile ids
|
||||
*/
|
||||
function getReplies()
|
||||
{
|
||||
// XXX: cache me
|
||||
@ -1115,6 +1149,30 @@ class Notice extends Memcached_DataObject
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send e-mail notifications to local @-reply targets.
|
||||
*
|
||||
* Replies must already have been saved; this is expected to be run
|
||||
* from the distrib queue handler.
|
||||
*/
|
||||
function sendReplyNotifications()
|
||||
{
|
||||
// Don't send reply notifications for repeats
|
||||
|
||||
if (!empty($this->repeat_of)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$recipientIds = $this->getReplies();
|
||||
|
||||
foreach ($recipientIds as $recipientId) {
|
||||
$user = User::staticGet('id', $recipientId);
|
||||
if (!empty($user)) {
|
||||
mail_notify_attn($user, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull list of groups this notice needs to be delivered to,
|
||||
* as previously recorded by saveGroups() or saveKnownGroups().
|
||||
@ -1154,7 +1212,10 @@ class Notice extends Memcached_DataObject
|
||||
return $groups;
|
||||
}
|
||||
|
||||
function asAtomEntry($namespace=false, $source=false, $author=true)
|
||||
// This has gotten way too long. Needs to be sliced up into functional bits
|
||||
// or ideally exported to a utility class.
|
||||
|
||||
function asAtomEntry($namespace=false, $source=false, $author=true, $cur=null)
|
||||
{
|
||||
$profile = $this->getProfile();
|
||||
|
||||
@ -1167,149 +1228,332 @@ class Notice extends Memcached_DataObject
|
||||
'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/',
|
||||
'xmlns:media' => 'http://purl.org/syndication/atommedia',
|
||||
'xmlns:poco' => 'http://portablecontacts.net/spec/1.0',
|
||||
'xmlns:ostatus' => 'http://ostatus.org/schema/1.0');
|
||||
'xmlns:ostatus' => 'http://ostatus.org/schema/1.0',
|
||||
'xmlns:statusnet' => 'http://status.net/schema/api/1/');
|
||||
} else {
|
||||
$attrs = array();
|
||||
}
|
||||
|
||||
$xs->elementStart('entry', $attrs);
|
||||
if (Event::handle('StartActivityStart', array(&$this, &$xs, &$attrs))) {
|
||||
$xs->elementStart('entry', $attrs);
|
||||
Event::handle('EndActivityStart', array(&$this, &$xs, &$attrs));
|
||||
}
|
||||
|
||||
if ($source) {
|
||||
$xs->elementStart('source');
|
||||
$xs->element('id', null, $profile->profileurl);
|
||||
$xs->element('title', null, $profile->nickname . " - " . common_config('site', 'name'));
|
||||
$xs->element('link', array('href' => $profile->profileurl));
|
||||
$user = User::staticGet('id', $profile->id);
|
||||
if (!empty($user)) {
|
||||
$atom_feed = common_local_url('ApiTimelineUser',
|
||||
array('format' => 'atom',
|
||||
'id' => $profile->nickname));
|
||||
$xs->element('link', array('rel' => 'self',
|
||||
'type' => 'application/atom+xml',
|
||||
'href' => $profile->profileurl));
|
||||
$xs->element('link', array('rel' => 'license',
|
||||
'href' => common_config('license', 'url')));
|
||||
if (Event::handle('StartActivitySource', array(&$this, &$xs))) {
|
||||
|
||||
if ($source) {
|
||||
|
||||
$atom_feed = $profile->getAtomFeed();
|
||||
|
||||
if (!empty($atom_feed)) {
|
||||
|
||||
$xs->elementStart('source');
|
||||
|
||||
// XXX: we should store the actual feed ID
|
||||
|
||||
$xs->element('id', null, $atom_feed);
|
||||
|
||||
// XXX: we should store the actual feed title
|
||||
|
||||
$xs->element('title', null, $profile->getBestName());
|
||||
|
||||
$xs->element('link', array('rel' => 'alternate',
|
||||
'type' => 'text/html',
|
||||
'href' => $profile->profileurl));
|
||||
|
||||
$xs->element('link', array('rel' => 'self',
|
||||
'type' => 'application/atom+xml',
|
||||
'href' => $atom_feed));
|
||||
|
||||
$xs->element('icon', null, $profile->avatarUrl(AVATAR_PROFILE_SIZE));
|
||||
|
||||
$notice = $profile->getCurrentNotice();
|
||||
|
||||
if (!empty($notice)) {
|
||||
$xs->element('updated', null, self::utcDate($notice->created));
|
||||
}
|
||||
|
||||
$user = User::staticGet('id', $profile->id);
|
||||
|
||||
if (!empty($user)) {
|
||||
$xs->element('link', array('rel' => 'license',
|
||||
'href' => common_config('license', 'url')));
|
||||
}
|
||||
|
||||
$xs->elementEnd('source');
|
||||
}
|
||||
}
|
||||
|
||||
$xs->element('icon', null, $profile->avatarUrl(AVATAR_PROFILE_SIZE));
|
||||
$xs->element('updated', null, common_date_w3dtf($this->created));
|
||||
Event::handle('EndActivitySource', array(&$this, &$xs));
|
||||
}
|
||||
|
||||
if ($source) {
|
||||
$xs->elementEnd('source');
|
||||
$title = common_xml_safe_str($this->content);
|
||||
|
||||
if (Event::handle('StartActivityTitle', array(&$this, &$xs, &$title))) {
|
||||
$xs->element('title', null, $title);
|
||||
Event::handle('EndActivityTitle', array($this, &$xs, $title));
|
||||
}
|
||||
|
||||
$xs->element('title', null, common_xml_safe_str($this->content));
|
||||
$atomAuthor = '';
|
||||
|
||||
if ($author) {
|
||||
$xs->raw($profile->asAtomAuthor());
|
||||
$xs->raw($profile->asActivityActor());
|
||||
$atomAuthor = $profile->asAtomAuthor($cur);
|
||||
}
|
||||
|
||||
$xs->element('link', array('rel' => 'alternate',
|
||||
'type' => 'text/html',
|
||||
'href' => $this->bestUrl()));
|
||||
if (Event::handle('StartActivityAuthor', array(&$this, &$xs, &$atomAuthor))) {
|
||||
if (!empty($atomAuthor)) {
|
||||
$xs->raw($atomAuthor);
|
||||
Event::handle('EndActivityAuthor', array(&$this, &$xs, &$atomAuthor));
|
||||
}
|
||||
}
|
||||
|
||||
$xs->element('id', null, $this->uri);
|
||||
$actor = '';
|
||||
|
||||
$xs->element('published', null, common_date_w3dtf($this->created));
|
||||
$xs->element('updated', null, common_date_w3dtf($this->created));
|
||||
if ($author) {
|
||||
$actor = $profile->asActivityActor();
|
||||
}
|
||||
|
||||
if (Event::handle('StartActivityActor', array(&$this, &$xs, &$actor))) {
|
||||
if (!empty($actor)) {
|
||||
$xs->raw($actor);
|
||||
Event::handle('EndActivityActor', array(&$this, &$xs, &$actor));
|
||||
}
|
||||
}
|
||||
|
||||
$url = $this->bestUrl();
|
||||
|
||||
if (Event::handle('StartActivityLink', array(&$this, &$xs, &$url))) {
|
||||
$xs->element('link', array('rel' => 'alternate',
|
||||
'type' => 'text/html',
|
||||
'href' => $url));
|
||||
Event::handle('EndActivityLink', array(&$this, &$xs, $url));
|
||||
}
|
||||
|
||||
$id = $this->uri;
|
||||
|
||||
if (Event::handle('StartActivityId', array(&$this, &$xs, &$id))) {
|
||||
$xs->element('id', null, $id);
|
||||
Event::handle('EndActivityId', array(&$this, &$xs, $id));
|
||||
}
|
||||
|
||||
$published = self::utcDate($this->created);
|
||||
|
||||
if (Event::handle('StartActivityPublished', array(&$this, &$xs, &$published))) {
|
||||
$xs->element('published', null, $published);
|
||||
Event::handle('EndActivityPublished', array(&$this, &$xs, $published));
|
||||
}
|
||||
|
||||
$updated = $published; // XXX: notices are usually immutable
|
||||
|
||||
if (Event::handle('StartActivityUpdated', array(&$this, &$xs, &$updated))) {
|
||||
$xs->element('updated', null, $updated);
|
||||
Event::handle('EndActivityUpdated', array(&$this, &$xs, $updated));
|
||||
}
|
||||
|
||||
$content = common_xml_safe_str($this->rendered);
|
||||
|
||||
if (Event::handle('StartActivityContent', array(&$this, &$xs, &$content))) {
|
||||
$xs->element('content', array('type' => 'html'), $content);
|
||||
Event::handle('EndActivityContent', array(&$this, &$xs, $content));
|
||||
}
|
||||
|
||||
// Most of our notices represent POSTing a NOTE. This is the default verb
|
||||
// for activity streams, so we normally just leave it out.
|
||||
|
||||
$verb = ActivityVerb::POST;
|
||||
|
||||
if (Event::handle('StartActivityVerb', array(&$this, &$xs, &$verb))) {
|
||||
$xs->element('activity:verb', null, $verb);
|
||||
Event::handle('EndActivityVerb', array(&$this, &$xs, $verb));
|
||||
}
|
||||
|
||||
// We use the default behavior for activity streams: if there's no activity:object,
|
||||
// then treat the entry itself as the object. Here, you can set the type of that object,
|
||||
// which is normally a NOTE.
|
||||
|
||||
$type = ActivityObject::NOTE;
|
||||
|
||||
if (Event::handle('StartActivityDefaultObjectType', array(&$this, &$xs, &$type))) {
|
||||
$xs->element('activity:object-type', null, $type);
|
||||
Event::handle('EndActivityDefaultObjectType', array(&$this, &$xs, $type));
|
||||
}
|
||||
|
||||
// Since we usually use the entry itself as an object, we don't have an explicit
|
||||
// object. Some extensions may want to add them (for photo, event, music, etc.).
|
||||
|
||||
$objects = array();
|
||||
|
||||
if (Event::handle('StartActivityObjects', array(&$this, &$xs, &$objects))) {
|
||||
foreach ($objects as $object) {
|
||||
$xs->raw($object->asString());
|
||||
}
|
||||
Event::handle('EndActivityObjects', array(&$this, &$xs, $objects));
|
||||
}
|
||||
|
||||
$noticeInfoAttr = array('local_id' => $this->id); // local notice ID (useful to clients for ordering)
|
||||
|
||||
$ns = $this->getSource();
|
||||
|
||||
if (!empty($ns)) {
|
||||
$noticeInfoAttr['source'] = $ns->code;
|
||||
if (!empty($ns->url)) {
|
||||
$noticeInfoAttr['source_link'] = $ns->url;
|
||||
if (!empty($ns->name)) {
|
||||
$noticeInfoAttr['source'] = '<a href="'
|
||||
. htmlspecialchars($ns->url)
|
||||
. '" rel="nofollow">'
|
||||
. htmlspecialchars($ns->name)
|
||||
. '</a>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($cur)) {
|
||||
$noticeInfoAttr['favorite'] = ($cur->hasFave($this)) ? "true" : "false";
|
||||
$profile = $cur->getProfile();
|
||||
$noticeInfoAttr['repeated'] = ($profile->hasRepeated($this->id)) ? "true" : "false";
|
||||
}
|
||||
|
||||
if (!empty($this->repeat_of)) {
|
||||
$noticeInfoAttr['repeat_of'] = $this->repeat_of;
|
||||
}
|
||||
|
||||
if (Event::handle('StartActivityNoticeInfo', array(&$this, &$xs, &$noticeInfoAttr))) {
|
||||
$xs->element('statusnet:notice_info', $noticeInfoAttr, null);
|
||||
Event::handle('EndActivityNoticeInfo', array(&$this, &$xs, $noticeInfoAttr));
|
||||
}
|
||||
|
||||
$replyNotice = null;
|
||||
|
||||
if ($this->reply_to) {
|
||||
$reply_notice = Notice::staticGet('id', $this->reply_to);
|
||||
if (!empty($reply_notice)) {
|
||||
$replyNotice = Notice::staticGet('id', $this->reply_to);
|
||||
}
|
||||
|
||||
if (Event::handle('StartActivityInReplyTo', array(&$this, &$xs, &$replyNotice))) {
|
||||
if (!empty($replyNotice)) {
|
||||
$xs->element('link', array('rel' => 'related',
|
||||
'href' => $reply_notice->bestUrl()));
|
||||
'href' => $replyNotice->bestUrl()));
|
||||
$xs->element('thr:in-reply-to',
|
||||
array('ref' => $reply_notice->uri,
|
||||
'href' => $reply_notice->bestUrl()));
|
||||
array('ref' => $replyNotice->uri,
|
||||
'href' => $replyNotice->bestUrl()));
|
||||
Event::handle('EndActivityInReplyTo', array(&$this, &$xs, $replyNotice));
|
||||
}
|
||||
}
|
||||
|
||||
$conv = null;
|
||||
|
||||
if (!empty($this->conversation)) {
|
||||
|
||||
$conv = Conversation::staticGet('id', $this->conversation);
|
||||
|
||||
if (!empty($conv)) {
|
||||
$xs->element(
|
||||
'link', array(
|
||||
'rel' => 'ostatus:conversation',
|
||||
'href' => $conv->uri
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (Event::handle('StartActivityConversation', array(&$this, &$xs, &$conv))) {
|
||||
if (!empty($conv)) {
|
||||
$xs->element('link', array('rel' => 'ostatus:conversation',
|
||||
'href' => $conv->uri));
|
||||
}
|
||||
Event::handle('EndActivityConversation', array(&$this, &$xs, $conv));
|
||||
}
|
||||
|
||||
$replyProfiles = array();
|
||||
|
||||
$reply_ids = $this->getReplies();
|
||||
|
||||
foreach ($reply_ids as $id) {
|
||||
$profile = Profile::staticGet('id', $id);
|
||||
if (!empty($profile)) {
|
||||
$xs->element(
|
||||
'link', array(
|
||||
'rel' => 'ostatus:attention',
|
||||
'href' => $profile->getUri()
|
||||
)
|
||||
);
|
||||
if (!empty($profile)) {
|
||||
$replyProfiles[] = $profile;
|
||||
}
|
||||
}
|
||||
|
||||
if (Event::handle('StartActivityAttentionProfiles', array(&$this, &$xs, &$replyProfiles))) {
|
||||
foreach ($replyProfiles as $profile) {
|
||||
$xs->element('link', array('rel' => 'ostatus:attention',
|
||||
'href' => $profile->getUri()));
|
||||
$xs->element('link', array('rel' => 'mentioned',
|
||||
'href' => $profile->getUri()));
|
||||
}
|
||||
Event::handle('EndActivityAttentionProfiles', array(&$this, &$xs, $replyProfiles));
|
||||
}
|
||||
|
||||
$groups = $this->getGroups();
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$xs->element(
|
||||
'link', array(
|
||||
'rel' => 'ostatus:attention',
|
||||
'href' => $group->permalink()
|
||||
)
|
||||
);
|
||||
if (Event::handle('StartActivityAttentionGroups', array(&$this, &$xs, &$groups))) {
|
||||
foreach ($groups as $group) {
|
||||
$xs->element('link', array('rel' => 'ostatus:attention',
|
||||
'href' => $group->permalink()));
|
||||
$xs->element('link', array('rel' => 'mentioned',
|
||||
'href' => $group->permalink()));
|
||||
}
|
||||
Event::handle('EndActivityAttentionGroups', array(&$this, &$xs, $groups));
|
||||
}
|
||||
|
||||
$repeat = null;
|
||||
|
||||
if (!empty($this->repeat_of)) {
|
||||
$repeat = Notice::staticGet('id', $this->repeat_of);
|
||||
}
|
||||
|
||||
if (Event::handle('StartActivityForward', array(&$this, &$xs, &$repeat))) {
|
||||
if (!empty($repeat)) {
|
||||
$xs->element(
|
||||
'ostatus:forward',
|
||||
array('ref' => $repeat->uri, 'href' => $repeat->bestUrl())
|
||||
);
|
||||
$xs->element('ostatus:forward',
|
||||
array('ref' => $repeat->uri,
|
||||
'href' => $repeat->bestUrl()));
|
||||
}
|
||||
|
||||
Event::handle('EndActivityForward', array(&$this, &$xs, $repeat));
|
||||
}
|
||||
|
||||
$xs->element(
|
||||
'content',
|
||||
array('type' => 'html'),
|
||||
common_xml_safe_str($this->rendered)
|
||||
);
|
||||
$tags = $this->getTags();
|
||||
|
||||
$tag = new Notice_tag();
|
||||
$tag->notice_id = $this->id;
|
||||
if ($tag->find()) {
|
||||
while ($tag->fetch()) {
|
||||
$xs->element('category', array('term' => $tag->tag));
|
||||
if (Event::handle('StartActivityCategories', array(&$this, &$xs, &$tags))) {
|
||||
foreach ($tags as $tag) {
|
||||
$xs->element('category', array('term' => $tag));
|
||||
}
|
||||
Event::handle('EndActivityCategories', array(&$this, &$xs, $tags));
|
||||
}
|
||||
$tag->free();
|
||||
|
||||
# Enclosures
|
||||
// Enclosures
|
||||
|
||||
$enclosures = array();
|
||||
|
||||
$attachments = $this->attachments();
|
||||
if($attachments){
|
||||
foreach($attachments as $attachment){
|
||||
$enclosure=$attachment->getEnclosure();
|
||||
if ($enclosure) {
|
||||
$attributes = array('rel'=>'enclosure','href'=>$enclosure->url,'type'=>$enclosure->mimetype,'length'=>$enclosure->size);
|
||||
if($enclosure->title){
|
||||
$attributes['title']=$enclosure->title;
|
||||
}
|
||||
$xs->element('link', $attributes, null);
|
||||
}
|
||||
|
||||
foreach ($attachments as $attachment) {
|
||||
$enclosure = $attachment->getEnclosure();
|
||||
if ($enclosure) {
|
||||
$enclosures[] = $enclosure;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($this->lat) && !empty($this->lon)) {
|
||||
$xs->element('georss:point', null, $this->lat . ' ' . $this->lon);
|
||||
if (Event::handle('StartActivityEnclosures', array(&$this, &$xs, &$enclosures))) {
|
||||
foreach ($enclosures as $enclosure) {
|
||||
$attributes = array('rel' => 'enclosure',
|
||||
'href' => $enclosure->url,
|
||||
'type' => $enclosure->mimetype,
|
||||
'length' => $enclosure->size);
|
||||
|
||||
if ($enclosure->title) {
|
||||
$attributes['title'] = $enclosure->title;
|
||||
}
|
||||
|
||||
$xs->element('link', $attributes, null);
|
||||
}
|
||||
Event::handle('EndActivityEnclosures', array(&$this, &$xs, $enclosures));
|
||||
}
|
||||
|
||||
$xs->elementEnd('entry');
|
||||
$lat = $this->lat;
|
||||
$lon = $this->lon;
|
||||
|
||||
if (Event::handle('StartActivityGeo', array(&$this, &$xs, &$lat, &$lon))) {
|
||||
if (!empty($lat) && !empty($lon)) {
|
||||
$xs->element('georss:point', null, $lat . ' ' . $lon);
|
||||
}
|
||||
Event::handle('EndActivityGeo', array(&$this, &$xs, $lat, $lon));
|
||||
}
|
||||
|
||||
if (Event::handle('StartActivityEnd', array(&$this, &$xs))) {
|
||||
$xs->elementEnd('entry');
|
||||
Event::handle('EndActivityEnd', array(&$this, &$xs));
|
||||
}
|
||||
|
||||
return $xs->getString();
|
||||
}
|
||||
@ -1343,7 +1587,7 @@ class Notice extends Memcached_DataObject
|
||||
|
||||
function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0)
|
||||
{
|
||||
$cache = common_memcache();
|
||||
$cache = Cache::instance();
|
||||
|
||||
if (empty($cache) ||
|
||||
$since_id != 0 || $max_id != 0 ||
|
||||
@ -1353,7 +1597,7 @@ class Notice extends Memcached_DataObject
|
||||
$max_id)));
|
||||
}
|
||||
|
||||
$idkey = common_cache_key($cachekey);
|
||||
$idkey = Cache::key($cachekey);
|
||||
|
||||
$idstr = $cache->get($idkey);
|
||||
|
||||
@ -1535,17 +1779,17 @@ class Notice extends Memcached_DataObject
|
||||
|
||||
function repeatStream($limit=100)
|
||||
{
|
||||
$cache = common_memcache();
|
||||
$cache = Cache::instance();
|
||||
|
||||
if (empty($cache)) {
|
||||
$ids = $this->_repeatStreamDirect($limit);
|
||||
} else {
|
||||
$idstr = $cache->get(common_cache_key('notice:repeats:'.$this->id));
|
||||
$idstr = $cache->get(Cache::key('notice:repeats:'.$this->id));
|
||||
if ($idstr !== false) {
|
||||
$ids = explode(',', $idstr);
|
||||
} else {
|
||||
$ids = $this->_repeatStreamDirect(100);
|
||||
$cache->set(common_cache_key('notice:repeats:'.$this->id), implode(',', $ids));
|
||||
$cache->set(Cache::key('notice:repeats:'.$this->id), implode(',', $ids));
|
||||
}
|
||||
if ($limit < 100) {
|
||||
// We do a max of 100, so slice down to limit
|
||||
@ -1699,10 +1943,10 @@ class Notice extends Memcached_DataObject
|
||||
|
||||
if ($tag->find()) {
|
||||
while ($tag->fetch()) {
|
||||
self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, common_keyize($tag->tag));
|
||||
self::blow('profile:notice_ids_tagged:%d:%s;last', $this->profile_id, common_keyize($tag->tag));
|
||||
self::blow('notice_tag:notice_ids:%s', common_keyize($tag->tag));
|
||||
self::blow('notice_tag:notice_ids:%s;last', common_keyize($tag->tag));
|
||||
self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, Cache::keyize($tag->tag));
|
||||
self::blow('profile:notice_ids_tagged:%d:%s;last', $this->profile_id, Cache::keyize($tag->tag));
|
||||
self::blow('notice_tag:notice_ids:%s', Cache::keyize($tag->tag));
|
||||
self::blow('notice_tag:notice_ids:%s;last', Cache::keyize($tag->tag));
|
||||
$tag->delete();
|
||||
}
|
||||
}
|
||||
@ -1781,4 +2025,73 @@ class Notice extends Memcached_DataObject
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source of the notice
|
||||
*
|
||||
* @return Notice_source $ns A notice source object. 'code' is the only attribute
|
||||
* guaranteed to be populated.
|
||||
*/
|
||||
function getSource()
|
||||
{
|
||||
$ns = new Notice_source();
|
||||
if (!empty($this->source)) {
|
||||
switch ($this->source) {
|
||||
case 'web':
|
||||
case 'xmpp':
|
||||
case 'mail':
|
||||
case 'omb':
|
||||
case 'system':
|
||||
case 'api':
|
||||
$ns->code = $this->source;
|
||||
break;
|
||||
default:
|
||||
$ns = Notice_source::staticGet($this->source);
|
||||
if (!$ns) {
|
||||
$ns = new Notice_source();
|
||||
$ns->code = $this->source;
|
||||
$app = Oauth_application::staticGet('name', $this->source);
|
||||
if ($app) {
|
||||
$ns->name = $app->name;
|
||||
$ns->url = $app->source_url;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $ns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the notice was locally created
|
||||
*
|
||||
* @return boolean locality
|
||||
*/
|
||||
|
||||
public function isLocal()
|
||||
{
|
||||
return ($this->is_local == Notice::LOCAL_PUBLIC ||
|
||||
$this->is_local == Notice::LOCAL_NONPUBLIC);
|
||||
}
|
||||
|
||||
public function getTags()
|
||||
{
|
||||
$tags = array();
|
||||
$tag = new Notice_tag();
|
||||
$tag->notice_id = $this->id;
|
||||
if ($tag->find()) {
|
||||
while ($tag->fetch()) {
|
||||
$tags[] = $tag->tag;
|
||||
}
|
||||
}
|
||||
$tag->free();
|
||||
return $tags;
|
||||
}
|
||||
|
||||
static private function utcDate($dt)
|
||||
{
|
||||
$dateStr = date('d F Y H:i:s', strtotime($dt));
|
||||
$d = new DateTime($dateStr, new DateTimeZone('UTC'));
|
||||
return $d->format(DATE_W3C);
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ class Notice_tag extends Memcached_DataObject
|
||||
|
||||
$ids = Notice::stream(array('Notice_tag', '_streamDirect'),
|
||||
array($tag),
|
||||
'notice_tag:notice_ids:' . common_keyize($tag),
|
||||
'notice_tag:notice_ids:' . Cache::keyize($tag),
|
||||
$offset, $limit);
|
||||
|
||||
return Notice::getStreamByIds($ids);
|
||||
@ -82,9 +82,9 @@ class Notice_tag extends Memcached_DataObject
|
||||
|
||||
function blowCache($blowLast=false)
|
||||
{
|
||||
self::blow('notice_tag:notice_ids:%s', common_keyize($this->tag));
|
||||
self::blow('notice_tag:notice_ids:%s', Cache::keyize($this->tag));
|
||||
if ($blowLast) {
|
||||
self::blow('notice_tag:notice_ids:%s;last', common_keyize($this->tag));
|
||||
self::blow('notice_tag:notice_ids:%s;last', Cache::keyize($this->tag));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,17 +152,16 @@ class Profile extends Memcached_DataObject
|
||||
*
|
||||
* @return mixed Notice or null
|
||||
*/
|
||||
|
||||
function getCurrentNotice()
|
||||
{
|
||||
$notice = new Notice();
|
||||
$notice->profile_id = $this->id;
|
||||
// @fixme change this to sort on notice.id only when indexes are updated
|
||||
$notice->orderBy('created DESC, notice.id DESC');
|
||||
$notice->limit(1);
|
||||
if ($notice->find(true)) {
|
||||
$notice = $this->getNotices(0, 1);
|
||||
|
||||
if ($notice->fetch()) {
|
||||
return $notice;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
|
||||
@ -430,10 +429,10 @@ class Profile extends Memcached_DataObject
|
||||
|
||||
function subscriptionCount()
|
||||
{
|
||||
$c = common_memcache();
|
||||
$c = Cache::instance();
|
||||
|
||||
if (!empty($c)) {
|
||||
$cnt = $c->get(common_cache_key('profile:subscription_count:'.$this->id));
|
||||
$cnt = $c->get(Cache::key('profile:subscription_count:'.$this->id));
|
||||
if (is_integer($cnt)) {
|
||||
return (int) $cnt;
|
||||
}
|
||||
@ -447,7 +446,7 @@ class Profile extends Memcached_DataObject
|
||||
$cnt = ($cnt > 0) ? $cnt - 1 : $cnt;
|
||||
|
||||
if (!empty($c)) {
|
||||
$c->set(common_cache_key('profile:subscription_count:'.$this->id), $cnt);
|
||||
$c->set(Cache::key('profile:subscription_count:'.$this->id), $cnt);
|
||||
}
|
||||
|
||||
return $cnt;
|
||||
@ -455,9 +454,9 @@ class Profile extends Memcached_DataObject
|
||||
|
||||
function subscriberCount()
|
||||
{
|
||||
$c = common_memcache();
|
||||
$c = Cache::instance();
|
||||
if (!empty($c)) {
|
||||
$cnt = $c->get(common_cache_key('profile:subscriber_count:'.$this->id));
|
||||
$cnt = $c->get(Cache::key('profile:subscriber_count:'.$this->id));
|
||||
if (is_integer($cnt)) {
|
||||
return (int) $cnt;
|
||||
}
|
||||
@ -465,13 +464,11 @@ class Profile extends Memcached_DataObject
|
||||
|
||||
$sub = new Subscription();
|
||||
$sub->subscribed = $this->id;
|
||||
|
||||
$sub->whereAdd('subscriber != subscribed');
|
||||
$cnt = (int) $sub->count('distinct subscriber');
|
||||
|
||||
$cnt = ($cnt > 0) ? $cnt - 1 : $cnt;
|
||||
|
||||
if (!empty($c)) {
|
||||
$c->set(common_cache_key('profile:subscriber_count:'.$this->id), $cnt);
|
||||
$c->set(Cache::key('profile:subscriber_count:'.$this->id), $cnt);
|
||||
}
|
||||
|
||||
return $cnt;
|
||||
@ -479,9 +476,9 @@ class Profile extends Memcached_DataObject
|
||||
|
||||
function faveCount()
|
||||
{
|
||||
$c = common_memcache();
|
||||
$c = Cache::instance();
|
||||
if (!empty($c)) {
|
||||
$cnt = $c->get(common_cache_key('profile:fave_count:'.$this->id));
|
||||
$cnt = $c->get(Cache::key('profile:fave_count:'.$this->id));
|
||||
if (is_integer($cnt)) {
|
||||
return (int) $cnt;
|
||||
}
|
||||
@ -492,7 +489,7 @@ class Profile extends Memcached_DataObject
|
||||
$cnt = (int) $faves->count('distinct notice_id');
|
||||
|
||||
if (!empty($c)) {
|
||||
$c->set(common_cache_key('profile:fave_count:'.$this->id), $cnt);
|
||||
$c->set(Cache::key('profile:fave_count:'.$this->id), $cnt);
|
||||
}
|
||||
|
||||
return $cnt;
|
||||
@ -500,10 +497,10 @@ class Profile extends Memcached_DataObject
|
||||
|
||||
function noticeCount()
|
||||
{
|
||||
$c = common_memcache();
|
||||
$c = Cache::instance();
|
||||
|
||||
if (!empty($c)) {
|
||||
$cnt = $c->get(common_cache_key('profile:notice_count:'.$this->id));
|
||||
$cnt = $c->get(Cache::key('profile:notice_count:'.$this->id));
|
||||
if (is_integer($cnt)) {
|
||||
return (int) $cnt;
|
||||
}
|
||||
@ -514,7 +511,7 @@ class Profile extends Memcached_DataObject
|
||||
$cnt = (int) $notices->count('distinct id');
|
||||
|
||||
if (!empty($c)) {
|
||||
$c->set(common_cache_key('profile:notice_count:'.$this->id), $cnt);
|
||||
$c->set(Cache::key('profile:notice_count:'.$this->id), $cnt);
|
||||
}
|
||||
|
||||
return $cnt;
|
||||
@ -522,33 +519,33 @@ class Profile extends Memcached_DataObject
|
||||
|
||||
function blowSubscriberCount()
|
||||
{
|
||||
$c = common_memcache();
|
||||
$c = Cache::instance();
|
||||
if (!empty($c)) {
|
||||
$c->delete(common_cache_key('profile:subscriber_count:'.$this->id));
|
||||
$c->delete(Cache::key('profile:subscriber_count:'.$this->id));
|
||||
}
|
||||
}
|
||||
|
||||
function blowSubscriptionCount()
|
||||
{
|
||||
$c = common_memcache();
|
||||
$c = Cache::instance();
|
||||
if (!empty($c)) {
|
||||
$c->delete(common_cache_key('profile:subscription_count:'.$this->id));
|
||||
$c->delete(Cache::key('profile:subscription_count:'.$this->id));
|
||||
}
|
||||
}
|
||||
|
||||
function blowFaveCount()
|
||||
{
|
||||
$c = common_memcache();
|
||||
$c = Cache::instance();
|
||||
if (!empty($c)) {
|
||||
$c->delete(common_cache_key('profile:fave_count:'.$this->id));
|
||||
$c->delete(Cache::key('profile:fave_count:'.$this->id));
|
||||
}
|
||||
}
|
||||
|
||||
function blowNoticeCount()
|
||||
{
|
||||
$c = common_memcache();
|
||||
$c = Cache::instance();
|
||||
if (!empty($c)) {
|
||||
$c->delete(common_cache_key('profile:notice_count:'.$this->id));
|
||||
$c->delete(Cache::key('profile:notice_count:'.$this->id));
|
||||
}
|
||||
}
|
||||
|
||||
@ -735,14 +732,18 @@ class Profile extends Memcached_DataObject
|
||||
'role' => $name));
|
||||
|
||||
if (empty($role)) {
|
||||
throw new Exception('Cannot revoke role "'.$name.'" for user #'.$this->id.'; does not exist.');
|
||||
// TRANS: Exception thrown when trying to revoke an existing role for a user that does not exist.
|
||||
// TRANS: %1$s is the role name, %2$s is the user ID (number).
|
||||
throw new Exception(sprintf(_('Cannot revoke role "%1$s" for user #%2$d; does not exist.'),$name, $this->id));
|
||||
}
|
||||
|
||||
$result = $role->delete();
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($role, 'DELETE', __FILE__);
|
||||
throw new Exception('Cannot revoke role "'.$name.'" for user #'.$this->id.'; database error.');
|
||||
// TRANS: Exception thrown when trying to revoke a role for a user with a failing database query.
|
||||
// TRANS: %1$s is the role name, %2$s is the user ID (number).
|
||||
throw new Exception(sprintf(_('Cannot revoke role "%1$s" for user #%2$d; database error.'),$name, $this->id));
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -849,15 +850,23 @@ class Profile extends Memcached_DataObject
|
||||
*
|
||||
* Assumes that Atom has been previously set up as the base namespace.
|
||||
*
|
||||
* @param Profile $cur the current authenticated user
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function asAtomAuthor()
|
||||
function asAtomAuthor($cur = null)
|
||||
{
|
||||
$xs = new XMLStringer(true);
|
||||
|
||||
$xs->elementStart('author');
|
||||
$xs->element('name', null, $this->nickname);
|
||||
$xs->element('uri', null, $this->getUri());
|
||||
if ($cur != null) {
|
||||
$attrs = Array();
|
||||
$attrs['following'] = $cur->isSubscribed($this) ? 'true' : 'false';
|
||||
$attrs['blocking'] = $cur->hasBlocked($this) ? 'true' : 'false';
|
||||
$xs->element('statusnet:profile_info', $attrs, null);
|
||||
}
|
||||
$xs->elementEnd('author');
|
||||
|
||||
return $xs->getString();
|
||||
@ -935,4 +944,41 @@ class Profile extends Memcached_DataObject
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function getAtomFeed()
|
||||
{
|
||||
$feed = null;
|
||||
|
||||
if (Event::handle('StartProfileGetAtomFeed', array($this, &$feed))) {
|
||||
$user = User::staticGet('id', $this->id);
|
||||
if (!empty($user)) {
|
||||
$feed = common_local_url('ApiTimelineUser', array('id' => $user->id,
|
||||
'format' => 'atom'));
|
||||
}
|
||||
Event::handle('EndProfileGetAtomFeed', array($this, $feed));
|
||||
}
|
||||
|
||||
return $feed;
|
||||
}
|
||||
|
||||
static function fromURI($uri)
|
||||
{
|
||||
$profile = null;
|
||||
|
||||
if (Event::handle('StartGetProfileFromURI', array($uri, &$profile))) {
|
||||
// Get a local user or remote (OMB 0.1) profile
|
||||
$user = User::staticGet('uri', $uri);
|
||||
if (!empty($user)) {
|
||||
$profile = $user->getProfile();
|
||||
} else {
|
||||
$remote_profile = Remote_profile::staticGet('uri', $uri);
|
||||
if (!empty($remote_profile)) {
|
||||
$profile = Profile::staticGet('id', $remote_profile->profile_id);
|
||||
}
|
||||
}
|
||||
Event::handle('EndGetProfileFromURI', array($uri, $profile));
|
||||
}
|
||||
|
||||
return $profile;
|
||||
}
|
||||
}
|
||||
|
@ -64,4 +64,17 @@ class Queue_item extends Memcached_DataObject
|
||||
$qi = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release a claimed item.
|
||||
*/
|
||||
function releaseCLaim()
|
||||
{
|
||||
// DB_DataObject doesn't let us save nulls right now
|
||||
$sql = sprintf("UPDATE queue_item SET claimed=NULL WHERE id=%d", $this->id);
|
||||
$this->query($sql);
|
||||
|
||||
$this->claimed = null;
|
||||
$this->encache();
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,8 @@ class Remote_profile extends Memcached_DataObject
|
||||
if ($profile) {
|
||||
return $profile->hasright($right);
|
||||
} else {
|
||||
throw new Exception("Missing profile");
|
||||
// TRANS: Exception thrown when a right for a non-existing user profile is checked.
|
||||
throw new Exception(_("Missing profile."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,20 @@ class Reply extends Memcached_DataObject
|
||||
/* the code above is auto generated do not remove the tag below */
|
||||
###END_AUTOCODE
|
||||
|
||||
/**
|
||||
* Wrapper for record insertion to update related caches
|
||||
*/
|
||||
function insert()
|
||||
{
|
||||
$result = parent::insert();
|
||||
|
||||
if ($result) {
|
||||
self::blow('reply:stream:%d', $this->profile_id);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
|
||||
{
|
||||
$ids = Notice::stream(array('Reply', '_streamDirect'),
|
||||
|
@ -116,6 +116,7 @@ class Safe_DataObject extends DB_DataObject
|
||||
if ($this->_call($method, $params, $return)) {
|
||||
return $return;
|
||||
} else {
|
||||
// Low level exception. No need for i18n as discussed with Brion.
|
||||
throw new Exception('Call to undefined method ' .
|
||||
get_class($this) . '::' . $method);
|
||||
}
|
||||
@ -125,7 +126,7 @@ class Safe_DataObject extends DB_DataObject
|
||||
* Work around memory-leak bugs...
|
||||
* Had to copy-paste the whole function in order to patch a couple lines of it.
|
||||
* Would be nice if this code was better factored.
|
||||
*
|
||||
*
|
||||
* @param optional string name of database to assign / read
|
||||
* @param optional array structure of database, and keys
|
||||
* @param optional array table links
|
||||
@ -136,108 +137,103 @@ class Safe_DataObject extends DB_DataObject
|
||||
*/
|
||||
function databaseStructure()
|
||||
{
|
||||
|
||||
global $_DB_DATAOBJECT;
|
||||
|
||||
// Assignment code
|
||||
|
||||
|
||||
// Assignment code
|
||||
|
||||
if ($args = func_get_args()) {
|
||||
|
||||
|
||||
if (count($args) == 1) {
|
||||
|
||||
|
||||
// this returns all the tables and their structure..
|
||||
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
||||
$this->debug("Loading Generator as databaseStructure called with args",1);
|
||||
}
|
||||
|
||||
|
||||
$x = new DB_DataObject;
|
||||
$x->_database = $args[0];
|
||||
$this->_connect();
|
||||
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
|
||||
|
||||
|
||||
$tables = $DB->getListOf('tables');
|
||||
class_exists('DB_DataObject_Generator') ? '' :
|
||||
class_exists('DB_DataObject_Generator') ? '' :
|
||||
require_once 'DB/DataObject/Generator.php';
|
||||
|
||||
|
||||
foreach($tables as $table) {
|
||||
$y = new DB_DataObject_Generator;
|
||||
$y->fillTableSchema($x->_database,$table);
|
||||
}
|
||||
return $_DB_DATAOBJECT['INI'][$x->_database];
|
||||
return $_DB_DATAOBJECT['INI'][$x->_database];
|
||||
} else {
|
||||
|
||||
|
||||
$_DB_DATAOBJECT['INI'][$args[0]] = isset($_DB_DATAOBJECT['INI'][$args[0]]) ?
|
||||
$_DB_DATAOBJECT['INI'][$args[0]] + $args[1] : $args[1];
|
||||
|
||||
|
||||
if (isset($args[1])) {
|
||||
$_DB_DATAOBJECT['LINKS'][$args[0]] = isset($_DB_DATAOBJECT['LINKS'][$args[0]]) ?
|
||||
$_DB_DATAOBJECT['LINKS'][$args[0]] + $args[2] : $args[2];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (!$this->_database) {
|
||||
$this->_connect();
|
||||
}
|
||||
|
||||
|
||||
// loaded already?
|
||||
if (!empty($_DB_DATAOBJECT['INI'][$this->_database])) {
|
||||
|
||||
|
||||
// database loaded - but this is table is not available..
|
||||
if (
|
||||
empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])
|
||||
empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])
|
||||
&& !empty($_DB_DATAOBJECT['CONFIG']['proxy'])
|
||||
) {
|
||||
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
||||
$this->debug("Loading Generator to fetch Schema",1);
|
||||
}
|
||||
class_exists('DB_DataObject_Generator') ? '' :
|
||||
class_exists('DB_DataObject_Generator') ? '' :
|
||||
require_once 'DB/DataObject/Generator.php';
|
||||
|
||||
|
||||
|
||||
|
||||
$x = new DB_DataObject_Generator;
|
||||
$x->fillTableSchema($this->_database,$this->__table);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (empty($_DB_DATAOBJECT['CONFIG'])) {
|
||||
DB_DataObject::_loadConfig();
|
||||
}
|
||||
|
||||
|
||||
// if you supply this with arguments, then it will take those
|
||||
// as the database and links array...
|
||||
|
||||
|
||||
$schemas = isset($_DB_DATAOBJECT['CONFIG']['schema_location']) ?
|
||||
array("{$_DB_DATAOBJECT['CONFIG']['schema_location']}/{$this->_database}.ini") :
|
||||
array() ;
|
||||
|
||||
|
||||
if (isset($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"])) {
|
||||
$schemas = is_array($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]) ?
|
||||
$_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"] :
|
||||
explode(PATH_SEPARATOR,$_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* BEGIN CHANGED FROM UPSTREAM */
|
||||
$_DB_DATAOBJECT['INI'][$this->_database] = $this->parseIniFiles($schemas);
|
||||
/* END CHANGED FROM UPSTREAM */
|
||||
|
||||
// now have we loaded the structure..
|
||||
|
||||
// now have we loaded the structure..
|
||||
|
||||
if (!empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) {
|
||||
return true;
|
||||
}
|
||||
// - if not try building it..
|
||||
if (!empty($_DB_DATAOBJECT['CONFIG']['proxy'])) {
|
||||
class_exists('DB_DataObject_Generator') ? '' :
|
||||
class_exists('DB_DataObject_Generator') ? '' :
|
||||
require_once 'DB/DataObject/Generator.php';
|
||||
|
||||
|
||||
$x = new DB_DataObject_Generator;
|
||||
$x->fillTableSchema($this->_database,$this->__table);
|
||||
// should this fail!!!???
|
||||
@ -245,7 +241,8 @@ class Safe_DataObject extends DB_DataObject
|
||||
}
|
||||
$this->debug("Cant find database schema: {$this->_database}/{$this->__table} \n".
|
||||
"in links file data: " . print_r($_DB_DATAOBJECT['INI'],true),"databaseStructure",5);
|
||||
// we have to die here!! - it causes chaos if we dont (including looping forever!)
|
||||
// we have to die here!! - it causes chaos if we don't (including looping forever!)
|
||||
// Low level exception. No need for i18n as discussed with Brion.
|
||||
$this->raiseError( "Unable to load schema for database and table (turn debugging up to 5 for full error message)", DB_DATAOBJECT_ERROR_INVALIDARGS, PEAR_ERROR_DIE);
|
||||
return false;
|
||||
}
|
||||
@ -271,7 +268,7 @@ class Safe_DataObject extends DB_DataObject
|
||||
if (file_exists($ini) && is_file($ini)) {
|
||||
$data = array_merge($data, parse_ini_file($ini, true));
|
||||
|
||||
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
||||
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
||||
if (!is_readable ($ini)) {
|
||||
$this->debug("ini file is not readable: $ini","databaseStructure",1);
|
||||
} else {
|
||||
|
@ -27,7 +27,8 @@ class Status_network extends Safe_DataObject
|
||||
/* the code below is auto generated do not remove the above tag */
|
||||
|
||||
public $__table = 'status_network'; // table name
|
||||
public $nickname; // varchar(64) primary_key not_null
|
||||
public $site_id; // int(4) primary_key not_null
|
||||
public $nickname; // varchar(64) unique_key not_null
|
||||
public $hostname; // varchar(255) unique_key
|
||||
public $pathname; // varchar(255) unique_key
|
||||
public $dbhost; // varchar(255)
|
||||
@ -39,7 +40,6 @@ class Status_network extends Safe_DataObject
|
||||
public $logo; // varchar(255)
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
public $tags; // text
|
||||
|
||||
/* Static get */
|
||||
function staticGet($k,$v=NULL) {
|
||||
@ -144,26 +144,49 @@ class Status_network extends Safe_DataObject
|
||||
return parent::update($orig);
|
||||
}
|
||||
|
||||
/**
|
||||
* DB_DataObject doesn't allow updating keys (even non-primary)
|
||||
*/
|
||||
function updateKeys(&$orig)
|
||||
{
|
||||
$this->_connect();
|
||||
foreach (array('hostname', 'pathname') as $k) {
|
||||
if (strcmp($this->$k, $orig->$k) != 0) {
|
||||
$parts[] = $k . ' = ' . $this->_quote($this->$k);
|
||||
}
|
||||
}
|
||||
if (count($parts) == 0) {
|
||||
// No changes
|
||||
return true;
|
||||
}
|
||||
|
||||
$toupdate = implode(', ', $parts);
|
||||
|
||||
$table = common_database_tablename($this->tableName());
|
||||
$qry = 'UPDATE ' . $table . ' SET ' . $toupdate .
|
||||
' WHERE nickname = ' . $this->_quote($this->nickname);
|
||||
$orig->decache();
|
||||
$result = $this->query($qry);
|
||||
if ($result) {
|
||||
$this->encache();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
function delete()
|
||||
{
|
||||
$this->decache(); # while we still have the values!
|
||||
return parent::delete();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $servername hostname
|
||||
* @param string $pathname URL base path
|
||||
* @param string $wildcard hostname suffix to match wildcard config
|
||||
* @return mixed Status_network or null
|
||||
*/
|
||||
static function setupSite($servername, $pathname, $wildcard)
|
||||
static function getFromHostname($servername, $wildcard)
|
||||
{
|
||||
global $config;
|
||||
|
||||
$sn = null;
|
||||
|
||||
// XXX I18N, probably not crucial for hostnames
|
||||
// XXX This probably needs a tune up
|
||||
|
||||
if (0 == strncasecmp(strrev($wildcard), strrev($servername), strlen($wildcard))) {
|
||||
// special case for exact match
|
||||
if (0 == strcasecmp($servername, $wildcard)) {
|
||||
@ -182,6 +205,23 @@ class Status_network extends Safe_DataObject
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $servername hostname
|
||||
* @param string $pathname URL base path
|
||||
* @param string $wildcard hostname suffix to match wildcard config
|
||||
*/
|
||||
static function setupSite($servername, $pathname, $wildcard)
|
||||
{
|
||||
global $config;
|
||||
|
||||
$sn = null;
|
||||
|
||||
// XXX I18N, probably not crucial for hostnames
|
||||
// XXX This probably needs a tune up
|
||||
$sn = self::getFromHostname($servername, $wildcard);
|
||||
|
||||
if (!empty($sn)) {
|
||||
|
||||
@ -268,9 +308,56 @@ class Status_network extends Safe_DataObject
|
||||
*/
|
||||
function getTags()
|
||||
{
|
||||
return array_filter(explode("|", strval($this->tags)));
|
||||
$result = Status_network_tag::getTags($this->site_id);
|
||||
|
||||
// XXX : for backwards compatibility
|
||||
if (empty($result)) {
|
||||
return explode('|', $this->tags);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a given set of tags
|
||||
* @param array tags
|
||||
* @fixme only add/remove differentials
|
||||
*/
|
||||
function setTags($tags)
|
||||
{
|
||||
$this->clearTags();
|
||||
foreach ($tags as $tag) {
|
||||
if (!empty($tag)) {
|
||||
$snt = new Status_network_tag();
|
||||
$snt->site_id = $this->site_id;
|
||||
$snt->tag = $tag;
|
||||
$snt->created = common_sql_now();
|
||||
|
||||
$id = $snt->insert();
|
||||
if (!$id) {
|
||||
// TRANS: Exception thrown when a tag cannot be saved.
|
||||
throw new Exception(_("Unable to save tag."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function clearTags()
|
||||
{
|
||||
$tag = new Status_network_tag();
|
||||
$tag->site_id = $this->site_id;
|
||||
|
||||
if ($tag->find()) {
|
||||
while($tag->fetch()) {
|
||||
$tag->delete();
|
||||
}
|
||||
}
|
||||
|
||||
$tag->free();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this site record has a particular meta-info tag attached.
|
||||
* @param string $tag
|
||||
|
133
classes/Status_network_tag.php
Normal file
133
classes/Status_network_tag.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/*
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2008, 2009, 2010 StatusNet, 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('STATUSNET')) { exit(1); }
|
||||
|
||||
class Status_network_tag extends Safe_DataObject
|
||||
{
|
||||
###START_AUTOCODE
|
||||
/* the code below is auto generated do not remove the above tag */
|
||||
|
||||
public $__table = 'status_network_tag'; // table name
|
||||
public $site_id; // int(4) primary_key not_null
|
||||
public $tag; // varchar(64) primary_key not_null
|
||||
public $created; // datetime() not_null
|
||||
|
||||
|
||||
function __construct()
|
||||
{
|
||||
global $config;
|
||||
global $_DB_DATAOBJECT;
|
||||
|
||||
$sn = new Status_network();
|
||||
$sn->_connect();
|
||||
|
||||
$config['db']['table_'. $this->__table] = $sn->_database;
|
||||
|
||||
$this->_connect();
|
||||
}
|
||||
|
||||
|
||||
/* Static get */
|
||||
function staticGet($k,$v=null)
|
||||
{
|
||||
$i = DB_DataObject::staticGet('Status_network_tag',$k,$v);
|
||||
|
||||
// Don't use local process cache; if we're fetching multiple
|
||||
// times it's because we're reloading it in a long-running
|
||||
// process; we need a fresh copy!
|
||||
global $_DB_DATAOBJECT;
|
||||
unset($_DB_DATAOBJECT['CACHE']['status_network_tag']);
|
||||
return $i;
|
||||
}
|
||||
|
||||
/* the code above is auto generated do not remove the tag below */
|
||||
###END_AUTOCODE
|
||||
|
||||
|
||||
function pkeyGet($kv)
|
||||
{
|
||||
return Memcached_DataObject::pkeyGet('Status_network_tag', $kv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the (possibly cached) tag entries for the given site id.
|
||||
* Uses status_network's cache settings.
|
||||
*
|
||||
* @param string $site_id
|
||||
* @return array of strings
|
||||
*/
|
||||
static function getTags($site_id)
|
||||
{
|
||||
$key = 'status_network_tags:' . $site_id;
|
||||
if (Status_network::$cache) {
|
||||
$packed = Status_network::$cache->get($key);
|
||||
if (is_string($packed)) {
|
||||
if ($packed == '') {
|
||||
return array();
|
||||
} else {
|
||||
return explode('|', $packed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = array();
|
||||
|
||||
$tags = new Status_network_tag();
|
||||
$tags->site_id = $site_id;
|
||||
if ($tags->find()) {
|
||||
while ($tags->fetch()) {
|
||||
$result[] = $tags->tag;
|
||||
}
|
||||
}
|
||||
|
||||
if (Status_network::$cache) {
|
||||
$packed = implode('|', $result);
|
||||
Status_network::$cache->set($key, $packed, 3600);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the cached tag entries for this site.
|
||||
* Needed after inserting/deleting a tag entry.
|
||||
*/
|
||||
function decache()
|
||||
{
|
||||
$key = 'status_network_tags:' . $this->site_id;
|
||||
if (Status_network::$cache) {
|
||||
Status_network::$cache->delete($key);
|
||||
}
|
||||
}
|
||||
|
||||
function insert()
|
||||
{
|
||||
$ret = parent::insert();
|
||||
$this->decache();
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function delete()
|
||||
{
|
||||
$ret = parent::delete();
|
||||
$this->decache();
|
||||
return $ret;
|
||||
}
|
||||
}
|
@ -71,14 +71,17 @@ class Subscription extends Memcached_DataObject
|
||||
}
|
||||
|
||||
if (!$subscriber->hasRight(Right::SUBSCRIBE)) {
|
||||
// TRANS: Exception thrown when trying to subscribe while being banned from subscribing.
|
||||
throw new Exception(_('You have been banned from subscribing.'));
|
||||
}
|
||||
|
||||
if (self::exists($subscriber, $other)) {
|
||||
// TRANS: Exception thrown when trying to subscribe while already subscribed.
|
||||
throw new Exception(_('Already subscribed!'));
|
||||
}
|
||||
|
||||
if ($other->hasBlocked($subscriber)) {
|
||||
// TRANS: Exception thrown when trying to subscribe to a user who has blocked the subscribing user.
|
||||
throw new Exception(_('User has blocked you.'));
|
||||
}
|
||||
|
||||
@ -129,6 +132,7 @@ class Subscription extends Memcached_DataObject
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($sub, 'INSERT', __FILE__);
|
||||
// TRANS: Exception thrown when a subscription could not be stored on the server.
|
||||
throw new Exception(_('Could not save subscription.'));
|
||||
}
|
||||
|
||||
@ -160,17 +164,18 @@ class Subscription extends Memcached_DataObject
|
||||
* Cancel a subscription
|
||||
*
|
||||
*/
|
||||
|
||||
function cancel($subscriber, $other)
|
||||
{
|
||||
if (!self::exists($subscriber, $other)) {
|
||||
// TRANS: Exception thrown when trying to unsibscribe without a subscription.
|
||||
throw new Exception(_('Not subscribed!'));
|
||||
}
|
||||
|
||||
// Don't allow deleting self subs
|
||||
|
||||
if ($subscriber->id == $other->id) {
|
||||
throw new Exception(_('Couldn\'t delete self-subscription.'));
|
||||
// TRANS: Exception thrown when trying to unsubscribe a user from themselves.
|
||||
throw new Exception(_('Could not delete self-subscription.'));
|
||||
}
|
||||
|
||||
if (Event::handle('StartUnsubscribe', array($subscriber, $other))) {
|
||||
@ -197,7 +202,8 @@ class Subscription extends Memcached_DataObject
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($token, 'DELETE', __FILE__);
|
||||
throw new Exception(_('Couldn\'t delete subscription OMB token.'));
|
||||
// TRANS: Exception thrown when the OMB token for a subscription could not deleted on the server.
|
||||
throw new Exception(_('Could not delete subscription OMB token.'));
|
||||
}
|
||||
} else {
|
||||
common_log(LOG_ERR, "Couldn't find credentials with token {$token->tok}");
|
||||
@ -208,7 +214,8 @@ class Subscription extends Memcached_DataObject
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($sub, 'DELETE', __FILE__);
|
||||
throw new Exception(_('Couldn\'t delete subscription.'));
|
||||
// TRANS: Exception thrown when a subscription could not be deleted on the server.
|
||||
throw new Exception(_('Could not delete subscription.'));
|
||||
}
|
||||
|
||||
self::blow('user:notices_with_friends:%d', $subscriber->id);
|
||||
|
@ -355,11 +355,12 @@ class User extends Memcached_DataObject
|
||||
__FILE__);
|
||||
} else {
|
||||
$notice = Notice::saveNew($welcomeuser->id,
|
||||
// TRANS: Notice given on user registration.
|
||||
// TRANS: %1$s is the sitename, $2$s is the registering user's nickname.
|
||||
sprintf(_('Welcome to %1$s, @%2$s!'),
|
||||
common_config('site', 'name'),
|
||||
$user->nickname),
|
||||
'system');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,7 +371,6 @@ class User extends Memcached_DataObject
|
||||
}
|
||||
|
||||
// Things we do when the email changes
|
||||
|
||||
function emailChanged()
|
||||
{
|
||||
|
||||
@ -388,7 +388,7 @@ class User extends Memcached_DataObject
|
||||
|
||||
function hasFave($notice)
|
||||
{
|
||||
$cache = common_memcache();
|
||||
$cache = Cache::instance();
|
||||
|
||||
// XXX: Kind of a hack.
|
||||
|
||||
@ -459,9 +459,9 @@ class User extends Memcached_DataObject
|
||||
return $profile->getNotices($offset, $limit, $since_id, $before_id);
|
||||
}
|
||||
|
||||
function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE, $own=false)
|
||||
function favoriteNotices($own=false, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
|
||||
{
|
||||
$ids = Fave::stream($this->id, $offset, $limit, $own);
|
||||
$ids = Fave::stream($this->id, $offset, $limit, $own, $since_id, $max_id);
|
||||
return Notice::getStreamByIds($ids);
|
||||
}
|
||||
|
||||
@ -487,14 +487,14 @@ class User extends Memcached_DataObject
|
||||
|
||||
function blowFavesCache()
|
||||
{
|
||||
$cache = common_memcache();
|
||||
$cache = Cache::instance();
|
||||
if ($cache) {
|
||||
// Faves don't happen chronologically, so we need to blow
|
||||
// ;last cache, too
|
||||
$cache->delete(common_cache_key('fave:ids_by_user:'.$this->id));
|
||||
$cache->delete(common_cache_key('fave:ids_by_user:'.$this->id.';last'));
|
||||
$cache->delete(common_cache_key('fave:ids_by_user_own:'.$this->id));
|
||||
$cache->delete(common_cache_key('fave:ids_by_user_own:'.$this->id.';last'));
|
||||
$cache->delete(Cache::key('fave:ids_by_user:'.$this->id));
|
||||
$cache->delete(Cache::key('fave:ids_by_user:'.$this->id.';last'));
|
||||
$cache->delete(Cache::key('fave:ids_by_user_own:'.$this->id));
|
||||
$cache->delete(Cache::key('fave:ids_by_user_own:'.$this->id.';last'));
|
||||
}
|
||||
$profile = $this->getProfile();
|
||||
$profile->blowFaveCount();
|
||||
@ -519,7 +519,7 @@ class User extends Memcached_DataObject
|
||||
if ($this->id == $other->id) {
|
||||
common_log(LOG_WARNING,
|
||||
sprintf(
|
||||
"Profile ID %d (%s) tried to block his or herself.",
|
||||
"Profile ID %d (%s) tried to block themself.",
|
||||
$this->id,
|
||||
$this->nickname
|
||||
)
|
||||
|
@ -154,6 +154,21 @@ class User_group extends Memcached_DataObject
|
||||
return $members;
|
||||
}
|
||||
|
||||
function getMemberCount()
|
||||
{
|
||||
// XXX: WORM cache this
|
||||
|
||||
$members = $this->getMembers();
|
||||
$member_count = 0;
|
||||
|
||||
/** $member->count() doesn't work. */
|
||||
while ($members->fetch()) {
|
||||
$member_count++;
|
||||
}
|
||||
|
||||
return $member_count;
|
||||
}
|
||||
|
||||
function getAdmins($offset=0, $limit=null)
|
||||
{
|
||||
$qry =
|
||||
@ -477,6 +492,7 @@ class User_group extends Memcached_DataObject
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($group, 'INSERT', __FILE__);
|
||||
// TRANS: Server exception thrown when creating a group failed.
|
||||
throw new ServerException(_('Could not create group.'));
|
||||
}
|
||||
|
||||
@ -486,6 +502,7 @@ class User_group extends Memcached_DataObject
|
||||
$result = $group->update($orig);
|
||||
if (!$result) {
|
||||
common_log_db_error($group, 'UPDATE', __FILE__);
|
||||
// TRANS: Server exception thrown when updating a group URI failed.
|
||||
throw new ServerException(_('Could not set group URI.'));
|
||||
}
|
||||
}
|
||||
@ -493,6 +510,7 @@ class User_group extends Memcached_DataObject
|
||||
$result = $group->setAliases($aliases);
|
||||
|
||||
if (!$result) {
|
||||
// TRANS: Server exception thrown when creating group aliases failed.
|
||||
throw new ServerException(_('Could not create aliases.'));
|
||||
}
|
||||
|
||||
@ -507,6 +525,7 @@ class User_group extends Memcached_DataObject
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($member, 'INSERT', __FILE__);
|
||||
// TRANS: Server exception thrown when setting group membership failed.
|
||||
throw new ServerException(_('Could not set group membership.'));
|
||||
}
|
||||
|
||||
@ -521,6 +540,7 @@ class User_group extends Memcached_DataObject
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($local_group, 'INSERT', __FILE__);
|
||||
// TRANS: Server exception thrown when saving local group information failed.
|
||||
throw new ServerException(_('Could not save local group info.'));
|
||||
}
|
||||
}
|
||||
|
@ -68,4 +68,27 @@ class User_im_prefs extends Memcached_DataObject
|
||||
{
|
||||
return array(false,false);
|
||||
}
|
||||
|
||||
/**
|
||||
* We have two compound keys with unique constraints:
|
||||
* (transport, user_id) which is our primary key, and
|
||||
* (transport, screenname) which is an additional constraint.
|
||||
*
|
||||
* Currently there's not a way to represent that second key
|
||||
* in the general keys list, so we're adding it here to the
|
||||
* list of keys to use for caching, ensuring that it gets
|
||||
* cleared as well when we change.
|
||||
*
|
||||
* @return array of cache keys
|
||||
*/
|
||||
function _allCacheKeys()
|
||||
{
|
||||
$ukeys = 'transport,screenname';
|
||||
$uvals = $this->transport . ',' . $this->screenname;
|
||||
|
||||
$ckeys = parent::_allCacheKeys();
|
||||
$ckeys[] = $this->cacheKey($this->tableName(), $ukeys, $uvals);
|
||||
return $ckeys;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
[status_network]
|
||||
site_id = 129
|
||||
nickname = 130
|
||||
hostname = 2
|
||||
pathname = 2
|
||||
@ -11,9 +12,19 @@ theme = 2
|
||||
logo = 2
|
||||
created = 142
|
||||
modified = 384
|
||||
tags = 34
|
||||
|
||||
[status_network__keys]
|
||||
nickname = K
|
||||
site_id = K
|
||||
nickname = U
|
||||
hostname = U
|
||||
pathname = U
|
||||
|
||||
[status_network_tag]
|
||||
site_id = 129
|
||||
tag = 130
|
||||
created = 142
|
||||
|
||||
[status_network_tag__keys]
|
||||
site_id = K
|
||||
tag = K
|
||||
|
||||
|
@ -647,8 +647,10 @@ modified = 384
|
||||
[user_im_prefs__keys]
|
||||
user_id = K
|
||||
transport = K
|
||||
transport = U
|
||||
screenname = U
|
||||
; There's another unique index on (transport, screenname)
|
||||
; but we have no way to represent a compound index other than
|
||||
; the primary key in here. To ensure proper cache purging,
|
||||
; we need to tweak the class.
|
||||
|
||||
[user_urlshortener_prefs]
|
||||
user_id = 129
|
||||
|
@ -45,7 +45,7 @@ $config['site']['path'] = 'statusnet';
|
||||
// lighttpd, nginx), you can enable X-Sendfile support for better
|
||||
// performance. Presently, only attachment serving when the site is
|
||||
// in private mode will use X-Sendfile.
|
||||
// $config['site']['X-Sendfile'] = false;
|
||||
// $config['site']['use_x_sendfile'] = false;
|
||||
// You may also need to enable X-Sendfile support for your web server and
|
||||
// allow it to access files outside of the web root. For Apache with
|
||||
// mod_xsendfile, you can add these to your .htaccess or server config:
|
||||
|
@ -81,3 +81,60 @@ ALTER TABLE profile ADD COLUMN lon decimal(10,7) /*comment 'longitude'*/;
|
||||
ALTER TABLE profile ADD COLUMN location_id integer /* comment 'location id if possible'*/;
|
||||
ALTER TABLE profile ADD COLUMN location_ns integer /* comment 'namespace for location'*/;
|
||||
|
||||
ALTER TABLE consumer add COLUMN consumer_secret varchar(255) not null ; /*comment 'secret value'*/
|
||||
|
||||
ALTER TABLE token ADD COLUMN verifier varchar(255); /* comment 'verifier string for OAuth 1.0a',*/
|
||||
ALTER TABLE token ADD COLUMN verified_callback varchar(255); /* comment 'verified callback URL for OAuth 1.0a',*/
|
||||
|
||||
create table queue_item_new (
|
||||
id serial /* comment 'unique identifier'*/,
|
||||
frame bytea not null /* comment 'data: object reference or opaque string'*/,
|
||||
transport varchar(8) not null /*comment 'queue for what? "email", "jabber", "sms", "irc", ...'*/,
|
||||
created timestamp not null default CURRENT_TIMESTAMP /*comment 'date this record was created'*/,
|
||||
claimed timestamp /*comment 'date this item was claimed'*/,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
insert into queue_item_new (frame,transport,created,claimed)
|
||||
select ('0x' || notice_id::text)::bytea,transport,created,claimed from queue_item;
|
||||
alter table queue_item rename to queue_item_old;
|
||||
alter table queue_item_new rename to queue_item;
|
||||
|
||||
ALTER TABLE confirm_address ALTER column sent set default CURRENT_TIMESTAMP;
|
||||
|
||||
create table user_location_prefs (
|
||||
user_id integer not null /*comment 'user who has the preference'*/ references "user" (id),
|
||||
share_location int default 1 /* comment 'Whether to share location data'*/,
|
||||
created timestamp not null /*comment 'date this record was created'*/,
|
||||
modified timestamp /* comment 'date this record was modified'*/,
|
||||
|
||||
primary key (user_id)
|
||||
);
|
||||
|
||||
create table inbox (
|
||||
|
||||
user_id integer not null /* comment 'user receiving the notice' */ references "user" (id),
|
||||
notice_ids bytea /* comment 'packed list of notice ids' */,
|
||||
|
||||
primary key (user_id)
|
||||
|
||||
);
|
||||
|
||||
create table user_location_prefs (
|
||||
user_id integer not null /*comment 'user who has the preference'*/ references "user" (id),
|
||||
share_location int default 1 /* comment 'Whether to share location data'*/,
|
||||
created timestamp not null /*comment 'date this record was created'*/,
|
||||
modified timestamp /* comment 'date this record was modified'*/,
|
||||
|
||||
primary key (user_id)
|
||||
);
|
||||
|
||||
create table inbox (
|
||||
|
||||
user_id integer not null /* comment 'user receiving the notice' */ references "user" (id),
|
||||
notice_ids bytea /* comment 'packed list of notice ids' */,
|
||||
|
||||
primary key (user_id)
|
||||
|
||||
);
|
||||
|
||||
|
@ -9,13 +9,16 @@ VALUES
|
||||
('bti','bti','http://gregkh.github.com/bti/', now()),
|
||||
('choqok', 'Choqok', 'http://choqok.gnufolks.org/', now()),
|
||||
('cliqset', 'Cliqset', 'http://www.cliqset.com/', now()),
|
||||
('DarterosStatus', 'Darteros Status', 'http://www.darteros.com/doc/Darteros_Status', now()),
|
||||
('deskbar','Deskbar-Applet','http://www.gnome.org/projects/deskbar-applet/', now()),
|
||||
('Do','Gnome Do','http://do.davebsd.com/wiki/index.php?title=Microblog_Plugin', now()),
|
||||
('drupal','Drupal','http://drupal.org/', now()),
|
||||
('eventbox','EventBox','http://thecosmicmachine.com/eventbox/ ', now()),
|
||||
('eventbox','EventBox','http://thecosmicmachine.com/eventbox/', now()),
|
||||
('identica-mode','Emacs Identica-mode','http://nongnu.org/identica-mode/', now()),
|
||||
('Facebook','Facebook','http://apps.facebook.com/identica/', now()),
|
||||
('feed2omb','feed2omb','http://projects.ciarang.com/p/feed2omb/', now()),
|
||||
('get2gnow', 'get2gnow', 'http://uberchicgeekchick.com/?projects=get2gnow', now()),
|
||||
('gNewBook', 'gNewBook', 'http://www.gnewbook.org/', now()),
|
||||
('gravity', 'Gravity', 'http://mobileways.de/gravity', now()),
|
||||
('Gwibber','Gwibber','http://launchpad.net/gwibber', now()),
|
||||
('HelloTxt','HelloTxt','http://hellotxt.com/', now()),
|
||||
@ -48,11 +51,13 @@ VALUES
|
||||
('smob','SMOB','http://smob.sioc-project.org/', now()),
|
||||
('socialoomphBfD4pMqz31', 'SocialOomph', 'http://www.socialoomph.com/', now()),
|
||||
('spaz','Spaz','http://funkatron.com/spaz', now()),
|
||||
('StatusNet Desktop', 'StatusNet Desktop', 'http://status.net/desktop', now()),
|
||||
('tarpipe','tarpipe','http://tarpipe.com/', now()),
|
||||
('tjunar','Tjunar','http://nederflash.nl/boek/titels/tjunar-air', now()),
|
||||
('tr.im','tr.im','http://tr.im/', now()),
|
||||
('triklepost', 'Tricklepost', 'http://github.com/zcopley/tricklepost/tree/master', now()),
|
||||
('tweenky','Tweenky','http://beta.tweenky.com/', now()),
|
||||
('TweetDeck', 'TweetDeck', 'http://www.tweetdeck.com/', now()),
|
||||
('twhirl','Twhirl','http://www.twhirl.org/', now()),
|
||||
('twibble','twibble','http://www.twibble.de/', now()),
|
||||
('Twidge','Twidge','http://software.complete.org/twidge', now()),
|
||||
|
14
db/site.sql
14
db/site.sql
@ -1,8 +1,9 @@
|
||||
/* For managing multiple sites */
|
||||
|
||||
create table status_network (
|
||||
|
||||
nickname varchar(64) primary key comment 'nickname',
|
||||
|
||||
site_id integer auto_increment primary key comment 'unique id',
|
||||
nickname varchar(64) unique key comment 'nickname',
|
||||
hostname varchar(255) unique key comment 'alternate hostname if any',
|
||||
pathname varchar(255) unique key comment 'alternate pathname if any',
|
||||
|
||||
@ -21,3 +22,12 @@ create table status_network (
|
||||
modified timestamp comment 'date this record was modified'
|
||||
|
||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
|
||||
create table status_network_tag (
|
||||
site_id integer comment 'unique id',
|
||||
tag varchar(64) comment 'tag name',
|
||||
created datetime not null comment 'date the record was created',
|
||||
|
||||
constraint primary key (site_id, tag)
|
||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user