Merge branch 'testing'
This commit is contained in:
commit
f6bf952980
66
EVENTS.txt
66
EVENTS.txt
@ -290,6 +290,18 @@ StartRegistrationTry: before validating and saving a new user
|
|||||||
EndRegistrationTry: after saving a new user (note: no profile or user object!)
|
EndRegistrationTry: after saving a new user (note: no profile or user object!)
|
||||||
- $action: action object being shown
|
- $action: action object being shown
|
||||||
|
|
||||||
|
StartAvatarFormData: before displaying avatar form
|
||||||
|
- $action: action object being shown
|
||||||
|
|
||||||
|
EndAvatarFormData: after displaying avatar form
|
||||||
|
- $action: action object being shown
|
||||||
|
|
||||||
|
StartAvatarSaveForm: before saving the avatar
|
||||||
|
- $action: action object being shown
|
||||||
|
|
||||||
|
EndAvatarSaveForm: after saving the avatar
|
||||||
|
- $action: action object being shown
|
||||||
|
|
||||||
StartNewQueueManager: before trying to start a new queue manager; good for plugins implementing new queue manager classes
|
StartNewQueueManager: before trying to start a new queue manager; good for plugins implementing new queue manager classes
|
||||||
- $qm: empty queue manager to set
|
- $qm: empty queue manager to set
|
||||||
|
|
||||||
@ -574,3 +586,57 @@ EndShortenUrl: After a URL has been shortened
|
|||||||
- $shortenerName: name of the requested shortener
|
- $shortenerName: name of the requested shortener
|
||||||
- $shortenedUrl: short version of the url
|
- $shortenedUrl: short version of the url
|
||||||
|
|
||||||
|
StartCssLinkElement: Before a <link rel="stylesheet"..> element is written
|
||||||
|
- $action
|
||||||
|
- &$src
|
||||||
|
- &$theme
|
||||||
|
- &$media
|
||||||
|
|
||||||
|
EndCssLinkElement: After a <link rel="stylesheet"..> element is written
|
||||||
|
- $action
|
||||||
|
- $src
|
||||||
|
- $theme
|
||||||
|
- $media
|
||||||
|
|
||||||
|
StartStyleElement: Before a <style...> element is written
|
||||||
|
- $action
|
||||||
|
- &$code
|
||||||
|
- &$type
|
||||||
|
- &$media
|
||||||
|
|
||||||
|
EndStyleElement: After a <style...> element is written
|
||||||
|
- $action
|
||||||
|
- $code
|
||||||
|
- $type
|
||||||
|
- $media
|
||||||
|
|
||||||
|
StartScriptElement: Before a <script...> element is written
|
||||||
|
- $action
|
||||||
|
- &$src
|
||||||
|
- &$type
|
||||||
|
|
||||||
|
EndScriptElement: After a <script...> element is written
|
||||||
|
- $action
|
||||||
|
- $src
|
||||||
|
- $type
|
||||||
|
|
||||||
|
StartInlineScriptElement: Before a <script...> element is written
|
||||||
|
- $action
|
||||||
|
- &$code
|
||||||
|
- &$type
|
||||||
|
|
||||||
|
EndInlineScriptElement: After a <script...> element is written
|
||||||
|
- $action
|
||||||
|
- $code
|
||||||
|
- $type
|
||||||
|
|
||||||
|
StartLog: Before writing to the logs
|
||||||
|
- &$priority
|
||||||
|
- &$msg
|
||||||
|
- &$filename
|
||||||
|
|
||||||
|
EndLog: After writing to the logs
|
||||||
|
- $priority
|
||||||
|
- $msg
|
||||||
|
- $filename
|
||||||
|
|
||||||
|
18
Makefile
Normal file
18
Makefile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Warning: do not transform tabs to spaces in this file.
|
||||||
|
|
||||||
|
all : translations
|
||||||
|
|
||||||
|
core_mo = $(patsubst %.po,%.mo,$(wildcard locale/*/LC_MESSAGES/statusnet.po))
|
||||||
|
plugin_mo = $(patsubst %.po,%.mo,$(wildcard plugins/*/locale/*/LC_MESSAGES/*.po))
|
||||||
|
|
||||||
|
translations : $(core_mo) $(plugin_mo)
|
||||||
|
|
||||||
|
clean :
|
||||||
|
rm -f $(core_mo) $(plugin_mo)
|
||||||
|
|
||||||
|
updatepo :
|
||||||
|
php scripts/update_po_templates.php --all
|
||||||
|
|
||||||
|
%.mo : %.po
|
||||||
|
msgfmt -o $@ $<
|
||||||
|
|
205
README
205
README
@ -2,8 +2,8 @@
|
|||||||
README
|
README
|
||||||
------
|
------
|
||||||
|
|
||||||
StatusNet 0.8.2 ("Life and How to Live It")
|
StatusNet 0.9.0 ("Stand") Release Candidate 2
|
||||||
1 Nov 2009
|
22 Dec 2009
|
||||||
|
|
||||||
This is the README file for StatusNet (formerly Laconica), the Open
|
This is the README file for StatusNet (formerly Laconica), the Open
|
||||||
Source microblogging platform. It includes installation instructions,
|
Source microblogging platform. It includes installation instructions,
|
||||||
@ -16,10 +16,10 @@ About
|
|||||||
|
|
||||||
StatusNet (formerly Laconica) is a Free and Open Source microblogging
|
StatusNet (formerly Laconica) is a Free and Open Source microblogging
|
||||||
platform. It helps people in a community, company or group to exchange
|
platform. It helps people in a community, company or group to exchange
|
||||||
short (140 character) messages over the Web. Users can choose which
|
short (140 characters, by default) messages over the Web. Users can
|
||||||
people to "follow" and receive only their friends' or colleagues'
|
choose which people to "follow" and receive only their friends' or
|
||||||
status messages. It provides a similar service to sites like Twitter,
|
colleagues' status messages. It provides a similar service to sites
|
||||||
Jaiku, Yammer, and Plurk.
|
like Twitter, Jaiku, Yammer, and Plurk.
|
||||||
|
|
||||||
With a little work, status messages can be sent to mobile phones,
|
With a little work, status messages can be sent to mobile phones,
|
||||||
instant messenger programs (GTalk/Jabber), and specially-designed
|
instant messenger programs (GTalk/Jabber), and specially-designed
|
||||||
@ -77,81 +77,96 @@ for additional terms.
|
|||||||
New this version
|
New this version
|
||||||
================
|
================
|
||||||
|
|
||||||
This is a minor feature and bugfix release since version 0.8.1,
|
This is a major feature release since version 0.8.2, released Nov 1 2009.
|
||||||
released Aug 26 2009. Notable changes this version:
|
Notable changes this version:
|
||||||
|
|
||||||
- New script for deleting user accounts. Not particularly safe or
|
- Records of deleted notices are stored without the notice content.
|
||||||
community-friendly. Better for deleting abusive accounts than for
|
- Much of the optional core featureset has been moved to plugins.
|
||||||
users who are 'retiring'.
|
- OpenID support moved from core to a plugin. Helps test the strength of
|
||||||
- Improved detection of URLs in notices, specifically for punctuation
|
our plugin architecture and makes it easy to disable this
|
||||||
chars like ~, :, $, _, -, +, !, @, and %.
|
functionality for e.g. intranet sites.
|
||||||
- Removed some extra <dl> semantic HTML code.
|
- Many additional hook events (see EVENTS.txt for details).
|
||||||
- Correct error in status-network database ini file (having multiple
|
- OMB 0.1 support re-implemented using libomb.
|
||||||
statusnet sites with a single codebase)
|
- Re-structure database so notices, messages, bios and group
|
||||||
- Fixed error output for Twitter posting failures.
|
descriptions can be over 140 characters. Limit defined by
|
||||||
- Fixed bug in Twitter queue handler that requeued inapplicable
|
site administrator as configuration option; can be unlimited.
|
||||||
notices ad infinitum.
|
- Configuration data now optionally stored in the database, which
|
||||||
- Improve FOAF output for remote users.
|
overrides any settings in config files.
|
||||||
- new commands to join and leave groups.
|
- Twitter integration re-implemented as a plugin.
|
||||||
- Fixed bug in which you cannot turn off importing friends timelines
|
- Facebook integration re-implemented as a plugin.
|
||||||
flag.
|
- Role-based authorization framework. Users can have named roles, and
|
||||||
- Better error handling in Twitter posting.
|
roles can have rights (e.g., to delete notices, change configuration
|
||||||
- Show oEmbed data for XHTML files as well as plain HTML.
|
data, or ban uncooperative users). Default roles 'admin' (for
|
||||||
- Updated bug database link in README.
|
configuration) and 'moderator' (for community management) added.
|
||||||
- add support for HTTP Basic Auth in PHP CGI or FastCGI (e.g. GoDaddy).
|
- Plugin for PubSubHubBub (PuSH) support.
|
||||||
- autofocus input to selected entry elements depending on page.
|
- Considerable code style cleanup to meet PEAR code standards.
|
||||||
- updated layout for filter-by-tag form.
|
- Made a common library for HTTP-client access which uses available
|
||||||
- better layout for inbox and outbox pages.
|
HTTP libraries where possible.
|
||||||
- fix highlighting search terms in attributes of notice list elements.
|
- Added statuses/home_timeline method to API.
|
||||||
- Correctly handle errors in linkback plugin.
|
- Hooks for plugins to handle notices offline, either by defining
|
||||||
- Updated biz theme.
|
their own queue handler scripts or to use a default plugin queue
|
||||||
- Updated cloudy theme.
|
handler script.
|
||||||
- Don't match '::' as an IPv6 address.
|
- Plugins can now modify the database schema, adding their own tables
|
||||||
- Use the same decision logic for deciding whether to mark an
|
or modifying existing ones.
|
||||||
attachment as an enclosure in RSS or as a paperclip item in Web
|
- Groups API.
|
||||||
output.
|
- Twitter API supports Web caching for some methods.
|
||||||
- Fixed a bug in the Piwik plugin that hard-coded the site ID.
|
- Twitter API refactored into one-action-per-method.
|
||||||
- Add a param, inreplyto, to notice/new to allow an explicit response
|
- Realtime plugin supports a tear-off window.
|
||||||
to another notice.
|
- FOAF for groups.
|
||||||
- Show username in subject of emails.
|
- Moved all JavaScript tags to just before </body> by default,
|
||||||
- Check if avatar exists before trying to delete it.
|
significantly speeding up apparent page load time.
|
||||||
- Correctly add omb_version to response for request token in OMB.
|
- Added a Realtime plugin for Orbited server.
|
||||||
- Add a few more SMS carriers.
|
- Added a mobile plugin to give a more mobile-phone-friendly layout
|
||||||
- Add a few more notice sources.
|
when a mobile browser is detected.
|
||||||
- Vary: header.
|
- Use CSS sprites for most common icons.
|
||||||
- Improvements to the AutoCompletePlugin.
|
- Fixes for images and buttons on Web output.
|
||||||
- Check for 'dl' before using it.
|
- New plugin requires that users validate their email before posting.
|
||||||
- Make it impossible to delete self-subscriptions via the API.
|
- New plugin UserFlag lets users flag other profiles for review.
|
||||||
- Fix pagination of tagged user pages.
|
- Considerably better i18n support. Use TranslateWiki to update
|
||||||
- Make PiwikAnalyticsPlugin work with addPlugin().
|
translations.
|
||||||
- Removed trailing single space in user nicknames in notice lists.
|
- Notices and profiles now store location information.
|
||||||
- Show context link if a notice starts a conversation.
|
- New plugin, Geonames, for turning location names and lat/long pairs
|
||||||
- blacklist all files and directories in install dir.
|
into structured IDs and vice versa. Architecture reusable for other
|
||||||
- handle GoDaddy-style PATH_INFO, including script name.
|
systems.
|
||||||
- add home_timeline synonym for friends_timeline.
|
- Better check of license compatibility between site licenses.
|
||||||
- Add a popup window for the realtime plugin.
|
- Some improvements in XMPP output.
|
||||||
- Add some more streams for the realtime plugin.
|
- Media upload in the API.
|
||||||
- Fix a bug that overwrote group creation timestamp on every edit.
|
- Replies appear in the user's inbox.
|
||||||
- Moved HTTP error code strings to a class variable.
|
- Improved the UI on the bookmarklet.
|
||||||
- The Twitter API now returns server errors in the correct format.
|
- StatusNet identities can be used as OpenID identities.
|
||||||
- Reset the doctype for HTML output.
|
- Script to register a user.
|
||||||
- Fixed a number of notices.
|
- Script to make someone a group admin.
|
||||||
- Don't show search suggestions for private sites.
|
- Script to make someone a site admin or moderator.
|
||||||
- Some corrections to FBConnect nav overrides.
|
- 'login' command.
|
||||||
- Slightly less database-intensive session management.
|
- Pluggable authentication.
|
||||||
- Updated name of software in installer script.
|
- LDAP authentication plugin.
|
||||||
- Include long-form attachment URLs if url-shortener is disabled.
|
- Script for console interaction with the site (!).
|
||||||
- Include updated localisations for Polish, Greek, Hebrew, Icelandic,
|
- Users don't see group posts from people they've blocked.
|
||||||
Norwegian, and Chinese.
|
- Admin panel interface for changing site configuration.
|
||||||
- Include upstream fixes to gettext.php.
|
- Users can be sandboxed (limited contributions) or silenced
|
||||||
- Correct for regression in Facebook API for updates.
|
(no contributions) by moderators.
|
||||||
- Ignore "Sent from my iPhone" (and similar) in mail updates.
|
- Many changes to make language usage more consistent.
|
||||||
- Use the NICKNAME_FMT constant for detecting nicknames.
|
- Sphinx search moved to a plugin.
|
||||||
- Check for site servername config'd.
|
- GeoURL plugin.
|
||||||
- Compatibility fix for empty status updates with Twitter API.
|
- Profile and group lists support hAtom.
|
||||||
- Option to show files privately (EXPERIMENTAL! Use with caution.)
|
- Massive refactoring of util.js.
|
||||||
- a script to register a new user.
|
- Mapstraction plugin to show maps on inbox and profile pages.
|
||||||
- a script to make a user admin of a group.
|
- Play/pause buttons for realtime notices.
|
||||||
|
- Support for geo microformat.
|
||||||
|
- Partial support for feed subscriptions, RSSCloud, PubSubHubBub.
|
||||||
|
- Support for geolocation in browser (Chrome, Firefox).
|
||||||
|
- Quit trying to negotiate HTML format. Always use text/html.
|
||||||
|
We lose, and so do Web standards. Boo.
|
||||||
|
- Better logging of request info.
|
||||||
|
- Better output for errors in Web interface.
|
||||||
|
- No longer store .mo files; these need to be generated.
|
||||||
|
- Minify plugin.
|
||||||
|
- Events to allow pluginizing logger.
|
||||||
|
- New framework for plugin localization.
|
||||||
|
- Gravatar plugin.
|
||||||
|
- Add support for "repeats" (similar to Twitter's "retweets").
|
||||||
|
- Support for repeats in Twitter API.
|
||||||
|
- Better notification of direct messages.
|
||||||
|
|
||||||
Prerequisites
|
Prerequisites
|
||||||
=============
|
=============
|
||||||
@ -358,7 +373,7 @@ It's possible to configure the software so it looks like this instead:
|
|||||||
|
|
||||||
These "fancy URLs" are more readable and memorable for users. To use
|
These "fancy URLs" are more readable and memorable for users. To use
|
||||||
fancy URLs, you must either have Apache 2.x with .htaccess enabled and
|
fancy URLs, you must either have Apache 2.x with .htaccess enabled and
|
||||||
mod_redirect enabled, -OR- know how to configure "url redirection" in
|
mod_rewrite enabled, -OR- know how to configure "url redirection" in
|
||||||
your server.
|
your server.
|
||||||
|
|
||||||
1. Copy the htaccess.sample file to .htaccess in your StatusNet
|
1. Copy the htaccess.sample file to .htaccess in your StatusNet
|
||||||
@ -384,6 +399,18 @@ like:
|
|||||||
If you changed your HTTP server configuration, you may need to restart
|
If you changed your HTTP server configuration, you may need to restart
|
||||||
the server first.
|
the server first.
|
||||||
|
|
||||||
|
If it doesn't work, double-check that AllowOverride for the StatusNet
|
||||||
|
directory is 'All' in your Apache configuration file. This is usually
|
||||||
|
/etc/httpd.conf, /etc/apache/httpd.conf, or (on Debian and Ubuntu)
|
||||||
|
/etc/apache2/sites-available/default. See the Apache documentation for
|
||||||
|
.htaccess files for more details:
|
||||||
|
|
||||||
|
http://httpd.apache.org/docs/2.2/howto/htaccess.html
|
||||||
|
|
||||||
|
Also, check that mod_rewrite is installed and enabled:
|
||||||
|
|
||||||
|
http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html
|
||||||
|
|
||||||
Sphinx
|
Sphinx
|
||||||
------
|
------
|
||||||
|
|
||||||
@ -1407,6 +1434,21 @@ contentlimit: max length of the plain-text content of a message.
|
|||||||
Default is null, meaning to use the site-wide text limit.
|
Default is null, meaning to use the site-wide text limit.
|
||||||
0 means no limit.
|
0 means no limit.
|
||||||
|
|
||||||
|
logincommand
|
||||||
|
------------
|
||||||
|
|
||||||
|
Configuration options for the login command.
|
||||||
|
|
||||||
|
disabled: whether to enable this command. If enabled, users who send
|
||||||
|
the text 'login' to the site through any channel will
|
||||||
|
receive a link to login to the site automatically in return.
|
||||||
|
Possibly useful for users who primarily use an XMPP or SMS
|
||||||
|
interface and can't be bothered to remember their site
|
||||||
|
password. Note that the security implications of this are
|
||||||
|
pretty serious and have not been thoroughly tested. You
|
||||||
|
should enable it only after you've convinced yourself that
|
||||||
|
it is safe. Default is 'false'.
|
||||||
|
|
||||||
Plugins
|
Plugins
|
||||||
=======
|
=======
|
||||||
|
|
||||||
@ -1573,6 +1615,7 @@ if anyone's been overlooked in error.
|
|||||||
* Federico Marani
|
* Federico Marani
|
||||||
* Craig Andrews
|
* Craig Andrews
|
||||||
* mEDI
|
* mEDI
|
||||||
|
* Brett Taylor
|
||||||
|
|
||||||
Thanks also to the developers of our upstream library code and to the
|
Thanks also to the developers of our upstream library code and to the
|
||||||
thousands of people who have tried out Identi.ca, installed StatusNet,
|
thousands of people who have tried out Identi.ca, installed StatusNet,
|
||||||
|
@ -160,12 +160,11 @@ class AllAction extends ProfileAction
|
|||||||
|
|
||||||
function showPageTitle()
|
function showPageTitle()
|
||||||
{
|
{
|
||||||
$user =& common_current_user();
|
$user = common_current_user();
|
||||||
if ($user && ($user->id == $this->user->id)) {
|
if ($user && ($user->id == $this->user->id)) {
|
||||||
$this->element('h1', null, _("You and friends"));
|
$this->element('h1', null, _("You and friends"));
|
||||||
} else {
|
} else {
|
||||||
$this->element('h1', null, sprintf(_('%s and friends'), $this->user->nickname));
|
$this->element('h1', null, sprintf(_('%s and friends'), $this->user->nickname));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,17 @@ class ApiBlockCreateAction extends ApiAuthAction
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't allow blocking yourself!
|
||||||
|
|
||||||
|
if ($this->user->id == $this->other->id) {
|
||||||
|
$this->clientError(
|
||||||
|
_("You cannot block yourself!"),
|
||||||
|
403,
|
||||||
|
$this->format
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->user->hasBlocked($this->other)
|
if ($this->user->hasBlocked($this->other)
|
||||||
|| $this->user->block($this->other)
|
|| $this->user->block($this->other)
|
||||||
) {
|
) {
|
||||||
|
@ -175,7 +175,7 @@ class ApiDirectMessageNewAction extends ApiAuthAction
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mail_notify_message($message, $this->user, $this->other);
|
$message->notify();
|
||||||
|
|
||||||
if ($this->format == 'xml') {
|
if ($this->format == 'xml') {
|
||||||
$this->showSingleXmlDirectMessage($message);
|
$this->showSingleXmlDirectMessage($message);
|
||||||
|
136
actions/apistatusesretweet.php
Normal file
136
actions/apistatusesretweet.php
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Repeat a notice through the API
|
||||||
|
*
|
||||||
|
* 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 Evan Prodromou <evan@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
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once INSTALLDIR . '/lib/apiauth.php';
|
||||||
|
require_once INSTALLDIR . '/lib/mediafile.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repeat a notice through the API
|
||||||
|
*
|
||||||
|
* @category API
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@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 ApiStatusesRetweetAction extends ApiAuthAction
|
||||||
|
{
|
||||||
|
var $original = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take arguments for running
|
||||||
|
*
|
||||||
|
* @param array $args $_REQUEST args
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
function prepare($args)
|
||||||
|
{
|
||||||
|
parent::prepare($args);
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
|
||||||
|
$this->clientError(_('This method requires a POST.'),
|
||||||
|
400, $this->format);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $this->trimmed('id');
|
||||||
|
|
||||||
|
$this->original = Notice::staticGet('id', $id);
|
||||||
|
|
||||||
|
if (empty($this->original)) {
|
||||||
|
$this->clientError(_('No such notice.'),
|
||||||
|
400, $this->format);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->user = $this->auth_user;
|
||||||
|
|
||||||
|
if ($this->user->id == $notice->profile_id) {
|
||||||
|
$this->clientError(_('Cannot repeat your own notice.'),
|
||||||
|
400, $this->format);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$profile = $this->user->getProfile();
|
||||||
|
|
||||||
|
if ($profile->hasRepeated($id)) {
|
||||||
|
$this->clientError(_('Already repeated that notice.'),
|
||||||
|
400, $this->format);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the request
|
||||||
|
*
|
||||||
|
* Make a new notice for the update, save it, and show it
|
||||||
|
*
|
||||||
|
* @param array $args $_REQUEST data (unused)
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function handle($args)
|
||||||
|
{
|
||||||
|
parent::handle($args);
|
||||||
|
|
||||||
|
$repeat = $this->original->repeat($this->user->id, $this->source);
|
||||||
|
|
||||||
|
common_broadcast_notice($repeat);
|
||||||
|
|
||||||
|
$this->showNotice($repeat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the resulting notice
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function showNotice($notice)
|
||||||
|
{
|
||||||
|
if (!empty($notice)) {
|
||||||
|
if ($this->format == 'xml') {
|
||||||
|
$this->showSingleXmlStatus($notice);
|
||||||
|
} elseif ($this->format == 'json') {
|
||||||
|
$this->show_single_json_status($notice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
116
actions/apistatusesretweets.php
Normal file
116
actions/apistatusesretweets.php
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Show up to 100 repeats of a notice
|
||||||
|
*
|
||||||
|
* 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 Evan Prodromou <evan@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
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once INSTALLDIR . '/lib/apiauth.php';
|
||||||
|
require_once INSTALLDIR . '/lib/mediafile.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show up to 100 repeats of a notice
|
||||||
|
*
|
||||||
|
* @category API
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@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 ApiStatusesRetweetsAction extends ApiAuthAction
|
||||||
|
{
|
||||||
|
const MAXCOUNT = 100;
|
||||||
|
|
||||||
|
var $original = null;
|
||||||
|
var $cnt = self::MAXCOUNT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take arguments for running
|
||||||
|
*
|
||||||
|
* @param array $args $_REQUEST args
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
function prepare($args)
|
||||||
|
{
|
||||||
|
parent::prepare($args);
|
||||||
|
|
||||||
|
$id = $this->trimmed('id');
|
||||||
|
|
||||||
|
$this->original = Notice::staticGet('id', $id);
|
||||||
|
|
||||||
|
if (empty($this->original)) {
|
||||||
|
$this->clientError(_('No such notice.'),
|
||||||
|
400, $this->format);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cnt = $this->trimmed('count');
|
||||||
|
|
||||||
|
if (empty($cnt) || !is_integer($cnt)) {
|
||||||
|
$cnt = 100;
|
||||||
|
} else {
|
||||||
|
$this->cnt = min((int)$cnt, self::MAXCOUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the request
|
||||||
|
*
|
||||||
|
* Make a new notice for the update, save it, and show it
|
||||||
|
*
|
||||||
|
* @param array $args $_REQUEST data (unused)
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function handle($args)
|
||||||
|
{
|
||||||
|
parent::handle($args);
|
||||||
|
|
||||||
|
$strm = $this->original->repeatStream($this->cnt);
|
||||||
|
|
||||||
|
switch ($this->format) {
|
||||||
|
case 'xml':
|
||||||
|
$this->showXmlTimeline($strm);
|
||||||
|
break;
|
||||||
|
case 'json':
|
||||||
|
$this->showJsonTimeline($strm);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$this->clientError(_('API method not found!'), $code = 404);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -231,19 +231,22 @@ class ApiStatusesUpdateAction extends ApiAuthAction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->notice = Notice::saveNew(
|
$content = html_entity_decode($status_shortened, ENT_NOQUOTES, 'UTF-8');
|
||||||
$this->user->id,
|
|
||||||
html_entity_decode($status_shortened, ENT_NOQUOTES, 'UTF-8'),
|
$options = array('reply_to' => $reply_to);
|
||||||
|
|
||||||
|
if (!empty($location)) {
|
||||||
|
$options['lat'] = $location->lat;
|
||||||
|
$options['lon'] = $location->lon;
|
||||||
|
$options['location_id'] = $location->location_id;
|
||||||
|
$options['location_ns'] = $location->location_ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->notice =
|
||||||
|
Notice::saveNew($this->user->id,
|
||||||
|
$content,
|
||||||
$this->source,
|
$this->source,
|
||||||
1,
|
$options);
|
||||||
$reply_to,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
empty($location) ? null : $location->lat,
|
|
||||||
empty($location) ? null : $location->lon,
|
|
||||||
empty($location) ? null : $location->location_id,
|
|
||||||
empty($location) ? null : $location->location_ns
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isset($upload)) {
|
if (isset($upload)) {
|
||||||
$upload->attachToNotice($this->notice);
|
$upload->attachToNotice($this->notice);
|
||||||
|
@ -101,6 +101,7 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction
|
|||||||
function showTimeline()
|
function showTimeline()
|
||||||
{
|
{
|
||||||
$profile = $this->user->getProfile();
|
$profile = $this->user->getProfile();
|
||||||
|
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
|
||||||
|
|
||||||
$sitename = common_config('site', 'name');
|
$sitename = common_config('site', 'name');
|
||||||
$title = sprintf(
|
$title = sprintf(
|
||||||
@ -121,20 +122,21 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction
|
|||||||
$profile->getBestName(),
|
$profile->getBestName(),
|
||||||
$this->user->nickname
|
$this->user->nickname
|
||||||
);
|
);
|
||||||
|
$logo = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE);
|
||||||
|
|
||||||
switch($this->format) {
|
switch($this->format) {
|
||||||
case 'xml':
|
case 'xml':
|
||||||
$this->showXmlTimeline($this->notices);
|
$this->showXmlTimeline($this->notices);
|
||||||
break;
|
break;
|
||||||
case 'rss':
|
case 'rss':
|
||||||
$this->showRssTimeline($this->notices, $title, $link, $subtitle);
|
$this->showRssTimeline($this->notices, $title, $link, $subtitle, null, $logo);
|
||||||
break;
|
break;
|
||||||
case 'atom':
|
case 'atom':
|
||||||
$selfuri = common_root_url() .
|
$selfuri = common_root_url() .
|
||||||
ltrim($_SERVER['QUERY_STRING'], 'p=');
|
ltrim($_SERVER['QUERY_STRING'], 'p=');
|
||||||
$this->showAtomTimeline(
|
$this->showAtomTimeline(
|
||||||
$this->notices, $title, $id, $link, $subtitle,
|
$this->notices, $title, $id, $link, $subtitle,
|
||||||
null, $selfuri
|
null, $selfuri, $logo
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'json':
|
case 'json':
|
||||||
|
@ -110,6 +110,7 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction
|
|||||||
function showTimeline()
|
function showTimeline()
|
||||||
{
|
{
|
||||||
$profile = $this->user->getProfile();
|
$profile = $this->user->getProfile();
|
||||||
|
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
|
||||||
$sitename = common_config('site', 'name');
|
$sitename = common_config('site', 'name');
|
||||||
$title = sprintf(_("%s and friends"), $this->user->nickname);
|
$title = sprintf(_("%s and friends"), $this->user->nickname);
|
||||||
$taguribase = common_config('integration', 'taguri');
|
$taguribase = common_config('integration', 'taguri');
|
||||||
@ -121,13 +122,14 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction
|
|||||||
_('Updates from %1$s and friends on %2$s!'),
|
_('Updates from %1$s and friends on %2$s!'),
|
||||||
$this->user->nickname, $sitename
|
$this->user->nickname, $sitename
|
||||||
);
|
);
|
||||||
|
$logo = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE);
|
||||||
|
|
||||||
switch($this->format) {
|
switch($this->format) {
|
||||||
case 'xml':
|
case 'xml':
|
||||||
$this->showXmlTimeline($this->notices);
|
$this->showXmlTimeline($this->notices);
|
||||||
break;
|
break;
|
||||||
case 'rss':
|
case 'rss':
|
||||||
$this->showRssTimeline($this->notices, $title, $link, $subtitle);
|
$this->showRssTimeline($this->notices, $title, $link, $subtitle, null, $logo);
|
||||||
break;
|
break;
|
||||||
case 'atom':
|
case 'atom':
|
||||||
|
|
||||||
@ -144,7 +146,7 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction
|
|||||||
|
|
||||||
$this->showAtomTimeline(
|
$this->showAtomTimeline(
|
||||||
$this->notices, $title, $id, $link,
|
$this->notices, $title, $id, $link,
|
||||||
$subtitle, null, $selfuri
|
$subtitle, null, $selfuri, $logo
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'json':
|
case 'json':
|
||||||
@ -167,17 +169,13 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction
|
|||||||
$notices = array();
|
$notices = array();
|
||||||
|
|
||||||
if (!empty($this->auth_user) && $this->auth_user->id == $this->user->id) {
|
if (!empty($this->auth_user) && $this->auth_user->id == $this->user->id) {
|
||||||
$notice = $this->user->noticeInbox(
|
$notice = $this->user->ownFriendsTimeline(($this->page-1) * $this->count,
|
||||||
($this->page-1) * $this->count,
|
|
||||||
$this->count, $this->since_id,
|
$this->count, $this->since_id,
|
||||||
$this->max_id, $this->since
|
$this->max_id, $this->since);
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
$notice = $this->user->noticesWithFriends(
|
$notice = $this->user->friendsTimeline(($this->page-1) * $this->count,
|
||||||
($this->page-1) * $this->count,
|
|
||||||
$this->count, $this->since_id,
|
$this->count, $this->since_id,
|
||||||
$this->max_id, $this->since
|
$this->max_id, $this->since);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while ($notice->fetch()) {
|
while ($notice->fetch()) {
|
||||||
|
@ -105,6 +105,7 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
|
|||||||
function showTimeline()
|
function showTimeline()
|
||||||
{
|
{
|
||||||
$sitename = common_config('site', 'name');
|
$sitename = common_config('site', 'name');
|
||||||
|
$avatar = $this->group->homepage_logo;
|
||||||
$title = sprintf(_("%s timeline"), $this->group->nickname);
|
$title = sprintf(_("%s timeline"), $this->group->nickname);
|
||||||
$taguribase = common_config('integration', 'taguri');
|
$taguribase = common_config('integration', 'taguri');
|
||||||
$id = "tag:$taguribase:GroupTimeline:" . $this->group->id;
|
$id = "tag:$taguribase:GroupTimeline:" . $this->group->id;
|
||||||
@ -117,13 +118,14 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
|
|||||||
$this->group->nickname,
|
$this->group->nickname,
|
||||||
$sitename
|
$sitename
|
||||||
);
|
);
|
||||||
|
$logo = ($avatar) ? $avatar : User_group::defaultLogo(AVATAR_PROFILE_SIZE);
|
||||||
|
|
||||||
switch($this->format) {
|
switch($this->format) {
|
||||||
case 'xml':
|
case 'xml':
|
||||||
$this->showXmlTimeline($this->notices);
|
$this->showXmlTimeline($this->notices);
|
||||||
break;
|
break;
|
||||||
case 'rss':
|
case 'rss':
|
||||||
$this->showRssTimeline($this->notices, $title, $link, $subtitle);
|
$this->showRssTimeline($this->notices, $title, $link, $subtitle, null, $logo);
|
||||||
break;
|
break;
|
||||||
case 'atom':
|
case 'atom':
|
||||||
$selfuri = common_root_url() .
|
$selfuri = common_root_url() .
|
||||||
@ -136,7 +138,8 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
|
|||||||
$link,
|
$link,
|
||||||
$subtitle,
|
$subtitle,
|
||||||
null,
|
null,
|
||||||
$selfuri
|
$selfuri,
|
||||||
|
$logo
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'json':
|
case 'json':
|
||||||
|
249
actions/apitimelinehome.php
Normal file
249
actions/apitimelinehome.php
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Show the home timeline
|
||||||
|
*
|
||||||
|
* 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 Craig Andrews <candrews@integralblue.com>
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @author Jeffery To <jeffery.to@gmail.com>
|
||||||
|
* @author mac65 <mac65@mac65.com>
|
||||||
|
* @author Mike Cochrane <mikec@mikenz.geek.nz>
|
||||||
|
* @author Robin Millette <robin@millette.info>
|
||||||
|
* @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
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once INSTALLDIR . '/lib/apibareauth.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the most recent notices (default 20) posted by the target user.
|
||||||
|
* This is the equivalent of 'You and friends' page accessed via Web.
|
||||||
|
*
|
||||||
|
* @category API
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Craig Andrews <candrews@integralblue.com>
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @author Jeffery To <jeffery.to@gmail.com>
|
||||||
|
* @author mac65 <mac65@mac65.com>
|
||||||
|
* @author Mike Cochrane <mikec@mikenz.geek.nz>
|
||||||
|
* @author Robin Millette <robin@millette.info>
|
||||||
|
* @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 ApiTimelineHomeAction extends ApiBareAuthAction
|
||||||
|
{
|
||||||
|
var $notices = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take arguments for running
|
||||||
|
*
|
||||||
|
* @param array $args $_REQUEST args
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
function prepare($args)
|
||||||
|
{
|
||||||
|
parent::prepare($args);
|
||||||
|
common_debug("api home_timeline");
|
||||||
|
$this->user = $this->getTargetUser($this->arg('id'));
|
||||||
|
|
||||||
|
if (empty($this->user)) {
|
||||||
|
$this->clientError(_('No such user.'), 404, $this->format);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->notices = $this->getNotices();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the request
|
||||||
|
*
|
||||||
|
* Just show the notices
|
||||||
|
*
|
||||||
|
* @param array $args $_REQUEST data (unused)
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function handle($args)
|
||||||
|
{
|
||||||
|
parent::handle($args);
|
||||||
|
$this->showTimeline();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the timeline of notices
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function showTimeline()
|
||||||
|
{
|
||||||
|
$profile = $this->user->getProfile();
|
||||||
|
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
|
||||||
|
$sitename = common_config('site', 'name');
|
||||||
|
$title = sprintf(_("%s and friends"), $this->user->nickname);
|
||||||
|
$taguribase = common_config('integration', 'taguri');
|
||||||
|
$id = "tag:$taguribase:HomeTimeline:" . $this->user->id;
|
||||||
|
$link = common_local_url(
|
||||||
|
'all', array('nickname' => $this->user->nickname)
|
||||||
|
);
|
||||||
|
$subtitle = sprintf(
|
||||||
|
_('Updates from %1$s and friends on %2$s!'),
|
||||||
|
$this->user->nickname, $sitename
|
||||||
|
);
|
||||||
|
$logo = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE);
|
||||||
|
|
||||||
|
switch($this->format) {
|
||||||
|
case 'xml':
|
||||||
|
$this->showXmlTimeline($this->notices);
|
||||||
|
break;
|
||||||
|
case 'rss':
|
||||||
|
$this->showRssTimeline($this->notices, $title, $link, $subtitle, null, $logo);
|
||||||
|
break;
|
||||||
|
case 'atom':
|
||||||
|
|
||||||
|
$target_id = $this->arg('id');
|
||||||
|
|
||||||
|
if (isset($target_id)) {
|
||||||
|
$selfuri = common_root_url() .
|
||||||
|
'api/statuses/home_timeline/' .
|
||||||
|
$target_id . '.atom';
|
||||||
|
} else {
|
||||||
|
$selfuri = common_root_url() .
|
||||||
|
'api/statuses/home_timeline.atom';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->showAtomTimeline(
|
||||||
|
$this->notices, $title, $id, $link,
|
||||||
|
$subtitle, null, $selfuri, $logo
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'json':
|
||||||
|
$this->showJsonTimeline($this->notices);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$this->clientError(_('API method not found!'), $code = 404);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get notices
|
||||||
|
*
|
||||||
|
* @return array notices
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getNotices()
|
||||||
|
{
|
||||||
|
$notices = array();
|
||||||
|
|
||||||
|
if (!empty($this->auth_user) && $this->auth_user->id == $this->user->id) {
|
||||||
|
$notice = $this->user->noticeInbox(
|
||||||
|
($this->page-1) * $this->count,
|
||||||
|
$this->count, $this->since_id,
|
||||||
|
$this->max_id, $this->since
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$notice = $this->user->noticesWithFriends(
|
||||||
|
($this->page-1) * $this->count,
|
||||||
|
$this->count, $this->since_id,
|
||||||
|
$this->max_id, $this->since
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($notice->fetch()) {
|
||||||
|
$notices[] = clone($notice);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $notices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this action read only?
|
||||||
|
*
|
||||||
|
* @param array $args other arguments
|
||||||
|
*
|
||||||
|
* @return boolean true
|
||||||
|
*/
|
||||||
|
|
||||||
|
function isReadOnly($args)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When was this feed last modified?
|
||||||
|
*
|
||||||
|
* @return string datestamp of the latest notice in the stream
|
||||||
|
*/
|
||||||
|
|
||||||
|
function lastModified()
|
||||||
|
{
|
||||||
|
if (!empty($this->notices) && (count($this->notices) > 0)) {
|
||||||
|
return strtotime($this->notices[0]->created);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An entity tag for this stream
|
||||||
|
*
|
||||||
|
* Returns an Etag based on the action name, language, user ID, and
|
||||||
|
* timestamps of the first and last notice in the timeline
|
||||||
|
*
|
||||||
|
* @return string etag
|
||||||
|
*/
|
||||||
|
|
||||||
|
function etag()
|
||||||
|
{
|
||||||
|
if (!empty($this->notices) && (count($this->notices) > 0)) {
|
||||||
|
|
||||||
|
$last = count($this->notices) - 1;
|
||||||
|
|
||||||
|
return '"' . implode(
|
||||||
|
':',
|
||||||
|
array($this->arg('action'),
|
||||||
|
common_language(),
|
||||||
|
$this->user->id,
|
||||||
|
strtotime($this->notices[0]->created),
|
||||||
|
strtotime($this->notices[$last]->created))
|
||||||
|
)
|
||||||
|
. '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -110,6 +110,7 @@ class ApiTimelineMentionsAction extends ApiBareAuthAction
|
|||||||
function showTimeline()
|
function showTimeline()
|
||||||
{
|
{
|
||||||
$profile = $this->user->getProfile();
|
$profile = $this->user->getProfile();
|
||||||
|
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
|
||||||
|
|
||||||
$sitename = common_config('site', 'name');
|
$sitename = common_config('site', 'name');
|
||||||
$title = sprintf(
|
$title = sprintf(
|
||||||
@ -126,20 +127,21 @@ class ApiTimelineMentionsAction extends ApiBareAuthAction
|
|||||||
_('%1$s updates that reply to updates from %2$s / %3$s.'),
|
_('%1$s updates that reply to updates from %2$s / %3$s.'),
|
||||||
$sitename, $this->user->nickname, $profile->getBestName()
|
$sitename, $this->user->nickname, $profile->getBestName()
|
||||||
);
|
);
|
||||||
|
$logo = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE);
|
||||||
|
|
||||||
switch($this->format) {
|
switch($this->format) {
|
||||||
case 'xml':
|
case 'xml':
|
||||||
$this->showXmlTimeline($this->notices);
|
$this->showXmlTimeline($this->notices);
|
||||||
break;
|
break;
|
||||||
case 'rss':
|
case 'rss':
|
||||||
$this->showRssTimeline($this->notices, $title, $link, $subtitle);
|
$this->showRssTimeline($this->notices, $title, $link, $subtitle, null, $logo);
|
||||||
break;
|
break;
|
||||||
case 'atom':
|
case 'atom':
|
||||||
$selfuri = common_root_url() .
|
$selfuri = common_root_url() .
|
||||||
ltrim($_SERVER['QUERY_STRING'], 'p=');
|
ltrim($_SERVER['QUERY_STRING'], 'p=');
|
||||||
$this->showAtomTimeline(
|
$this->showAtomTimeline(
|
||||||
$this->notices, $title, $id, $link, $subtitle,
|
$this->notices, $title, $id, $link, $subtitle,
|
||||||
null, $selfuri
|
null, $selfuri, $logo
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'json':
|
case 'json':
|
||||||
|
@ -103,6 +103,7 @@ class ApiTimelinePublicAction extends ApiPrivateAuthAction
|
|||||||
function showTimeline()
|
function showTimeline()
|
||||||
{
|
{
|
||||||
$sitename = common_config('site', 'name');
|
$sitename = common_config('site', 'name');
|
||||||
|
$sitelogo = (common_config('site', 'logo')) ? common_config('site', 'logo') : Theme::path('logo.png');
|
||||||
$title = sprintf(_("%s public timeline"), $sitename);
|
$title = sprintf(_("%s public timeline"), $sitename);
|
||||||
$taguribase = common_config('integration', 'taguri');
|
$taguribase = common_config('integration', 'taguri');
|
||||||
$id = "tag:$taguribase:PublicTimeline";
|
$id = "tag:$taguribase:PublicTimeline";
|
||||||
@ -114,13 +115,13 @@ class ApiTimelinePublicAction extends ApiPrivateAuthAction
|
|||||||
$this->showXmlTimeline($this->notices);
|
$this->showXmlTimeline($this->notices);
|
||||||
break;
|
break;
|
||||||
case 'rss':
|
case 'rss':
|
||||||
$this->showRssTimeline($this->notices, $title, $link, $subtitle);
|
$this->showRssTimeline($this->notices, $title, $link, $subtitle, null, $sitelogo);
|
||||||
break;
|
break;
|
||||||
case 'atom':
|
case 'atom':
|
||||||
$selfuri = common_root_url() . 'api/statuses/public_timeline.atom';
|
$selfuri = common_root_url() . 'api/statuses/public_timeline.atom';
|
||||||
$this->showAtomTimeline(
|
$this->showAtomTimeline(
|
||||||
$this->notices, $title, $id, $link,
|
$this->notices, $title, $id, $link,
|
||||||
$subtitle, null, $selfuri
|
$subtitle, null, $selfuri, $sitelogo
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'json':
|
case 'json':
|
||||||
|
126
actions/apitimelineretweetedbyme.php
Normal file
126
actions/apitimelineretweetedbyme.php
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Show authenticating user's most recent repeats
|
||||||
|
*
|
||||||
|
* 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 Evan Prodromou <evan@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
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once INSTALLDIR . '/lib/apiauth.php';
|
||||||
|
require_once INSTALLDIR . '/lib/mediafile.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show authenticating user's most recent repeats
|
||||||
|
*
|
||||||
|
* @category API
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@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 ApiTimelineRetweetedByMeAction extends ApiAuthAction
|
||||||
|
{
|
||||||
|
const DEFAULTCOUNT = 20;
|
||||||
|
const MAXCOUNT = 200;
|
||||||
|
const MAXNOTICES = 3200;
|
||||||
|
|
||||||
|
var $repeats = null;
|
||||||
|
var $cnt = self::DEFAULTCOUNT;
|
||||||
|
var $page = 1;
|
||||||
|
var $since_id = null;
|
||||||
|
var $max_id = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take arguments for running
|
||||||
|
*
|
||||||
|
* @param array $args $_REQUEST args
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
function prepare($args)
|
||||||
|
{
|
||||||
|
parent::prepare($args);
|
||||||
|
|
||||||
|
$cnt = $this->int('count', self::DEFAULTCOUNT, self::MAXCOUNT, 1);
|
||||||
|
|
||||||
|
$page = $this->int('page', 1, (self::MAXNOTICES/$this->cnt));
|
||||||
|
|
||||||
|
$since_id = $this->int('since_id');
|
||||||
|
|
||||||
|
$max_id = $this->int('max_id');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the request
|
||||||
|
*
|
||||||
|
* show a timeline of the user's repeated notices
|
||||||
|
*
|
||||||
|
* @param array $args $_REQUEST data (unused)
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function handle($args)
|
||||||
|
{
|
||||||
|
parent::handle($args);
|
||||||
|
|
||||||
|
$offset = ($this->page-1) * $this->cnt;
|
||||||
|
$limit = $this->cnt;
|
||||||
|
|
||||||
|
$strm = $this->auth_user->repeatedByMe($offset, $limit, $this->since_id, $this->max_id);
|
||||||
|
|
||||||
|
switch ($this->format) {
|
||||||
|
case 'xml':
|
||||||
|
$this->showXmlTimeline($strm);
|
||||||
|
break;
|
||||||
|
case 'json':
|
||||||
|
$this->showJsonTimeline($strm);
|
||||||
|
break;
|
||||||
|
case 'atom':
|
||||||
|
$profile = $this->auth_user->getProfile();
|
||||||
|
|
||||||
|
$title = sprintf(_("Repeated by %s"), $this->auth_user->nickname);
|
||||||
|
$taguribase = common_config('integration', 'taguri');
|
||||||
|
$id = "tag:$taguribase:RepeatedByMe:" . $this->auth_user->id;
|
||||||
|
$link = common_local_url('showstream',
|
||||||
|
array('nickname' => $this->auth_user->nickname));
|
||||||
|
|
||||||
|
$this->showAtomTimeline($strm, $title, $id, $link);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$this->clientError(_('API method not found!'), $code = 404);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
125
actions/apitimelineretweetedtome.php
Normal file
125
actions/apitimelineretweetedtome.php
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Show most recent notices that are repeats in user's inbox
|
||||||
|
*
|
||||||
|
* 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 Evan Prodromou <evan@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
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once INSTALLDIR . '/lib/apiauth.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show most recent notices that are repeats in user's inbox
|
||||||
|
*
|
||||||
|
* @category API
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@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 ApiTimelineRetweetedToMeAction extends ApiAuthAction
|
||||||
|
{
|
||||||
|
const DEFAULTCOUNT = 20;
|
||||||
|
const MAXCOUNT = 200;
|
||||||
|
const MAXNOTICES = 3200;
|
||||||
|
|
||||||
|
var $repeats = null;
|
||||||
|
var $cnt = self::DEFAULTCOUNT;
|
||||||
|
var $page = 1;
|
||||||
|
var $since_id = null;
|
||||||
|
var $max_id = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take arguments for running
|
||||||
|
*
|
||||||
|
* @param array $args $_REQUEST args
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
function prepare($args)
|
||||||
|
{
|
||||||
|
parent::prepare($args);
|
||||||
|
|
||||||
|
$cnt = $this->int('count', self::DEFAULTCOUNT, self::MAXCOUNT, 1);
|
||||||
|
|
||||||
|
$page = $this->int('page', 1, (self::MAXNOTICES/$this->cnt));
|
||||||
|
|
||||||
|
$since_id = $this->int('since_id');
|
||||||
|
|
||||||
|
$max_id = $this->int('max_id');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the request
|
||||||
|
*
|
||||||
|
* show a timeline of the user's repeated notices
|
||||||
|
*
|
||||||
|
* @param array $args $_REQUEST data (unused)
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function handle($args)
|
||||||
|
{
|
||||||
|
parent::handle($args);
|
||||||
|
|
||||||
|
$offset = ($this->page-1) * $this->cnt;
|
||||||
|
$limit = $this->cnt;
|
||||||
|
|
||||||
|
$strm = $this->auth_user->repeatedToMe($offset, $limit, $this->since_id, $this->max_id);
|
||||||
|
|
||||||
|
switch ($this->format) {
|
||||||
|
case 'xml':
|
||||||
|
$this->showXmlTimeline($strm);
|
||||||
|
break;
|
||||||
|
case 'json':
|
||||||
|
$this->showJsonTimeline($strm);
|
||||||
|
break;
|
||||||
|
case 'atom':
|
||||||
|
$profile = $this->auth_user->getProfile();
|
||||||
|
|
||||||
|
$title = sprintf(_("Repeated to %s"), $this->auth_user->nickname);
|
||||||
|
$taguribase = common_config('integration', 'taguri');
|
||||||
|
$id = "tag:$taguribase:RepeatedToMe:" . $this->auth_user->id;
|
||||||
|
$link = common_local_url('all',
|
||||||
|
array('nickname' => $this->auth_user->nickname));
|
||||||
|
|
||||||
|
$this->showAtomTimeline($strm, $title, $id, $link);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$this->clientError(_('API method not found!'), $code = 404);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
126
actions/apitimelineretweetsofme.php
Normal file
126
actions/apitimelineretweetsofme.php
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Show authenticating user's most recent notices that have been repeated
|
||||||
|
*
|
||||||
|
* 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 Evan Prodromou <evan@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
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once INSTALLDIR . '/lib/apiauth.php';
|
||||||
|
require_once INSTALLDIR . '/lib/mediafile.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show authenticating user's most recent notices that have been repeated
|
||||||
|
*
|
||||||
|
* @category API
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@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 ApiTimelineRetweetsOfMeAction extends ApiAuthAction
|
||||||
|
{
|
||||||
|
const DEFAULTCOUNT = 20;
|
||||||
|
const MAXCOUNT = 200;
|
||||||
|
const MAXNOTICES = 3200;
|
||||||
|
|
||||||
|
var $repeats = null;
|
||||||
|
var $cnt = self::DEFAULTCOUNT;
|
||||||
|
var $page = 1;
|
||||||
|
var $since_id = null;
|
||||||
|
var $max_id = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take arguments for running
|
||||||
|
*
|
||||||
|
* @param array $args $_REQUEST args
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
function prepare($args)
|
||||||
|
{
|
||||||
|
parent::prepare($args);
|
||||||
|
|
||||||
|
$cnt = $this->int('count', self::DEFAULTCOUNT, self::MAXCOUNT, 1);
|
||||||
|
|
||||||
|
$page = $this->int('page', 1, (self::MAXNOTICES/$this->cnt));
|
||||||
|
|
||||||
|
$since_id = $this->int('since_id');
|
||||||
|
|
||||||
|
$max_id = $this->int('max_id');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the request
|
||||||
|
*
|
||||||
|
* show a timeline of the user's repeated notices
|
||||||
|
*
|
||||||
|
* @param array $args $_REQUEST data (unused)
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function handle($args)
|
||||||
|
{
|
||||||
|
parent::handle($args);
|
||||||
|
|
||||||
|
$offset = ($this->page-1) * $this->cnt;
|
||||||
|
$limit = $this->cnt;
|
||||||
|
|
||||||
|
$strm = $this->auth_user->repeatsOfMe($offset, $limit, $this->since_id, $this->max_id);
|
||||||
|
|
||||||
|
switch ($this->format) {
|
||||||
|
case 'xml':
|
||||||
|
$this->showXmlTimeline($strm);
|
||||||
|
break;
|
||||||
|
case 'json':
|
||||||
|
$this->showJsonTimeline($strm);
|
||||||
|
break;
|
||||||
|
case 'atom':
|
||||||
|
$profile = $this->auth_user->getProfile();
|
||||||
|
|
||||||
|
$title = sprintf(_("Repeats of %s"), $this->auth_user->nickname);
|
||||||
|
$taguribase = common_config('integration', 'taguri');
|
||||||
|
$id = "tag:$taguribase:RepeatsOfMe:" . $this->auth_user->id;
|
||||||
|
$link = common_local_url('showstream',
|
||||||
|
array('nickname' => $this->auth_user->nickname));
|
||||||
|
|
||||||
|
$this->showAtomTimeline($strm, $title, $id, $link);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$this->clientError(_('API method not found!'), $code = 404);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -98,6 +98,7 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction
|
|||||||
function showTimeline()
|
function showTimeline()
|
||||||
{
|
{
|
||||||
$sitename = common_config('site', 'name');
|
$sitename = common_config('site', 'name');
|
||||||
|
$sitelogo = (common_config('site', 'logo')) ? common_config('site', 'logo') : Theme::path('logo.png');
|
||||||
$title = sprintf(_("Notices tagged with %s"), $this->tag);
|
$title = sprintf(_("Notices tagged with %s"), $this->tag);
|
||||||
$link = common_local_url(
|
$link = common_local_url(
|
||||||
'tag',
|
'tag',
|
||||||
@ -116,7 +117,7 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction
|
|||||||
$this->showXmlTimeline($this->notices);
|
$this->showXmlTimeline($this->notices);
|
||||||
break;
|
break;
|
||||||
case 'rss':
|
case 'rss':
|
||||||
$this->showRssTimeline($this->notices, $title, $link, $subtitle);
|
$this->showRssTimeline($this->notices, $title, $link, $subtitle, null, $sitelogo);
|
||||||
break;
|
break;
|
||||||
case 'atom':
|
case 'atom':
|
||||||
$selfuri = common_root_url() .
|
$selfuri = common_root_url() .
|
||||||
@ -129,7 +130,8 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction
|
|||||||
$link,
|
$link,
|
||||||
$subtitle,
|
$subtitle,
|
||||||
null,
|
null,
|
||||||
$selfuri
|
$selfuri,
|
||||||
|
$sitelogo
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'json':
|
case 'json':
|
||||||
|
@ -112,6 +112,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
|
|||||||
function showTimeline()
|
function showTimeline()
|
||||||
{
|
{
|
||||||
$profile = $this->user->getProfile();
|
$profile = $this->user->getProfile();
|
||||||
|
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
|
||||||
|
|
||||||
$sitename = common_config('site', 'name');
|
$sitename = common_config('site', 'name');
|
||||||
$title = sprintf(_("%s timeline"), $this->user->nickname);
|
$title = sprintf(_("%s timeline"), $this->user->nickname);
|
||||||
@ -125,6 +126,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
|
|||||||
_('Updates from %1$s on %2$s!'),
|
_('Updates from %1$s on %2$s!'),
|
||||||
$this->user->nickname, $sitename
|
$this->user->nickname, $sitename
|
||||||
);
|
);
|
||||||
|
$logo = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE);
|
||||||
|
|
||||||
// FriendFeed's SUP protocol
|
// FriendFeed's SUP protocol
|
||||||
// Also added RSS and Atom feeds
|
// Also added RSS and Atom feeds
|
||||||
@ -139,7 +141,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
|
|||||||
case 'rss':
|
case 'rss':
|
||||||
$this->showRssTimeline(
|
$this->showRssTimeline(
|
||||||
$this->notices, $title, $link,
|
$this->notices, $title, $link,
|
||||||
$subtitle, $suplink
|
$subtitle, $suplink, $logo
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'atom':
|
case 'atom':
|
||||||
@ -153,7 +155,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
|
|||||||
}
|
}
|
||||||
$this->showAtomTimeline(
|
$this->showAtomTimeline(
|
||||||
$this->notices, $title, $id, $link,
|
$this->notices, $title, $id, $link,
|
||||||
$subtitle, $suplink, $selfuri
|
$subtitle, $suplink, $selfuri, $logo
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'json':
|
case 'json':
|
||||||
|
@ -119,6 +119,7 @@ class AvatarsettingsAction extends AccountSettingsAction
|
|||||||
$this->element('legend', null, _('Avatar settings'));
|
$this->element('legend', null, _('Avatar settings'));
|
||||||
$this->hidden('token', common_session_token());
|
$this->hidden('token', common_session_token());
|
||||||
|
|
||||||
|
if (Event::handle('StartAvatarFormData', array($this))) {
|
||||||
$this->elementStart('ul', 'form_data');
|
$this->elementStart('ul', 'form_data');
|
||||||
if ($original) {
|
if ($original) {
|
||||||
$this->elementStart('li', array('id' => 'avatar_original',
|
$this->elementStart('li', array('id' => 'avatar_original',
|
||||||
@ -165,6 +166,8 @@ class AvatarsettingsAction extends AccountSettingsAction
|
|||||||
$this->submit('upload', _('Upload'));
|
$this->submit('upload', _('Upload'));
|
||||||
$this->elementEnd('li');
|
$this->elementEnd('li');
|
||||||
$this->elementEnd('ul');
|
$this->elementEnd('ul');
|
||||||
|
}
|
||||||
|
Event::handle('EndAvatarFormData', array($this));
|
||||||
|
|
||||||
$this->elementEnd('fieldset');
|
$this->elementEnd('fieldset');
|
||||||
$this->elementEnd('form');
|
$this->elementEnd('form');
|
||||||
@ -267,6 +270,7 @@ class AvatarsettingsAction extends AccountSettingsAction
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Event::handle('StartAvatarSaveForm', array($this))) {
|
||||||
if ($this->arg('upload')) {
|
if ($this->arg('upload')) {
|
||||||
$this->uploadAvatar();
|
$this->uploadAvatar();
|
||||||
} else if ($this->arg('crop')) {
|
} else if ($this->arg('crop')) {
|
||||||
@ -276,6 +280,8 @@ class AvatarsettingsAction extends AccountSettingsAction
|
|||||||
} else {
|
} else {
|
||||||
$this->showForm(_('Unexpected form submission.'));
|
$this->showForm(_('Unexpected form submission.'));
|
||||||
}
|
}
|
||||||
|
Event::handle('EndAvatarSaveForm', array($this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,15 +31,15 @@ class FileAction extends Action
|
|||||||
parent::prepare($args);
|
parent::prepare($args);
|
||||||
$this->id = $this->trimmed('notice');
|
$this->id = $this->trimmed('notice');
|
||||||
if (empty($this->id)) {
|
if (empty($this->id)) {
|
||||||
$this->clientError(_('No notice id'));
|
$this->clientError(_('No notice ID.'));
|
||||||
}
|
}
|
||||||
$notice = Notice::staticGet('id', $this->id);
|
$notice = Notice::staticGet('id', $this->id);
|
||||||
if (empty($notice)) {
|
if (empty($notice)) {
|
||||||
$this->clientError(_('No notice'));
|
$this->clientError(_('No notice.'));
|
||||||
}
|
}
|
||||||
$atts = $notice->attachments();
|
$atts = $notice->attachments();
|
||||||
if (empty($atts)) {
|
if (empty($atts)) {
|
||||||
$this->clientError(_('No attachments'));
|
$this->clientError(_('No attachments.'));
|
||||||
}
|
}
|
||||||
foreach ($atts as $att) {
|
foreach ($atts as $att) {
|
||||||
if (!empty($att->filename)) {
|
if (!empty($att->filename)) {
|
||||||
@ -48,7 +48,7 @@ class FileAction extends Action
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (empty($this->filerec)) {
|
if (empty($this->filerec)) {
|
||||||
$this->clientError(_('No uploaded attachments'));
|
$this->clientError(_('No uploaded attachments.'));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -88,14 +88,14 @@ class groupRssAction extends Rss10Action
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$nickname) {
|
if (!$nickname) {
|
||||||
$this->clientError(_('No nickname'), 404);
|
$this->clientError(_('No nickname.'), 404);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->group = User_group::staticGet('nickname', $nickname);
|
$this->group = User_group::staticGet('nickname', $nickname);
|
||||||
|
|
||||||
if (!$this->group) {
|
if (!$this->group) {
|
||||||
$this->clientError(_('No such group'), 404);
|
$this->clientError(_('No such group.'), 404);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,10 +75,16 @@ class LoginAction extends Action
|
|||||||
function handle($args)
|
function handle($args)
|
||||||
{
|
{
|
||||||
parent::handle($args);
|
parent::handle($args);
|
||||||
|
|
||||||
|
$disabled = common_config('logincommand','disabled');
|
||||||
|
$disabled = isset($disabled) && $disabled;
|
||||||
|
|
||||||
if (common_is_real_login()) {
|
if (common_is_real_login()) {
|
||||||
$this->clientError(_('Already logged in.'));
|
$this->clientError(_('Already logged in.'));
|
||||||
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
$this->checkLogin();
|
$this->checkLogin();
|
||||||
|
} else if (!$disabled && isset($args['user_id']) && isset($args['token'])){
|
||||||
|
$this->checkLogin($args['user_id'],$args['token']);
|
||||||
} else {
|
} else {
|
||||||
common_ensure_session();
|
common_ensure_session();
|
||||||
$this->showForm();
|
$this->showForm();
|
||||||
@ -95,7 +101,7 @@ class LoginAction extends Action
|
|||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function checkLogin()
|
function checkLogin($user_id=null, $token=null)
|
||||||
{
|
{
|
||||||
if(isset($token) && isset($user_id)){
|
if(isset($token) && isset($user_id)){
|
||||||
//Token based login (from the LoginCommand)
|
//Token based login (from the LoginCommand)
|
||||||
@ -137,11 +143,6 @@ class LoginAction extends Action
|
|||||||
$user = common_check_user($nickname, $password);
|
$user = common_check_user($nickname, $password);
|
||||||
}
|
}
|
||||||
|
|
||||||
$nickname = common_canonical_nickname($this->trimmed('nickname'));
|
|
||||||
$password = $this->arg('password');
|
|
||||||
|
|
||||||
$user = common_check_user($nickname, $password);
|
|
||||||
|
|
||||||
if (!$user) {
|
if (!$user) {
|
||||||
$this->showForm(_('Incorrect username or password.'));
|
$this->showForm(_('Incorrect username or password.'));
|
||||||
return;
|
return;
|
||||||
|
@ -173,7 +173,7 @@ class NewmessageAction extends Action
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->notify($user, $this->other, $message);
|
$message->notify();
|
||||||
|
|
||||||
if ($this->boolean('ajax')) {
|
if ($this->boolean('ajax')) {
|
||||||
$this->startHTML('text/xml;charset=utf-8');
|
$this->startHTML('text/xml;charset=utf-8');
|
||||||
@ -247,12 +247,6 @@ class NewmessageAction extends Action
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function notify($from, $to, $message)
|
|
||||||
{
|
|
||||||
mail_notify_message($message, $from, $to);
|
|
||||||
// XXX: Jabber, SMS notifications... probably queued
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do nothing (override)
|
// Do nothing (override)
|
||||||
|
|
||||||
function showNoticeForm()
|
function showNoticeForm()
|
||||||
|
@ -187,10 +187,12 @@ class NewnoticeAction extends Action
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$notice = Notice::saveNew($user->id, $content_shortened, 'web', 1,
|
$notice = Notice::saveNew($user->id, $content_shortened, 'web',
|
||||||
($replyto == 'false') ? null : $replyto,
|
array('reply_to' => ($replyto == 'false') ? null : $replyto,
|
||||||
null, null,
|
'lat' => $lat,
|
||||||
$lat, $lon, $location_id, $location_ns);
|
'lon' => $lon,
|
||||||
|
'location_id' => $location_id,
|
||||||
|
'location_ns' => $location_ns));
|
||||||
|
|
||||||
if (isset($upload)) {
|
if (isset($upload)) {
|
||||||
$upload->attachToNotice($notice);
|
$upload->attachToNotice($notice);
|
||||||
|
126
actions/repeat.php
Normal file
126
actions/repeat.php
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repeat action.
|
||||||
|
*
|
||||||
|
* PHP version 5
|
||||||
|
*
|
||||||
|
* @category Action
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||||
|
* @link http://status.net/
|
||||||
|
*
|
||||||
|
* StatusNet - the distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2008, 2009, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repeat action
|
||||||
|
*
|
||||||
|
* @category Action
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
class RepeatAction extends Action
|
||||||
|
{
|
||||||
|
var $user = null;
|
||||||
|
var $notice = null;
|
||||||
|
|
||||||
|
function prepare($args)
|
||||||
|
{
|
||||||
|
parent::prepare($args);
|
||||||
|
|
||||||
|
$this->user = common_current_user();
|
||||||
|
|
||||||
|
if (empty($this->user)) {
|
||||||
|
$this->clientError(_("Only logged-in users can repeat notices."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $this->trimmed('notice');
|
||||||
|
|
||||||
|
if (empty($id)) {
|
||||||
|
$this->clientError(_("No notice specified."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->notice = Notice::staticGet('id', $id);
|
||||||
|
|
||||||
|
if (empty($this->notice)) {
|
||||||
|
$this->clientError(_("No notice specified."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->user->id == $this->notice->profile_id) {
|
||||||
|
$this->clientError(_("You can't repeat your own notice."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = $this->trimmed('token-'.$id);
|
||||||
|
|
||||||
|
if (empty($token) || $token != common_session_token()) {
|
||||||
|
$this->clientError(_("There was a problem with your session token. Try again, please."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$profile = $this->user->getProfile();
|
||||||
|
|
||||||
|
if ($profile->hasRepeated($id)) {
|
||||||
|
$this->clientError(_("You already repeated that notice."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class handler.
|
||||||
|
*
|
||||||
|
* @param array $args query arguments
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function handle($args)
|
||||||
|
{
|
||||||
|
$repeat = $this->notice->repeat($this->user->id, 'web');
|
||||||
|
|
||||||
|
common_broadcast_notice($repeat);
|
||||||
|
|
||||||
|
if ($this->boolean('ajax')) {
|
||||||
|
$this->startHTML('text/xml;charset=utf-8');
|
||||||
|
$this->elementStart('head');
|
||||||
|
$this->element('title', null, _('Repeated'));
|
||||||
|
$this->elementEnd('head');
|
||||||
|
$this->elementStart('body');
|
||||||
|
$this->element('p', array('id' => 'repeat_response',
|
||||||
|
'class' => 'repeated'),
|
||||||
|
_('Repeated!'));
|
||||||
|
$this->elementEnd('body');
|
||||||
|
$this->elementEnd('html');
|
||||||
|
} else {
|
||||||
|
// FIXME!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -269,4 +269,50 @@ class ProfileNoticeListItem extends NoticeListItem
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* show a link to the author of repeat
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function showRepeat()
|
||||||
|
{
|
||||||
|
if (!empty($this->repeat)) {
|
||||||
|
|
||||||
|
// FIXME: this code is almost identical to default; need to refactor
|
||||||
|
|
||||||
|
$attrs = array('href' => $this->profile->profileurl,
|
||||||
|
'class' => 'url');
|
||||||
|
|
||||||
|
if (!empty($this->profile->fullname)) {
|
||||||
|
$attrs['title'] = $this->profile->fullname . ' (' . $this->profile->nickname . ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->out->elementStart('span', 'repeat');
|
||||||
|
|
||||||
|
$this->out->elementStart('a', $attrs);
|
||||||
|
|
||||||
|
$avatar = $this->profile->getAvatar(AVATAR_MINI_SIZE);
|
||||||
|
|
||||||
|
$this->out->element('img', array('src' => ($avatar) ?
|
||||||
|
$avatar->displayUrl() :
|
||||||
|
Avatar::defaultImage(AVATAR_MINI_SIZE),
|
||||||
|
'class' => 'avatar photo',
|
||||||
|
'width' => AVATAR_MINI_SIZE,
|
||||||
|
'height' => AVATAR_MINI_SIZE,
|
||||||
|
'alt' =>
|
||||||
|
($this->profile->fullname) ?
|
||||||
|
$this->profile->fullname :
|
||||||
|
$this->profile->nickname));
|
||||||
|
|
||||||
|
$this->out->elementEnd('a');
|
||||||
|
|
||||||
|
$text_link = XMLStringer::estring('a', $attrs, $this->profile->nickname);
|
||||||
|
|
||||||
|
$this->out->raw(sprintf(_('Repeat of %s'), $text_link));
|
||||||
|
|
||||||
|
$this->out->elementEnd('span');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ class SubscribersAction extends GalleryAction
|
|||||||
|
|
||||||
function showPageNotice()
|
function showPageNotice()
|
||||||
{
|
{
|
||||||
$user =& common_current_user();
|
$user = common_current_user();
|
||||||
if ($user && ($user->id == $this->profile->id)) {
|
if ($user && ($user->id == $this->profile->id)) {
|
||||||
$this->element('p', null,
|
$this->element('p', null,
|
||||||
_('These are the people who listen to '.
|
_('These are the people who listen to '.
|
||||||
|
@ -59,7 +59,7 @@ class SubscriptionsAction extends GalleryAction
|
|||||||
|
|
||||||
function showPageNotice()
|
function showPageNotice()
|
||||||
{
|
{
|
||||||
$user =& common_current_user();
|
$user = common_current_user();
|
||||||
if ($user && ($user->id == $this->profile->id)) {
|
if ($user && ($user->id == $this->profile->id)) {
|
||||||
$this->element('p', null,
|
$this->element('p', null,
|
||||||
_('These are the people whose notices '.
|
_('These are the people whose notices '.
|
||||||
|
@ -30,13 +30,13 @@ class TagotherAction extends Action
|
|||||||
{
|
{
|
||||||
parent::prepare($args);
|
parent::prepare($args);
|
||||||
if (!common_logged_in()) {
|
if (!common_logged_in()) {
|
||||||
$this->clientError(_('Not logged in'), 403);
|
$this->clientError(_('Not logged in.'), 403);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = $this->trimmed('id');
|
$id = $this->trimmed('id');
|
||||||
if (!$id) {
|
if (!$id) {
|
||||||
$this->clientError(_('No id argument.'));
|
$this->clientError(_('No ID argument.'));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ class TwitapisearchatomAction extends ApiAction
|
|||||||
* @see Action::__construct
|
* @see Action::__construct
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function __construct($output='php://output', $indent=true)
|
function __construct($output='php://output', $indent=null)
|
||||||
{
|
{
|
||||||
parent::__construct($output, $indent);
|
parent::__construct($output, $indent);
|
||||||
}
|
}
|
||||||
|
@ -67,9 +67,9 @@ class UserbyidAction extends Action
|
|||||||
parent::handle($args);
|
parent::handle($args);
|
||||||
$id = $this->trimmed('id');
|
$id = $this->trimmed('id');
|
||||||
if (!$id) {
|
if (!$id) {
|
||||||
$this->clientError(_('No id.'));
|
$this->clientError(_('No ID.'));
|
||||||
}
|
}
|
||||||
$user =& User::staticGet($id);
|
$user = User::staticGet($id);
|
||||||
if (!$user) {
|
if (!$user) {
|
||||||
$this->clientError(_('No such user.'));
|
$this->clientError(_('No such user.'));
|
||||||
}
|
}
|
||||||
@ -88,4 +88,3 @@ class UserbyidAction extends Action
|
|||||||
common_redirect($url, 303);
|
common_redirect($url, 303);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ class Design extends Memcached_DataObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (0 != mb_strlen($css)) {
|
if (0 != mb_strlen($css)) {
|
||||||
$out->element('style', array('type' => 'text/css'), $css);
|
$out->style($css);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
55
classes/Login_token.php
Normal file
55
classes/Login_token.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Table Definition for login_token
|
||||||
|
*
|
||||||
|
* StatusNet - the distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2009, 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') && !defined('LACONICA')) { exit(1); }
|
||||||
|
|
||||||
|
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
|
||||||
|
|
||||||
|
class Login_token extends Memcached_DataObject
|
||||||
|
{
|
||||||
|
###START_AUTOCODE
|
||||||
|
/* the code below is auto generated do not remove the above tag */
|
||||||
|
|
||||||
|
public $__table = 'login_token'; // table name
|
||||||
|
public $user_id; // int(4) primary_key not_null
|
||||||
|
public $token; // char(32) not_null
|
||||||
|
public $created; // datetime() not_null
|
||||||
|
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||||
|
|
||||||
|
/* Static get */
|
||||||
|
function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('Login_token',$k,$v); }
|
||||||
|
|
||||||
|
/* the code above is auto generated do not remove the tag below */
|
||||||
|
###END_AUTOCODE
|
||||||
|
|
||||||
|
/*
|
||||||
|
DB_DataObject calculates the sequence key(s) by taking the first key returned by the keys() function.
|
||||||
|
In this case, the keys() function returns user_id as the first key. user_id is not a sequence, but
|
||||||
|
DB_DataObject's sequenceKey() will incorrectly think it is. Then, since the sequenceKey() is a numeric
|
||||||
|
type, but is not set to autoincrement in the database, DB_DataObject will create a _seq table and
|
||||||
|
manage the sequence itself. This is not the correct behavior for the user_id in this class.
|
||||||
|
So we override that incorrect behavior, and simply say there is no sequence key.
|
||||||
|
*/
|
||||||
|
function sequenceKey()
|
||||||
|
{
|
||||||
|
return array(false,false);
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,20 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
|
|||||||
|
|
||||||
class Memcached_DataObject extends DB_DataObject
|
class Memcached_DataObject extends DB_DataObject
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Destructor to free global memory resources associated with
|
||||||
|
* this data object when it's unset or goes out of scope.
|
||||||
|
* DB_DataObject doesn't do this yet by itself.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function __destruct()
|
||||||
|
{
|
||||||
|
$this->free();
|
||||||
|
if (method_exists('DB_DataObject', '__destruct')) {
|
||||||
|
parent::__destruct();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function &staticGet($cls, $k, $v=null)
|
function &staticGet($cls, $k, $v=null)
|
||||||
{
|
{
|
||||||
if (is_null($v)) {
|
if (is_null($v)) {
|
||||||
|
@ -89,4 +89,12 @@ class Message extends Memcached_DataObject
|
|||||||
$contentlimit = self::maxContent();
|
$contentlimit = self::maxContent();
|
||||||
return ($contentlimit > 0 && !empty($content) && (mb_strlen($content) > $contentlimit));
|
return ($contentlimit > 0 && !empty($content) && (mb_strlen($content) > $contentlimit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function notify()
|
||||||
|
{
|
||||||
|
$from = User::staticGet('id', $this->from_profile);
|
||||||
|
$to = User::staticGet('id', $this->to_profile);
|
||||||
|
|
||||||
|
mail_notify_message($this, $from, $to);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,13 +55,13 @@ class Notice extends Memcached_DataObject
|
|||||||
|
|
||||||
public $__table = 'notice'; // table name
|
public $__table = 'notice'; // table name
|
||||||
public $id; // int(4) primary_key not_null
|
public $id; // int(4) primary_key not_null
|
||||||
public $profile_id; // int(4) not_null
|
public $profile_id; // int(4) multiple_key not_null
|
||||||
public $uri; // varchar(255) unique_key
|
public $uri; // varchar(255) unique_key
|
||||||
public $content; // text()
|
public $content; // text
|
||||||
public $rendered; // text()
|
public $rendered; // text
|
||||||
public $url; // varchar(255)
|
public $url; // varchar(255)
|
||||||
public $created; // datetime() not_null
|
public $created; // datetime multiple_key not_null default_0000-00-00%2000%3A00%3A00
|
||||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
|
||||||
public $reply_to; // int(4)
|
public $reply_to; // int(4)
|
||||||
public $is_local; // tinyint(1)
|
public $is_local; // tinyint(1)
|
||||||
public $source; // varchar(32)
|
public $source; // varchar(32)
|
||||||
@ -70,9 +70,11 @@ class Notice extends Memcached_DataObject
|
|||||||
public $lon; // decimal(10,7)
|
public $lon; // decimal(10,7)
|
||||||
public $location_id; // int(4)
|
public $location_id; // int(4)
|
||||||
public $location_ns; // int(4)
|
public $location_ns; // int(4)
|
||||||
|
public $repeat_of; // int(4)
|
||||||
|
|
||||||
/* Static get */
|
/* Static get */
|
||||||
function staticGet($k,$v=NULL) {
|
function staticGet($k,$v=NULL)
|
||||||
|
{
|
||||||
return Memcached_DataObject::staticGet('Notice',$k,$v);
|
return Memcached_DataObject::staticGet('Notice',$k,$v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +115,12 @@ class Notice extends Memcached_DataObject
|
|||||||
|
|
||||||
//Null any notices that are replies to this notice
|
//Null any notices that are replies to this notice
|
||||||
$this->query(sprintf("UPDATE notice set reply_to = null WHERE reply_to = %d", $this->id));
|
$this->query(sprintf("UPDATE notice set reply_to = null WHERE reply_to = %d", $this->id));
|
||||||
|
|
||||||
|
//Null any notices that are repeats of this notice
|
||||||
|
//XXX: probably need to uncache these, too
|
||||||
|
|
||||||
|
$this->query(sprintf("UPDATE notice set repeat_of = null WHERE repeat_of = %d", $this->id));
|
||||||
|
|
||||||
$related = array('Reply',
|
$related = array('Reply',
|
||||||
'Fave',
|
'Fave',
|
||||||
'Notice_tag',
|
'Notice_tag',
|
||||||
@ -167,9 +175,48 @@ class Notice extends Memcached_DataObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static function saveNew($profile_id, $content, $source=null,
|
/**
|
||||||
$is_local=Notice::LOCAL_PUBLIC, $reply_to=null, $uri=null, $created=null,
|
* Save a new notice and push it out to subscribers' inboxes.
|
||||||
$lat=null, $lon=null, $location_id=null, $location_ns=null) {
|
* Poster's permissions are checked before sending.
|
||||||
|
*
|
||||||
|
* @param int $profile_id Profile ID of the poster
|
||||||
|
* @param string $content source message text; links may be shortened
|
||||||
|
* per current user's preference
|
||||||
|
* @param string $source source key ('web', 'api', etc)
|
||||||
|
* @param array $options Associative array of optional properties:
|
||||||
|
* string 'created' timestamp of notice; defaults to now
|
||||||
|
* int 'is_local' source/gateway ID, one of:
|
||||||
|
* Notice::LOCAL_PUBLIC - Local, ok to appear in public timeline
|
||||||
|
* Notice::REMOTE_OMB - Sent from a remote OMB service;
|
||||||
|
* hide from public timeline but show in
|
||||||
|
* local "and friends" timelines
|
||||||
|
* Notice::LOCAL_NONPUBLIC - Local, but hide from public timeline
|
||||||
|
* Notice::GATEWAY - From another non-OMB service;
|
||||||
|
* will not appear in public views
|
||||||
|
* float 'lat' decimal latitude for geolocation
|
||||||
|
* float 'lon' decimal longitude for geolocation
|
||||||
|
* int 'location_id' geoname identifier
|
||||||
|
* int 'location_ns' geoname namespace to interpret location_id
|
||||||
|
* int 'reply_to'; notice ID this is a reply to
|
||||||
|
* int 'repeat_of'; notice ID this is a repeat of
|
||||||
|
* string 'uri' permalink to notice; defaults to local notice URL
|
||||||
|
*
|
||||||
|
* @return Notice
|
||||||
|
* @throws ClientException
|
||||||
|
*/
|
||||||
|
static function saveNew($profile_id, $content, $source, $options=null) {
|
||||||
|
$defaults = array('uri' => null,
|
||||||
|
'reply_to' => null,
|
||||||
|
'repeat_of' => null);
|
||||||
|
|
||||||
|
if (!empty($options)) {
|
||||||
|
$options = $options + $defaults;
|
||||||
|
extract($options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($is_local)) {
|
||||||
|
$is_local = Notice::LOCAL_PUBLIC;
|
||||||
|
}
|
||||||
|
|
||||||
$profile = Profile::staticGet($profile_id);
|
$profile = Profile::staticGet($profile_id);
|
||||||
|
|
||||||
@ -225,7 +272,14 @@ class Notice extends Memcached_DataObject
|
|||||||
$notice->source = $source;
|
$notice->source = $source;
|
||||||
$notice->uri = $uri;
|
$notice->uri = $uri;
|
||||||
|
|
||||||
|
// Handle repeat case
|
||||||
|
|
||||||
|
if (isset($repeat_of)) {
|
||||||
|
$notice->repeat_of = $repeat_of;
|
||||||
|
$notice->reply_to = $repeat_of;
|
||||||
|
} else {
|
||||||
$notice->reply_to = self::getReplyTo($reply_to, $profile_id, $source, $final);
|
$notice->reply_to = self::getReplyTo($reply_to, $profile_id, $source, $final);
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($notice->reply_to)) {
|
if (!empty($notice->reply_to)) {
|
||||||
$reply = Notice::staticGet('id', $notice->reply_to);
|
$reply = Notice::staticGet('id', $notice->reply_to);
|
||||||
@ -423,10 +477,60 @@ class Notice extends Memcached_DataObject
|
|||||||
$this->blowTagCache($blowLast);
|
$this->blowTagCache($blowLast);
|
||||||
$this->blowGroupCache($blowLast);
|
$this->blowGroupCache($blowLast);
|
||||||
$this->blowConversationCache($blowLast);
|
$this->blowConversationCache($blowLast);
|
||||||
|
$this->blowRepeatCache();
|
||||||
$profile = Profile::staticGet($this->profile_id);
|
$profile = Profile::staticGet($this->profile_id);
|
||||||
$profile->blowNoticeCount();
|
$profile->blowNoticeCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function blowRepeatCache()
|
||||||
|
{
|
||||||
|
if (!empty($this->repeat_of)) {
|
||||||
|
$cache = common_memcache();
|
||||||
|
if (!empty($cache)) {
|
||||||
|
// XXX: only blow if <100 in cache
|
||||||
|
$ck = common_cache_key('notice:repeats:'.$this->repeat_of);
|
||||||
|
$result = $cache->delete($ck);
|
||||||
|
|
||||||
|
$user = User::staticGet('id', $this->profile_id);
|
||||||
|
|
||||||
|
if (!empty($user)) {
|
||||||
|
$uk = common_cache_key('user:repeated_by_me:'.$user->id);
|
||||||
|
$cache->delete($uk);
|
||||||
|
$user->free();
|
||||||
|
unset($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
$original = Notice::staticGet('id', $this->repeat_of);
|
||||||
|
|
||||||
|
if (!empty($original)) {
|
||||||
|
$originalUser = User::staticGet('id', $original->profile_id);
|
||||||
|
if (!empty($originalUser)) {
|
||||||
|
$ouk = common_cache_key('user:repeats_of_me:'.$originalUser->id);
|
||||||
|
$cache->delete($ouk);
|
||||||
|
$originalUser->free();
|
||||||
|
unset($originalUser);
|
||||||
|
}
|
||||||
|
$original->free();
|
||||||
|
unset($original);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ni = new Notice_inbox();
|
||||||
|
|
||||||
|
$ni->notice_id = $this->id;
|
||||||
|
|
||||||
|
if ($ni->find()) {
|
||||||
|
while ($ni->fetch()) {
|
||||||
|
$tmk = common_cache_key('user:repeated_to_me:'.$ni->user_id);
|
||||||
|
$cache->delete($tmk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ni->free();
|
||||||
|
unset($ni);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function blowConversationCache($blowLast=false)
|
function blowConversationCache($blowLast=false)
|
||||||
{
|
{
|
||||||
$cache = common_memcache();
|
$cache = common_memcache();
|
||||||
@ -456,8 +560,18 @@ class Notice extends Memcached_DataObject
|
|||||||
if ($member->find()) {
|
if ($member->find()) {
|
||||||
while ($member->fetch()) {
|
while ($member->fetch()) {
|
||||||
$cache->delete(common_cache_key('notice_inbox:by_user:' . $member->profile_id));
|
$cache->delete(common_cache_key('notice_inbox:by_user:' . $member->profile_id));
|
||||||
|
$cache->delete(common_cache_key('notice_inbox:by_user_own:' . $member->profile_id));
|
||||||
|
if (empty($this->repeat_of)) {
|
||||||
|
$cache->delete(common_cache_key('user:friends_timeline:' . $member->profile_id));
|
||||||
|
$cache->delete(common_cache_key('user:friends_timeline_own:' . $member->profile_id));
|
||||||
|
}
|
||||||
if ($blowLast) {
|
if ($blowLast) {
|
||||||
$cache->delete(common_cache_key('notice_inbox:by_user:' . $member->profile_id . ';last'));
|
$cache->delete(common_cache_key('notice_inbox:by_user:' . $member->profile_id . ';last'));
|
||||||
|
$cache->delete(common_cache_key('notice_inbox:by_user_own:' . $member->profile_id . ';last'));
|
||||||
|
if (empty($this->repeat_of)) {
|
||||||
|
$cache->delete(common_cache_key('user:friends_timeline:' . $member->profile_id . ';last'));
|
||||||
|
$cache->delete(common_cache_key('user:friends_timeline_own:' . $member->profile_id . ';last'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -505,9 +619,17 @@ class Notice extends Memcached_DataObject
|
|||||||
while ($user->fetch()) {
|
while ($user->fetch()) {
|
||||||
$cache->delete(common_cache_key('notice_inbox:by_user:'.$user->id));
|
$cache->delete(common_cache_key('notice_inbox:by_user:'.$user->id));
|
||||||
$cache->delete(common_cache_key('notice_inbox:by_user_own:'.$user->id));
|
$cache->delete(common_cache_key('notice_inbox:by_user_own:'.$user->id));
|
||||||
|
if (empty($this->repeat_of)) {
|
||||||
|
$cache->delete(common_cache_key('user:friends_timeline:'.$user->id));
|
||||||
|
$cache->delete(common_cache_key('user:friends_timeline_own:'.$user->id));
|
||||||
|
}
|
||||||
if ($blowLast) {
|
if ($blowLast) {
|
||||||
$cache->delete(common_cache_key('notice_inbox:by_user:'.$user->id.';last'));
|
$cache->delete(common_cache_key('notice_inbox:by_user:'.$user->id.';last'));
|
||||||
$cache->delete(common_cache_key('notice_inbox:by_user_own:'.$user->id.';last'));
|
$cache->delete(common_cache_key('notice_inbox:by_user_own:'.$user->id.';last'));
|
||||||
|
if (empty($this->repeat_of)) {
|
||||||
|
$cache->delete(common_cache_key('user:friends_timeline:'.$user->id.';last'));
|
||||||
|
$cache->delete(common_cache_key('user:friends_timeline_own:'.$user->id.';last'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$user->free();
|
$user->free();
|
||||||
@ -581,193 +703,6 @@ class Notice extends Memcached_DataObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# XXX: too many args; we need to move to named params or even a separate
|
|
||||||
# class for notice streams
|
|
||||||
|
|
||||||
static function getStream($qry, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0, $order=null, $since=null) {
|
|
||||||
|
|
||||||
if (common_config('memcached', 'enabled')) {
|
|
||||||
|
|
||||||
# Skip the cache if this is a since, since_id or max_id qry
|
|
||||||
if ($since_id > 0 || $max_id > 0 || $since) {
|
|
||||||
return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since);
|
|
||||||
} else {
|
|
||||||
return Notice::getCachedStream($qry, $cachekey, $offset, $limit, $order);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since);
|
|
||||||
}
|
|
||||||
|
|
||||||
static function getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since) {
|
|
||||||
|
|
||||||
$needAnd = false;
|
|
||||||
$needWhere = true;
|
|
||||||
|
|
||||||
if (preg_match('/\bWHERE\b/i', $qry)) {
|
|
||||||
$needWhere = false;
|
|
||||||
$needAnd = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($since_id > 0) {
|
|
||||||
|
|
||||||
if ($needWhere) {
|
|
||||||
$qry .= ' WHERE ';
|
|
||||||
$needWhere = false;
|
|
||||||
} else {
|
|
||||||
$qry .= ' AND ';
|
|
||||||
}
|
|
||||||
|
|
||||||
$qry .= ' notice.id > ' . $since_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($max_id > 0) {
|
|
||||||
|
|
||||||
if ($needWhere) {
|
|
||||||
$qry .= ' WHERE ';
|
|
||||||
$needWhere = false;
|
|
||||||
} else {
|
|
||||||
$qry .= ' AND ';
|
|
||||||
}
|
|
||||||
|
|
||||||
$qry .= ' notice.id <= ' . $max_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($since) {
|
|
||||||
|
|
||||||
if ($needWhere) {
|
|
||||||
$qry .= ' WHERE ';
|
|
||||||
$needWhere = false;
|
|
||||||
} else {
|
|
||||||
$qry .= ' AND ';
|
|
||||||
}
|
|
||||||
|
|
||||||
$qry .= ' notice.created > \'' . date('Y-m-d H:i:s', $since) . '\'';
|
|
||||||
}
|
|
||||||
|
|
||||||
# Allow ORDER override
|
|
||||||
|
|
||||||
if ($order) {
|
|
||||||
$qry .= $order;
|
|
||||||
} else {
|
|
||||||
$qry .= ' ORDER BY notice.created DESC, notice.id DESC ';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (common_config('db','type') == 'pgsql') {
|
|
||||||
$qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
|
|
||||||
} else {
|
|
||||||
$qry .= ' LIMIT ' . $offset . ', ' . $limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$notice = new Notice();
|
|
||||||
|
|
||||||
$notice->query($qry);
|
|
||||||
|
|
||||||
return $notice;
|
|
||||||
}
|
|
||||||
|
|
||||||
# XXX: this is pretty long and should probably be broken up into
|
|
||||||
# some helper functions
|
|
||||||
|
|
||||||
static function getCachedStream($qry, $cachekey, $offset, $limit, $order) {
|
|
||||||
|
|
||||||
# If outside our cache window, just go to the DB
|
|
||||||
|
|
||||||
if ($offset + $limit > NOTICE_CACHE_WINDOW) {
|
|
||||||
return Notice::getStreamDirect($qry, $offset, $limit, null, null, $order, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get the cache; if we can't, just go to the DB
|
|
||||||
|
|
||||||
$cache = common_memcache();
|
|
||||||
|
|
||||||
if (empty($cache)) {
|
|
||||||
return Notice::getStreamDirect($qry, $offset, $limit, null, null, $order, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get the notices out of the cache
|
|
||||||
|
|
||||||
$notices = $cache->get(common_cache_key($cachekey));
|
|
||||||
|
|
||||||
# On a cache hit, return a DB-object-like wrapper
|
|
||||||
|
|
||||||
if ($notices !== false) {
|
|
||||||
$wrapper = new ArrayWrapper(array_slice($notices, $offset, $limit));
|
|
||||||
return $wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
# If the cache was invalidated because of new data being
|
|
||||||
# added, we can try and just get the new stuff. We keep an additional
|
|
||||||
# copy of the data at the key + ';last'
|
|
||||||
|
|
||||||
# No cache hit. Try to get the *last* cached version
|
|
||||||
|
|
||||||
$last_notices = $cache->get(common_cache_key($cachekey) . ';last');
|
|
||||||
|
|
||||||
if ($last_notices) {
|
|
||||||
|
|
||||||
# Reverse-chron order, so last ID is last.
|
|
||||||
|
|
||||||
$last_id = $last_notices[0]->id;
|
|
||||||
|
|
||||||
# XXX: this assumes monotonically increasing IDs; a fair
|
|
||||||
# bet with our DB.
|
|
||||||
|
|
||||||
$new_notice = Notice::getStreamDirect($qry, 0, NOTICE_CACHE_WINDOW,
|
|
||||||
$last_id, null, $order, null);
|
|
||||||
|
|
||||||
if ($new_notice) {
|
|
||||||
$new_notices = array();
|
|
||||||
while ($new_notice->fetch()) {
|
|
||||||
$new_notices[] = clone($new_notice);
|
|
||||||
}
|
|
||||||
$new_notice->free();
|
|
||||||
$notices = array_slice(array_merge($new_notices, $last_notices),
|
|
||||||
0, NOTICE_CACHE_WINDOW);
|
|
||||||
|
|
||||||
# Store the array in the cache for next time
|
|
||||||
|
|
||||||
$result = $cache->set(common_cache_key($cachekey), $notices);
|
|
||||||
$result = $cache->set(common_cache_key($cachekey) . ';last', $notices);
|
|
||||||
|
|
||||||
# return a wrapper of the array for use now
|
|
||||||
|
|
||||||
return new ArrayWrapper(array_slice($notices, $offset, $limit));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Otherwise, get the full cache window out of the DB
|
|
||||||
|
|
||||||
$notice = Notice::getStreamDirect($qry, 0, NOTICE_CACHE_WINDOW, null, null, $order, null);
|
|
||||||
|
|
||||||
# If there are no hits, just return the value
|
|
||||||
|
|
||||||
if (empty($notice)) {
|
|
||||||
return $notice;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Pack results into an array
|
|
||||||
|
|
||||||
$notices = array();
|
|
||||||
|
|
||||||
while ($notice->fetch()) {
|
|
||||||
$notices[] = clone($notice);
|
|
||||||
}
|
|
||||||
|
|
||||||
$notice->free();
|
|
||||||
|
|
||||||
# Store the array in the cache for next time
|
|
||||||
|
|
||||||
$result = $cache->set(common_cache_key($cachekey), $notices);
|
|
||||||
$result = $cache->set(common_cache_key($cachekey) . ';last', $notices);
|
|
||||||
|
|
||||||
# return a wrapper of the array for use now
|
|
||||||
|
|
||||||
$wrapper = new ArrayWrapper(array_slice($notices, $offset, $limit));
|
|
||||||
|
|
||||||
return $wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStreamByIds($ids)
|
function getStreamByIds($ids)
|
||||||
{
|
{
|
||||||
$cache = common_memcache();
|
$cache = common_memcache();
|
||||||
@ -788,10 +723,24 @@ class Notice extends Memcached_DataObject
|
|||||||
return $notice;
|
return $notice;
|
||||||
}
|
}
|
||||||
$notice->whereAdd('id in (' . implode(', ', $ids) . ')');
|
$notice->whereAdd('id in (' . implode(', ', $ids) . ')');
|
||||||
$notice->orderBy('id DESC');
|
|
||||||
|
|
||||||
$notice->find();
|
$notice->find();
|
||||||
return $notice;
|
|
||||||
|
$temp = array();
|
||||||
|
|
||||||
|
while ($notice->fetch()) {
|
||||||
|
$temp[$notice->id] = clone($notice);
|
||||||
|
}
|
||||||
|
|
||||||
|
$wrapped = array();
|
||||||
|
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
if (array_key_exists($id, $temp)) {
|
||||||
|
$wrapped[] = $temp[$id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ArrayWrapper($wrapped);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -948,39 +897,7 @@ class Notice extends Memcached_DataObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$cnt = 0;
|
Notice_inbox::bulkInsert($this->id, $this->created, $ni);
|
||||||
|
|
||||||
$qryhdr = 'INSERT INTO notice_inbox (user_id, notice_id, source, created) VALUES ';
|
|
||||||
$qry = $qryhdr;
|
|
||||||
|
|
||||||
foreach ($ni as $id => $source) {
|
|
||||||
if ($cnt > 0) {
|
|
||||||
$qry .= ', ';
|
|
||||||
}
|
|
||||||
$qry .= '('.$id.', '.$this->id.', '.$source.", '".$this->created. "') ";
|
|
||||||
$cnt++;
|
|
||||||
if (rand() % NOTICE_INBOX_SOFT_LIMIT == 0) {
|
|
||||||
// FIXME: Causes lag in replicated servers
|
|
||||||
// Notice_inbox::gc($id);
|
|
||||||
}
|
|
||||||
if ($cnt >= MAX_BOXCARS) {
|
|
||||||
$inbox = new Notice_inbox();
|
|
||||||
$result = $inbox->query($qry);
|
|
||||||
if (PEAR::isError($result)) {
|
|
||||||
common_log_db_error($inbox, $qry);
|
|
||||||
}
|
|
||||||
$qry = $qryhdr;
|
|
||||||
$cnt = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($cnt > 0) {
|
|
||||||
$inbox = new Notice_inbox();
|
|
||||||
$result = $inbox->query($qry);
|
|
||||||
if (PEAR::isError($result)) {
|
|
||||||
common_log_db_error($inbox, $qry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1079,6 +996,9 @@ class Notice extends Memcached_DataObject
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array of integer profile IDs
|
||||||
|
*/
|
||||||
function saveReplies()
|
function saveReplies()
|
||||||
{
|
{
|
||||||
// Alternative reply format
|
// Alternative reply format
|
||||||
@ -1157,8 +1077,8 @@ class Notice extends Memcached_DataObject
|
|||||||
|
|
||||||
$recipientIds = array_keys($replied);
|
$recipientIds = array_keys($replied);
|
||||||
|
|
||||||
foreach ($recipientIds as $recipient) {
|
foreach ($recipientIds as $recipientId) {
|
||||||
$user = User::staticGet('id', $recipient);
|
$user = User::staticGet('id', $recipientId);
|
||||||
if ($user) {
|
if ($user) {
|
||||||
mail_notify_attn($user, $this);
|
mail_notify_attn($user, $this);
|
||||||
}
|
}
|
||||||
@ -1441,4 +1361,72 @@ class Notice extends Memcached_DataObject
|
|||||||
|
|
||||||
return $location;
|
return $location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function repeat($repeater_id, $source)
|
||||||
|
{
|
||||||
|
$author = Profile::staticGet('id', $this->profile_id);
|
||||||
|
|
||||||
|
// FIXME: truncate on long repeats...?
|
||||||
|
|
||||||
|
$content = sprintf(_('RT @%1$s %2$s'),
|
||||||
|
$author->nickname,
|
||||||
|
$this->content);
|
||||||
|
|
||||||
|
return self::saveNew($repeater_id, $content, $source,
|
||||||
|
array('repeat_of' => $this->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are supposed to be in chron order!
|
||||||
|
|
||||||
|
function repeatStream($limit=100)
|
||||||
|
{
|
||||||
|
$cache = common_memcache();
|
||||||
|
|
||||||
|
if (empty($cache)) {
|
||||||
|
$ids = $this->_repeatStreamDirect($limit);
|
||||||
|
} else {
|
||||||
|
$idstr = $cache->get(common_cache_key('notice:repeats:'.$this->id));
|
||||||
|
if (!empty($idstr)) {
|
||||||
|
$ids = explode(',', $idstr);
|
||||||
|
} else {
|
||||||
|
$ids = $this->_repeatStreamDirect(100);
|
||||||
|
$cache->set(common_cache_key('notice:repeats:'.$this->id), implode(',', $ids));
|
||||||
|
}
|
||||||
|
if ($limit < 100) {
|
||||||
|
// We do a max of 100, so slice down to limit
|
||||||
|
$ids = array_slice($ids, 0, $limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Notice::getStreamByIds($ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _repeatStreamDirect($limit)
|
||||||
|
{
|
||||||
|
$notice = new Notice();
|
||||||
|
|
||||||
|
$notice->selectAdd(); // clears it
|
||||||
|
$notice->selectAdd('id');
|
||||||
|
|
||||||
|
$notice->repeat_of = $this->id;
|
||||||
|
|
||||||
|
$notice->orderBy('created'); // NB: asc!
|
||||||
|
|
||||||
|
if (!is_null($offset)) {
|
||||||
|
$notice->limit($offset, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = array();
|
||||||
|
|
||||||
|
if ($notice->find()) {
|
||||||
|
while ($notice->fetch()) {
|
||||||
|
$ids[] = $notice->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$notice->free();
|
||||||
|
$notice = NULL;
|
||||||
|
|
||||||
|
return $ids;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ define('NOTICE_INBOX_SOFT_LIMIT', 1000);
|
|||||||
define('NOTICE_INBOX_SOURCE_SUB', 1);
|
define('NOTICE_INBOX_SOURCE_SUB', 1);
|
||||||
define('NOTICE_INBOX_SOURCE_GROUP', 2);
|
define('NOTICE_INBOX_SOURCE_GROUP', 2);
|
||||||
define('NOTICE_INBOX_SOURCE_REPLY', 3);
|
define('NOTICE_INBOX_SOURCE_REPLY', 3);
|
||||||
|
define('NOTICE_INBOX_SOURCE_FORWARD', 4);
|
||||||
define('NOTICE_INBOX_SOURCE_GATEWAY', -1);
|
define('NOTICE_INBOX_SOURCE_GATEWAY', -1);
|
||||||
|
|
||||||
class Notice_inbox extends Memcached_DataObject
|
class Notice_inbox extends Memcached_DataObject
|
||||||
@ -83,7 +84,7 @@ class Notice_inbox extends Memcached_DataObject
|
|||||||
$inbox->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\'');
|
$inbox->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\'');
|
||||||
}
|
}
|
||||||
|
|
||||||
$inbox->orderBy('notice_id DESC');
|
$inbox->orderBy('created DESC');
|
||||||
|
|
||||||
if (!is_null($offset)) {
|
if (!is_null($offset)) {
|
||||||
$inbox->limit($offset, $limit);
|
$inbox->limit($offset, $limit);
|
||||||
@ -141,4 +142,43 @@ class Notice_inbox extends Memcached_DataObject
|
|||||||
'WHERE user_id = ' . $user_id . ' ' .
|
'WHERE user_id = ' . $user_id . ' ' .
|
||||||
'AND notice_id in ('.implode(',', $notices).')');
|
'AND notice_id in ('.implode(',', $notices).')');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function bulkInsert($notice_id, $created, $ni)
|
||||||
|
{
|
||||||
|
$cnt = 0;
|
||||||
|
|
||||||
|
$qryhdr = 'INSERT INTO notice_inbox (user_id, notice_id, source, created) VALUES ';
|
||||||
|
$qry = $qryhdr;
|
||||||
|
|
||||||
|
foreach ($ni as $id => $source) {
|
||||||
|
if ($cnt > 0) {
|
||||||
|
$qry .= ', ';
|
||||||
|
}
|
||||||
|
$qry .= '('.$id.', '.$notice_id.', '.$source.", '".$created. "') ";
|
||||||
|
$cnt++;
|
||||||
|
if (rand() % NOTICE_INBOX_SOFT_LIMIT == 0) {
|
||||||
|
// FIXME: Causes lag in replicated servers
|
||||||
|
// Notice_inbox::gc($id);
|
||||||
|
}
|
||||||
|
if ($cnt >= MAX_BOXCARS) {
|
||||||
|
$inbox = new Notice_inbox();
|
||||||
|
$result = $inbox->query($qry);
|
||||||
|
if (PEAR::isError($result)) {
|
||||||
|
common_log_db_error($inbox, $qry);
|
||||||
|
}
|
||||||
|
$qry = $qryhdr;
|
||||||
|
$cnt = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cnt > 0) {
|
||||||
|
$inbox = new Notice_inbox();
|
||||||
|
$result = $inbox->query($qry);
|
||||||
|
if (PEAR::isError($result)) {
|
||||||
|
common_log_db_error($inbox, $qry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -716,4 +716,15 @@ class Profile extends Memcached_DataObject
|
|||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasRepeated($notice_id)
|
||||||
|
{
|
||||||
|
// XXX: not really a pkey, but should work
|
||||||
|
|
||||||
|
$notice = Memcached_DataObject::pkeyGet('Notice',
|
||||||
|
array('profile_id' => $this->id,
|
||||||
|
'repeat_of' => $notice_id));
|
||||||
|
|
||||||
|
return !empty($notice);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
277
classes/User.php
277
classes/User.php
@ -180,6 +180,27 @@ class User extends Memcached_DataObject
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new user account and profile and set up default subscriptions.
|
||||||
|
* If a new-user welcome message is configured, this will be sent.
|
||||||
|
*
|
||||||
|
* @param array $fields associative array of optional properties
|
||||||
|
* string 'bio'
|
||||||
|
* string 'email'
|
||||||
|
* bool 'email_confirmed' pass true to mark email as pre-confirmed
|
||||||
|
* string 'fullname'
|
||||||
|
* string 'homepage'
|
||||||
|
* string 'location' informal string description of geolocation
|
||||||
|
* float 'lat' decimal latitude for geolocation
|
||||||
|
* float 'lon' decimal longitude for geolocation
|
||||||
|
* int 'location_id' geoname identifier
|
||||||
|
* int 'location_ns' geoname namespace to interpret location_id
|
||||||
|
* string 'nickname' REQUIRED
|
||||||
|
* string 'password' (may be missing for eg OpenID registrations)
|
||||||
|
* string 'code' invite code
|
||||||
|
* ?string 'uri' permalink to notice; defaults to local notice URL
|
||||||
|
* @return mixed User object or false on failure
|
||||||
|
*/
|
||||||
static function register($fields) {
|
static function register($fields) {
|
||||||
|
|
||||||
// MAGICALLY put fields into current scope
|
// MAGICALLY put fields into current scope
|
||||||
@ -329,7 +350,7 @@ class User extends Memcached_DataObject
|
|||||||
|
|
||||||
$profile->query('COMMIT');
|
$profile->query('COMMIT');
|
||||||
|
|
||||||
if ($email && !$user->email) {
|
if (!empty($email) && !$user->email) {
|
||||||
mail_confirm_address($user, $confirm->code, $profile->nickname, $email);
|
mail_confirm_address($user, $confirm->code, $profile->nickname, $email);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,6 +494,77 @@ class User extends Memcached_DataObject
|
|||||||
return Notice::getStreamByIds($ids);
|
return Notice::getStreamByIds($ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function friendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
|
||||||
|
{
|
||||||
|
$ids = Notice::stream(array($this, '_friendsTimelineDirect'),
|
||||||
|
array(false),
|
||||||
|
'user:friends_timeline:'.$this->id,
|
||||||
|
$offset, $limit, $since_id, $before_id, $since);
|
||||||
|
|
||||||
|
return Notice::getStreamByIds($ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ownFriendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
|
||||||
|
{
|
||||||
|
$ids = Notice::stream(array($this, '_friendsTimelineDirect'),
|
||||||
|
array(true),
|
||||||
|
'user:friends_timeline_own:'.$this->id,
|
||||||
|
$offset, $limit, $since_id, $before_id, $since);
|
||||||
|
|
||||||
|
return Notice::getStreamByIds($ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _friendsTimelineDirect($own, $offset, $limit, $since_id, $max_id, $since)
|
||||||
|
{
|
||||||
|
$qry =
|
||||||
|
'SELECT notice.id AS id ' .
|
||||||
|
'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' .
|
||||||
|
'WHERE notice_inbox.user_id = ' . $this->id . ' ' .
|
||||||
|
'AND notice.repeat_of IS NULL ';
|
||||||
|
|
||||||
|
if (!$own) {
|
||||||
|
// XXX: autoload notice inbox for constant
|
||||||
|
$inbox = new Notice_inbox();
|
||||||
|
|
||||||
|
$qry .= 'AND notice_inbox.source != ' . NOTICE_INBOX_SOURCE_GATEWAY . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($since_id != 0) {
|
||||||
|
$qry .= 'AND notice.id > ' . $since_id . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($max_id != 0) {
|
||||||
|
$qry .= 'AND notice.id <= ' . $max_id . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($since)) {
|
||||||
|
$qry .= 'AND notice.modified > \'' . date('Y-m-d H:i:s', $since) . '\' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: we sort by fave time, not by notice time!
|
||||||
|
|
||||||
|
$qry .= 'ORDER BY notice.id DESC ';
|
||||||
|
|
||||||
|
if (!is_null($offset)) {
|
||||||
|
$qry .= "LIMIT $limit OFFSET $offset";
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = array();
|
||||||
|
|
||||||
|
$notice = new Notice();
|
||||||
|
|
||||||
|
$notice->query($qry);
|
||||||
|
|
||||||
|
while ($notice->fetch()) {
|
||||||
|
$ids[] = $notice->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$notice->free();
|
||||||
|
$notice = NULL;
|
||||||
|
|
||||||
|
return $ids;
|
||||||
|
}
|
||||||
|
|
||||||
function blowFavesCache()
|
function blowFavesCache()
|
||||||
{
|
{
|
||||||
$cache = common_memcache();
|
$cache = common_memcache();
|
||||||
@ -502,6 +594,19 @@ class User extends Memcached_DataObject
|
|||||||
{
|
{
|
||||||
// Add a new block record
|
// Add a new block record
|
||||||
|
|
||||||
|
// no blocking (and thus unsubbing from) yourself
|
||||||
|
|
||||||
|
if ($this->id == $other->id) {
|
||||||
|
common_log(LOG_WARNING,
|
||||||
|
sprintf(
|
||||||
|
"Profile ID %d (%s) tried to block his or herself.",
|
||||||
|
$profile->id,
|
||||||
|
$profile->nickname
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$block = new Profile_block();
|
$block = new Profile_block();
|
||||||
|
|
||||||
// Begin a transaction
|
// Begin a transaction
|
||||||
@ -520,16 +625,7 @@ class User extends Memcached_DataObject
|
|||||||
|
|
||||||
// Cancel their subscription, if it exists
|
// Cancel their subscription, if it exists
|
||||||
|
|
||||||
$sub = Subscription::pkeyGet(array('subscriber' => $other->id,
|
subs_unsubscribe_to($other->getUser(),$this->getProfile());
|
||||||
'subscribed' => $this->id));
|
|
||||||
|
|
||||||
if ($sub) {
|
|
||||||
$result = $sub->delete();
|
|
||||||
if (!$result) {
|
|
||||||
common_log_db_error($sub, 'DELETE', __FILE__);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$block->query('COMMIT');
|
$block->query('COMMIT');
|
||||||
|
|
||||||
@ -737,4 +833,163 @@ class User extends Memcached_DataObject
|
|||||||
$profile = $this->getProfile();
|
$profile = $this->getProfile();
|
||||||
return $profile->isSilenced();
|
return $profile->isSilenced();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function repeatedByMe($offset=0, $limit=20, $since_id=null, $max_id=null)
|
||||||
|
{
|
||||||
|
$ids = Notice::stream(array($this, '_repeatedByMeDirect'),
|
||||||
|
array(),
|
||||||
|
'user:repeated_by_me:'.$this->id,
|
||||||
|
$offset, $limit, $since_id, $max_id, null);
|
||||||
|
|
||||||
|
return Notice::getStreamByIds($ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _repeatedByMeDirect($offset, $limit, $since_id, $max_id, $since)
|
||||||
|
{
|
||||||
|
$notice = new Notice();
|
||||||
|
|
||||||
|
$notice->selectAdd(); // clears it
|
||||||
|
$notice->selectAdd('id');
|
||||||
|
|
||||||
|
$notice->profile_id = $this->id;
|
||||||
|
$notice->whereAdd('repeat_of IS NOT NULL');
|
||||||
|
|
||||||
|
$notice->orderBy('id DESC');
|
||||||
|
|
||||||
|
if (!is_null($offset)) {
|
||||||
|
$notice->limit($offset, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($since_id != 0) {
|
||||||
|
$notice->whereAdd('id > ' . $since_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($max_id != 0) {
|
||||||
|
$notice->whereAdd('id <= ' . $max_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($since)) {
|
||||||
|
$notice->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\'');
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = array();
|
||||||
|
|
||||||
|
if ($notice->find()) {
|
||||||
|
while ($notice->fetch()) {
|
||||||
|
$ids[] = $notice->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$notice->free();
|
||||||
|
$notice = NULL;
|
||||||
|
|
||||||
|
return $ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
function repeatsOfMe($offset=0, $limit=20, $since_id=null, $max_id=null)
|
||||||
|
{
|
||||||
|
$ids = Notice::stream(array($this, '_repeatsOfMeDirect'),
|
||||||
|
array(),
|
||||||
|
'user:repeats_of_me:'.$this->id,
|
||||||
|
$offset, $limit, $since_id, $max_id, null);
|
||||||
|
|
||||||
|
return Notice::getStreamByIds($ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _repeatsOfMeDirect($offset, $limit, $since_id, $max_id, $since)
|
||||||
|
{
|
||||||
|
$qry =
|
||||||
|
'SELECT DISTINCT original.id AS id ' .
|
||||||
|
'FROM notice original JOIN notice rept ON original.id = rept.repeat_of ' .
|
||||||
|
'WHERE original.profile_id = ' . $this->id . ' ';
|
||||||
|
|
||||||
|
if ($since_id != 0) {
|
||||||
|
$qry .= 'AND original.id > ' . $since_id . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($max_id != 0) {
|
||||||
|
$qry .= 'AND original.id <= ' . $max_id . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($since)) {
|
||||||
|
$qry .= 'AND original.modified > \'' . date('Y-m-d H:i:s', $since) . '\' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: we sort by fave time, not by notice time!
|
||||||
|
|
||||||
|
$qry .= 'ORDER BY original.id DESC ';
|
||||||
|
|
||||||
|
if (!is_null($offset)) {
|
||||||
|
$qry .= "LIMIT $limit OFFSET $offset";
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = array();
|
||||||
|
|
||||||
|
$notice = new Notice();
|
||||||
|
|
||||||
|
$notice->query($qry);
|
||||||
|
|
||||||
|
while ($notice->fetch()) {
|
||||||
|
$ids[] = $notice->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$notice->free();
|
||||||
|
$notice = NULL;
|
||||||
|
|
||||||
|
return $ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null)
|
||||||
|
{
|
||||||
|
$ids = Notice::stream(array($this, '_repeatedToMeDirect'),
|
||||||
|
array(),
|
||||||
|
'user:repeated_to_me:'.$this->id,
|
||||||
|
$offset, $limit, $since_id, $max_id, null);
|
||||||
|
|
||||||
|
return Notice::getStreamByIds($ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _repeatedToMeDirect($offset, $limit, $since_id, $max_id, $since)
|
||||||
|
{
|
||||||
|
$qry =
|
||||||
|
'SELECT notice.id AS id ' .
|
||||||
|
'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' .
|
||||||
|
'WHERE notice_inbox.user_id = ' . $this->id . ' ' .
|
||||||
|
'AND notice.repeat_of IS NOT NULL ';
|
||||||
|
|
||||||
|
if ($since_id != 0) {
|
||||||
|
$qry .= 'AND notice.id > ' . $since_id . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($max_id != 0) {
|
||||||
|
$qry .= 'AND notice.id <= ' . $max_id . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($since)) {
|
||||||
|
$qry .= 'AND notice.modified > \'' . date('Y-m-d H:i:s', $since) . '\' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: we sort by fave time, not by notice time!
|
||||||
|
|
||||||
|
$qry .= 'ORDER BY notice.id DESC ';
|
||||||
|
|
||||||
|
if (!is_null($offset)) {
|
||||||
|
$qry .= "LIMIT $limit OFFSET $offset";
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = array();
|
||||||
|
|
||||||
|
$notice = new Notice();
|
||||||
|
|
||||||
|
$notice->query($qry);
|
||||||
|
|
||||||
|
while ($notice->fetch()) {
|
||||||
|
$ids[] = $notice->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$notice->free();
|
||||||
|
$notice = NULL;
|
||||||
|
|
||||||
|
return $ids;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
[avatar]
|
[avatar]
|
||||||
profile_id = 129
|
profile_id = 129
|
||||||
original = 17
|
original = 17
|
||||||
@ -306,6 +307,7 @@ lat = 1
|
|||||||
lon = 1
|
lon = 1
|
||||||
location_id = 1
|
location_id = 1
|
||||||
location_ns = 1
|
location_ns = 1
|
||||||
|
repeat_of = 1
|
||||||
|
|
||||||
[notice__keys]
|
[notice__keys]
|
||||||
id = N
|
id = N
|
||||||
|
@ -154,7 +154,7 @@ $config['sphinx']['port'] = 3312;
|
|||||||
// $config['queue']['subsystem'] = 'stomp';
|
// $config['queue']['subsystem'] = 'stomp';
|
||||||
// $config['queue']['stomp_server'] = 'tcp://localhost:61613';
|
// $config['queue']['stomp_server'] = 'tcp://localhost:61613';
|
||||||
// use different queue_basename for each statusnet instance managed by the server
|
// use different queue_basename for each statusnet instance managed by the server
|
||||||
// $config['queue']['queue_basename'] = 'statusnet';
|
// $config['queue']['queue_basename'] = '/queue/statusnet/';
|
||||||
|
|
||||||
// The following customise the behaviour of the various daemons:
|
// The following customise the behaviour of the various daemons:
|
||||||
// $config['daemon']['piddir'] = '/var/run';
|
// $config['daemon']['piddir'] = '/var/run';
|
||||||
@ -236,6 +236,11 @@ $config['sphinx']['port'] = 3312;
|
|||||||
// Use a different hostname for SSL-encrypted pages
|
// Use a different hostname for SSL-encrypted pages
|
||||||
// $config['site']['sslserver'] = 'secure.example.org';
|
// $config['site']['sslserver'] = 'secure.example.org';
|
||||||
|
|
||||||
|
// Indent HTML and XML
|
||||||
|
// Enable (default) for easier to read markup for developers,
|
||||||
|
// disable to save some bandwidth.
|
||||||
|
// $config['site']['indent'] = true;
|
||||||
|
|
||||||
// If you have a lot of status networks on the same server, you can
|
// If you have a lot of status networks on the same server, you can
|
||||||
// store the site data in a database and switch as follows
|
// store the site data in a database and switch as follows
|
||||||
// Status_network::setupDB('localhost', 'statusnet', 'statuspass', 'statusnet');
|
// Status_network::setupDB('localhost', 'statusnet', 'statuspass', 'statusnet');
|
||||||
|
@ -4,8 +4,10 @@ alter table notice
|
|||||||
add column lon decimal(10,7) comment 'longitude',
|
add column lon decimal(10,7) comment 'longitude',
|
||||||
add column location_id integer comment 'location id if possible',
|
add column location_id integer comment 'location id if possible',
|
||||||
add column location_ns integer comment 'namespace for location',
|
add column location_ns integer comment 'namespace for location',
|
||||||
|
add column repeat_of integer comment 'notice this is a repeat of' references notice (id),
|
||||||
drop index notice_profile_id_idx,
|
drop index notice_profile_id_idx,
|
||||||
add index notice_profile_id_idx (profile_id,created,id);
|
add index notice_profile_id_idx (profile_id,created,id),
|
||||||
|
add index notice_repeatof_idx (repeat_of);
|
||||||
|
|
||||||
alter table message
|
alter table message
|
||||||
modify column content text comment 'message content';
|
modify column content text comment 'message content';
|
||||||
@ -73,3 +75,12 @@ create table location_namespace (
|
|||||||
modified timestamp comment 'date this record was modified'
|
modified timestamp comment 'date this record was modified'
|
||||||
|
|
||||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
||||||
|
|
||||||
|
create table login_token (
|
||||||
|
user_id integer not null comment 'user owning this token' references user (id),
|
||||||
|
token char(32) not null comment 'token useable for logging in',
|
||||||
|
created datetime not null comment 'date this record was created',
|
||||||
|
modified timestamp comment 'date this record was modified',
|
||||||
|
|
||||||
|
constraint primary key (user_id)
|
||||||
|
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
||||||
|
@ -39,6 +39,25 @@ create table profile_role (
|
|||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create table location_namespace (
|
||||||
|
|
||||||
|
id integer /*comment 'identity for this namespace'*/,
|
||||||
|
description text /* comment 'description of the namespace'*/ ,
|
||||||
|
created integer not null /*comment 'date the record was created*/ ,
|
||||||
|
/* modified timestamp comment 'date this record was modified',*/
|
||||||
|
primary key (id)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
create table login_token (
|
||||||
|
user_id integer not null /* comment 'user owning this token'*/ references "user" (id),
|
||||||
|
token char(32) not null /* comment 'token useable for logging in'*/,
|
||||||
|
created timestamp not null DEFAULT CURRENT_TIMESTAMP /* comment 'date this record was created'*/,
|
||||||
|
modified timestamp /* comment 'date this record was modified'*/,
|
||||||
|
|
||||||
|
primary key (user_id)
|
||||||
|
);
|
||||||
|
|
||||||
DROP index fave_user_id_idx;
|
DROP index fave_user_id_idx;
|
||||||
CREATE index fave_user_id_idx on fave (user_id,modified);
|
CREATE index fave_user_id_idx on fave (user_id,modified);
|
||||||
|
|
||||||
@ -55,6 +74,7 @@ ALTER TABLE notice ADD COLUMN lat decimal(10, 7) /* comment 'latitude'*/;
|
|||||||
ALTER TABLE notice ADD COLUMN lon decimal(10,7) /* comment 'longitude'*/;
|
ALTER TABLE notice ADD COLUMN lon decimal(10,7) /* comment 'longitude'*/;
|
||||||
ALTER TABLE notice ADD COLUMN location_id integer /* comment 'location id if possible'*/ ;
|
ALTER TABLE notice ADD COLUMN location_id integer /* comment 'location id if possible'*/ ;
|
||||||
ALTER TABLE notice ADD COLUMN location_ns integer /* comment 'namespace for location'*/;
|
ALTER TABLE notice ADD COLUMN location_ns integer /* comment 'namespace for location'*/;
|
||||||
|
ALTER TABLE notice ADD COLUMN repeat_of integer /* comment 'notice this is a repeat of' */ references notice (id);
|
||||||
|
|
||||||
ALTER TABLE profile ADD COLUMN lat decimal(10,7) /*comment 'latitude'*/ ;
|
ALTER TABLE profile ADD COLUMN lat decimal(10,7) /*comment 'latitude'*/ ;
|
||||||
ALTER TABLE profile ADD COLUMN lon decimal(10,7) /*comment 'longitude'*/;
|
ALTER TABLE profile ADD COLUMN lon decimal(10,7) /*comment 'longitude'*/;
|
||||||
|
@ -129,11 +129,13 @@ create table notice (
|
|||||||
lon decimal(10,7) comment 'longitude',
|
lon decimal(10,7) comment 'longitude',
|
||||||
location_id integer comment 'location id if possible',
|
location_id integer comment 'location id if possible',
|
||||||
location_ns integer comment 'namespace for location',
|
location_ns integer comment 'namespace for location',
|
||||||
|
repeat_of integer comment 'notice this is a repeat of' references notice (id),
|
||||||
|
|
||||||
index notice_profile_id_idx (profile_id,created,id),
|
index notice_profile_id_idx (profile_id,created,id),
|
||||||
index notice_conversation_idx (conversation),
|
index notice_conversation_idx (conversation),
|
||||||
index notice_created_idx (created),
|
index notice_created_idx (created),
|
||||||
index notice_replyto_idx (reply_to),
|
index notice_replyto_idx (reply_to),
|
||||||
|
index notice_repeatof_idx (repeat_of),
|
||||||
FULLTEXT(content)
|
FULLTEXT(content)
|
||||||
) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
|
) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
@ -575,3 +577,13 @@ create table location_namespace (
|
|||||||
modified timestamp comment 'date this record was modified'
|
modified timestamp comment 'date this record was modified'
|
||||||
|
|
||||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
||||||
|
|
||||||
|
create table login_token (
|
||||||
|
user_id integer not null comment 'user owning this token' references user (id),
|
||||||
|
token char(32) not null comment 'token useable for logging in',
|
||||||
|
created datetime not null comment 'date this record was created',
|
||||||
|
modified timestamp comment 'date this record was modified',
|
||||||
|
|
||||||
|
constraint primary key (user_id)
|
||||||
|
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
||||||
|
|
||||||
|
@ -135,7 +135,9 @@ create table notice (
|
|||||||
lat decimal(10,7) /* comment 'latitude'*/ ,
|
lat decimal(10,7) /* comment 'latitude'*/ ,
|
||||||
lon decimal(10,7) /* comment 'longitude'*/ ,
|
lon decimal(10,7) /* comment 'longitude'*/ ,
|
||||||
location_id integer /* comment 'location id if possible'*/ ,
|
location_id integer /* comment 'location id if possible'*/ ,
|
||||||
location_ns integer /* comment 'namespace for location'*/
|
location_ns integer /* comment 'namespace for location'*/ ,
|
||||||
|
repeat_of integer /* comment 'notice this is a repeat of' */ references notice (id) ,
|
||||||
|
|
||||||
/* FULLTEXT(content) */
|
/* FULLTEXT(content) */
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -409,7 +411,6 @@ create table user_group (
|
|||||||
mini_logo varchar(255) /* comment 'mini logo' */,
|
mini_logo varchar(255) /* comment 'mini logo' */,
|
||||||
design_id integer /*comment 'id of a design' */ references design(id),
|
design_id integer /*comment 'id of a design' */ references design(id),
|
||||||
|
|
||||||
|
|
||||||
created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
|
created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
|
||||||
modified timestamp /* comment 'date this record was modified' */
|
modified timestamp /* comment 'date this record was modified' */
|
||||||
|
|
||||||
@ -447,7 +448,6 @@ create table group_inbox (
|
|||||||
);
|
);
|
||||||
create index group_inbox_created_idx on group_inbox using btree(created);
|
create index group_inbox_created_idx on group_inbox using btree(created);
|
||||||
|
|
||||||
|
|
||||||
/*attachments and URLs stuff */
|
/*attachments and URLs stuff */
|
||||||
create sequence file_seq;
|
create sequence file_seq;
|
||||||
create table file (
|
create table file (
|
||||||
@ -543,7 +543,6 @@ create table deleted_notice (
|
|||||||
|
|
||||||
CREATE index deleted_notice_profile_id_idx on deleted_notice (profile_id);
|
CREATE index deleted_notice_profile_id_idx on deleted_notice (profile_id);
|
||||||
|
|
||||||
|
|
||||||
/* Textsearch stuff */
|
/* Textsearch stuff */
|
||||||
|
|
||||||
create index textsearch_idx on profile using gist(textsearch);
|
create index textsearch_idx on profile using gist(textsearch);
|
||||||
@ -551,7 +550,6 @@ create index noticecontent_idx on notice using gist(to_tsvector('english',conten
|
|||||||
create trigger textsearchupdate before insert or update on profile for each row
|
create trigger textsearchupdate before insert or update on profile for each row
|
||||||
execute procedure tsvector_update_trigger(textsearch, 'pg_catalog.english', nickname, fullname, location, bio, homepage);
|
execute procedure tsvector_update_trigger(textsearch, 'pg_catalog.english', nickname, fullname, location, bio, homepage);
|
||||||
|
|
||||||
|
|
||||||
create table config (
|
create table config (
|
||||||
|
|
||||||
section varchar(32) /* comment 'configuration section'*/,
|
section varchar(32) /* comment 'configuration section'*/,
|
||||||
@ -571,3 +569,23 @@ create table profile_role (
|
|||||||
primary key (profile_id, role)
|
primary key (profile_id, role)
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create table location_namespace (
|
||||||
|
|
||||||
|
id integer /*comment 'identity for this namespace'*/,
|
||||||
|
description text /* comment 'description of the namespace'*/ ,
|
||||||
|
created integer not null /*comment 'date the record was created*/ ,
|
||||||
|
/* modified timestamp comment 'date this record was modified',*/
|
||||||
|
primary key (id)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
create table login_token (
|
||||||
|
user_id integer not null /* comment 'user owning this token'*/ references "user" (id),
|
||||||
|
token char(32) not null /* comment 'token useable for logging in'*/,
|
||||||
|
created timestamp not null DEFAULT CURRENT_TIMESTAMP /* comment 'date this record was created'*/,
|
||||||
|
modified timestamp /* comment 'date this record was modified'*/,
|
||||||
|
|
||||||
|
primary key (user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* @author Alan Knowles <alan@akbkhome.com>
|
* @author Alan Knowles <alan@akbkhome.com>
|
||||||
* @copyright 1997-2006 The PHP Group
|
* @copyright 1997-2006 The PHP Group
|
||||||
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
||||||
* @version CVS: $Id: DataObject.php 284150 2009-07-15 23:27:59Z alan_k $
|
* @version CVS: $Id: DataObject.php 291349 2009-11-27 09:15:18Z alan_k $
|
||||||
* @link http://pear.php.net/package/DB_DataObject
|
* @link http://pear.php.net/package/DB_DataObject
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
* @access private
|
* @access private
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
var $_DB_DataObject_version = "1.8.12";
|
var $_DB_DataObject_version = "1.9.0";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Database table (used by table extends)
|
* The Database table (used by table extends)
|
||||||
@ -309,6 +309,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An autoloading, caching static get method using key, value (based on get)
|
* An autoloading, caching static get method using key, value (based on get)
|
||||||
|
* (depreciated - use ::get / and your own caching method)
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
* $object = DB_DataObject::staticGet("DbTable_mytable",12);
|
* $object = DB_DataObject::staticGet("DbTable_mytable",12);
|
||||||
@ -943,6 +944,10 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
$this->$key = $keyvalue;
|
$this->$key = $keyvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we haven't set disable_null_strings to "full"
|
||||||
|
$ignore_null = !isset($options['disable_null_strings'])
|
||||||
|
|| !is_string($options['disable_null_strings'])
|
||||||
|
|| strtolower($options['disable_null_strings']) !== 'full' ;
|
||||||
|
|
||||||
|
|
||||||
foreach($items as $k => $v) {
|
foreach($items as $k => $v) {
|
||||||
@ -953,7 +958,10 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!isset($this->$k)) {
|
|
||||||
|
|
||||||
|
// Ignore variables which aren't set to a value
|
||||||
|
if ( !isset($this->$k) && $ignore_null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// dont insert data into mysql timestamps
|
// dont insert data into mysql timestamps
|
||||||
@ -980,8 +988,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!($v & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($this,$k)) {
|
||||||
if (!isset($options['disable_null_strings']) && is_string($this->$k) && (strtolower($this->$k) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) {
|
|
||||||
$rightq .= " NULL ";
|
$rightq .= " NULL ";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1194,8 +1201,14 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
$options = $_DB_DATAOBJECT['CONFIG'];
|
$options = $_DB_DATAOBJECT['CONFIG'];
|
||||||
|
|
||||||
|
|
||||||
|
$ignore_null = !isset($options['disable_null_strings'])
|
||||||
|
|| !is_string($options['disable_null_strings'])
|
||||||
|
|| strtolower($options['disable_null_strings']) !== 'full' ;
|
||||||
|
|
||||||
|
|
||||||
foreach($items as $k => $v) {
|
foreach($items as $k => $v) {
|
||||||
if (!isset($this->$k)) {
|
|
||||||
|
if (!isset($this->$k) && $ignore_null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// ignore stuff thats
|
// ignore stuff thats
|
||||||
@ -1234,7 +1247,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
}
|
}
|
||||||
|
|
||||||
// special values ... at least null is handled...
|
// special values ... at least null is handled...
|
||||||
if (!isset($options['disable_null_strings']) && (strtolower($this->$k) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) {
|
if (!($v & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($this,$k)) {
|
||||||
$settings .= "$kSql = NULL ";
|
$settings .= "$kSql = NULL ";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1796,10 +1809,15 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$_DB_DATAOBJECT['INI'][$this->_database] = array();
|
||||||
foreach ($schemas as $ini) {
|
foreach ($schemas as $ini) {
|
||||||
if (file_exists($ini) && is_file($ini)) {
|
if (file_exists($ini) && is_file($ini)) {
|
||||||
$_DB_DATAOBJECT['INI'][$this->_database] = parse_ini_file($ini, true);
|
|
||||||
|
$_DB_DATAOBJECT['INI'][$this->_database] = array_merge(
|
||||||
|
$_DB_DATAOBJECT['INI'][$this->_database],
|
||||||
|
parse_ini_file($ini, true)
|
||||||
|
);
|
||||||
|
|
||||||
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
||||||
if (!is_readable ($ini)) {
|
if (!is_readable ($ini)) {
|
||||||
$this->debug("ini file is not readable: $ini","databaseStructure",1);
|
$this->debug("ini file is not readable: $ini","databaseStructure",1);
|
||||||
@ -2479,6 +2497,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
$this->_query= $x->_query;
|
$this->_query= $x->_query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
foreach($keys as $k => $v) {
|
foreach($keys as $k => $v) {
|
||||||
// index keys is an indexed array
|
// index keys is an indexed array
|
||||||
/* these filter checks are a bit suspicious..
|
/* these filter checks are a bit suspicious..
|
||||||
@ -2519,7 +2538,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($options['disable_null_strings']) && (strtolower($this->$k) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) {
|
if (!($v & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($this,$k)) {
|
||||||
$this->whereAdd(" $kSql IS NULL");
|
$this->whereAdd(" $kSql IS NULL");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2624,15 +2643,31 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
}
|
}
|
||||||
|
|
||||||
// does this need multi db support??
|
// does this need multi db support??
|
||||||
$p = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
|
$cp = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
|
||||||
$_DB_DATAOBJECT['CONFIG']['class_prefix'] : '';
|
explode(PATH_SEPARATOR, $_DB_DATAOBJECT['CONFIG']['class_prefix']) : '';
|
||||||
$class = $p . preg_replace('/[^A-Z0-9]/i','_',ucfirst($table));
|
|
||||||
$ce = substr(phpversion(),0,1) > 4 ? class_exists($class,false) : class_exists($class);
|
|
||||||
|
|
||||||
$class = $ce ? $class : DB_DataObject::_autoloadClass($class);
|
// multiprefix support.
|
||||||
|
$tbl = preg_replace('/[^A-Z0-9]/i','_',ucfirst($table));
|
||||||
|
if (is_array($cp)) {
|
||||||
|
$class = array();
|
||||||
|
foreach($cp as $cpr) {
|
||||||
|
$ce = substr(phpversion(),0,1) > 4 ? class_exists($cpr . $tbl,false) : class_exists($cpr . $tbl);
|
||||||
|
if ($ce) {
|
||||||
|
$class = $cpr . $tbl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$class[] = $cpr . $tbl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$class = $tbl;
|
||||||
|
$ce = substr(phpversion(),0,1) > 4 ? class_exists($class,false) : class_exists($class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$rclass = $ce ? $class : DB_DataObject::_autoloadClass($class, $table);
|
||||||
|
|
||||||
// proxy = full|light
|
// proxy = full|light
|
||||||
if (!$class && isset($_DB_DATAOBJECT['CONFIG']['proxy'])) {
|
if (!$rclass && isset($_DB_DATAOBJECT['CONFIG']['proxy'])) {
|
||||||
|
|
||||||
DB_DataObject::debug("FAILED TO Autoload $database.$table - using proxy.","FACTORY",1);
|
DB_DataObject::debug("FAILED TO Autoload $database.$table - using proxy.","FACTORY",1);
|
||||||
|
|
||||||
@ -2653,12 +2688,14 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
return $x->$proxyMethod( $d->_database, $table);
|
return $x->$proxyMethod( $d->_database, $table);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$class) {
|
if (!$rclass) {
|
||||||
return DB_DataObject::raiseError(
|
return DB_DataObject::raiseError(
|
||||||
"factory could not find class $class from $table",
|
"factory could not find class " .
|
||||||
|
(is_array($class) ? implode(PATH_SEPARATOR, $class) : $class ).
|
||||||
|
"from $table",
|
||||||
DB_DATAOBJECT_ERROR_INVALIDCONFIG);
|
DB_DATAOBJECT_ERROR_INVALIDCONFIG);
|
||||||
}
|
}
|
||||||
$ret = new $class;
|
$ret = new $rclass;
|
||||||
if (!empty($database)) {
|
if (!empty($database)) {
|
||||||
DB_DataObject::debug("Setting database to $database","FACTORY",1);
|
DB_DataObject::debug("Setting database to $database","FACTORY",1);
|
||||||
$ret->database($database);
|
$ret->database($database);
|
||||||
@ -2668,11 +2705,12 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
/**
|
/**
|
||||||
* autoload Class
|
* autoload Class
|
||||||
*
|
*
|
||||||
* @param string $class Class
|
* @param string|array $class Class
|
||||||
|
* @param string $table Table trying to load.
|
||||||
* @access private
|
* @access private
|
||||||
* @return string classname on Success
|
* @return string classname on Success
|
||||||
*/
|
*/
|
||||||
function _autoloadClass($class)
|
function _autoloadClass($class, $table=false)
|
||||||
{
|
{
|
||||||
global $_DB_DATAOBJECT;
|
global $_DB_DATAOBJECT;
|
||||||
|
|
||||||
@ -2682,32 +2720,62 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
$class_prefix = empty($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
|
$class_prefix = empty($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
|
||||||
'' : $_DB_DATAOBJECT['CONFIG']['class_prefix'];
|
'' : $_DB_DATAOBJECT['CONFIG']['class_prefix'];
|
||||||
|
|
||||||
$table = substr($class,strlen($class_prefix));
|
$table = $table ? $table : substr($class,strlen($class_prefix));
|
||||||
|
|
||||||
// only include the file if it exists - and barf badly if it has parse errors :)
|
// only include the file if it exists - and barf badly if it has parse errors :)
|
||||||
if (!empty($_DB_DATAOBJECT['CONFIG']['proxy']) || empty($_DB_DATAOBJECT['CONFIG']['class_location'])) {
|
if (!empty($_DB_DATAOBJECT['CONFIG']['proxy']) || empty($_DB_DATAOBJECT['CONFIG']['class_location'])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// support for:
|
||||||
|
// class_location = mydir/ => maps to mydir/Tablename.php
|
||||||
|
// class_location = mydir/myfile_%s.php => maps to mydir/myfile_Tablename
|
||||||
|
// with directory sepr
|
||||||
|
// class_location = mydir/:mydir2/: => tries all of thes locations.
|
||||||
|
$cl = $_DB_DATAOBJECT['CONFIG']['class_location'];
|
||||||
|
|
||||||
|
|
||||||
if (strpos($_DB_DATAOBJECT['CONFIG']['class_location'],'%s') !== false) {
|
switch (true) {
|
||||||
$file = sprintf($_DB_DATAOBJECT['CONFIG']['class_location'], preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)));
|
case (strpos($cl ,'%s') !== false):
|
||||||
} else {
|
$file = sprintf($cl , preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)));
|
||||||
$file = $_DB_DATAOBJECT['CONFIG']['class_location'].'/'.preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)).".php";
|
break;
|
||||||
|
|
||||||
|
case (strpos($cl , PATH_SEPARATOR) !== false):
|
||||||
|
$file = array();
|
||||||
|
foreach(explode(PATH_SEPARATOR, $cl ) as $p) {
|
||||||
|
$file[] = $p .'/'.preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)).".php";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$file = $cl .'/'.preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)).".php";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file_exists($file)) {
|
$cls = is_array($class) ? $class : array($class);
|
||||||
|
|
||||||
|
if (is_array($file) || !file_exists($file)) {
|
||||||
$found = false;
|
$found = false;
|
||||||
foreach(explode(PATH_SEPARATOR, ini_get('include_path')) as $p) {
|
|
||||||
if (file_exists("$p/$file")) {
|
$file = is_array($file) ? $file : array($file);
|
||||||
$file = "$p/$file";
|
$search = implode(PATH_SEPARATOR, $file);
|
||||||
|
foreach($file as $f) {
|
||||||
|
foreach(explode(PATH_SEPARATOR, '' . PATH_SEPARATOR . ini_get('include_path')) as $p) {
|
||||||
|
$ff = empty($p) ? $f : "$p/$f";
|
||||||
|
|
||||||
|
if (file_exists($ff)) {
|
||||||
|
$file = $ff;
|
||||||
$found = true;
|
$found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!$found) {
|
if (!$found) {
|
||||||
DB_DataObject::raiseError(
|
DB_DataObject::raiseError(
|
||||||
"autoload:Could not find class {$class} using class_location value",
|
"autoload:Could not find class " . implode(',', $cls) .
|
||||||
|
" using class_location value :" . $search .
|
||||||
|
" using include_path value :" . ini_get('include_path'),
|
||||||
DB_DATAOBJECT_ERROR_INVALIDCONFIG);
|
DB_DATAOBJECT_ERROR_INVALIDCONFIG);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2716,11 +2784,17 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
include_once $file;
|
include_once $file;
|
||||||
|
|
||||||
|
|
||||||
$ce = substr(phpversion(),0,1) > 4 ? class_exists($class,false) : class_exists($class);
|
$ce = false;
|
||||||
|
foreach($cls as $c) {
|
||||||
|
$ce = substr(phpversion(),0,1) > 4 ? class_exists($c,false) : class_exists($c);
|
||||||
|
if ($ce) {
|
||||||
|
$class = $c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!$ce) {
|
if (!$ce) {
|
||||||
DB_DataObject::raiseError(
|
DB_DataObject::raiseError(
|
||||||
"autoload:Could not autoload {$class}",
|
"autoload:Could not autoload " . implode(',', $cls) ,
|
||||||
DB_DATAOBJECT_ERROR_INVALIDCONFIG);
|
DB_DATAOBJECT_ERROR_INVALIDCONFIG);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2786,7 +2860,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$_DB_DATAOBJECT['LINKS'][$this->_database] = array();
|
||||||
foreach ($schemas as $ini) {
|
foreach ($schemas as $ini) {
|
||||||
|
|
||||||
$links =
|
$links =
|
||||||
@ -2794,9 +2868,13 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
$_DB_DATAOBJECT['CONFIG']["links_{$this->_database}"] :
|
$_DB_DATAOBJECT['CONFIG']["links_{$this->_database}"] :
|
||||||
str_replace('.ini','.links.ini',$ini);
|
str_replace('.ini','.links.ini',$ini);
|
||||||
|
|
||||||
if (empty($_DB_DATAOBJECT['LINKS'][$this->_database]) && file_exists($links) && is_file($links)) {
|
if (file_exists($links) && is_file($links)) {
|
||||||
/* not sure why $links = ... here - TODO check if that works */
|
/* not sure why $links = ... here - TODO check if that works */
|
||||||
$_DB_DATAOBJECT['LINKS'][$this->_database] = parse_ini_file($links, true);
|
$_DB_DATAOBJECT['LINKS'][$this->_database] = array_merge(
|
||||||
|
$_DB_DATAOBJECT['LINKS'][$this->_database],
|
||||||
|
parse_ini_file($links, true)
|
||||||
|
);
|
||||||
|
|
||||||
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
||||||
$this->debug("Loaded links.ini file: $links","links",1);
|
$this->debug("Loaded links.ini file: $links","links",1);
|
||||||
}
|
}
|
||||||
@ -2977,14 +3055,12 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
}
|
}
|
||||||
if ($link) {
|
if ($link) {
|
||||||
if ($obj->get($link, $this->$row)) {
|
if ($obj->get($link, $this->$row)) {
|
||||||
$obj->free();
|
|
||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($obj->get($this->$row)) {
|
if ($obj->get($this->$row)) {
|
||||||
$obj->free();
|
|
||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -3316,13 +3392,22 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ignore_null = !isset($options['disable_null_strings'])
|
||||||
|
|| !is_string($options['disable_null_strings'])
|
||||||
|
|| strtolower($options['disable_null_strings']) !== 'full' ;
|
||||||
|
|
||||||
|
|
||||||
foreach($items as $k => $v) {
|
foreach($items as $k => $v) {
|
||||||
if (!isset($obj->$k)) {
|
if (!isset($obj->$k) && $ignore_null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$kSql = ($quoteIdentifiers ? $DB->quoteIdentifier($k) : $k);
|
$kSql = ($quoteIdentifiers ? $DB->quoteIdentifier($k) : $k);
|
||||||
|
|
||||||
|
if (DB_DataObject::_is_null($obj,$k)) {
|
||||||
|
$obj->whereAdd("{$joinAs}.{$kSql} IS NULL");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if ($v & DB_DATAOBJECT_STR) {
|
if ($v & DB_DATAOBJECT_STR) {
|
||||||
$obj->whereAdd("{$joinAs}.{$kSql} = " . $this->_quote((string) (
|
$obj->whereAdd("{$joinAs}.{$kSql} = " . $this->_quote((string) (
|
||||||
@ -3345,14 +3430,9 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
$this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
|
$this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!isset($options['disable_null_strings']) && strtolower($value) === 'null') {
|
|
||||||
$obj->whereAdd("{$joinAs}.{$kSql} IS NULL");
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
$obj->whereAdd("{$joinAs}.{$kSql} = $value");
|
$obj->whereAdd("{$joinAs}.{$kSql} = $value");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* this is probably an error condition! */
|
/* this is probably an error condition! */
|
||||||
@ -3514,7 +3594,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($from[sprintf($format,$k)])) {
|
if (!isset($from[sprintf($format,$k)]) && !DB_DataObject::_is_null($from, sprintf($format,$k))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3643,7 +3723,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
|
|
||||||
// if not null - and it's not set.......
|
// if not null - and it's not set.......
|
||||||
|
|
||||||
if (!isset($this->$key) && ($val & DB_DATAOBJECT_NOTNULL)) {
|
if ($val & DB_DATAOBJECT_NOTNULL && DB_DataObject::_is_null($this, $key)) {
|
||||||
// dont check empty sequence key values..
|
// dont check empty sequence key values..
|
||||||
if (($key == $seq[0]) && ($seq[1] == true)) {
|
if (($key == $seq[0]) && ($seq[1] == true)) {
|
||||||
continue;
|
continue;
|
||||||
@ -3653,7 +3733,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!isset($options['disable_null_strings']) && is_string($this->$key) && (strtolower($this->$key) == 'null')) {
|
if (DB_DataObject::_is_null($this, $key)) {
|
||||||
if ($val & DB_DATAOBJECT_NOTNULL) {
|
if ($val & DB_DATAOBJECT_NOTNULL) {
|
||||||
$this->debug("'null' field used for '$key', but it is defined as NOT NULL", 'VALIDATION', 4);
|
$this->debug("'null' field used for '$key', but it is defined as NOT NULL", 'VALIDATION', 4);
|
||||||
$ret[$key] = false;
|
$ret[$key] = false;
|
||||||
@ -3868,13 +3948,14 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
//echo "FROM VALUE $col, {$cols[$col]}, $value\n";
|
//echo "FROM VALUE $col, {$cols[$col]}, $value\n";
|
||||||
switch (true) {
|
switch (true) {
|
||||||
// set to null and column is can be null...
|
// set to null and column is can be null...
|
||||||
case (!isset($options['disable_null_strings']) && (strtolower($value) == 'null') && (!($cols[$col] & DB_DATAOBJECT_NOTNULL))):
|
case ((!($cols[$col] & DB_DATAOBJECT_NOTNULL)) && DB_DataObject::_is_null($value, false)):
|
||||||
case (is_object($value) && is_a($value,'DB_DataObject_Cast')):
|
case (is_object($value) && is_a($value,'DB_DataObject_Cast')):
|
||||||
$this->$col = $value;
|
$this->$col = $value;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// fail on setting null on a not null field..
|
// fail on setting null on a not null field..
|
||||||
case (!isset($options['disable_null_strings']) && (strtolower($value) == 'null') && ($cols[$col] & DB_DATAOBJECT_NOTNULL)):
|
case (($cols[$col] & DB_DATAOBJECT_NOTNULL) && DB_DataObject::_is_null($value,false)):
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case (($cols[$col] & DB_DATAOBJECT_DATE) && ($cols[$col] & DB_DATAOBJECT_TIME)):
|
case (($cols[$col] & DB_DATAOBJECT_DATE) && ($cols[$col] & DB_DATAOBJECT_TIME)):
|
||||||
@ -4190,9 +4271,65 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->num_rows = array();
|
$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->num_rows = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_array($this->_link_loaded)) {
|
||||||
|
foreach ($this->_link_loaded as $do) {
|
||||||
|
$do->free();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Evaluate whether or not a value is set to null, taking the 'disable_null_strings' option into account.
|
||||||
|
* If the value is a string set to "null" and the "disable_null_strings" option is not set to
|
||||||
|
* true, then the value is considered to be null.
|
||||||
|
* If the value is actually a PHP NULL value, and "disable_null_strings" has been set to
|
||||||
|
* the value "full", then it will also be considered null. - this can not differenticate between not set
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param object|array $obj_or_ar
|
||||||
|
* @param string|false $prop prperty
|
||||||
|
|
||||||
|
* @access private
|
||||||
|
* @return bool object
|
||||||
|
*/
|
||||||
|
function _is_null($obj_or_ar , $prop)
|
||||||
|
{
|
||||||
|
global $_DB_DATAOBJECT;
|
||||||
|
|
||||||
|
|
||||||
|
$isset = $prop === false ? isset($obj_or_ar) :
|
||||||
|
(is_array($obj_or_ar) ? isset($obj_or_ar[$prop]) : isset($obj_or_ar->$prop));
|
||||||
|
|
||||||
|
$value = $isset ?
|
||||||
|
($prop === false ? $obj_or_ar :
|
||||||
|
(is_array($obj_or_ar) ? $obj_or_ar[$prop] : $obj_or_ar->$prop))
|
||||||
|
: null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$options = $_DB_DATAOBJECT['CONFIG'];
|
||||||
|
|
||||||
|
$null_strings = !isset($options['disable_null_strings'])
|
||||||
|
|| $options['disable_null_strings'] === false;
|
||||||
|
|
||||||
|
$crazy_null = isset($options['disable_null_strings'])
|
||||||
|
&& is_string($options['disable_null_strings'])
|
||||||
|
&& strtolower($options['disable_null_strings'] === 'full');
|
||||||
|
|
||||||
|
if ( $null_strings && $isset && is_string($value) && (strtolower($value) === 'null') ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $crazy_null && !$isset ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- LEGACY BC METHODS - NOT DOCUMENTED - See Documentation on New Methods. ---*/
|
/* ---- LEGACY BC METHODS - NOT DOCUMENTED - See Documentation on New Methods. ---*/
|
||||||
|
|
||||||
function _get_table() { return $this->table(); }
|
function _get_table() { return $this->table(); }
|
||||||
@ -4214,3 +4351,4 @@ if (!defined('DB_DATAOBJECT_NO_OVERLOAD')) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* @author Alan Knowles <alan@akbkhome.com>
|
* @author Alan Knowles <alan@akbkhome.com>
|
||||||
* @copyright 1997-2006 The PHP Group
|
* @copyright 1997-2006 The PHP Group
|
||||||
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
||||||
* @version CVS: $Id: Generator.php 284150 2009-07-15 23:27:59Z alan_k $
|
* @version CVS: $Id: Generator.php 289384 2009-10-09 00:17:26Z alan_k $
|
||||||
* @link http://pear.php.net/package/DB_DataObject
|
* @link http://pear.php.net/package/DB_DataObject
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -33,7 +33,7 @@
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Config _$ptions
|
* Config _$ptions
|
||||||
* [DB_DataObject_Generator]
|
* [DB_DataObject]
|
||||||
* ; optional default = DB/DataObject.php
|
* ; optional default = DB/DataObject.php
|
||||||
* extends_location =
|
* extends_location =
|
||||||
* ; optional default = DB_DataObject
|
* ; optional default = DB_DataObject
|
||||||
@ -775,11 +775,9 @@ class DB_DataObject_Generator extends DB_DataObject
|
|||||||
//echo "Generating Class files: \n";
|
//echo "Generating Class files: \n";
|
||||||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||||||
|
|
||||||
|
$this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
|
||||||
|
$this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
|
||||||
|
|
||||||
if ($extends = @$options['extends']) {
|
|
||||||
$this->_extends = $extends;
|
|
||||||
$this->_extendsFile = $options['extends_location'];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($this->tables as $this->table) {
|
foreach($this->tables as $this->table) {
|
||||||
$this->table = trim($this->table);
|
$this->table = trim($this->table);
|
||||||
@ -814,7 +812,7 @@ class DB_DataObject_Generator extends DB_DataObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* class being extended (can be overridden by [DB_DataObject_Generator] extends=xxxx
|
* class being extended (can be overridden by [DB_DataObject] extends=xxxx
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
* @access private
|
* @access private
|
||||||
@ -1142,10 +1140,9 @@ class DB_DataObject_Generator extends DB_DataObject
|
|||||||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||||||
$class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];
|
$class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];
|
||||||
|
|
||||||
if ($extends = @$options['extends']) {
|
$this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
|
||||||
$this->_extends = $extends;
|
$this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
|
||||||
$this->_extendsFile = $options['extends_location'];
|
|
||||||
}
|
|
||||||
$classname = $this->classname = $this->getClassNameFromTableName($this->table);
|
$classname = $this->classname = $this->getClassNameFromTableName($this->table);
|
||||||
|
|
||||||
$out = $this->_generateClassTable();
|
$out = $this->_generateClassTable();
|
||||||
|
66
js/util.js
66
js/util.js
@ -57,19 +57,29 @@ var SN = { // StatusNet
|
|||||||
U: { // Utils
|
U: { // Utils
|
||||||
FormNoticeEnhancements: function(form) {
|
FormNoticeEnhancements: function(form) {
|
||||||
form_id = form.attr('id');
|
form_id = form.attr('id');
|
||||||
$('#'+form_id+' #'+SN.C.S.NoticeDataText).unbind('keyup');
|
|
||||||
$('#'+form_id+' #'+SN.C.S.NoticeDataText).unbind('keydown');
|
if (jQuery.data(form[0], 'ElementData') === undefined) {
|
||||||
if (maxLength > 0) {
|
MaxLength = $('#'+form_id+' #'+SN.C.S.NoticeTextCount).text();
|
||||||
$('#'+form_id+' #'+SN.C.S.NoticeDataText).bind('keyup', function(e) {
|
if (typeof(MaxLength) == 'undefined') {
|
||||||
|
MaxLength = SN.C.I.MaxLength;
|
||||||
|
}
|
||||||
|
jQuery.data(form[0], 'ElementData', {MaxLength:MaxLength});
|
||||||
|
|
||||||
|
SN.U.Counter(form);
|
||||||
|
|
||||||
|
NDT = $('#'+form_id+' #'+SN.C.S.NoticeDataText);
|
||||||
|
|
||||||
|
NDT.bind('keyup', function(e) {
|
||||||
SN.U.Counter(form);
|
SN.U.Counter(form);
|
||||||
});
|
});
|
||||||
// run once in case there's something in there
|
|
||||||
SN.U.Counter(form);
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#'+form_id+' #'+SN.C.S.NoticeDataText).bind('keydown', function(e) {
|
NDT.bind('keydown', function(e) {
|
||||||
SN.U.SubmitOnReturn(e, form);
|
SN.U.SubmitOnReturn(e, form);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('#'+form_id+' #'+SN.C.S.NoticeTextCount).text(jQuery.data(form[0], 'ElementData').MaxLength);
|
||||||
|
}
|
||||||
|
|
||||||
if ($('body')[0].id != 'conversation') {
|
if ($('body')[0].id != 'conversation') {
|
||||||
$('#'+form_id+' textarea').focus();
|
$('#'+form_id+' textarea').focus();
|
||||||
@ -91,15 +101,14 @@ var SN = { // StatusNet
|
|||||||
Counter: function(form) {
|
Counter: function(form) {
|
||||||
SN.C.I.FormNoticeCurrent = form;
|
SN.C.I.FormNoticeCurrent = form;
|
||||||
form_id = form.attr('id');
|
form_id = form.attr('id');
|
||||||
if (typeof(maxLength) == "undefined") {
|
|
||||||
maxLength = SN.C.I.MaxLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxLength <= 0) {
|
var MaxLength = jQuery.data(form[0], 'ElementData').MaxLength;
|
||||||
|
|
||||||
|
if (MaxLength <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var remaining = maxLength - $('#'+form_id+' #'+SN.C.S.NoticeDataText).val().length;
|
var remaining = MaxLength - $('#'+form_id+' #'+SN.C.S.NoticeDataText).val().length;
|
||||||
var counter = $('#'+form_id+' #'+SN.C.S.NoticeTextCount);
|
var counter = $('#'+form_id+' #'+SN.C.S.NoticeTextCount);
|
||||||
|
|
||||||
if (remaining.toString() != counter.text()) {
|
if (remaining.toString() != counter.text()) {
|
||||||
@ -184,30 +193,33 @@ var SN = { // StatusNet
|
|||||||
form.removeClass(SN.C.S.Processing);
|
form.removeClass(SN.C.S.Processing);
|
||||||
$('#'+form_id+' #'+SN.C.S.NoticeActionSubmit).removeClass(SN.C.S.Disabled);
|
$('#'+form_id+' #'+SN.C.S.NoticeActionSubmit).removeClass(SN.C.S.Disabled);
|
||||||
$('#'+form_id+' #'+SN.C.S.NoticeActionSubmit).removeAttr(SN.C.S.Disabled, SN.C.S.Disabled);
|
$('#'+form_id+' #'+SN.C.S.NoticeActionSubmit).removeAttr(SN.C.S.Disabled, SN.C.S.Disabled);
|
||||||
|
$('#'+form_id+' .form_response').remove();
|
||||||
if (textStatus == 'timeout') {
|
if (textStatus == 'timeout') {
|
||||||
form.append('<p class="error>Sorry! We had trouble sending your notice. The servers are overloaded. Please try again, and contact the site administrator if this problem persists.</p>');
|
form.append('<p class="form_response error">Sorry! We had trouble sending your notice. The servers are overloaded. Please try again, and contact the site administrator if this problem persists.</p>');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ($('.'+SN.C.S.Error, xhr.responseXML).length > 0) {
|
if ($('.'+SN.C.S.Error, xhr.responseXML).length > 0) {
|
||||||
form.append(document._importNode($('.'+SN.C.S.Error, xhr.responseXML)[0], true));
|
form.append(document._importNode($('.'+SN.C.S.Error, xhr.responseXML)[0], true));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(jQuery.inArray(parseInt(xhr.status), SN.C.I.HTTP20x30x) < 0) {
|
if (parseInt(xhr.status) === 0 || jQuery.inArray(parseInt(xhr.status), SN.C.I.HTTP20x30x) >= 0) {
|
||||||
form.append('<p class="error>(Sorry! We had trouble sending your notice ('+xhr.status+' '+xhr.statusText+'). Please report the problem to the site administrator if this happens again.</p>');
|
$('#'+form_id).resetForm();
|
||||||
|
$('#'+form_id+' #'+SN.C.S.NoticeDataAttachSelected).remove();
|
||||||
|
SN.U.FormNoticeEnhancements($('#'+form_id));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$('#'+form_id+' #'+SN.C.S.NoticeDataText).val('');
|
form.append('<p class="form_response error">(Sorry! We had trouble sending your notice ('+xhr.status+' '+xhr.statusText+'). Please report the problem to the site administrator if this happens again.</p>');
|
||||||
SN.U.FormNoticeEnhancements($('#'+form_id));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
success: function(data, textStatus) {
|
success: function(data, textStatus) {
|
||||||
|
$('#'+form_id+' .form_response').remove();
|
||||||
var result;
|
var result;
|
||||||
if ($('#'+SN.C.S.Error, data).length > 0) {
|
if ($('#'+SN.C.S.Error, data).length > 0) {
|
||||||
result = document._importNode($('p', data)[0], true);
|
result = document._importNode($('p', data)[0], true);
|
||||||
result = result.textContent || result.innerHTML;
|
result = result.textContent || result.innerHTML;
|
||||||
form.append('<p class="error">'+result+'</p>');
|
form.append('<p class="form_response error">'+result+'</p>');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if($('body')[0].id == 'bookmarklet') {
|
if($('body')[0].id == 'bookmarklet') {
|
||||||
@ -217,7 +229,7 @@ var SN = { // StatusNet
|
|||||||
if ($('#'+SN.C.S.CommandResult, data).length > 0) {
|
if ($('#'+SN.C.S.CommandResult, data).length > 0) {
|
||||||
result = document._importNode($('p', data)[0], true);
|
result = document._importNode($('p', data)[0], true);
|
||||||
result = result.textContent || result.innerHTML;
|
result = result.textContent || result.innerHTML;
|
||||||
form.append('<p class="success">'+result+'</p>');
|
form.append('<p class="form_response success">'+result+'</p>');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var notices = $('#notices_primary .notices');
|
var notices = $('#notices_primary .notices');
|
||||||
@ -245,12 +257,10 @@ var SN = { // StatusNet
|
|||||||
else {
|
else {
|
||||||
result = document._importNode($('title', data)[0], true);
|
result = document._importNode($('title', data)[0], true);
|
||||||
result_title = result.textContent || result.innerHTML;
|
result_title = result.textContent || result.innerHTML;
|
||||||
form.append('<p class="success">'+result_title+'</p>');
|
form.append('<p class="form_response success">'+result_title+'</p>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$('#'+form_id+' #'+SN.C.S.NoticeDataText).val('');
|
$('#'+form_id).resetForm();
|
||||||
$('#'+form_id+' #'+SN.C.S.NoticeDataAttach).val('');
|
|
||||||
$('#'+form_id+' #'+SN.C.S.NoticeInReplyTo).val('');
|
|
||||||
$('#'+form_id+' #'+SN.C.S.NoticeDataAttachSelected).remove();
|
$('#'+form_id+' #'+SN.C.S.NoticeDataAttachSelected).remove();
|
||||||
SN.U.FormNoticeEnhancements($('#'+form_id));
|
SN.U.FormNoticeEnhancements($('#'+form_id));
|
||||||
}
|
}
|
||||||
@ -305,6 +315,10 @@ var SN = { // StatusNet
|
|||||||
$('.form_disfavor').each(function() { SN.U.FormXHR($(this)); });
|
$('.form_disfavor').each(function() { SN.U.FormXHR($(this)); });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
NoticeRepeat: function() {
|
||||||
|
$('.form_repeat').each(function() { SN.U.FormXHR($(this)); });
|
||||||
|
},
|
||||||
|
|
||||||
NoticeAttachments: function() {
|
NoticeAttachments: function() {
|
||||||
$('.notice a.attachment').each(function() {
|
$('.notice a.attachment').each(function() {
|
||||||
SN.U.NoticeWithAttachment($(this).closest('.notice'));
|
SN.U.NoticeWithAttachment($(this).closest('.notice'));
|
||||||
@ -438,7 +452,7 @@ var SN = { // StatusNet
|
|||||||
Notices: function() {
|
Notices: function() {
|
||||||
if ($('body.user_in').length > 0) {
|
if ($('body.user_in').length > 0) {
|
||||||
SN.U.NoticeFavor();
|
SN.U.NoticeFavor();
|
||||||
|
SN.U.NoticeRepeat();
|
||||||
SN.U.NoticeReply();
|
SN.U.NoticeReply();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,6 +466,8 @@ var SN = { // StatusNet
|
|||||||
$('.form_group_join').each(function() { SN.U.FormXHR($(this)); });
|
$('.form_group_join').each(function() { SN.U.FormXHR($(this)); });
|
||||||
$('.form_group_leave').each(function() { SN.U.FormXHR($(this)); });
|
$('.form_group_leave').each(function() { SN.U.FormXHR($(this)); });
|
||||||
$('.form_user_nudge').each(function() { SN.U.FormXHR($(this)); });
|
$('.form_user_nudge').each(function() { SN.U.FormXHR($(this)); });
|
||||||
|
|
||||||
|
SN.U.NewDirectMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ class Action extends HTMLOutputter // lawsuit
|
|||||||
* @see XMLOutputter::__construct
|
* @see XMLOutputter::__construct
|
||||||
* @see HTMLOutputter::__construct
|
* @see HTMLOutputter::__construct
|
||||||
*/
|
*/
|
||||||
function __construct($output='php://output', $indent=true)
|
function __construct($output='php://output', $indent=null)
|
||||||
{
|
{
|
||||||
parent::__construct($output, $indent);
|
parent::__construct($output, $indent);
|
||||||
}
|
}
|
||||||
@ -951,6 +951,36 @@ class Action extends HTMLOutputter // lawsuit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integer value of an argument
|
||||||
|
*
|
||||||
|
* @param string $key query key we're interested in
|
||||||
|
* @param string $defValue optional default value (default null)
|
||||||
|
* @param string $maxValue optional max value (default null)
|
||||||
|
* @param string $minValue optional min value (default null)
|
||||||
|
*
|
||||||
|
* @return integer integer value
|
||||||
|
*/
|
||||||
|
|
||||||
|
function int($key, $defValue=null, $maxValue=null, $minValue=null)
|
||||||
|
{
|
||||||
|
$arg = strtolower($this->trimmed($key));
|
||||||
|
|
||||||
|
if (is_null($arg) || !is_integer($arg)) {
|
||||||
|
return $defValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($maxValue)) {
|
||||||
|
$arg = min($arg, $maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($minValue)) {
|
||||||
|
$arg = max($arg, $minValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $arg;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server error
|
* Server error
|
||||||
*
|
*
|
||||||
|
49
lib/api.php
49
lib/api.php
@ -55,6 +55,7 @@ class ApiAction extends Action
|
|||||||
{
|
{
|
||||||
var $format = null;
|
var $format = null;
|
||||||
var $user = null;
|
var $user = null;
|
||||||
|
var $auth_user = null;
|
||||||
var $page = null;
|
var $page = null;
|
||||||
var $count = null;
|
var $count = null;
|
||||||
var $max_id = null;
|
var $max_id = null;
|
||||||
@ -190,13 +191,14 @@ class ApiAction extends Action
|
|||||||
$twitter_user['following'] = false;
|
$twitter_user['following'] = false;
|
||||||
$twitter_user['notifications'] = false;
|
$twitter_user['notifications'] = false;
|
||||||
|
|
||||||
if (isset($apidata['user'])) {
|
if (isset($this->auth_user)) {
|
||||||
|
|
||||||
$twitter_user['following'] = $apidata['user']->isSubscribed($profile);
|
$twitter_user['following'] = $this->auth_user->isSubscribed($profile);
|
||||||
|
|
||||||
// Notifications on?
|
// Notifications on?
|
||||||
$sub = Subscription::pkeyGet(array('subscriber' =>
|
$sub = Subscription::pkeyGet(array('subscriber' =>
|
||||||
$apidata['user']->id, 'subscribed' => $profile->id));
|
$this->auth_user->id,
|
||||||
|
'subscribed' => $profile->id));
|
||||||
|
|
||||||
if ($sub) {
|
if ($sub) {
|
||||||
$twitter_user['notifications'] = ($sub->jabber || $sub->sms);
|
$twitter_user['notifications'] = ($sub->jabber || $sub->sms);
|
||||||
@ -215,6 +217,21 @@ class ApiAction extends Action
|
|||||||
}
|
}
|
||||||
|
|
||||||
function twitterStatusArray($notice, $include_user=true)
|
function twitterStatusArray($notice, $include_user=true)
|
||||||
|
{
|
||||||
|
$base = $this->twitterSimpleStatusArray($notice, $include_user);
|
||||||
|
|
||||||
|
if (!empty($notice->repeat_of)) {
|
||||||
|
$original = Notice::staticGet('id', $notice->repeat_of);
|
||||||
|
if (!empty($original)) {
|
||||||
|
$original_array = $this->twitterSimpleStatusArray($original, $include_user);
|
||||||
|
$base['retweeted_status'] = $original_array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $base;
|
||||||
|
}
|
||||||
|
|
||||||
|
function twitterSimpleStatusArray($notice, $include_user=true)
|
||||||
{
|
{
|
||||||
$profile = $notice->getProfile();
|
$profile = $notice->getProfile();
|
||||||
|
|
||||||
@ -448,9 +465,9 @@ class ApiAction extends Action
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showTwitterXmlStatus($twitter_status)
|
function showTwitterXmlStatus($twitter_status, $tag='status')
|
||||||
{
|
{
|
||||||
$this->elementStart('status');
|
$this->elementStart($tag);
|
||||||
foreach($twitter_status as $element => $value) {
|
foreach($twitter_status as $element => $value) {
|
||||||
switch ($element) {
|
switch ($element) {
|
||||||
case 'user':
|
case 'user':
|
||||||
@ -465,11 +482,14 @@ class ApiAction extends Action
|
|||||||
case 'geo':
|
case 'geo':
|
||||||
$this->showGeoRSS($value);
|
$this->showGeoRSS($value);
|
||||||
break;
|
break;
|
||||||
|
case 'retweeted_status':
|
||||||
|
$this->showTwitterXmlStatus($value, 'retweeted_status');
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
$this->element($element, null, $value);
|
$this->element($element, null, $value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->elementEnd('status');
|
$this->elementEnd($tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showTwitterXmlGroup($twitter_group)
|
function showTwitterXmlGroup($twitter_group)
|
||||||
@ -588,7 +608,7 @@ class ApiAction extends Action
|
|||||||
$this->endDocument('xml');
|
$this->endDocument('xml');
|
||||||
}
|
}
|
||||||
|
|
||||||
function showRssTimeline($notice, $title, $link, $subtitle, $suplink=null)
|
function showRssTimeline($notice, $title, $link, $subtitle, $suplink=null, $logo=null)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->initDocument('rss');
|
$this->initDocument('rss');
|
||||||
@ -602,6 +622,15 @@ class ApiAction extends Action
|
|||||||
'href' => $suplink,
|
'href' => $suplink,
|
||||||
'type' => 'application/json'));
|
'type' => 'application/json'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!is_null($logo)) {
|
||||||
|
$this->elementStart('image');
|
||||||
|
$this->element('link', null, $link);
|
||||||
|
$this->element('title', null, $title);
|
||||||
|
$this->element('url', null, $logo);
|
||||||
|
$this->elementEnd('image');
|
||||||
|
}
|
||||||
|
|
||||||
$this->element('description', null, $subtitle);
|
$this->element('description', null, $subtitle);
|
||||||
$this->element('language', null, 'en-us');
|
$this->element('language', null, 'en-us');
|
||||||
$this->element('ttl', null, '40');
|
$this->element('ttl', null, '40');
|
||||||
@ -621,7 +650,7 @@ class ApiAction extends Action
|
|||||||
$this->endTwitterRss();
|
$this->endTwitterRss();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAtomTimeline($notice, $title, $id, $link, $subtitle=null, $suplink=null, $selfuri=null)
|
function showAtomTimeline($notice, $title, $id, $link, $subtitle=null, $suplink=null, $selfuri=null, $logo=null)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->initDocument('atom');
|
$this->initDocument('atom');
|
||||||
@ -630,6 +659,10 @@ class ApiAction extends Action
|
|||||||
$this->element('id', null, $id);
|
$this->element('id', null, $id);
|
||||||
$this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
|
$this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
|
||||||
|
|
||||||
|
if (!is_null($logo)) {
|
||||||
|
$this->element('logo',null,$logo);
|
||||||
|
}
|
||||||
|
|
||||||
if (!is_null($suplink)) {
|
if (!is_null($suplink)) {
|
||||||
# For FriendFeed's SUP protocol
|
# For FriendFeed's SUP protocol
|
||||||
$this->element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup',
|
$this->element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup',
|
||||||
|
@ -53,8 +53,6 @@ require_once INSTALLDIR . '/lib/api.php';
|
|||||||
class ApiAuthAction extends ApiAction
|
class ApiAuthAction extends ApiAction
|
||||||
{
|
{
|
||||||
|
|
||||||
var $auth_user = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take arguments for running, and output basic auth header if needed
|
* Take arguments for running, and output basic auth header if needed
|
||||||
*
|
*
|
||||||
|
100
lib/command.php
100
lib/command.php
@ -372,6 +372,7 @@ class MessageCommand extends Command
|
|||||||
}
|
}
|
||||||
$message = Message::saveNew($this->user->id, $other->id, $this->text, $channel->source());
|
$message = Message::saveNew($this->user->id, $other->id, $this->text, $channel->source());
|
||||||
if ($message) {
|
if ($message) {
|
||||||
|
$message->notify();
|
||||||
$channel->output($this->user, sprintf(_('Direct message to %s sent'), $this->other));
|
$channel->output($this->user, sprintf(_('Direct message to %s sent'), $this->other));
|
||||||
} else {
|
} else {
|
||||||
$channel->error($this->user, _('Error sending direct message.'));
|
$channel->error($this->user, _('Error sending direct message.'));
|
||||||
@ -379,6 +380,65 @@ class MessageCommand extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RepeatCommand extends Command
|
||||||
|
{
|
||||||
|
var $other = null;
|
||||||
|
function __construct($user, $other)
|
||||||
|
{
|
||||||
|
parent::__construct($user);
|
||||||
|
$this->other = $other;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execute($channel)
|
||||||
|
{
|
||||||
|
if(substr($this->other,0,1)=='#'){
|
||||||
|
//repeating a specific notice_id
|
||||||
|
|
||||||
|
$notice = Notice::staticGet(substr($this->other,1));
|
||||||
|
if (!$notice) {
|
||||||
|
$channel->error($this->user, _('Notice with that id does not exist'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$recipient = $notice->getProfile();
|
||||||
|
}else{
|
||||||
|
//repeating a given user's last notice
|
||||||
|
|
||||||
|
$recipient =
|
||||||
|
common_relative_profile($this->user, common_canonical_nickname($this->other));
|
||||||
|
|
||||||
|
if (!$recipient) {
|
||||||
|
$channel->error($this->user, _('No such user.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$notice = $recipient->getCurrentNotice();
|
||||||
|
if (!$notice) {
|
||||||
|
$channel->error($this->user, _('User has no last notice'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->user->id == $notice->profile_id)
|
||||||
|
{
|
||||||
|
$channel->error($this->user, _('Cannot repeat your own notice'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($recipient->hasRepeated($notice->id)) {
|
||||||
|
$channel->error($this->user, _('Already repeated that notice'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$repeat = $notice->repeat($this->user->id, $channel->source);
|
||||||
|
|
||||||
|
if ($repeat) {
|
||||||
|
common_broadcast_notice($repeat);
|
||||||
|
$channel->output($this->user, sprintf(_('Notice from %s repeated'), $recipient->nickname));
|
||||||
|
} else {
|
||||||
|
$channel->error($this->user, _('Error repeating notice.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ReplyCommand extends Command
|
class ReplyCommand extends Command
|
||||||
{
|
{
|
||||||
var $other = null;
|
var $other = null;
|
||||||
@ -433,8 +493,9 @@ class ReplyCommand extends Command
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$notice = Notice::saveNew($this->user->id, $this->text, $channel->source(), 1,
|
$notice = Notice::saveNew($this->user->id, $this->text, $channel->source(),
|
||||||
$notice->id);
|
array('reply_to' => $notice->id));
|
||||||
|
|
||||||
if ($notice) {
|
if ($notice) {
|
||||||
$channel->output($this->user, sprintf(_('Reply to %s sent'), $recipient->nickname));
|
$channel->output($this->user, sprintf(_('Reply to %s sent'), $recipient->nickname));
|
||||||
} else {
|
} else {
|
||||||
@ -579,6 +640,38 @@ class OnCommand extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LoginCommand extends Command
|
||||||
|
{
|
||||||
|
function execute($channel)
|
||||||
|
{
|
||||||
|
$disabled = common_config('logincommand','disabled');
|
||||||
|
$disabled = isset($disabled) && $disabled;
|
||||||
|
if($disabled) {
|
||||||
|
$channel->error($this->user, _('Login command is disabled'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$login_token = Login_token::staticGet('user_id',$this->user->id);
|
||||||
|
if($login_token){
|
||||||
|
$login_token->delete();
|
||||||
|
}
|
||||||
|
$login_token = new Login_token();
|
||||||
|
$login_token->user_id = $this->user->id;
|
||||||
|
$login_token->token = common_good_rand(16);
|
||||||
|
$login_token->created = common_sql_now();
|
||||||
|
$result = $login_token->insert();
|
||||||
|
if (!$result) {
|
||||||
|
common_log_db_error($login_token, 'INSERT', __FILE__);
|
||||||
|
$channel->error($this->user, sprintf(_('Could not create login token for %s'),
|
||||||
|
$this->user->nickname));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$channel->output($this->user,
|
||||||
|
sprintf(_('This link is useable only once, and is good for only 2 minutes: %s'),
|
||||||
|
common_local_url('login',
|
||||||
|
array('user_id'=>$login_token->user_id, 'token'=>$login_token->token))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SubscriptionsCommand extends Command
|
class SubscriptionsCommand extends Command
|
||||||
{
|
{
|
||||||
function execute($channel)
|
function execute($channel)
|
||||||
@ -663,9 +756,12 @@ class HelpCommand extends Command
|
|||||||
"whois <nickname> - get profile info on user\n".
|
"whois <nickname> - get profile info on user\n".
|
||||||
"fav <nickname> - add user's last notice as a 'fave'\n".
|
"fav <nickname> - add user's last notice as a 'fave'\n".
|
||||||
"fav #<notice_id> - add notice with the given id as a 'fave'\n".
|
"fav #<notice_id> - add notice with the given id as a 'fave'\n".
|
||||||
|
"repeat #<notice_id> - repeat a notice with a given id\n".
|
||||||
|
"repeat <nickname> - repeat the last notice from user\n".
|
||||||
"reply #<notice_id> - reply to notice with a given id\n".
|
"reply #<notice_id> - reply to notice with a given id\n".
|
||||||
"reply <nickname> - reply to the last notice from user\n".
|
"reply <nickname> - reply to the last notice from user\n".
|
||||||
"join <group> - join group\n".
|
"join <group> - join group\n".
|
||||||
|
"login - Get a link to login to the web interface\n".
|
||||||
"drop <group> - leave group\n".
|
"drop <group> - leave group\n".
|
||||||
"stats - get your stats\n".
|
"stats - get your stats\n".
|
||||||
"stop - same as 'off'\n".
|
"stop - same as 'off'\n".
|
||||||
|
@ -41,6 +41,12 @@ class CommandInterpreter
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new HelpCommand($user);
|
return new HelpCommand($user);
|
||||||
|
case 'login':
|
||||||
|
if ($arg) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return new LoginCommand($user);
|
||||||
|
}
|
||||||
case 'subscribers':
|
case 'subscribers':
|
||||||
if ($arg) {
|
if ($arg) {
|
||||||
return null;
|
return null;
|
||||||
@ -163,6 +169,19 @@ class CommandInterpreter
|
|||||||
} else {
|
} else {
|
||||||
return new ReplyCommand($user, $other, $extra);
|
return new ReplyCommand($user, $other, $extra);
|
||||||
}
|
}
|
||||||
|
case 'repeat':
|
||||||
|
case 'rp':
|
||||||
|
case 'rt':
|
||||||
|
case 'rd':
|
||||||
|
if (!$arg) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
list($other, $extra) = $this->split_arg($arg);
|
||||||
|
if ($extra) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return new RepeatCommand($user, $other);
|
||||||
|
}
|
||||||
case 'whois':
|
case 'whois':
|
||||||
if (!$arg) {
|
if (!$arg) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -22,7 +22,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
|
|||||||
//exit with 200 response, if this is checking fancy from the installer
|
//exit with 200 response, if this is checking fancy from the installer
|
||||||
if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; }
|
if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; }
|
||||||
|
|
||||||
define('STATUSNET_VERSION', '0.9.0rc1');
|
define('STATUSNET_VERSION', '0.9.0rc2');
|
||||||
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
|
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
|
||||||
|
|
||||||
define('STATUSNET_CODENAME', 'Stand');
|
define('STATUSNET_CODENAME', 'Stand');
|
||||||
|
36
lib/curry.php
Normal file
36
lib/curry.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* StatusNet - the distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2008, 2009, 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP 5.3 implementation of function currying, using native closures.
|
||||||
|
* On 5.2 and lower we use the fallback implementation in util.php
|
||||||
|
*
|
||||||
|
* @param callback $fn
|
||||||
|
* @param ... any remaining arguments will be appended to call-time params
|
||||||
|
* @return callback
|
||||||
|
*/
|
||||||
|
function curry($fn) {
|
||||||
|
$extra_args = func_get_args();
|
||||||
|
array_shift($extra_args);
|
||||||
|
return function() use ($fn, $extra_args) {
|
||||||
|
$args = func_get_args();
|
||||||
|
return call_user_func_array($fn,
|
||||||
|
array_merge($args, $extra_args));
|
||||||
|
};
|
||||||
|
}
|
@ -53,6 +53,7 @@ $default =
|
|||||||
'shorturllength' => 30,
|
'shorturllength' => 30,
|
||||||
'dupelimit' => 60, # default for same person saying the same thing
|
'dupelimit' => 60, # default for same person saying the same thing
|
||||||
'textlimit' => 140,
|
'textlimit' => 140,
|
||||||
|
'indent' => true,
|
||||||
),
|
),
|
||||||
'db' =>
|
'db' =>
|
||||||
array('database' => 'YOU HAVE TO SET THIS IN config.php',
|
array('database' => 'YOU HAVE TO SET THIS IN config.php',
|
||||||
@ -74,7 +75,7 @@ $default =
|
|||||||
array('enabled' => false,
|
array('enabled' => false,
|
||||||
'subsystem' => 'db', # default to database, or 'stomp'
|
'subsystem' => 'db', # default to database, or 'stomp'
|
||||||
'stomp_server' => null,
|
'stomp_server' => null,
|
||||||
'queue_basename' => 'statusnet',
|
'queue_basename' => '/queue/statusnet/',
|
||||||
'stomp_username' => null,
|
'stomp_username' => null,
|
||||||
'stomp_password' => null,
|
'stomp_password' => null,
|
||||||
),
|
),
|
||||||
@ -228,4 +229,6 @@ $default =
|
|||||||
array('namespace' => 1), // 1 = geonames, 2 = Yahoo Where on Earth
|
array('namespace' => 1), // 1 = geonames, 2 = Yahoo Where on Earth
|
||||||
'omb' =>
|
'omb' =>
|
||||||
array('timeout' => 5), // HTTP request timeout in seconds when contacting remote hosts for OMB updates
|
array('timeout' => 5), // HTTP request timeout in seconds when contacting remote hosts for OMB updates
|
||||||
|
'logincommand' =>
|
||||||
|
array('disabled' => true),
|
||||||
);
|
);
|
||||||
|
@ -50,7 +50,7 @@ class ErrorAction extends Action
|
|||||||
var $message = null;
|
var $message = null;
|
||||||
var $default = null;
|
var $default = null;
|
||||||
|
|
||||||
function __construct($message, $code, $output='php://output', $indent=true)
|
function __construct($message, $code, $output='php://output', $indent=null)
|
||||||
{
|
{
|
||||||
parent::__construct($output, $indent);
|
parent::__construct($output, $indent);
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ class HTMLOutputter extends XMLOutputter
|
|||||||
* @param boolean $indent Whether to indent output, default true
|
* @param boolean $indent Whether to indent output, default true
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function __construct($output='php://output', $indent=true)
|
function __construct($output='php://output', $indent=null)
|
||||||
{
|
{
|
||||||
parent::__construct($output, $indent);
|
parent::__construct($output, $indent);
|
||||||
}
|
}
|
||||||
@ -350,6 +350,7 @@ class HTMLOutputter extends XMLOutputter
|
|||||||
*/
|
*/
|
||||||
function script($src, $type='text/javascript')
|
function script($src, $type='text/javascript')
|
||||||
{
|
{
|
||||||
|
if(Event::handle('StartScriptElement', array($this,&$src,&$type))) {
|
||||||
$url = parse_url($src);
|
$url = parse_url($src);
|
||||||
if( empty($url->scheme) && empty($url->host) && empty($url->query) && empty($url->fragment))
|
if( empty($url->scheme) && empty($url->host) && empty($url->query) && empty($url->fragment))
|
||||||
{
|
{
|
||||||
@ -358,6 +359,34 @@ class HTMLOutputter extends XMLOutputter
|
|||||||
$this->element('script', array('type' => $type,
|
$this->element('script', array('type' => $type,
|
||||||
'src' => $src),
|
'src' => $src),
|
||||||
' ');
|
' ');
|
||||||
|
Event::handle('EndScriptElement', array($this,$src,$type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* output a script (almost always javascript) tag with inline
|
||||||
|
* code.
|
||||||
|
*
|
||||||
|
* @param string $code code to put in the script tag
|
||||||
|
* @param string $type 'type' attribute value of the tag
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function inlineScript($code, $type='text/javascript')
|
||||||
|
{
|
||||||
|
if(Event::handle('StartInlineScriptElement', array($this,&$code,&$type))) {
|
||||||
|
$this->elementStart('script', array('type' => $type));
|
||||||
|
if($type == 'text/javascript') {
|
||||||
|
$this->raw('/*<![CDATA[*/ '); // XHTML compat
|
||||||
|
}
|
||||||
|
$this->raw($code);
|
||||||
|
if($type == 'text/javascript') {
|
||||||
|
$this->raw(' /*]]>*/'); // XHTML compat
|
||||||
|
}
|
||||||
|
$this->elementEnd('script');
|
||||||
|
Event::handle('EndInlineScriptElement', array($this,$code,$type));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -371,19 +400,44 @@ class HTMLOutputter extends XMLOutputter
|
|||||||
*/
|
*/
|
||||||
function cssLink($src,$theme=null,$media=null)
|
function cssLink($src,$theme=null,$media=null)
|
||||||
{
|
{
|
||||||
|
if(Event::handle('StartCssLinkElement', array($this,&$src,&$theme,&$media))) {
|
||||||
$url = parse_url($src);
|
$url = parse_url($src);
|
||||||
if( empty($url->scheme) && empty($url->host) && empty($url->query) && empty($url->fragment))
|
if( empty($url->scheme) && empty($url->host) && empty($url->query) && empty($url->fragment))
|
||||||
{
|
{
|
||||||
if(file_exists(Theme::file($src,$theme))){
|
if(file_exists(Theme::file($src,$theme))){
|
||||||
$src = Theme::path($src, $theme) . '?version=' . STATUSNET_VERSION;
|
$src = Theme::path($src, $theme);
|
||||||
}else{
|
}else{
|
||||||
$src = common_path($src);
|
$src = common_path($src);
|
||||||
}
|
}
|
||||||
|
$src.= '?version=' . STATUSNET_VERSION;
|
||||||
}
|
}
|
||||||
$this->element('link', array('rel' => 'stylesheet',
|
$this->element('link', array('rel' => 'stylesheet',
|
||||||
'type' => 'text/css',
|
'type' => 'text/css',
|
||||||
'href' => $src,
|
'href' => $src,
|
||||||
'media' => $media));
|
'media' => $media));
|
||||||
|
Event::handle('EndCssLinkElement', array($this,$src,$theme,$media));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* output a style (almost always css) tag with inline
|
||||||
|
* code.
|
||||||
|
*
|
||||||
|
* @param string $code code to put in the style tag
|
||||||
|
* @param string $type 'type' attribute value of the tag
|
||||||
|
* @param string $media 'media' attribute value of the tag
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function style($code, $type = 'text/css', $media = null)
|
||||||
|
{
|
||||||
|
if(Event::handle('StartStyleElement', array($this,&$code,&$type,&$media))) {
|
||||||
|
$this->elementStart('style', array('type' => $type, 'media' => $media));
|
||||||
|
$this->raw($code);
|
||||||
|
$this->elementEnd('style');
|
||||||
|
Event::handle('EndStyleElement', array($this,$code,$type,$media));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -414,7 +468,6 @@ class HTMLOutputter extends XMLOutputter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal script to autofocus the given element on page onload.
|
* Internal script to autofocus the given element on page onload.
|
||||||
*
|
*
|
||||||
@ -425,13 +478,10 @@ class HTMLOutputter extends XMLOutputter
|
|||||||
*/
|
*/
|
||||||
function autofocus($id)
|
function autofocus($id)
|
||||||
{
|
{
|
||||||
$this->elementStart('script', array('type' => 'text/javascript'));
|
$this->inlineScript(
|
||||||
$this->raw('/*<![CDATA[*/'.
|
|
||||||
' $(document).ready(function() {'.
|
' $(document).ready(function() {'.
|
||||||
' var el = $("#' . $id . '");'.
|
' var el = $("#' . $id . '");'.
|
||||||
' if (el.length) { el.focus(); }'.
|
' if (el.length) { el.focus(); }'.
|
||||||
' });'.
|
' });');
|
||||||
' /*]]>*/');
|
|
||||||
$this->elementEnd('script');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
127
lib/language.php
127
lib/language.php
@ -36,6 +36,33 @@ if (!function_exists('gettext')) {
|
|||||||
require_once("php-gettext/gettext.inc");
|
require_once("php-gettext/gettext.inc");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!function_exists('dpgettext')) {
|
||||||
|
/**
|
||||||
|
* Context-aware dgettext wrapper; use when messages in different contexts
|
||||||
|
* won't be distinguished from the English source but need different translations.
|
||||||
|
* The context string will appear as msgctxt in the .po files.
|
||||||
|
*
|
||||||
|
* Not currently exposed in PHP's gettext module; implemented to be compat
|
||||||
|
* with gettext.h's macros.
|
||||||
|
*
|
||||||
|
* @param string $domain domain identifier, or null for default domain
|
||||||
|
* @param string $context context identifier, should be some key like "menu|file"
|
||||||
|
* @param string $msgid English source text
|
||||||
|
* @return string original or translated message
|
||||||
|
*/
|
||||||
|
function dpgettext($domain, $context, $msg)
|
||||||
|
{
|
||||||
|
$msgid = $context . "\004" . $msg;
|
||||||
|
$out = dcgettext($domain, $msgid, LC_MESSAGES);
|
||||||
|
if ($out == $msgid) {
|
||||||
|
return $msg;
|
||||||
|
} else {
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!function_exists('pgettext')) {
|
if (!function_exists('pgettext')) {
|
||||||
/**
|
/**
|
||||||
* Context-aware gettext wrapper; use when messages in different contexts
|
* Context-aware gettext wrapper; use when messages in different contexts
|
||||||
@ -50,9 +77,31 @@ if (!function_exists('pgettext')) {
|
|||||||
* @return string original or translated message
|
* @return string original or translated message
|
||||||
*/
|
*/
|
||||||
function pgettext($context, $msg)
|
function pgettext($context, $msg)
|
||||||
|
{
|
||||||
|
return dpgettext(textdomain(NULL), $context, $msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('dnpgettext')) {
|
||||||
|
/**
|
||||||
|
* Context-aware dngettext wrapper; use when messages in different contexts
|
||||||
|
* won't be distinguished from the English source but need different translations.
|
||||||
|
* The context string will appear as msgctxt in the .po files.
|
||||||
|
*
|
||||||
|
* Not currently exposed in PHP's gettext module; implemented to be compat
|
||||||
|
* with gettext.h's macros.
|
||||||
|
*
|
||||||
|
* @param string $domain domain identifier, or null for default domain
|
||||||
|
* @param string $context context identifier, should be some key like "menu|file"
|
||||||
|
* @param string $msg singular English source text
|
||||||
|
* @param string $plural plural English source text
|
||||||
|
* @param int $n number of items to control plural selection
|
||||||
|
* @return string original or translated message
|
||||||
|
*/
|
||||||
|
function dnpgettext($domain, $context, $msg, $plural, $n)
|
||||||
{
|
{
|
||||||
$msgid = $context . "\004" . $msg;
|
$msgid = $context . "\004" . $msg;
|
||||||
$out = dcgettext(textdomain(NULL), $msgid, LC_MESSAGES);
|
$out = dcngettext($domain, $msgid, $plural, $n, LC_MESSAGES);
|
||||||
if ($out == $msgid) {
|
if ($out == $msgid) {
|
||||||
return $msg;
|
return $msg;
|
||||||
} else {
|
} else {
|
||||||
@ -78,14 +127,78 @@ if (!function_exists('npgettext')) {
|
|||||||
*/
|
*/
|
||||||
function npgettext($context, $msg, $plural, $n)
|
function npgettext($context, $msg, $plural, $n)
|
||||||
{
|
{
|
||||||
$msgid = $context . "\004" . $msg;
|
return dnpgettext(textdomain(NULL), $msgid, $plural, $n, LC_MESSAGES);
|
||||||
$out = dcngettext(textdomain(NULL), $msgid, $plural, $n, LC_MESSAGES);
|
}
|
||||||
if ($out == $msgid) {
|
}
|
||||||
return $msg;
|
|
||||||
|
/**
|
||||||
|
* Shortcut for *gettext functions with smart domain detection.
|
||||||
|
*
|
||||||
|
* If calling from a plugin, this function checks which plugin was
|
||||||
|
* being called from and uses that as text domain, which will have
|
||||||
|
* been set up during plugin initialization.
|
||||||
|
*
|
||||||
|
* Also handles plurals and contexts depending on what parameters
|
||||||
|
* are passed to it:
|
||||||
|
*
|
||||||
|
* gettext -> _m($msg)
|
||||||
|
* ngettext -> _m($msg1, $msg2, $n)
|
||||||
|
* pgettext -> _m($ctx, $msg)
|
||||||
|
* npgettext -> _m($ctx, $msg1, $msg2, $n)
|
||||||
|
*
|
||||||
|
* @fixme may not work properly in eval'd code
|
||||||
|
*
|
||||||
|
* @param string $msg
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function _m($msg/*, ...*/)
|
||||||
|
{
|
||||||
|
$domain = _mdomain(debug_backtrace());
|
||||||
|
$args = func_get_args();
|
||||||
|
switch(count($args)) {
|
||||||
|
case 1: return dgettext($domain, $msg);
|
||||||
|
case 2: return dpgettext($domain, $args[0], $args[1]);
|
||||||
|
case 3: return dngettext($domain, $args[0], $args[1], $args[2]);
|
||||||
|
case 4: return dnpgettext($domain, $args[0], $args[1], $args[2], $args[3]);
|
||||||
|
default: throw new Exception("Bad parameter count to _m()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks for which plugin we've been called from to set the gettext domain.
|
||||||
|
*
|
||||||
|
* @param array $backtrace debug_backtrace() output
|
||||||
|
* @return string
|
||||||
|
* @private
|
||||||
|
* @fixme could explode if SN is under a 'plugins' folder or share name.
|
||||||
|
*/
|
||||||
|
function _mdomain($backtrace)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
0 =>
|
||||||
|
array
|
||||||
|
'file' => string '/var/www/mublog/plugins/FeedSub/FeedSubPlugin.php' (length=49)
|
||||||
|
'line' => int 77
|
||||||
|
'function' => string '_m' (length=2)
|
||||||
|
'args' =>
|
||||||
|
array
|
||||||
|
0 => &string 'Feeds' (length=5)
|
||||||
|
*/
|
||||||
|
static $cached;
|
||||||
|
$path = $backtrace[0]['file'];
|
||||||
|
if (!isset($cached[$path])) {
|
||||||
|
if (DIRECTORY_SEPARATOR !== '/') {
|
||||||
|
$path = strtr($path, DIRECTORY_SEPARATOR, '/');
|
||||||
|
}
|
||||||
|
$cut = strpos($path, '/plugins/') + 9;
|
||||||
|
$cut2 = strpos($path, '/', $cut);
|
||||||
|
if ($cut && $cut2) {
|
||||||
|
$cached[$path] = substr($path, $cut, $cut2 - $cut);
|
||||||
} else {
|
} else {
|
||||||
return $out;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return $cached[$path];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -159,6 +272,7 @@ function get_nice_language_list()
|
|||||||
function get_all_languages() {
|
function get_all_languages() {
|
||||||
return array(
|
return array(
|
||||||
'ar' => array('q' => 0.8, 'lang' => 'ar', 'name' => 'Arabic', 'direction' => 'rtl'),
|
'ar' => array('q' => 0.8, 'lang' => 'ar', 'name' => 'Arabic', 'direction' => 'rtl'),
|
||||||
|
'arz' => array('q' => 0.8, 'lang' => 'arz', 'name' => 'Egyptian Spoken Arabic', 'direction' => 'rtl'),
|
||||||
'bg' => array('q' => 0.8, 'lang' => 'bg', 'name' => 'Bulgarian', 'direction' => 'ltr'),
|
'bg' => array('q' => 0.8, 'lang' => 'bg', 'name' => 'Bulgarian', 'direction' => 'ltr'),
|
||||||
'ca' => array('q' => 0.5, 'lang' => 'ca', 'name' => 'Catalan', 'direction' => 'ltr'),
|
'ca' => array('q' => 0.5, 'lang' => 'ca', 'name' => 'Catalan', 'direction' => 'ltr'),
|
||||||
'cs' => array('q' => 0.5, 'lang' => 'cs', 'name' => 'Czech', 'direction' => 'ltr'),
|
'cs' => array('q' => 0.5, 'lang' => 'cs', 'name' => 'Czech', 'direction' => 'ltr'),
|
||||||
@ -173,6 +287,7 @@ function get_all_languages() {
|
|||||||
'ga' => array('q' => 0.5, 'lang' => 'ga', 'name' => 'Galician', 'direction' => 'ltr'),
|
'ga' => array('q' => 0.5, 'lang' => 'ga', 'name' => 'Galician', 'direction' => 'ltr'),
|
||||||
'he' => array('q' => 0.5, 'lang' => 'he', 'name' => 'Hebrew', 'direction' => 'rtl'),
|
'he' => array('q' => 0.5, 'lang' => 'he', 'name' => 'Hebrew', 'direction' => 'rtl'),
|
||||||
'hsb' => array('q' => 0.8, 'lang' => 'hsb', 'name' => 'Upper Sorbian', 'direction' => 'ltr'),
|
'hsb' => array('q' => 0.8, 'lang' => 'hsb', 'name' => 'Upper Sorbian', 'direction' => 'ltr'),
|
||||||
|
'ia' => array('q' => 0.8, 'lang' => 'ia', 'name' => 'Interlingua', 'direction' => 'ltr'),
|
||||||
'is' => array('q' => 0.1, 'lang' => 'is', 'name' => 'Icelandic', 'direction' => 'ltr'),
|
'is' => array('q' => 0.1, 'lang' => 'is', 'name' => 'Icelandic', 'direction' => 'ltr'),
|
||||||
'it' => array('q' => 1, 'lang' => 'it', 'name' => 'Italian', 'direction' => 'ltr'),
|
'it' => array('q' => 1, 'lang' => 'it', 'name' => 'Italian', 'direction' => 'ltr'),
|
||||||
'jp' => array('q' => 0.5, 'lang' => 'ja', 'name' => 'Japanese', 'direction' => 'ltr'),
|
'jp' => array('q' => 0.5, 'lang' => 'ja', 'name' => 'Japanese', 'direction' => 'ltr'),
|
||||||
|
@ -154,9 +154,6 @@ class MessageForm extends Form
|
|||||||
|
|
||||||
$contentLimit = Message::maxContent();
|
$contentLimit = Message::maxContent();
|
||||||
|
|
||||||
$this->out->element('script', array('type' => 'text/javascript'),
|
|
||||||
'maxLength = ' . $contentLimit . ';');
|
|
||||||
|
|
||||||
if ($contentLimit > 0) {
|
if ($contentLimit > 0) {
|
||||||
$this->out->elementStart('dl', 'form_note');
|
$this->out->elementStart('dl', 'form_note');
|
||||||
$this->out->element('dt', null, _('Available characters'));
|
$this->out->element('dt', null, _('Available characters'));
|
||||||
|
@ -178,9 +178,6 @@ class NoticeForm extends Form
|
|||||||
|
|
||||||
$contentLimit = Notice::maxContent();
|
$contentLimit = Notice::maxContent();
|
||||||
|
|
||||||
$this->out->element('script', array('type' => 'text/javascript'),
|
|
||||||
'maxLength = ' . $contentLimit . ';');
|
|
||||||
|
|
||||||
if ($contentLimit > 0) {
|
if ($contentLimit > 0) {
|
||||||
$this->out->elementStart('dl', 'form_note');
|
$this->out->elementStart('dl', 'form_note');
|
||||||
$this->out->element('dt', null, _('Available characters'));
|
$this->out->element('dt', null, _('Available characters'));
|
||||||
|
@ -147,6 +147,10 @@ class NoticeListItem extends Widget
|
|||||||
|
|
||||||
var $notice = null;
|
var $notice = null;
|
||||||
|
|
||||||
|
/** The notice that was repeated. */
|
||||||
|
|
||||||
|
var $repeat = null;
|
||||||
|
|
||||||
/** The profile of the author of the notice, extracted once for convenience. */
|
/** The profile of the author of the notice, extracted once for convenience. */
|
||||||
|
|
||||||
var $profile = null;
|
var $profile = null;
|
||||||
@ -162,8 +166,18 @@ class NoticeListItem extends Widget
|
|||||||
function __construct($notice, $out=null)
|
function __construct($notice, $out=null)
|
||||||
{
|
{
|
||||||
parent::__construct($out);
|
parent::__construct($out);
|
||||||
|
if (!empty($notice->repeat_of)) {
|
||||||
|
$original = Notice::staticGet('id', $notice->repeat_of);
|
||||||
|
if (empty($original)) { // could have been deleted
|
||||||
$this->notice = $notice;
|
$this->notice = $notice;
|
||||||
$this->profile = $notice->getProfile();
|
} else {
|
||||||
|
$this->notice = $original;
|
||||||
|
$this->repeat = $notice;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->notice = $notice;
|
||||||
|
}
|
||||||
|
$this->profile = $this->notice->getProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,6 +216,7 @@ class NoticeListItem extends Widget
|
|||||||
$this->showNoticeSource();
|
$this->showNoticeSource();
|
||||||
$this->showNoticeLocation();
|
$this->showNoticeLocation();
|
||||||
$this->showContext();
|
$this->showContext();
|
||||||
|
$this->showRepeat();
|
||||||
$this->out->elementEnd('div');
|
$this->out->elementEnd('div');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,6 +227,7 @@ class NoticeListItem extends Widget
|
|||||||
$this->out->elementStart('div', 'notice-options');
|
$this->out->elementStart('div', 'notice-options');
|
||||||
$this->showFaveForm();
|
$this->showFaveForm();
|
||||||
$this->showReplyLink();
|
$this->showReplyLink();
|
||||||
|
$this->showRepeatForm();
|
||||||
$this->showDeleteLink();
|
$this->showDeleteLink();
|
||||||
$this->out->elementEnd('div');
|
$this->out->elementEnd('div');
|
||||||
}
|
}
|
||||||
@ -227,8 +243,9 @@ class NoticeListItem extends Widget
|
|||||||
{
|
{
|
||||||
// XXX: RDFa
|
// XXX: RDFa
|
||||||
// TODO: add notice_type class e.g., notice_video, notice_image
|
// TODO: add notice_type class e.g., notice_video, notice_image
|
||||||
|
$id = (empty($this->repeat)) ? $this->notice->id : $this->repeat->id;
|
||||||
$this->out->elementStart('li', array('class' => 'hentry notice',
|
$this->out->elementStart('li', array('class' => 'hentry notice',
|
||||||
'id' => 'notice-' . $this->notice->id));
|
'id' => 'notice-' . $id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -507,6 +524,40 @@ class NoticeListItem extends Widget
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* show a link to the author of repeat
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function showRepeat()
|
||||||
|
{
|
||||||
|
if (!empty($this->repeat)) {
|
||||||
|
|
||||||
|
$repeater = Profile::staticGet('id', $this->repeat->profile_id);
|
||||||
|
|
||||||
|
$attrs = array('href' => $repeater->profileurl,
|
||||||
|
'class' => 'url');
|
||||||
|
|
||||||
|
if (!empty($repeater->fullname)) {
|
||||||
|
$attrs['title'] = $repeater->fullname . ' (' . $repeater->nickname . ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->out->elementStart('span', 'repeat vcard');
|
||||||
|
|
||||||
|
$this->out->raw(_('Repeated by'));
|
||||||
|
|
||||||
|
$avatar = $repeater->getAvatar(AVATAR_MINI_SIZE);
|
||||||
|
|
||||||
|
$this->out->elementStart('a', $attrs);
|
||||||
|
|
||||||
|
$this->out->element('span', 'nickname', $repeater->nickname);
|
||||||
|
$this->out->elementEnd('a');
|
||||||
|
|
||||||
|
$this->out->elementEnd('span');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* show a link to reply to the current notice
|
* show a link to reply to the current notice
|
||||||
*
|
*
|
||||||
@ -540,17 +591,41 @@ class NoticeListItem extends Widget
|
|||||||
{
|
{
|
||||||
$user = common_current_user();
|
$user = common_current_user();
|
||||||
|
|
||||||
|
$todel = (empty($this->repeat)) ? $this->notice : $this->repeat;
|
||||||
|
|
||||||
if (!empty($user) &&
|
if (!empty($user) &&
|
||||||
($this->notice->profile_id == $user->id || $user->hasRight(Right::DELETEOTHERSNOTICE))) {
|
($todel->profile_id == $user->id || $user->hasRight(Right::DELETEOTHERSNOTICE))) {
|
||||||
|
|
||||||
$deleteurl = common_local_url('deletenotice',
|
$deleteurl = common_local_url('deletenotice',
|
||||||
array('notice' => $this->notice->id));
|
array('notice' => $todel->id));
|
||||||
$this->out->element('a', array('href' => $deleteurl,
|
$this->out->element('a', array('href' => $deleteurl,
|
||||||
'class' => 'notice_delete',
|
'class' => 'notice_delete',
|
||||||
'title' => _('Delete this notice')), _('Delete'));
|
'title' => _('Delete this notice')), _('Delete'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* show the form to repeat a notice
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function showRepeatForm()
|
||||||
|
{
|
||||||
|
$user = common_current_user();
|
||||||
|
if ($user && $user->id != $this->notice->profile_id) {
|
||||||
|
$profile = $user->getProfile();
|
||||||
|
if ($profile->hasRepeated($this->notice->id)) {
|
||||||
|
$this->out->element('span', array('class' => 'repeated',
|
||||||
|
'title' => _('Notice repeated')),
|
||||||
|
_('Repeated'));
|
||||||
|
} else {
|
||||||
|
$rf = new RepeatForm($this->out, $this->notice);
|
||||||
|
$rf->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* finish the notice
|
* finish the notice
|
||||||
*
|
*
|
||||||
|
@ -359,9 +359,8 @@ class StatusNetOAuthDataStore extends OAuthDataStore
|
|||||||
$notice = Notice::saveNew($author->id,
|
$notice = Notice::saveNew($author->id,
|
||||||
$omb_notice->getContent(),
|
$omb_notice->getContent(),
|
||||||
'omb',
|
'omb',
|
||||||
false,
|
array('is_local' => Notice::REMOTE_OMB,
|
||||||
null,
|
'uri' => $omb_notice->getIdentifierURI()));
|
||||||
$omb_notice->getIdentifierURI());
|
|
||||||
|
|
||||||
common_broadcast_notice($notice, true);
|
common_broadcast_notice($notice, true);
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,8 @@ class Plugin
|
|||||||
Event::addHandler(mb_substr($method, 2), array($this, $method));
|
Event::addHandler(mb_substr($method, 2), array($this, $method));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->setupGettext();
|
||||||
}
|
}
|
||||||
|
|
||||||
function initialize()
|
function initialize()
|
||||||
@ -76,4 +78,31 @@ class Plugin
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this plugin has localization that needs to be set up.
|
||||||
|
* Gettext localizations can be called via the _m() helper function.
|
||||||
|
*/
|
||||||
|
protected function setupGettext()
|
||||||
|
{
|
||||||
|
$class = get_class($this);
|
||||||
|
if (substr($class, -6) == 'Plugin') {
|
||||||
|
$name = substr($class, 0, -6);
|
||||||
|
$path = INSTALLDIR . "/plugins/$name/locale";
|
||||||
|
if (file_exists($path) && is_dir($path)) {
|
||||||
|
bindtextdomain($name, $path);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function log($level, $msg)
|
||||||
|
{
|
||||||
|
common_log($level, get_class($this) . ': '.$msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function debug($msg)
|
||||||
|
{
|
||||||
|
$this->log(LOG_DEBUG, $msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ class ProfileFormAction extends Action
|
|||||||
if ($action) {
|
if ($action) {
|
||||||
common_redirect(common_local_url($action, $args), 303);
|
common_redirect(common_local_url($action, $args), 303);
|
||||||
} else {
|
} else {
|
||||||
$this->clientError(_("No return-to arguments"));
|
$this->clientError(_("No return-to arguments."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +134,6 @@ class ProfileFormAction extends Action
|
|||||||
|
|
||||||
function handlePost()
|
function handlePost()
|
||||||
{
|
{
|
||||||
$this->serverError(_("unimplemented method"));
|
$this->serverError(_("Unimplemented method."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
145
lib/repeatform.php
Normal file
145
lib/repeatform.php
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Form for repeating a notice
|
||||||
|
*
|
||||||
|
* 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 Form
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@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
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form for repeating a notice
|
||||||
|
*
|
||||||
|
* @category Form
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@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 RepeatForm extends Form
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Notice to repeat
|
||||||
|
*/
|
||||||
|
|
||||||
|
var $notice = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param HTMLOutputter $out output channel
|
||||||
|
* @param Notice $notice notice to repeat
|
||||||
|
*/
|
||||||
|
|
||||||
|
function __construct($out=null, $notice=null)
|
||||||
|
{
|
||||||
|
parent::__construct($out);
|
||||||
|
|
||||||
|
$this->notice = $notice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the form
|
||||||
|
*
|
||||||
|
* @return int ID of the form
|
||||||
|
*/
|
||||||
|
|
||||||
|
function id()
|
||||||
|
{
|
||||||
|
return 'repeat-' . $this->notice->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action of the form
|
||||||
|
*
|
||||||
|
* @return string URL of the action
|
||||||
|
*/
|
||||||
|
|
||||||
|
function action()
|
||||||
|
{
|
||||||
|
return common_local_url('repeat');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include a session token for CSRF protection
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function sessionToken()
|
||||||
|
{
|
||||||
|
$this->out->hidden('token-' . $this->notice->id,
|
||||||
|
common_session_token());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legend of the Form
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function formLegend()
|
||||||
|
{
|
||||||
|
$this->out->element('legend', null, _('Repeat this notice'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data elements
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function formData()
|
||||||
|
{
|
||||||
|
$this->out->hidden('notice-n'.$this->notice->id,
|
||||||
|
$this->notice->id,
|
||||||
|
'notice');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action elements
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function formActions()
|
||||||
|
{
|
||||||
|
$this->out->submit('repeat-submit-' . $this->notice->id,
|
||||||
|
_('Repeat'), 'submit', null, _('Repeat this notice'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class of the form.
|
||||||
|
*
|
||||||
|
* @return string the form's class
|
||||||
|
*/
|
||||||
|
|
||||||
|
function formClass()
|
||||||
|
{
|
||||||
|
return 'form_repeat';
|
||||||
|
}
|
||||||
|
}
|
@ -88,6 +88,8 @@ class Router
|
|||||||
|
|
||||||
$m->connect('doc/:title', array('action' => 'doc'));
|
$m->connect('doc/:title', array('action' => 'doc'));
|
||||||
|
|
||||||
|
$m->connect('main/login?user_id=:user_id&token=:token', array('action'=>'login'), array('user_id'=> '[0-9]+', 'token'=>'.+'));
|
||||||
|
|
||||||
// main stuff is repetitive
|
// main stuff is repetitive
|
||||||
|
|
||||||
$main = array('login', 'logout', 'register', 'subscribe',
|
$main = array('login', 'logout', 'register', 'subscribe',
|
||||||
@ -97,6 +99,7 @@ class Router
|
|||||||
'groupblock', 'groupunblock',
|
'groupblock', 'groupunblock',
|
||||||
'sandbox', 'unsandbox',
|
'sandbox', 'unsandbox',
|
||||||
'silence', 'unsilence',
|
'silence', 'unsilence',
|
||||||
|
'repeat',
|
||||||
'deleteuser');
|
'deleteuser');
|
||||||
|
|
||||||
foreach ($main as $a) {
|
foreach ($main as $a) {
|
||||||
@ -280,12 +283,13 @@ class Router
|
|||||||
array('action' => 'ApiTimelineFriends',
|
array('action' => 'ApiTimelineFriends',
|
||||||
'id' => '[a-zA-Z0-9]+',
|
'id' => '[a-zA-Z0-9]+',
|
||||||
'format' => '(xml|json|rss|atom)'));
|
'format' => '(xml|json|rss|atom)'));
|
||||||
|
|
||||||
$m->connect('api/statuses/home_timeline.:format',
|
$m->connect('api/statuses/home_timeline.:format',
|
||||||
array('action' => 'ApiTimelineFriends',
|
array('action' => 'ApiTimelineHome',
|
||||||
'format' => '(xml|json|rss|atom)'));
|
'format' => '(xml|json|rss|atom)'));
|
||||||
|
|
||||||
$m->connect('api/statuses/home_timeline/:id.:format',
|
$m->connect('api/statuses/home_timeline/:id.:format',
|
||||||
array('action' => 'ApiTimelineFriends',
|
array('action' => 'ApiTimelineHome',
|
||||||
'id' => '[a-zA-Z0-9]+',
|
'id' => '[a-zA-Z0-9]+',
|
||||||
'format' => '(xml|json|rss|atom)'));
|
'format' => '(xml|json|rss|atom)'));
|
||||||
|
|
||||||
@ -316,6 +320,18 @@ class Router
|
|||||||
'id' => '[a-zA-Z0-9]+',
|
'id' => '[a-zA-Z0-9]+',
|
||||||
'format' => '(xml|json|rss|atom)'));
|
'format' => '(xml|json|rss|atom)'));
|
||||||
|
|
||||||
|
$m->connect('api/statuses/retweeted_by_me.:format',
|
||||||
|
array('action' => 'ApiTimelineRetweetedByMe',
|
||||||
|
'format' => '(xml|json|atom)'));
|
||||||
|
|
||||||
|
$m->connect('api/statuses/retweeted_to_me.:format',
|
||||||
|
array('action' => 'ApiTimelineRetweetedToMe',
|
||||||
|
'format' => '(xml|json|atom)'));
|
||||||
|
|
||||||
|
$m->connect('api/statuses/retweets_of_me.:format',
|
||||||
|
array('action' => 'ApiTimelineRetweetsOfMe',
|
||||||
|
'format' => '(xml|json|atom)'));
|
||||||
|
|
||||||
$m->connect('api/statuses/friends.:format',
|
$m->connect('api/statuses/friends.:format',
|
||||||
array('action' => 'ApiUserFriends',
|
array('action' => 'ApiUserFriends',
|
||||||
'format' => '(xml|json)'));
|
'format' => '(xml|json)'));
|
||||||
@ -356,6 +372,16 @@ class Router
|
|||||||
'id' => '[0-9]+',
|
'id' => '[0-9]+',
|
||||||
'format' => '(xml|json)'));
|
'format' => '(xml|json)'));
|
||||||
|
|
||||||
|
$m->connect('api/statuses/retweet/:id.:format',
|
||||||
|
array('action' => 'ApiStatusesRetweet',
|
||||||
|
'id' => '[0-9]+',
|
||||||
|
'format' => '(xml|json)'));
|
||||||
|
|
||||||
|
$m->connect('api/statuses/retweets/:id.:format',
|
||||||
|
array('action' => 'ApiStatusesRetweets',
|
||||||
|
'id' => '[0-9]+',
|
||||||
|
'format' => '(xml|json)'));
|
||||||
|
|
||||||
// users
|
// users
|
||||||
|
|
||||||
$m->connect('api/users/show.:format',
|
$m->connect('api/users/show.:format',
|
||||||
|
@ -52,7 +52,7 @@ class Rss10Action extends Action
|
|||||||
* @see Action::__construct
|
* @see Action::__construct
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function __construct($output='php://output', $indent=true)
|
function __construct($output='php://output', $indent=null)
|
||||||
{
|
{
|
||||||
parent::__construct($output, $indent);
|
parent::__construct($output, $indent);
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ class Schema
|
|||||||
|
|
||||||
public function getTableDef($name)
|
public function getTableDef($name)
|
||||||
{
|
{
|
||||||
$res =& $this->conn->query('DESCRIBE ' . $name);
|
$res = $this->conn->query('DESCRIBE ' . $name);
|
||||||
|
|
||||||
if (PEAR::isError($res)) {
|
if (PEAR::isError($res)) {
|
||||||
throw new Exception($res->getMessage());
|
throw new Exception($res->getMessage());
|
||||||
@ -213,7 +213,7 @@ class Schema
|
|||||||
|
|
||||||
$sql .= "); ";
|
$sql .= "); ";
|
||||||
|
|
||||||
$res =& $this->conn->query($sql);
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
if (PEAR::isError($res)) {
|
if (PEAR::isError($res)) {
|
||||||
throw new Exception($res->getMessage());
|
throw new Exception($res->getMessage());
|
||||||
@ -234,7 +234,7 @@ class Schema
|
|||||||
|
|
||||||
public function dropTable($name)
|
public function dropTable($name)
|
||||||
{
|
{
|
||||||
$res =& $this->conn->query("DROP TABLE $name");
|
$res = $this->conn->query("DROP TABLE $name");
|
||||||
|
|
||||||
if (PEAR::isError($res)) {
|
if (PEAR::isError($res)) {
|
||||||
throw new Exception($res->getMessage());
|
throw new Exception($res->getMessage());
|
||||||
@ -269,7 +269,7 @@ class Schema
|
|||||||
$name = "$table_".implode("_", $columnNames)."_idx";
|
$name = "$table_".implode("_", $columnNames)."_idx";
|
||||||
}
|
}
|
||||||
|
|
||||||
$res =& $this->conn->query("ALTER TABLE $table ".
|
$res = $this->conn->query("ALTER TABLE $table ".
|
||||||
"ADD INDEX $name (".
|
"ADD INDEX $name (".
|
||||||
implode(",", $columnNames).")");
|
implode(",", $columnNames).")");
|
||||||
|
|
||||||
@ -291,7 +291,7 @@ class Schema
|
|||||||
|
|
||||||
public function dropIndex($table, $name)
|
public function dropIndex($table, $name)
|
||||||
{
|
{
|
||||||
$res =& $this->conn->query("ALTER TABLE $table DROP INDEX $name");
|
$res = $this->conn->query("ALTER TABLE $table DROP INDEX $name");
|
||||||
|
|
||||||
if (PEAR::isError($res)) {
|
if (PEAR::isError($res)) {
|
||||||
throw new Exception($res->getMessage());
|
throw new Exception($res->getMessage());
|
||||||
@ -314,7 +314,7 @@ class Schema
|
|||||||
{
|
{
|
||||||
$sql = "ALTER TABLE $table ADD COLUMN " . $this->_columnSql($columndef);
|
$sql = "ALTER TABLE $table ADD COLUMN " . $this->_columnSql($columndef);
|
||||||
|
|
||||||
$res =& $this->conn->query($sql);
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
if (PEAR::isError($res)) {
|
if (PEAR::isError($res)) {
|
||||||
throw new Exception($res->getMessage());
|
throw new Exception($res->getMessage());
|
||||||
@ -339,7 +339,7 @@ class Schema
|
|||||||
$sql = "ALTER TABLE $table MODIFY COLUMN " .
|
$sql = "ALTER TABLE $table MODIFY COLUMN " .
|
||||||
$this->_columnSql($columndef);
|
$this->_columnSql($columndef);
|
||||||
|
|
||||||
$res =& $this->conn->query($sql);
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
if (PEAR::isError($res)) {
|
if (PEAR::isError($res)) {
|
||||||
throw new Exception($res->getMessage());
|
throw new Exception($res->getMessage());
|
||||||
@ -363,7 +363,7 @@ class Schema
|
|||||||
{
|
{
|
||||||
$sql = "ALTER TABLE $table DROP COLUMN $columnName";
|
$sql = "ALTER TABLE $table DROP COLUMN $columnName";
|
||||||
|
|
||||||
$res =& $this->conn->query($sql);
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
if (PEAR::isError($res)) {
|
if (PEAR::isError($res)) {
|
||||||
throw new Exception($res->getMessage());
|
throw new Exception($res->getMessage());
|
||||||
@ -446,7 +446,7 @@ class Schema
|
|||||||
|
|
||||||
$sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase);
|
$sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase);
|
||||||
|
|
||||||
$res =& $this->conn->query($sql);
|
$res = $this->conn->query($sql);
|
||||||
|
|
||||||
if (PEAR::isError($res)) {
|
if (PEAR::isError($res)) {
|
||||||
throw new Exception($res->getMessage());
|
throw new Exception($res->getMessage());
|
||||||
|
@ -127,6 +127,12 @@ function subs_unsubscribe_to($user, $other)
|
|||||||
if (!$user->isSubscribed($other))
|
if (!$user->isSubscribed($other))
|
||||||
return _('Not subscribed!');
|
return _('Not subscribed!');
|
||||||
|
|
||||||
|
// Don't allow deleting self subs
|
||||||
|
|
||||||
|
if ($user->id == $other->id) {
|
||||||
|
return _('Couldn\'t delete self-subscription.');
|
||||||
|
}
|
||||||
|
|
||||||
$sub = DB_DataObject::factory('subscription');
|
$sub = DB_DataObject::factory('subscription');
|
||||||
|
|
||||||
$sub->subscriber = $user->id;
|
$sub->subscriber = $user->id;
|
||||||
|
40
lib/util.php
40
lib/util.php
@ -135,7 +135,7 @@ function common_check_user($nickname, $password)
|
|||||||
if (0 == strcmp(common_munge_password($password, $user->id),
|
if (0 == strcmp(common_munge_password($password, $user->id),
|
||||||
$user->password)) {
|
$user->password)) {
|
||||||
//internal checking passed
|
//internal checking passed
|
||||||
$authenticatedUser =& $user;
|
$authenticatedUser = $user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -531,8 +531,11 @@ function callback_helper($matches, $callback, $notice_id) {
|
|||||||
return substr($matches[0],0,$left) . $result . substr($matches[0],$right);
|
return substr($matches[0],0,$left) . $result . substr($matches[0],$right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version_compare(PHP_VERSION, '5.3.0', 'ge')) {
|
||||||
|
// lambda implementation in a separate file; PHP 5.2 won't parse it.
|
||||||
|
require_once INSTALLDIR . "/lib/curry.php";
|
||||||
|
} else {
|
||||||
function curry($fn) {
|
function curry($fn) {
|
||||||
//TODO switch to a PHP 5.3 function closure based approach if PHP 5.3 is used
|
|
||||||
$args = func_get_args();
|
$args = func_get_args();
|
||||||
array_shift($args);
|
array_shift($args);
|
||||||
$id = uniqid('_partial');
|
$id = uniqid('_partial');
|
||||||
@ -545,6 +548,7 @@ function curry($fn) {
|
|||||||
'$args,'.
|
'$args,'.
|
||||||
'$GLOBALS["'.$id.'"][1]));');
|
'$GLOBALS["'.$id.'"][1]));');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function common_linkify($url) {
|
function common_linkify($url) {
|
||||||
// It comes in special'd, so we unspecial it before passing to the stringifying
|
// It comes in special'd, so we unspecial it before passing to the stringifying
|
||||||
@ -1078,6 +1082,7 @@ function common_request_id()
|
|||||||
|
|
||||||
function common_log($priority, $msg, $filename=null)
|
function common_log($priority, $msg, $filename=null)
|
||||||
{
|
{
|
||||||
|
if(Event::handle('StartLog', array(&$priority, &$msg, &$filename))){
|
||||||
$msg = '[' . common_request_id() . '] ' . $msg;
|
$msg = '[' . common_request_id() . '] ' . $msg;
|
||||||
$logfile = common_config('site', 'logfile');
|
$logfile = common_config('site', 'logfile');
|
||||||
if ($logfile) {
|
if ($logfile) {
|
||||||
@ -1091,6 +1096,8 @@ function common_log($priority, $msg, $filename=null)
|
|||||||
common_ensure_syslog();
|
common_ensure_syslog();
|
||||||
syslog($priority, $msg);
|
syslog($priority, $msg);
|
||||||
}
|
}
|
||||||
|
Event::handle('EndLog', array($priority, $msg, $filename));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function common_debug($msg, $filename=null)
|
function common_debug($msg, $filename=null)
|
||||||
@ -1245,8 +1252,12 @@ function common_copy_args($from)
|
|||||||
return $to;
|
return $to;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Neutralise the evil effects of magic_quotes_gpc in the current request.
|
/**
|
||||||
// This is used before handing a request off to OAuthRequest::from_request.
|
* Neutralise the evil effects of magic_quotes_gpc in the current request.
|
||||||
|
* This is used before handing a request off to OAuthRequest::from_request.
|
||||||
|
* @fixme Doesn't consider vars other than _POST and _GET?
|
||||||
|
* @fixme Can't be undone and could corrupt data if run twice.
|
||||||
|
*/
|
||||||
function common_remove_magic_from_request()
|
function common_remove_magic_from_request()
|
||||||
{
|
{
|
||||||
if(get_magic_quotes_gpc()) {
|
if(get_magic_quotes_gpc()) {
|
||||||
@ -1448,6 +1459,17 @@ function common_database_tablename($tablename)
|
|||||||
return $tablename;
|
return $tablename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorten a URL with the current user's configured shortening service,
|
||||||
|
* or ur1.ca if configured, or not at all if no shortening is set up.
|
||||||
|
* Length is not considered.
|
||||||
|
*
|
||||||
|
* @param string $long_url
|
||||||
|
* @return string may return the original URL if shortening failed
|
||||||
|
*
|
||||||
|
* @fixme provide a way to specify a particular shortener
|
||||||
|
* @fixme provide a way to specify to use a given user's shortening preferences
|
||||||
|
*/
|
||||||
function common_shorten_url($long_url)
|
function common_shorten_url($long_url)
|
||||||
{
|
{
|
||||||
$user = common_current_user();
|
$user = common_current_user();
|
||||||
@ -1468,6 +1490,16 @@ function common_shorten_url($long_url)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed array($proxy, $ip) for web requests; proxy may be null
|
||||||
|
* null if not a web request
|
||||||
|
*
|
||||||
|
* @fixme X-Forwarded-For can be chained by multiple proxies;
|
||||||
|
we should parse the list and provide a cleaner array
|
||||||
|
* @fixme X-Forwarded-For can be forged by clients; only use them if trusted
|
||||||
|
* @fixme X_Forwarded_For headers will override X-Forwarded-For read through $_SERVER;
|
||||||
|
* use function to get exact request headers from Apache if possible.
|
||||||
|
*/
|
||||||
function common_client_ip()
|
function common_client_ip()
|
||||||
{
|
{
|
||||||
if (!isset($_SERVER) || !array_key_exists('REQUEST_METHOD', $_SERVER)) {
|
if (!isset($_SERVER) || !array_key_exists('REQUEST_METHOD', $_SERVER)) {
|
||||||
|
@ -67,10 +67,13 @@ class XMLOutputter
|
|||||||
* @param boolean $indent Whether to indent output, default true
|
* @param boolean $indent Whether to indent output, default true
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function __construct($output='php://output', $indent=true)
|
function __construct($output='php://output', $indent=null)
|
||||||
{
|
{
|
||||||
$this->xw = new XMLWriter();
|
$this->xw = new XMLWriter();
|
||||||
$this->xw->openURI($output);
|
$this->xw->openURI($output);
|
||||||
|
if(is_null($indent)) {
|
||||||
|
$indent = common_config('site', 'indent');
|
||||||
|
}
|
||||||
$this->xw->setIndent($indent);
|
$this->xw->setIndent($indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
5334
locale/arz/LC_MESSAGES/statusnet.po
Normal file
5334
locale/arz/LC_MESSAGES/statusnet.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
5477
locale/ia/LC_MESSAGES/statusnet.po
Normal file
5477
locale/ia/LC_MESSAGES/statusnet.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user