Merge branch '0.7.x' into 0.8.x
Conflicts: classes/Notice.php lib/action.php lib/router.php lib/twitter.php
This commit is contained in:
commit
b3a0eea3b6
73
README
73
README
|
@ -2,8 +2,8 @@
|
|||
README
|
||||
------
|
||||
|
||||
Laconica 0.7.1 ("West of the Fields")
|
||||
6 February 2009
|
||||
Laconica 0.7.2 ("Talk about the Passion")
|
||||
11 March 2009
|
||||
|
||||
This is the README file for Laconica, the Open Source microblogging
|
||||
platform. It includes installation instructions, descriptions of
|
||||
|
@ -71,8 +71,47 @@ for additional terms.
|
|||
New this version
|
||||
================
|
||||
|
||||
This is a minor bug-fix release since version 0.7.0, released Jan 29
|
||||
2009. Notable changes this version:
|
||||
This is a minor bug-fix and feature release since version 0.7.1,
|
||||
released Feb 9 2009. Notable changes this version:
|
||||
|
||||
- First version of a web-based installer
|
||||
- Use Net_URL_Mapper instead of mod_rewrite to map "fancy URLs",
|
||||
for a much simpler installation and use of PATH_INFO on sites
|
||||
that don't have mod_rewrite.
|
||||
- A plugin framework for system events, to make it easier to build
|
||||
server-side plugins.
|
||||
- A plugin for Google Analytics
|
||||
- A plugin to use blogspam.net to check notices for spam
|
||||
- A plugin to send linkbacks for notices about blog posts
|
||||
- Configurable check for duplicate notices in a specific time
|
||||
period
|
||||
- Better Atom feeds
|
||||
- First implementation of Twitter Search API
|
||||
- Add streamlined mobile device-friendly styles when enabled in config.
|
||||
- A queue server for sending notices to Twitter
|
||||
- A queue server for sending notices to Facebook
|
||||
- A queue server for sending notices to a ping server
|
||||
- Fixed a bug in nonces for OAuth in OpenMicroBlogging
|
||||
- Fixed bugs in transfer of avatars in OpenMicroBlogging
|
||||
- @-links go to permalinks for local users
|
||||
- Better handling of DB errors (instead of dreaded DB_DataObject blank
|
||||
screen)
|
||||
- Initial version of an RPM spec file
|
||||
- More consistent display of notices in notice search
|
||||
- A stylesheet for printed output
|
||||
- "Social graph" methods for Twitter API
|
||||
- Documentation for the JavaScript badge
|
||||
- Debugged a ton of problems that happened with E_NOTICE on
|
||||
- Better caching in RSS feeds
|
||||
- Optionally send email when an @-message is received
|
||||
- Automatically add tags for every group message
|
||||
- Add framebusting JavaScript to help avoid clickjacking attacks.
|
||||
- Optionally ignore some notice sources for public page.
|
||||
- Add default SMS carriers and notice sources to distribution file.
|
||||
- Change titles to use mixed case instead of all uppercase.
|
||||
- Use exceptions for error handling.
|
||||
|
||||
Changes in version 0.7.1:
|
||||
|
||||
- Vast improvement in auto-linking to URLs.
|
||||
- Link to group search from user's group page
|
||||
|
@ -749,16 +788,19 @@ to the end first before trying them.
|
|||
directory to your new directory.
|
||||
9. Copy htaccess.sample to .htaccess in the new directory. Change the
|
||||
RewriteBase to use the correct path.
|
||||
10. Rebuild the database. Go to your Laconica directory and run the
|
||||
rebuilddb.sh script like this:
|
||||
10. Rebuild the database. For MySQL, go to your Laconica directory and
|
||||
run the rebuilddb.sh script like this:
|
||||
|
||||
./scripts/rebuilddb.sh rootuser rootpassword database db/laconica.sql
|
||||
./scripts/rebuilddb.sh rootuser rootpassword database db/laconica.sql
|
||||
|
||||
Here, rootuser and rootpassword are the username and password for a
|
||||
user who can drop and create databases as well as tables; typically
|
||||
that's _not_ the user Laconica runs as.
|
||||
11. Use mysql client to log into your database and make sure that the
|
||||
notice, user, profile, subscription etc. tables are non-empty.
|
||||
Here, rootuser and rootpassword are the username and password for a
|
||||
user who can drop and create databases as well as tables; typically
|
||||
that's _not_ the user Laconica runs as.
|
||||
For PostgreSQL databases there is an equivalent, rebuilddb_psql.sh,
|
||||
which operates slightly differently. Read the documentation in that
|
||||
script before running it.
|
||||
11. Use mysql or psql client to log into your database and make sure that
|
||||
the notice, user, profile, subscription etc. tables are non-empty.
|
||||
12. Turn back on the Web server, and check that things still work.
|
||||
13. Turn back on XMPP bots and email maildaemon. Note that the XMPP
|
||||
bots have changed since version 0.5; see above for details.
|
||||
|
@ -883,6 +925,10 @@ notice: A plain string that will appear on every page. A good place
|
|||
to put introductory information about your service, or info about
|
||||
upgrades and outages, or other community info. Any HTML will
|
||||
be escaped.
|
||||
dupelimit: Time in which it's not OK for the same person to post the
|
||||
same notice; default = 60 seconds.
|
||||
logo: URL of an image file to use as the logo for the site. Overrides
|
||||
the logo in the theme, if any.
|
||||
|
||||
db
|
||||
--
|
||||
|
@ -1225,6 +1271,9 @@ if anyone's been overlooked in error.
|
|||
* Ken Sheppardson (Trac server, man-about-town)
|
||||
* Tiago 'gouki' Faria (i18n managerx)
|
||||
* Sean Murphy
|
||||
* Leslie Michael Orchard
|
||||
* Eric Helgeson
|
||||
* Ken Sedgwick
|
||||
|
||||
Thanks also to the developers of our upstream library code and to the
|
||||
thousands of people who have tried out Identi.ca, installed Laconi.ca,
|
||||
|
|
|
@ -77,12 +77,12 @@ class AllAction extends Action
|
|||
sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)),
|
||||
new Feed(Feed::RSS2,
|
||||
common_local_url('api', array('apiaction' => 'statuses',
|
||||
'method' => 'friends',
|
||||
'method' => 'friends_timeline',
|
||||
'argument' => $this->user->nickname.'.rss')),
|
||||
sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)),
|
||||
new Feed(Feed::ATOM,
|
||||
common_local_url('api', array('apiaction' => 'statuses',
|
||||
'method' => 'friends',
|
||||
'method' => 'friends_timeline',
|
||||
'argument' => $this->user->nickname.'.atom')),
|
||||
sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname)));
|
||||
}
|
||||
|
|
|
@ -81,8 +81,9 @@ class AllrssAction extends Rss10Action
|
|||
*/
|
||||
function getNotices($limit=0)
|
||||
{
|
||||
$user = $this->user;
|
||||
$notice = $user->noticesWithFriends(0, $limit);
|
||||
$user = $this->user;
|
||||
$notice = $user->noticesWithFriends(0, $limit);
|
||||
$notices = array();
|
||||
|
||||
while ($notice->fetch()) {
|
||||
$notices[] = clone($notice);
|
||||
|
|
|
@ -127,7 +127,9 @@ class ApiAction extends Action
|
|||
'laconica/wadl');
|
||||
|
||||
static $bareauth = array('statuses/user_timeline',
|
||||
'statuses/friends_timeline',
|
||||
'statuses/friends',
|
||||
'statuses/replies',
|
||||
'statuses/followers',
|
||||
'favorites/favorites');
|
||||
|
||||
|
|
|
@ -178,7 +178,7 @@ class FavoritedAction extends Action
|
|||
$qry = 'SELECT notice.*, '.
|
||||
$weightexpr . ' as weight ' .
|
||||
'FROM notice JOIN fave ON notice.id = fave.notice_id ' .
|
||||
'GROUP BY fave.notice_id ' .
|
||||
'GROUP BY id,profile_id,uri,content,rendered,url,created,notice.modified,reply_to,is_local,source ' .
|
||||
'ORDER BY weight DESC';
|
||||
|
||||
$offset = ($this->page - 1) * NOTICES_PER_PAGE;
|
||||
|
|
|
@ -62,9 +62,8 @@ class FinishopenidloginAction extends Action
|
|||
if ($this->error) {
|
||||
$this->element('div', array('class' => 'error'), $this->error);
|
||||
} else {
|
||||
global $config;
|
||||
$this->element('div', 'instructions',
|
||||
sprintf(_('This is the first time you\'ve logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), $config['site']['name']));
|
||||
sprintf(_('This is the first time you\'ve logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name')));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -136,16 +136,16 @@ class FinishremotesubscribeAction extends Action
|
|||
$profile->nickname = $nickname;
|
||||
$profile->profileurl = $profile_url;
|
||||
|
||||
if ($fullname) {
|
||||
if (!is_null($fullname)) {
|
||||
$profile->fullname = $fullname;
|
||||
}
|
||||
if ($homepage) {
|
||||
if (!is_null($homepage)) {
|
||||
$profile->homepage = $homepage;
|
||||
}
|
||||
if ($bio) {
|
||||
if (!is_null($bio)) {
|
||||
$profile->bio = $bio;
|
||||
}
|
||||
if ($location) {
|
||||
if (!is_null($location)) {
|
||||
$profile->location = $location;
|
||||
}
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ class GrouplogoAction extends Action
|
|||
|
||||
if ($nickname_arg != $nickname) {
|
||||
$args = array('nickname' => $nickname);
|
||||
common_redirect(common_local_url('editgroup', $args), 301);
|
||||
common_redirect(common_local_url('grouplogo', $args), 301);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ class JoingroupAction extends Action
|
|||
|
||||
if ($nickname_arg != $nickname) {
|
||||
$args = array('nickname' => $nickname);
|
||||
common_redirect(common_local_url('editgroup', $args), 301);
|
||||
common_redirect(common_local_url('joingroup', $args), 301);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ class LeavegroupAction extends Action
|
|||
|
||||
if ($nickname_arg != $nickname) {
|
||||
$args = array('nickname' => $nickname);
|
||||
common_redirect(common_local_url('editgroup', $args), 301);
|
||||
common_redirect(common_local_url('leavegroup', $args), 301);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -96,12 +96,6 @@ class LeavegroupAction extends Action
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($cur->isAdmin($this->group)) {
|
||||
$this->clientError(_('You may not leave a group while you are its administrator.'), 403);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -82,10 +82,9 @@ class NoticesearchrssAction extends Rss10Action
|
|||
|
||||
function getChannel()
|
||||
{
|
||||
global $config;
|
||||
$q = $this->trimmed('q');
|
||||
$c = array('url' => common_local_url('noticesearchrss', array('q' => $q)),
|
||||
'title' => $config['site']['name'] . sprintf(_(' Search Stream for "%s"'), $q),
|
||||
'title' => common_config('site', 'name') . sprintf(_(' Search Stream for "%s"'), $q),
|
||||
'link' => common_local_url('noticesearch', array('q' => $q)),
|
||||
'description' => sprintf(_('All updates matching search term "%s"'), $q));
|
||||
return $c;
|
||||
|
|
|
@ -119,7 +119,7 @@ class PeopletagAction extends Action
|
|||
'FROM profile JOIN profile_tag ' .
|
||||
'ON profile.id = profile_tag.tagger ' .
|
||||
'WHERE profile_tag.tagger = profile_tag.tagged ' .
|
||||
'AND tag = "%s" ' .
|
||||
"AND tag = '%s' " .
|
||||
'ORDER BY profile_tag.modified DESC%s';
|
||||
|
||||
$profile->query(sprintf($qry, $this->tag, $lim));
|
||||
|
|
|
@ -79,7 +79,7 @@ class PostnoticeAction extends Action
|
|||
}
|
||||
$notice = Notice::staticGet('uri', $notice_uri);
|
||||
if (!$notice) {
|
||||
$notice = Notice::saveNew($remote_profile->id, $content, 'omb', false, 0, $notice_uri);
|
||||
$notice = Notice::saveNew($remote_profile->id, $content, 'omb', false, null, $notice_uri);
|
||||
if (is_string($notice)) {
|
||||
common_server_serror($notice, 500);
|
||||
return false;
|
||||
|
|
|
@ -84,12 +84,11 @@ class PublicrssAction extends Rss10Action
|
|||
*/
|
||||
function getChannel()
|
||||
{
|
||||
global $config;
|
||||
$c = array(
|
||||
'url' => common_local_url('publicrss')
|
||||
, 'title' => sprintf(_('%s Public Stream'), $config['site']['name'])
|
||||
, 'title' => sprintf(_('%s Public Stream'), common_config('site', 'name'))
|
||||
, 'link' => common_local_url('public')
|
||||
, 'description' => sprintf(_('All updates for %s'), $config['site']['name']));
|
||||
, 'description' => sprintf(_('All updates for %s'), common_config('site', 'name')));
|
||||
return $c;
|
||||
}
|
||||
|
||||
|
|
|
@ -333,8 +333,6 @@ class RemotesubscribeAction extends Action
|
|||
|
||||
function requestAuthorization($user, $omb, $token, $secret)
|
||||
{
|
||||
global $config; # for license URL
|
||||
|
||||
$con = omb_oauth_consumer();
|
||||
$tok = new OAuthToken($token, $secret);
|
||||
|
||||
|
@ -358,7 +356,7 @@ class RemotesubscribeAction extends Action
|
|||
$req->set_parameter('omb_listenee', $user->uri);
|
||||
$req->set_parameter('omb_listenee_profile', common_profile_url($user->nickname));
|
||||
$req->set_parameter('omb_listenee_nickname', $user->nickname);
|
||||
$req->set_parameter('omb_listenee_license', $config['license']['url']);
|
||||
$req->set_parameter('omb_listenee_license', common_config('license', 'url'));
|
||||
|
||||
$profile = $user->getProfile();
|
||||
if (!$profile) {
|
||||
|
@ -367,16 +365,16 @@ class RemotesubscribeAction extends Action
|
|||
return;
|
||||
}
|
||||
|
||||
if ($profile->fullname) {
|
||||
if (!is_null($profile->fullname)) {
|
||||
$req->set_parameter('omb_listenee_fullname', $profile->fullname);
|
||||
}
|
||||
if ($profile->homepage) {
|
||||
if (!is_null($profile->homepage)) {
|
||||
$req->set_parameter('omb_listenee_homepage', $profile->homepage);
|
||||
}
|
||||
if ($profile->bio) {
|
||||
if (!is_null($profile->bio)) {
|
||||
$req->set_parameter('omb_listenee_bio', $profile->bio);
|
||||
}
|
||||
if ($profile->location) {
|
||||
if (!is_null($profile->location)) {
|
||||
$req->set_parameter('omb_listenee_location', $profile->location);
|
||||
}
|
||||
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
|
||||
|
|
|
@ -275,10 +275,8 @@ class ShowgroupAction extends Action
|
|||
$cur = common_current_user();
|
||||
if ($cur) {
|
||||
if ($cur->isMember($this->group)) {
|
||||
if (!$cur->isAdmin($this->group)) {
|
||||
$lf = new LeaveForm($this, $this->group);
|
||||
$lf->show();
|
||||
}
|
||||
$lf = new LeaveForm($this, $this->group);
|
||||
$lf->show();
|
||||
} else {
|
||||
$jf = new JoinForm($this, $this->group);
|
||||
$jf->show();
|
||||
|
|
|
@ -65,7 +65,9 @@ class SupAction extends Action
|
|||
|
||||
$notice->query('SELECT profile_id, max(id) AS max_id ' .
|
||||
'FROM notice ' .
|
||||
'WHERE created > (now() - ' . $seconds . ') ' .
|
||||
((common_config('db','type') == 'pgsql') ?
|
||||
'WHERE extract(epoch from created) > (extract(epoch from now()) - ' . $seconds . ') ' :
|
||||
'WHERE created > (now() - ' . $seconds . ') ' ) .
|
||||
'GROUP BY profile_id');
|
||||
|
||||
$updates = array();
|
||||
|
|
|
@ -38,7 +38,6 @@ class Twitapidirect_messagesAction extends TwitterapiAction
|
|||
|
||||
function show_messages($args, $apidata, $type)
|
||||
{
|
||||
|
||||
$user = $apidata['user'];
|
||||
|
||||
$count = $this->arg('count');
|
||||
|
@ -102,7 +101,17 @@ class Twitapidirect_messagesAction extends TwitterapiAction
|
|||
$this->show_rss_dmsgs($message, $title, $link, $subtitle);
|
||||
break;
|
||||
case 'atom':
|
||||
$this->show_atom_dmsgs($message, $title, $link, $subtitle);
|
||||
$selfuri = common_root_url() . 'api/direct_messages';
|
||||
$selfuri .= ($type == 'received') ? '.atom' : '/sent.atom';
|
||||
$taguribase = common_config('integration', 'taguri');
|
||||
|
||||
if ($type == 'sent') {
|
||||
$id = "tag:$taguribase:SentDirectMessages:" . $user->id;
|
||||
} else {
|
||||
$id = "tag:$taguribase:DirectMessages:" . $user->id;
|
||||
}
|
||||
|
||||
$this->show_atom_dmsgs($message, $title, $link, $subtitle, $selfuri, $id);
|
||||
break;
|
||||
case 'json':
|
||||
$this->show_json_dmsgs($message);
|
||||
|
@ -190,7 +199,7 @@ class Twitapidirect_messagesAction extends TwitterapiAction
|
|||
$this->init_document('xml');
|
||||
$this->elementStart('direct-messages', array('type' => 'array'));
|
||||
|
||||
if (is_array($messages)) {
|
||||
if (is_array($message)) {
|
||||
foreach ($message as $m) {
|
||||
$twitter_dm = $this->twitter_dmsg_array($m);
|
||||
$this->show_twitter_xml_dmsg($twitter_dm);
|
||||
|
@ -261,16 +270,17 @@ class Twitapidirect_messagesAction extends TwitterapiAction
|
|||
|
||||
}
|
||||
|
||||
function show_atom_dmsgs($message, $title, $link, $subtitle)
|
||||
function show_atom_dmsgs($message, $title, $link, $subtitle, $selfuri, $id)
|
||||
{
|
||||
|
||||
$this->init_document('atom');
|
||||
|
||||
$this->element('title', null, $title);
|
||||
$siteserver = common_config('site', 'server');
|
||||
$this->element('id', null, "tag:$siteserver,2008:DirectMessage");
|
||||
$this->element('id', null, $id);
|
||||
$this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
|
||||
$this->element('updated', null, common_date_iso8601(strftime('%c')));
|
||||
$this->element('link', array('href' => $selfuri, 'rel' => 'self',
|
||||
'type' => 'application/atom+xml'), null);
|
||||
$this->element('updated', null, common_date_iso8601('now'));
|
||||
$this->element('subtitle', null, $subtitle);
|
||||
|
||||
if (is_array($message)) {
|
||||
|
|
|
@ -61,10 +61,9 @@ class TwitapifavoritesAction extends TwitterapiAction
|
|||
}
|
||||
|
||||
$sitename = common_config('site', 'name');
|
||||
$siteserver = common_config('site', 'server');
|
||||
|
||||
$title = sprintf(_('%s / Favorites from %s'), $sitename, $user->nickname);
|
||||
$id = "tag:$siteserver:favorites:".$user->id;
|
||||
$taguribase = common_config('integration', 'taguri');
|
||||
$id = "tag:$taguribase:Favorites:".$user->id;
|
||||
$link = common_local_url('favorites', array('nickname' => $user->nickname));
|
||||
$subtitle = sprintf(_('%s updates favorited by %s / %s.'), $sitename, $profile->getBestName(), $user->nickname);
|
||||
|
||||
|
@ -76,7 +75,14 @@ class TwitapifavoritesAction extends TwitterapiAction
|
|||
$this->show_rss_timeline($notice, $title, $link, $subtitle);
|
||||
break;
|
||||
case 'atom':
|
||||
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle);
|
||||
if (isset($apidata['api_arg'])) {
|
||||
$selfuri = $selfuri = common_root_url() .
|
||||
'api/favorites/' . $apidata['api_arg'] . '.atom';
|
||||
} else {
|
||||
$selfuri = $selfuri = common_root_url() .
|
||||
'api/favorites.atom';
|
||||
}
|
||||
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle, null, $selfuri);
|
||||
break;
|
||||
case 'json':
|
||||
$this->show_json_timeline($notice);
|
||||
|
|
377
actions/twitapisearchatom.php
Normal file
377
actions/twitapisearchatom.php
Normal file
|
@ -0,0 +1,377 @@
|
|||
<?php
|
||||
/**
|
||||
* Laconica, the distributed open-source microblogging tool
|
||||
*
|
||||
* Action for showing Twitter-like Atom search results
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENCE: This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Search
|
||||
* @package Laconica
|
||||
* @author Zach Copley <zach@controlyourself.ca>
|
||||
* @copyright 2008-2009 Control Yourself, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://laconi.ca/
|
||||
*/
|
||||
|
||||
if (!defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once INSTALLDIR.'/lib/twitterapi.php';
|
||||
|
||||
/**
|
||||
* Action for outputting search results in Twitter compatible Atom
|
||||
* format.
|
||||
*
|
||||
* TODO: abstract Atom stuff into a ruseable base class like
|
||||
* RSS10Action.
|
||||
*
|
||||
* @category Search
|
||||
* @package Laconica
|
||||
* @author Zach Copley <zach@controlyourself.ca>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://laconi.ca/
|
||||
*
|
||||
* @see TwitterapiAction
|
||||
*/
|
||||
|
||||
class TwitapisearchatomAction extends TwitterapiAction
|
||||
{
|
||||
|
||||
var $cnt;
|
||||
var $query;
|
||||
var $lang;
|
||||
var $rpp;
|
||||
var $page;
|
||||
var $since_id;
|
||||
var $geocode;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Just wraps the Action constructor.
|
||||
*
|
||||
* @param string $output URI to output to, default = stdout
|
||||
* @param boolean $indent Whether to indent output, default true
|
||||
*
|
||||
* @see Action::__construct
|
||||
*/
|
||||
|
||||
function __construct($output='php://output', $indent=true)
|
||||
{
|
||||
parent::__construct($output, $indent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do we need to write to the database?
|
||||
*
|
||||
* @return boolean true
|
||||
*/
|
||||
|
||||
function isReadonly()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read arguments and initialize members
|
||||
*
|
||||
* @param array $args Arguments from $_REQUEST
|
||||
*
|
||||
* @return boolean success
|
||||
*
|
||||
*/
|
||||
|
||||
function prepare($args)
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
$this->query = $this->trimmed('q');
|
||||
$this->lang = $this->trimmed('lang');
|
||||
$this->rpp = $this->trimmed('rpp');
|
||||
|
||||
if (!$this->rpp) {
|
||||
$this->rpp = 15;
|
||||
}
|
||||
|
||||
if ($this->rpp > 100) {
|
||||
$this->rpp = 100;
|
||||
}
|
||||
|
||||
$this->page = $this->trimmed('page');
|
||||
|
||||
if (!$this->page) {
|
||||
$this->page = 1;
|
||||
}
|
||||
|
||||
// TODO: Suppport since_id -- we need to tweak the backend
|
||||
// Search classes to support it.
|
||||
|
||||
$this->since_id = $this->trimmed('since_id');
|
||||
$this->geocode = $this->trimmed('geocode');
|
||||
|
||||
// TODO: Also, language and geocode
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a request
|
||||
*
|
||||
* @param array $args Arguments from $_REQUEST
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function handle($args)
|
||||
{
|
||||
parent::handle($args);
|
||||
$this->showAtom();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notices to output as results. This also sets some class
|
||||
* attrs so we can use them to calculate pagination, and output
|
||||
* since_id and max_id.
|
||||
*
|
||||
* @return array an array of Notice objects sorted in reverse chron
|
||||
*/
|
||||
|
||||
function getNotices()
|
||||
{
|
||||
// TODO: Support search operators like from: and to:, boolean, etc.
|
||||
|
||||
$notices = array();
|
||||
$notice = new Notice();
|
||||
|
||||
// lcase it for comparison
|
||||
$q = strtolower($this->query);
|
||||
|
||||
$search_engine = $notice->getSearchEngine('identica_notices');
|
||||
$search_engine->set_sort_mode('chron');
|
||||
$search_engine->limit(($this->page - 1) * $this->rpp,
|
||||
$this->rpp + 1, true);
|
||||
$search_engine->query($q);
|
||||
$this->cnt = $notice->find();
|
||||
|
||||
$cnt = 0;
|
||||
|
||||
while ($notice->fetch()) {
|
||||
|
||||
++$cnt;
|
||||
|
||||
if (!$this->max_id) {
|
||||
$this->max_id = $notice->id;
|
||||
}
|
||||
|
||||
if ($cnt > $this->rpp) {
|
||||
break;
|
||||
}
|
||||
|
||||
$notices[] = clone($notice);
|
||||
}
|
||||
|
||||
return $notices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output search results as an Atom feed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function showAtom()
|
||||
{
|
||||
$notices = $this->getNotices();
|
||||
|
||||
$this->initAtom();
|
||||
$this->showFeed();
|
||||
|
||||
foreach ($notices as $n) {
|
||||
$this->showEntry($n);
|
||||
}
|
||||
|
||||
$this->endAtom();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show feed specific Atom elements
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function showFeed()
|
||||
{
|
||||
// TODO: A9 OpenSearch stuff like search.twitter.com?
|
||||
|
||||
$server = common_config('site', 'server');
|
||||
$sitename = common_config('site', 'name');
|
||||
|
||||
// XXX: Use xmlns:laconica instead?
|
||||
|
||||
$this->elementStart('feed',
|
||||
array('xmlns' => 'http://www.w3.org/2005/Atom',
|
||||
|
||||
// XXX: xmlns:twitter causes Atom validation to fail
|
||||
// It's used for the source attr on notices
|
||||
|
||||
'xmlns:twitter' => 'http://api.twitter.com/',
|
||||
'xml:lang' => 'en-US')); // XXX Other locales ?
|
||||
|
||||
$taguribase = common_config('integration', 'taguri');
|
||||
$this->element('id', null, "tag:$taguribase:search/$server");
|
||||
|
||||
$site_uri = common_path(false);
|
||||
|
||||
$search_uri = $site_uri . 'api/search.atom?q=' . urlencode($this->query);
|
||||
|
||||
if ($this->rpp != 15) {
|
||||
$search_uri .= '&rpp=' . $this->rpp;
|
||||
}
|
||||
|
||||
// FIXME: this alternate link is not quite right because our
|
||||
// web-based notice search doesn't support a rpp (responses per
|
||||
// page) param yet
|
||||
|
||||
$this->element('link', array('type' => 'text/html',
|
||||
'rel' => 'alternate',
|
||||
'href' => $site_uri . 'search/notice?q=' .
|
||||
urlencode($this->query)));
|
||||
|
||||
// self link
|
||||
|
||||
$self_uri = $search_uri;
|
||||
$self_uri .= ($this->page > 1) ? '&page=' . $this->page : '';
|
||||
|
||||
$this->element('link', array('type' => 'application/atom+xml',
|
||||
'rel' => 'self',
|
||||
'href' => $self_uri));
|
||||
|
||||
$this->element('title', null, "$this->query - $sitename Search");
|
||||
$this->element('updated', null, common_date_iso8601('now'));
|
||||
|
||||
// XXX: The below "rel" links are not valid Atom, but it's what
|
||||
// Twitter does...
|
||||
|
||||
// refresh link
|
||||
|
||||
$refresh_uri = $search_uri . "&since_id=" . $this->max_id;
|
||||
|
||||
$this->element('link', array('type' => 'application/atom+xml',
|
||||
'rel' => 'refresh',
|
||||
'href' => $refresh_uri));
|
||||
|
||||
// pagination links
|
||||
|
||||
if ($this->cnt > $this->rpp) {
|
||||
|
||||
$next_uri = $search_uri . "&max_id=" . $this->max_id .
|
||||
'&page=' . ($this->page + 1);
|
||||
|
||||
$this->element('link', array('type' => 'application/atom+xml',
|
||||
'rel' => 'next',
|
||||
'href' => $next_uri));
|
||||
}
|
||||
|
||||
if ($this->page > 1) {
|
||||
|
||||
$previous_uri = $search_uri . "&max_id=" . $this->max_id .
|
||||
'&page=' . ($this->page - 1);
|
||||
|
||||
$this->element('link', array('type' => 'application/atom+xml',
|
||||
'rel' => 'previous',
|
||||
'href' => $previous_uri));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an Atom entry similar to search.twitter.com's based on
|
||||
* a given notice
|
||||
*
|
||||
* @param Notice $notice the notice to use
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function showEntry($notice)
|
||||
{
|
||||
$server = common_config('site', 'server');
|
||||
$profile = $notice->getProfile();
|
||||
$nurl = common_local_url('shownotice', array('notice' => $notice->id));
|
||||
|
||||
$this->elementStart('entry');
|
||||
|
||||
$taguribase = common_config('integration', 'taguri');
|
||||
|
||||
$this->element('id', null, "tag:$taguribase:$notice->id");
|
||||
$this->element('published', null, common_date_w3dtf($notice->created));
|
||||
$this->element('link', array('type' => 'text/html',
|
||||
'rel' => 'alternate',
|
||||
'href' => $nurl));
|
||||
$this->element('title', null, common_xml_safe_str(trim($notice->content)));
|
||||
$this->element('content', array('type' => 'html'), $notice->rendered);
|
||||
$this->element('updated', null, common_date_w3dtf($notice->created));
|
||||
$this->element('link', array('type' => 'image/png',
|
||||
// XXX: Twitter uses rel="image" (not valid)
|
||||
'rel' => 'related',
|
||||
'href' => $profile->avatarUrl()));
|
||||
|
||||
// TODO: Here is where we'd put in a link to an atom feed for threads
|
||||
|
||||
$this->element("twitter:source", null,
|
||||
htmlentities($this->source_link($notice->source)));
|
||||
|
||||
$this->elementStart('author');
|
||||
|
||||
$name = $profile->nickname;
|
||||
|
||||
if ($profile->fullname) {
|
||||
$name .= ' (' . $profile->fullname . ')';
|
||||
}
|
||||
|
||||
$this->element('name', null, $name);
|
||||
$this->element('uri', null, common_profile_uri($profile));
|
||||
$this->elementEnd('author');
|
||||
|
||||
$this->elementEnd('entry');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Atom output, send headers
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function initAtom()
|
||||
{
|
||||
header('Content-Type: application/atom+xml; charset=utf-8');
|
||||
$this->startXml();
|
||||
}
|
||||
|
||||
/**
|
||||
* End the Atom feed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function endAtom()
|
||||
{
|
||||
$this->elementEnd('feed');
|
||||
}
|
||||
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
/**
|
||||
* Laconica, the distributed open-source microblogging tool
|
||||
*
|
||||
* List of replies
|
||||
* Action for showing Twitter-like JSON search results
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
|
@ -114,7 +114,7 @@ class TwitapisearchjsonAction extends TwitterapiAction
|
|||
function showResults()
|
||||
{
|
||||
|
||||
// TODO: Support search operators like from: and to:
|
||||
// TODO: Support search operators like from: and to:, boolean, etc.
|
||||
|
||||
$notice = new Notice();
|
||||
|
||||
|
@ -137,7 +137,7 @@ class TwitapisearchjsonAction extends TwitterapiAction
|
|||
}
|
||||
|
||||
/**
|
||||
* This is a read-only action
|
||||
* Do we need to write to the database?
|
||||
*
|
||||
* @return boolean true
|
||||
*/
|
||||
|
|
|
@ -29,10 +29,12 @@ class TwitapistatusesAction extends TwitterapiAction
|
|||
parent::handle($args);
|
||||
|
||||
$sitename = common_config('site', 'name');
|
||||
$siteserver = common_config('site', 'server');
|
||||
$title = sprintf(_("%s public timeline"), $sitename);
|
||||
$id = "tag:$siteserver:Statuses";
|
||||
|
||||
$taguribase = common_config('integration', 'taguri');
|
||||
$id = "tag:$taguribase:PublicTimeline";
|
||||
$link = common_root_url();
|
||||
|
||||
$subtitle = sprintf(_("%s updates from everyone!"), $sitename);
|
||||
|
||||
// Number of public statuses to return by default -- Twitter sends 20
|
||||
|
@ -70,7 +72,8 @@ class TwitapistatusesAction extends TwitterapiAction
|
|||
$this->show_rss_timeline($notice, $title, $link, $subtitle);
|
||||
break;
|
||||
case 'atom':
|
||||
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle);
|
||||
$selfuri = common_root_url() . 'api/statuses/public_timeline.atom';
|
||||
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle, null, $selfuri);
|
||||
break;
|
||||
case 'json':
|
||||
$this->show_json_timeline($notice);
|
||||
|
@ -114,17 +117,19 @@ class TwitapistatusesAction extends TwitterapiAction
|
|||
}
|
||||
|
||||
$since = strtotime($this->arg('since'));
|
||||
|
||||
$user = $this->get_user(null, $apidata);
|
||||
$user = $this->get_user($apidata['api_arg'], $apidata);
|
||||
$this->auth_user = $user;
|
||||
|
||||
if (empty($user)) {
|
||||
$this->clientError(_('No such user!'), 404, $apidata['content-type']);
|
||||
return;
|
||||
}
|
||||
|
||||
$profile = $user->getProfile();
|
||||
|
||||
$sitename = common_config('site', 'name');
|
||||
$siteserver = common_config('site', 'server');
|
||||
|
||||
$title = sprintf(_("%s and friends"), $user->nickname);
|
||||
$id = "tag:$siteserver:friends:" . $user->id;
|
||||
$taguribase = common_config('integration', 'taguri');
|
||||
$id = "tag:$taguribase:FriendsTimeline:" . $user->id;
|
||||
$link = common_local_url('all', array('nickname' => $user->nickname));
|
||||
$subtitle = sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, $sitename);
|
||||
|
||||
|
@ -138,7 +143,14 @@ class TwitapistatusesAction extends TwitterapiAction
|
|||
$this->show_rss_timeline($notice, $title, $link, $subtitle);
|
||||
break;
|
||||
case 'atom':
|
||||
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle);
|
||||
if (isset($apidata['api_arg'])) {
|
||||
$selfuri = $selfuri = common_root_url() .
|
||||
'api/statuses/friends_timeline/' . $apidata['api_arg'] . '.atom';
|
||||
} else {
|
||||
$selfuri = $selfuri = common_root_url() .
|
||||
'api/statuses/friends_timeline.atom';
|
||||
}
|
||||
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle, null, $selfuri);
|
||||
break;
|
||||
case 'json':
|
||||
$this->show_json_timeline($notice);
|
||||
|
@ -194,10 +206,9 @@ class TwitapistatusesAction extends TwitterapiAction
|
|||
$since = strtotime($this->arg('since'));
|
||||
|
||||
$sitename = common_config('site', 'name');
|
||||
$siteserver = common_config('site', 'server');
|
||||
|
||||
$title = sprintf(_("%s timeline"), $user->nickname);
|
||||
$id = "tag:$siteserver:user:".$user->id;
|
||||
$taguribase = common_config('integration', 'taguri');
|
||||
$id = "tag:$taguribase:UserTimeline:".$user->id;
|
||||
$link = common_local_url('showstream', array('nickname' => $user->nickname));
|
||||
$subtitle = sprintf(_('Updates from %1$s on %2$s!'), $user->nickname, $sitename);
|
||||
|
||||
|
@ -219,7 +230,14 @@ class TwitapistatusesAction extends TwitterapiAction
|
|||
$this->show_rss_timeline($notice, $title, $link, $subtitle, $suplink);
|
||||
break;
|
||||
case 'atom':
|
||||
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle, $suplink);
|
||||
if (isset($apidata['api_arg'])) {
|
||||
$selfuri = $selfuri = common_root_url() .
|
||||
'api/statuses/user_timeline/' . $apidata['api_arg'] . '.atom';
|
||||
} else {
|
||||
$selfuri = $selfuri = common_root_url() .
|
||||
'api/statuses/user_timeline.atom';
|
||||
}
|
||||
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle, $suplink, $selfuri);
|
||||
break;
|
||||
case 'json':
|
||||
$this->show_json_timeline($notice);
|
||||
|
@ -337,15 +355,14 @@ class TwitapistatusesAction extends TwitterapiAction
|
|||
$since_id = $this->arg('since_id');
|
||||
$before_id = $this->arg('before_id');
|
||||
|
||||
$user = $this->get_user($apidata['api_arg'], $apidata);
|
||||
$this->auth_user = $apidata['user'];
|
||||
$user = $this->auth_user;
|
||||
$profile = $user->getProfile();
|
||||
|
||||
$sitename = common_config('site', 'name');
|
||||
$siteserver = common_config('site', 'server');
|
||||
|
||||
$title = sprintf(_('%1$s / Updates replying to %2$s'), $sitename, $user->nickname);
|
||||
$id = "tag:$siteserver:replies:".$user->id;
|
||||
$taguribase = common_config('integration', 'taguri');
|
||||
$id = "tag:$taguribase:Replies:".$user->id;
|
||||
$link = common_local_url('replies', array('nickname' => $user->nickname));
|
||||
$subtitle = sprintf(_('%1$s updates that reply to updates from %2$s / %3$s.'), $sitename, $user->nickname, $profile->getBestName());
|
||||
|
||||
|
@ -383,7 +400,14 @@ class TwitapistatusesAction extends TwitterapiAction
|
|||
$this->show_rss_timeline($notices, $title, $link, $subtitle);
|
||||
break;
|
||||
case 'atom':
|
||||
$this->show_atom_timeline($notices, $title, $id, $link, $subtitle);
|
||||
if (isset($apidata['api_arg'])) {
|
||||
$selfuri = $selfuri = common_root_url() .
|
||||
'api/statuses/replies/' . $apidata['api_arg'] . '.atom';
|
||||
} else {
|
||||
$selfuri = $selfuri = common_root_url() .
|
||||
'api/statuses/replies.atom';
|
||||
}
|
||||
$this->show_atom_timeline($notices, $title, $id, $link, $subtitle, null, $selfuri);
|
||||
break;
|
||||
case 'json':
|
||||
$this->show_json_timeline($notices);
|
||||
|
|
|
@ -186,12 +186,12 @@ class TwittersettingsAction extends ConnectSettingsAction
|
|||
|
||||
$current_user = common_current_user();
|
||||
|
||||
$qry = 'SELECT user.* ' .
|
||||
$qry = 'SELECT "user".* ' .
|
||||
'FROM subscription ' .
|
||||
'JOIN user ON subscription.subscribed = user.id ' .
|
||||
'JOIN foreign_link ON foreign_link.user_id = user.id ' .
|
||||
'JOIN "user" ON subscription.subscribed = "user".id ' .
|
||||
'JOIN foreign_link ON foreign_link.user_id = "user".id ' .
|
||||
'WHERE subscriber = %d ' .
|
||||
'ORDER BY user.nickname';
|
||||
'ORDER BY "user".nickname';
|
||||
|
||||
$user = new User();
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ class UpdateprofileAction extends Action
|
|||
$server = omb_oauth_server();
|
||||
list($consumer, $token) = $server->verify_request($req);
|
||||
if ($this->update_profile($req, $consumer, $token)) {
|
||||
header('HTTP/1.1 200 OK');
|
||||
header('Content-type: text/plain');
|
||||
print "omb_version=".OMB_VERSION_01;
|
||||
}
|
||||
} catch (OAuthException $e) {
|
||||
|
@ -136,22 +138,24 @@ class UpdateprofileAction extends Action
|
|||
|
||||
$orig_profile = clone($profile);
|
||||
|
||||
if ($nickname) {
|
||||
/* Use values even if they are an empty string. Parsing an empty string in
|
||||
updateProfile is the specified way of clearing a parameter in OMB. */
|
||||
if (!is_null($nickname)) {
|
||||
$profile->nickname = $nickname;
|
||||
}
|
||||
if ($profile_url) {
|
||||
if (!is_null($profile_url)) {
|
||||
$profile->profileurl = $profile_url;
|
||||
}
|
||||
if ($fullname) {
|
||||
if (!is_null($fullname)) {
|
||||
$profile->fullname = $fullname;
|
||||
}
|
||||
if ($homepage) {
|
||||
if (!is_null($homepage)) {
|
||||
$profile->homepage = $homepage;
|
||||
}
|
||||
if ($bio) {
|
||||
if (!is_null($bio)) {
|
||||
$profile->bio = $bio;
|
||||
}
|
||||
if ($location) {
|
||||
if (!is_null($location)) {
|
||||
$profile->location = $location;
|
||||
}
|
||||
|
||||
|
@ -173,10 +177,6 @@ class UpdateprofileAction extends Action
|
|||
return false;
|
||||
}
|
||||
}
|
||||
header('HTTP/1.1 200 OK');
|
||||
header('Content-type: text/plain');
|
||||
print 'Updated profile';
|
||||
print "\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,9 +113,9 @@ class UserauthorizationAction extends Action
|
|||
$this->element('a', array('href' => $profile,
|
||||
'class' => 'external profile nickname'),
|
||||
$nickname);
|
||||
if ($fullname) {
|
||||
if (!is_null($fullname)) {
|
||||
$this->elementStart('div', 'fullname');
|
||||
if ($homepage) {
|
||||
if (!is_null($homepage)) {
|
||||
$this->element('a', array('href' => $homepage),
|
||||
$fullname);
|
||||
} else {
|
||||
|
@ -123,10 +123,10 @@ class UserauthorizationAction extends Action
|
|||
}
|
||||
$this->elementEnd('div');
|
||||
}
|
||||
if ($location) {
|
||||
if (!is_null($location)) {
|
||||
$this->element('div', 'location', $location);
|
||||
}
|
||||
if ($bio) {
|
||||
if (!is_null($bio)) {
|
||||
$this->element('div', 'bio', $bio);
|
||||
}
|
||||
$this->elementStart('div', 'license');
|
||||
|
@ -179,16 +179,16 @@ class UserauthorizationAction extends Action
|
|||
$params['omb_listener_nickname'] = $user->nickname;
|
||||
$params['omb_listener_profile'] = common_local_url('showstream',
|
||||
array('nickname' => $user->nickname));
|
||||
if ($profile->fullname) {
|
||||
if (!is_null($profile->fullname)) {
|
||||
$params['omb_listener_fullname'] = $profile->fullname;
|
||||
}
|
||||
if ($profile->homepage) {
|
||||
if (!is_null($profile->homepage)) {
|
||||
$params['omb_listener_homepage'] = $profile->homepage;
|
||||
}
|
||||
if ($profile->bio) {
|
||||
if (!is_null($profile->bio)) {
|
||||
$params['omb_listener_bio'] = $profile->bio;
|
||||
}
|
||||
if ($profile->location) {
|
||||
if (!is_null($profile->location)) {
|
||||
$params['omb_listener_location'] = $profile->location;
|
||||
}
|
||||
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
|
||||
|
@ -197,7 +197,7 @@ class UserauthorizationAction extends Action
|
|||
}
|
||||
$parts = array();
|
||||
foreach ($params as $k => $v) {
|
||||
$parts[] = $k . '=' . OAuthUtil::urlencodeRFC3986($v);
|
||||
$parts[] = $k . '=' . OAuthUtil::urlencode_rfc3986($v);
|
||||
}
|
||||
$query_string = implode('&', $parts);
|
||||
$parsed = parse_url($callback);
|
||||
|
@ -267,16 +267,16 @@ class UserauthorizationAction extends Action
|
|||
$profile->nickname = $nickname;
|
||||
$profile->profileurl = $profile_url;
|
||||
|
||||
if ($fullname) {
|
||||
if (!is_null($fullname)) {
|
||||
$profile->fullname = $fullname;
|
||||
}
|
||||
if ($homepage) {
|
||||
if (!is_null($homepage)) {
|
||||
$profile->homepage = $homepage;
|
||||
}
|
||||
if ($bio) {
|
||||
if (!is_null($bio)) {
|
||||
$profile->bio = $bio;
|
||||
}
|
||||
if ($location) {
|
||||
if (!is_null($location)) {
|
||||
$profile->location = $location;
|
||||
}
|
||||
|
||||
|
@ -409,7 +409,7 @@ class UserauthorizationAction extends Action
|
|||
'omb_listenee_profile', 'omb_listenee_nickname',
|
||||
'omb_listenee_license') as $param)
|
||||
{
|
||||
if (!$req->get_parameter($param)) {
|
||||
if (is_null($req->get_parameter($param))) {
|
||||
throw new OAuthException("Required parameter '$param' not found");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ class UserrssAction extends Rss10Action
|
|||
|
||||
$notice = $user->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit);
|
||||
|
||||
$notices = array();
|
||||
while ($notice->fetch()) {
|
||||
$notices[] = clone($notice);
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -4,22 +4,21 @@
|
|||
*/
|
||||
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
|
||||
|
||||
class Nonce extends Memcached_DataObject
|
||||
class Nonce extends Memcached_DataObject
|
||||
{
|
||||
###START_AUTOCODE
|
||||
/* the code below is auto generated do not remove the above tag */
|
||||
|
||||
public $__table = 'nonce'; // table name
|
||||
public $consumer_key; // varchar(255) primary_key not_null
|
||||
public $tok; // char(32) primary_key not_null
|
||||
public $tok; // char(32)
|
||||
public $nonce; // char(32) primary_key not_null
|
||||
public $ts; // datetime() not_null
|
||||
public $ts; // datetime() primary_key not_null
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
/* Static get */
|
||||
function staticGet($k,$v=null)
|
||||
{ return Memcached_DataObject::staticGet('Nonce',$k,$v); }
|
||||
function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Nonce',$k,$v); }
|
||||
|
||||
/* the code above is auto generated do not remove the tag below */
|
||||
###END_AUTOCODE
|
||||
|
|
|
@ -122,6 +122,8 @@ class Notice extends Memcached_DataObject
|
|||
|
||||
$profile = Profile::staticGet($profile_id);
|
||||
|
||||
$final = common_shorten_links($content);
|
||||
|
||||
if (!$profile) {
|
||||
common_log(LOG_ERR, 'Problem saving notice. Unknown user.');
|
||||
return _('Problem saving notice. Unknown user.');
|
||||
|
@ -132,7 +134,12 @@ class Notice extends Memcached_DataObject
|
|||
return _('Too many notices too fast; take a breather and post again in a few minutes.');
|
||||
}
|
||||
|
||||
$banned = common_config('profile', 'banned');
|
||||
if (common_config('site', 'dupelimit') > 0 && !Notice::checkDupes($profile_id, $final)) {
|
||||
common_log(LOG_WARNING, 'Dupe posting by profile #' . $profile_id . '; throttled.');
|
||||
return _('Too many duplicate messages too quickly; take a breather and post again in a few minutes.');
|
||||
}
|
||||
|
||||
$banned = common_config('profile', 'banned');
|
||||
|
||||
if ( in_array($profile_id, $banned) || in_array($profile->nickname, $banned)) {
|
||||
common_log(LOG_WARNING, "Attempted post from banned user: $profile->nickname (user id = $profile_id).");
|
||||
|
@ -156,11 +163,12 @@ class Notice extends Memcached_DataObject
|
|||
|
||||
$notice->query('BEGIN');
|
||||
|
||||
$notice->created = common_sql_now();
|
||||
$notice->content = common_shorten_links($content);
|
||||
$notice->rendered = common_render_content($notice->content, $notice);
|
||||
$notice->source = $source;
|
||||
$notice->uri = $uri;
|
||||
$notice->reply_to = $reply_to;
|
||||
$notice->created = common_sql_now();
|
||||
$notice->content = $final;
|
||||
$notice->rendered = common_render_content($final, $notice);
|
||||
$notice->source = $source;
|
||||
$notice->uri = $uri;
|
||||
|
||||
if (!empty($reply_to)) {
|
||||
$reply_notice = Notice::staticGet('id', $reply_to);
|
||||
|
@ -212,6 +220,36 @@ class Notice extends Memcached_DataObject
|
|||
return $notice;
|
||||
}
|
||||
|
||||
static function checkDupes($profile_id, $content) {
|
||||
$profile = Profile::staticGet($profile_id);
|
||||
if (!$profile) {
|
||||
return false;
|
||||
}
|
||||
$notice = $profile->getNotices(0, NOTICE_CACHE_WINDOW);
|
||||
if ($notice) {
|
||||
$last = 0;
|
||||
while ($notice->fetch()) {
|
||||
if (time() - strtotime($notice->created) >= common_config('site', 'dupelimit')) {
|
||||
return true;
|
||||
} else if ($notice->content == $content) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
# If we get here, oldest item in cache window is not
|
||||
# old enough for dupe limit; do direct check against DB
|
||||
$notice = new Notice();
|
||||
$notice->profile_id = $profile_id;
|
||||
$notice->content = $content;
|
||||
if (common_config('db','type') == 'pgsql')
|
||||
$notice->whereAdd('extract(epoch from now() - created) < ' . common_config('site', 'dupelimit'));
|
||||
else
|
||||
$notice->whereAdd('now() - created < ' . common_config('site', 'dupelimit'));
|
||||
|
||||
$cnt = $notice->count();
|
||||
return ($cnt == 0);
|
||||
}
|
||||
|
||||
static function checkEditThrottle($profile_id) {
|
||||
$profile = Profile::staticGet($profile_id);
|
||||
if (!$profile) {
|
||||
|
|
|
@ -121,7 +121,8 @@ class User extends Memcached_DataObject
|
|||
static $blacklist = array('rss', 'xrds', 'doc', 'main',
|
||||
'settings', 'notice', 'user',
|
||||
'search', 'avatar', 'tag', 'tags',
|
||||
'api', 'message', 'group', 'groups');
|
||||
'api', 'message', 'group', 'groups',
|
||||
'local');
|
||||
$merged = array_merge($blacklist, common_config('nickname', 'blacklist'));
|
||||
return !in_array($nickname, $merged);
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ id = N
|
|||
|
||||
[nonce]
|
||||
consumer_key = 130
|
||||
tok = 130
|
||||
tok = 2
|
||||
nonce = 130
|
||||
ts = 142
|
||||
created = 142
|
||||
|
@ -153,8 +153,8 @@ modified = 384
|
|||
|
||||
[nonce__keys]
|
||||
consumer_key = K
|
||||
tok = K
|
||||
nonce = K
|
||||
ts = K
|
||||
|
||||
[notice]
|
||||
id = 129
|
||||
|
|
|
@ -37,6 +37,9 @@ $config['site']['path'] = 'laconica';
|
|||
# Enables extra log information, for example full details of PEAR DB errors
|
||||
#$config['site']['logdebug'] = true;
|
||||
|
||||
#To set your own logo, overriding the one in the theme
|
||||
#$config['site']['logo'] = '/mylogo.png';
|
||||
|
||||
# This is a PEAR DB DSN, see http://pear.php.net/manual/en/package.database.db.intro-dsn.php
|
||||
# Set it to match your actual database
|
||||
|
||||
|
@ -159,3 +162,15 @@ $config['sphinx']['port'] = 3312;
|
|||
# Add Google Analytics
|
||||
# require_once('plugins/GoogleAnalyticsPlugin.php');
|
||||
# $ga = new GoogleAnalyticsPlugin('your secret code');
|
||||
|
||||
#Don't allow saying the same thing more than once per hour
|
||||
#$config['site']['dupelimit'] = 3600;
|
||||
#Don't enforce the dupe limit
|
||||
#$config['site']['dupelimit'] = -1;
|
||||
|
||||
#Base string for minting Tag URIs in Atom feeds. Defaults to
|
||||
#"yourserver,2009". This needs to be configured properly for your Atom
|
||||
#feeds to validate. See: http://www.faqs.org/rfcs/rfc4151.html and
|
||||
#http://taguri.org/ Examples:
|
||||
#$config['integration']['taguri'] = 'example.net,2008';
|
||||
#$config['integration']['taguri'] = 'admin@example.net,2009-03-09'
|
||||
|
|
|
@ -183,15 +183,14 @@ create table token (
|
|||
|
||||
create table nonce (
|
||||
consumer_key varchar(255) not null comment 'unique identifier, root URL',
|
||||
tok char(32) not null comment 'identifying value',
|
||||
tok char(32) null comment 'buggy old value, ignored',
|
||||
nonce char(32) not null comment 'nonce',
|
||||
ts datetime not null comment 'timestamp sent',
|
||||
|
||||
created datetime not null comment 'date this record was created',
|
||||
modified timestamp comment 'date this record was modified',
|
||||
|
||||
constraint primary key (consumer_key, tok, nonce),
|
||||
constraint foreign key (consumer_key, tok) references token (consumer_key, tok)
|
||||
constraint primary key (consumer_key, ts, nonce)
|
||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
||||
|
||||
/* One-to-many relationship of user to openid_url */
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/* local and remote users have profiles */
|
||||
|
||||
create sequence profile_seq;
|
||||
create table profile (
|
||||
id serial primary key /* comment 'unique identifier' */,
|
||||
id bigint default nextval('profile_seq') primary key /* comment 'unique identifier' */,
|
||||
nickname varchar(64) not null /* comment 'nickname or username' */,
|
||||
fullname varchar(255) /* comment 'display name' */,
|
||||
profileurl varchar(255) /* comment 'URL, cached so we dont regenerate' */,
|
||||
|
@ -30,8 +31,9 @@ create table avatar (
|
|||
);
|
||||
create index avatar_profile_id_idx on avatar using btree(profile_id);
|
||||
|
||||
create sequence sms_carrier_seq;
|
||||
create table sms_carrier (
|
||||
id serial primary key /* comment 'primary key for SMS carrier' */,
|
||||
id bigint default nextval('sms_carrier_seq') primary key /* comment 'primary key for SMS carrier' */,
|
||||
name varchar(64) unique /* comment 'name of the carrier' */,
|
||||
email_pattern varchar(255) not null /* comment 'sprintf pattern for making an email address from a phone number' */,
|
||||
created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
|
||||
|
@ -101,9 +103,10 @@ create table subscription (
|
|||
create index subscription_subscriber_idx on subscription using btree(subscriber);
|
||||
create index subscription_subscribed_idx on subscription using btree(subscribed);
|
||||
|
||||
create sequence notice_seq;
|
||||
create table notice (
|
||||
|
||||
id serial primary key /* comment 'unique identifier' */,
|
||||
id bigint default nextval('notice_seq') primary key /* comment 'unique identifier' */,
|
||||
profile_id integer not null /* comment 'who made the update' */ references profile (id) ,
|
||||
uri varchar(255) unique /* comment 'universally unique identifier, usually a tag URI' */,
|
||||
content varchar(140) /* comment 'update content' */,
|
||||
|
@ -180,14 +183,13 @@ create table token (
|
|||
create table nonce (
|
||||
consumer_key varchar(255) not null /* comment 'unique identifier, root URL' */,
|
||||
tok char(32) not null /* comment 'identifying value' */,
|
||||
nonce char(32) not null /* comment 'nonce' */,
|
||||
nonce char(32) null /* comment 'buggy old value, ignored */,
|
||||
ts integer not null /* comment 'timestamp sent' values are epoch, and only used internally */,
|
||||
|
||||
created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
|
||||
modified timestamp /* comment 'date this record was modified' */,
|
||||
|
||||
primary key (consumer_key, tok, nonce),
|
||||
foreign key (consumer_key, tok) references token (consumer_key, tok)
|
||||
primary key (consumer_key, ts, nonce)
|
||||
);
|
||||
|
||||
/* One-to-many relationship of user to openid_url */
|
||||
|
@ -318,9 +320,10 @@ create table invitation (
|
|||
create index invitation_address_idx on invitation using btree(address,address_type);
|
||||
create index invitation_user_id_idx on invitation using btree(user_id);
|
||||
|
||||
create sequence message_seq;
|
||||
create table message (
|
||||
|
||||
id serial primary key /* comment 'unique identifier' */,
|
||||
id bigint default nextval('message_seq') primary key /* comment 'unique identifier' */,
|
||||
uri varchar(255) unique /* comment 'universally unique identifier' */,
|
||||
from_profile integer not null /* comment 'who the message is from' */ references profile (id),
|
||||
to_profile integer not null /* comment 'who the message is to' */ references profile (id),
|
||||
|
@ -368,9 +371,10 @@ create table profile_block (
|
|||
|
||||
);
|
||||
|
||||
create sequence user_group_seq;
|
||||
create table user_group (
|
||||
|
||||
id serial primary key /* comment 'unique identifier' */,
|
||||
id bigint default nextval('user_group_seq') primary key /* comment 'unique identifier' */,
|
||||
|
||||
nickname varchar(64) unique /* comment 'nickname for addressing' */,
|
||||
fullname varchar(255) /* comment 'display name' */,
|
||||
|
|
397
extlib/PEAR/Exception.php
Normal file
397
extlib/PEAR/Exception.php
Normal file
|
@ -0,0 +1,397 @@
|
|||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
|
||||
/**
|
||||
* PEAR_Exception
|
||||
*
|
||||
* PHP versions 4 and 5
|
||||
*
|
||||
* LICENSE: This source file is subject to version 3.0 of the PHP license
|
||||
* that is available through the world-wide-web at the following URI:
|
||||
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
|
||||
* the PHP License and are unable to obtain it through the web, please
|
||||
* send a note to license@php.net so we can mail you a copy immediately.
|
||||
*
|
||||
* @category pear
|
||||
* @package PEAR
|
||||
* @author Tomas V. V. Cox <cox@idecnet.com>
|
||||
* @author Hans Lellelid <hans@velum.net>
|
||||
* @author Bertrand Mansion <bmansion@mamasam.com>
|
||||
* @author Greg Beaver <cellog@php.net>
|
||||
* @copyright 1997-2008 The PHP Group
|
||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||
* @version CVS: $Id: Exception.php,v 1.29 2008/01/03 20:26:35 cellog Exp $
|
||||
* @link http://pear.php.net/package/PEAR
|
||||
* @since File available since Release 1.3.3
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Base PEAR_Exception Class
|
||||
*
|
||||
* 1) Features:
|
||||
*
|
||||
* - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception))
|
||||
* - Definable triggers, shot when exceptions occur
|
||||
* - Pretty and informative error messages
|
||||
* - Added more context info available (like class, method or cause)
|
||||
* - cause can be a PEAR_Exception or an array of mixed
|
||||
* PEAR_Exceptions/PEAR_ErrorStack warnings
|
||||
* - callbacks for specific exception classes and their children
|
||||
*
|
||||
* 2) Ideas:
|
||||
*
|
||||
* - Maybe a way to define a 'template' for the output
|
||||
*
|
||||
* 3) Inherited properties from PHP Exception Class:
|
||||
*
|
||||
* protected $message
|
||||
* protected $code
|
||||
* protected $line
|
||||
* protected $file
|
||||
* private $trace
|
||||
*
|
||||
* 4) Inherited methods from PHP Exception Class:
|
||||
*
|
||||
* __clone
|
||||
* __construct
|
||||
* getMessage
|
||||
* getCode
|
||||
* getFile
|
||||
* getLine
|
||||
* getTraceSafe
|
||||
* getTraceSafeAsString
|
||||
* __toString
|
||||
*
|
||||
* 5) Usage example
|
||||
*
|
||||
* <code>
|
||||
* require_once 'PEAR/Exception.php';
|
||||
*
|
||||
* class Test {
|
||||
* function foo() {
|
||||
* throw new PEAR_Exception('Error Message', ERROR_CODE);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* function myLogger($pear_exception) {
|
||||
* echo $pear_exception->getMessage();
|
||||
* }
|
||||
* // each time a exception is thrown the 'myLogger' will be called
|
||||
* // (its use is completely optional)
|
||||
* PEAR_Exception::addObserver('myLogger');
|
||||
* $test = new Test;
|
||||
* try {
|
||||
* $test->foo();
|
||||
* } catch (PEAR_Exception $e) {
|
||||
* print $e;
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* @category pear
|
||||
* @package PEAR
|
||||
* @author Tomas V.V.Cox <cox@idecnet.com>
|
||||
* @author Hans Lellelid <hans@velum.net>
|
||||
* @author Bertrand Mansion <bmansion@mamasam.com>
|
||||
* @author Greg Beaver <cellog@php.net>
|
||||
* @copyright 1997-2008 The PHP Group
|
||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||
* @version Release: 1.7.2
|
||||
* @link http://pear.php.net/package/PEAR
|
||||
* @since Class available since Release 1.3.3
|
||||
*
|
||||
*/
|
||||
class PEAR_Exception extends Exception
|
||||
{
|
||||
const OBSERVER_PRINT = -2;
|
||||
const OBSERVER_TRIGGER = -4;
|
||||
const OBSERVER_DIE = -8;
|
||||
protected $cause;
|
||||
private static $_observers = array();
|
||||
private static $_uniqueid = 0;
|
||||
private $_trace;
|
||||
|
||||
/**
|
||||
* Supported signatures:
|
||||
* - PEAR_Exception(string $message);
|
||||
* - PEAR_Exception(string $message, int $code);
|
||||
* - PEAR_Exception(string $message, Exception $cause);
|
||||
* - PEAR_Exception(string $message, Exception $cause, int $code);
|
||||
* - PEAR_Exception(string $message, PEAR_Error $cause);
|
||||
* - PEAR_Exception(string $message, PEAR_Error $cause, int $code);
|
||||
* - PEAR_Exception(string $message, array $causes);
|
||||
* - PEAR_Exception(string $message, array $causes, int $code);
|
||||
* @param string exception message
|
||||
* @param int|Exception|PEAR_Error|array|null exception cause
|
||||
* @param int|null exception code or null
|
||||
*/
|
||||
public function __construct($message, $p2 = null, $p3 = null)
|
||||
{
|
||||
if (is_int($p2)) {
|
||||
$code = $p2;
|
||||
$this->cause = null;
|
||||
} elseif (is_object($p2) || is_array($p2)) {
|
||||
// using is_object allows both Exception and PEAR_Error
|
||||
if (is_object($p2) && !($p2 instanceof Exception)) {
|
||||
if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) {
|
||||
throw new PEAR_Exception('exception cause must be Exception, ' .
|
||||
'array, or PEAR_Error');
|
||||
}
|
||||
}
|
||||
$code = $p3;
|
||||
if (is_array($p2) && isset($p2['message'])) {
|
||||
// fix potential problem of passing in a single warning
|
||||
$p2 = array($p2);
|
||||
}
|
||||
$this->cause = $p2;
|
||||
} else {
|
||||
$code = null;
|
||||
$this->cause = null;
|
||||
}
|
||||
parent::__construct($message, $code);
|
||||
$this->signal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $callback - A valid php callback, see php func is_callable()
|
||||
* - A PEAR_Exception::OBSERVER_* constant
|
||||
* - An array(const PEAR_Exception::OBSERVER_*,
|
||||
* mixed $options)
|
||||
* @param string $label The name of the observer. Use this if you want
|
||||
* to remove it later with removeObserver()
|
||||
*/
|
||||
public static function addObserver($callback, $label = 'default')
|
||||
{
|
||||
self::$_observers[$label] = $callback;
|
||||
}
|
||||
|
||||
public static function removeObserver($label = 'default')
|
||||
{
|
||||
unset(self::$_observers[$label]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int unique identifier for an observer
|
||||
*/
|
||||
public static function getUniqueId()
|
||||
{
|
||||
return self::$_uniqueid++;
|
||||
}
|
||||
|
||||
private function signal()
|
||||
{
|
||||
foreach (self::$_observers as $func) {
|
||||
if (is_callable($func)) {
|
||||
call_user_func($func, $this);
|
||||
continue;
|
||||
}
|
||||
settype($func, 'array');
|
||||
switch ($func[0]) {
|
||||
case self::OBSERVER_PRINT :
|
||||
$f = (isset($func[1])) ? $func[1] : '%s';
|
||||
printf($f, $this->getMessage());
|
||||
break;
|
||||
case self::OBSERVER_TRIGGER :
|
||||
$f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
|
||||
trigger_error($this->getMessage(), $f);
|
||||
break;
|
||||
case self::OBSERVER_DIE :
|
||||
$f = (isset($func[1])) ? $func[1] : '%s';
|
||||
die(printf($f, $this->getMessage()));
|
||||
break;
|
||||
default:
|
||||
trigger_error('invalid observer type', E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return specific error information that can be used for more detailed
|
||||
* error messages or translation.
|
||||
*
|
||||
* This method may be overridden in child exception classes in order
|
||||
* to add functionality not present in PEAR_Exception and is a placeholder
|
||||
* to define API
|
||||
*
|
||||
* The returned array must be an associative array of parameter => value like so:
|
||||
* <pre>
|
||||
* array('name' => $name, 'context' => array(...))
|
||||
* </pre>
|
||||
* @return array
|
||||
*/
|
||||
public function getErrorData()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exception that caused this exception to be thrown
|
||||
* @access public
|
||||
* @return Exception|array The context of the exception
|
||||
*/
|
||||
public function getCause()
|
||||
{
|
||||
return $this->cause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function must be public to call on caused exceptions
|
||||
* @param array
|
||||
*/
|
||||
public function getCauseMessage(&$causes)
|
||||
{
|
||||
$trace = $this->getTraceSafe();
|
||||
$cause = array('class' => get_class($this),
|
||||
'message' => $this->message,
|
||||
'file' => 'unknown',
|
||||
'line' => 'unknown');
|
||||
if (isset($trace[0])) {
|
||||
if (isset($trace[0]['file'])) {
|
||||
$cause['file'] = $trace[0]['file'];
|
||||
$cause['line'] = $trace[0]['line'];
|
||||
}
|
||||
}
|
||||
$causes[] = $cause;
|
||||
if ($this->cause instanceof PEAR_Exception) {
|
||||
$this->cause->getCauseMessage($causes);
|
||||
} elseif ($this->cause instanceof Exception) {
|
||||
$causes[] = array('class' => get_class($this->cause),
|
||||
'message' => $this->cause->getMessage(),
|
||||
'file' => $this->cause->getFile(),
|
||||
'line' => $this->cause->getLine());
|
||||
} elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) {
|
||||
$causes[] = array('class' => get_class($this->cause),
|
||||
'message' => $this->cause->getMessage(),
|
||||
'file' => 'unknown',
|
||||
'line' => 'unknown');
|
||||
} elseif (is_array($this->cause)) {
|
||||
foreach ($this->cause as $cause) {
|
||||
if ($cause instanceof PEAR_Exception) {
|
||||
$cause->getCauseMessage($causes);
|
||||
} elseif ($cause instanceof Exception) {
|
||||
$causes[] = array('class' => get_class($cause),
|
||||
'message' => $cause->getMessage(),
|
||||
'file' => $cause->getFile(),
|
||||
'line' => $cause->getLine());
|
||||
} elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) {
|
||||
$causes[] = array('class' => get_class($cause),
|
||||
'message' => $cause->getMessage(),
|
||||
'file' => 'unknown',
|
||||
'line' => 'unknown');
|
||||
} elseif (is_array($cause) && isset($cause['message'])) {
|
||||
// PEAR_ErrorStack warning
|
||||
$causes[] = array(
|
||||
'class' => $cause['package'],
|
||||
'message' => $cause['message'],
|
||||
'file' => isset($cause['context']['file']) ?
|
||||
$cause['context']['file'] :
|
||||
'unknown',
|
||||
'line' => isset($cause['context']['line']) ?
|
||||
$cause['context']['line'] :
|
||||
'unknown',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getTraceSafe()
|
||||
{
|
||||
if (!isset($this->_trace)) {
|
||||
$this->_trace = $this->getTrace();
|
||||
if (empty($this->_trace)) {
|
||||
$backtrace = debug_backtrace();
|
||||
$this->_trace = array($backtrace[count($backtrace)-1]);
|
||||
}
|
||||
}
|
||||
return $this->_trace;
|
||||
}
|
||||
|
||||
public function getErrorClass()
|
||||
{
|
||||
$trace = $this->getTraceSafe();
|
||||
return $trace[0]['class'];
|
||||
}
|
||||
|
||||
public function getErrorMethod()
|
||||
{
|
||||
$trace = $this->getTraceSafe();
|
||||
return $trace[0]['function'];
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
if (isset($_SERVER['REQUEST_URI'])) {
|
||||
return $this->toHtml();
|
||||
}
|
||||
return $this->toText();
|
||||
}
|
||||
|
||||
public function toHtml()
|
||||
{
|
||||
$trace = $this->getTraceSafe();
|
||||
$causes = array();
|
||||
$this->getCauseMessage($causes);
|
||||
$html = '<table border="1" cellspacing="0">' . "\n";
|
||||
foreach ($causes as $i => $cause) {
|
||||
$html .= '<tr><td colspan="3" bgcolor="#ff9999">'
|
||||
. str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
|
||||
. htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> '
|
||||
. 'on line <b>' . $cause['line'] . '</b>'
|
||||
. "</td></tr>\n";
|
||||
}
|
||||
$html .= '<tr><td colspan="3" bgcolor="#aaaaaa" align="center"><b>Exception trace</b></td></tr>' . "\n"
|
||||
. '<tr><td align="center" bgcolor="#cccccc" width="20"><b>#</b></td>'
|
||||
. '<td align="center" bgcolor="#cccccc"><b>Function</b></td>'
|
||||
. '<td align="center" bgcolor="#cccccc"><b>Location</b></td></tr>' . "\n";
|
||||
|
||||
foreach ($trace as $k => $v) {
|
||||
$html .= '<tr><td align="center">' . $k . '</td>'
|
||||
. '<td>';
|
||||
if (!empty($v['class'])) {
|
||||
$html .= $v['class'] . $v['type'];
|
||||
}
|
||||
$html .= $v['function'];
|
||||
$args = array();
|
||||
if (!empty($v['args'])) {
|
||||
foreach ($v['args'] as $arg) {
|
||||
if (is_null($arg)) $args[] = 'null';
|
||||
elseif (is_array($arg)) $args[] = 'Array';
|
||||
elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')';
|
||||
elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
|
||||
elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
|
||||
else {
|
||||
$arg = (string)$arg;
|
||||
$str = htmlspecialchars(substr($arg, 0, 16));
|
||||
if (strlen($arg) > 16) $str .= '…';
|
||||
$args[] = "'" . $str . "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
$html .= '(' . implode(', ',$args) . ')'
|
||||
. '</td>'
|
||||
. '<td>' . (isset($v['file']) ? $v['file'] : 'unknown')
|
||||
. ':' . (isset($v['line']) ? $v['line'] : 'unknown')
|
||||
. '</td></tr>' . "\n";
|
||||
}
|
||||
$html .= '<tr><td align="center">' . ($k+1) . '</td>'
|
||||
. '<td>{main}</td>'
|
||||
. '<td> </td></tr>' . "\n"
|
||||
. '</table>';
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function toText()
|
||||
{
|
||||
$causes = array();
|
||||
$this->getCauseMessage($causes);
|
||||
$causeMsg = '';
|
||||
foreach ($causes as $i => $cause) {
|
||||
$causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
|
||||
. $cause['message'] . ' in ' . $cause['file']
|
||||
. ' on line ' . $cause['line'] . "\n";
|
||||
}
|
||||
return $causeMsg . $this->getTraceAsString();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
10
index.php
10
index.php
|
@ -63,7 +63,7 @@ function handleError($error)
|
|||
|
||||
function main()
|
||||
{
|
||||
global $user, $action;
|
||||
global $user, $action, $config;
|
||||
|
||||
if (!_have_config()) {
|
||||
$msg = sprintf(_("No configuration file found. Try running ".
|
||||
|
@ -131,11 +131,11 @@ function main()
|
|||
if (common_config('db', 'mirror') && $action_obj->isReadOnly()) {
|
||||
if (is_array(common_config('db', 'mirror'))) {
|
||||
// "load balancing", ha ha
|
||||
$k = array_rand($config['db']['mirror']);
|
||||
|
||||
$mirror = $config['db']['mirror'][$k];
|
||||
$arr = common_config('db', 'mirror');
|
||||
$k = array_rand($arr);
|
||||
$mirror = $arr[$k];
|
||||
} else {
|
||||
$mirror = $config['db']['mirror'];
|
||||
$mirror = common_config('db', 'mirror');
|
||||
}
|
||||
$config['db']['database'] = $mirror;
|
||||
}
|
||||
|
|
24
js/flowplayer-3.0.5.min.js
vendored
24
js/flowplayer-3.0.5.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,8 +0,0 @@
|
|||
/*
|
||||
* SimpleModal 1.2.2 - jQuery Plugin
|
||||
* http://www.ericmmartin.com/projects/simplemodal/
|
||||
* Copyright (c) 2008 Eric Martin
|
||||
* Dual licensed under the MIT and GPL licenses
|
||||
* Revision: $Id: jquery.simplemodal.js 181 2008-12-16 16:51:44Z emartin24 $
|
||||
*/
|
||||
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(g($){m f=$.Q.1Q&&1a($.Q.1D)==6&&!10[\'2g\'],1f=$.Q.1Q&&!$.2a,w=[];$.y=g(a,b){I $.y.12.1n(a,b)};$.y.D=g(){$.y.12.D()};$.1P.y=g(a){I $.y.12.1n(3,a)};$.y.1O={V:29,1J:\'r-H\',1B:{},1z:\'r-n\',20:{},1Z:{},v:2t,D:1o,1T:\'<a 2j="2h" 2f="2e"></a>\',X:\'r-D\',l:F,1g:K,1e:F,1d:F,1c:F};$.y.12={7:F,4:{},1n:g(a,b){8(3.4.j){I K}3.7=$.U({},$.y.1O,b);3.v=3.7.v;3.1w=K;8(J a==\'27\'){a=a 25 1A?a:$(a);8(a.1v().1v().23()>0){3.4.T=a.1v();8(!3.7.1g){3.4.21=a.2x(1o)}}}q 8(J a==\'2w\'||J a==\'1r\'){a=$(\'<1q/>\').2s(a)}q{2r(\'2q 2p: 2o j 2l: \'+J a);I K}3.4.j=a.11(\'r-j\').E(3.7.1Z);a=F;3.1S();3.1R();8($.1m(3.7.1d)){3.7.1d.1l(3,[3.4])}I 3},1S:g(){w=3.1k();8(f){3.4.x=$(\'<x 2d="2c:K;"/>\').E($.U(3.7.2b,{1j:\'1i\',V:0,l:\'1h\',A:w[0],z:w[1],v:3.7.v,L:0,B:0})).O(\'u\')}3.4.H=$(\'<1q/>\').1N(\'1M\',3.7.1J).11(\'r-H\').E($.U(3.7.1B,{1j:\'1i\',V:3.7.V/1b,A:w[0],z:w[1],l:\'1h\',B:0,L:0,v:3.7.v+1})).O(\'u\');3.4.n=$(\'<1q/>\').1N(\'1M\',3.7.1z).11(\'r-n\').E($.U(3.7.20,{1j:\'1i\',l:\'1h\',v:3.7.v+2})).1K(3.7.D?$(3.7.1T).11(3.7.X):\'\').O(\'u\');3.19();8(f||1f){3.18()}3.4.n.1K(3.4.j.1I())},1H:g(){m a=3;$(\'.\'+3.7.X).1G(\'1L.r\',g(e){e.28();a.D()});$(10).1G(\'1F.r\',g(){w=a.1k();a.19();8(f||1f){a.18()}q{a.4.x&&a.4.x.E({A:w[0],z:w[1]});a.4.H.E({A:w[0],z:w[1]})}})},1E:g(){$(\'.\'+3.7.X).1C(\'1L.r\');$(10).1C(\'1F.r\')},18:g(){m p=3.7.l;$.26([3.4.x||F,3.4.H,3.4.n],g(i,e){8(e){m a=\'k.u.17\',N=\'k.u.1W\',16=\'k.u.24\',S=\'k.u.1y\',R=\'k.u.1x\',15=\'k.u.22\',1t=\'k.P.17\',1s=\'k.P.1W\',C=\'k.P.1y\',G=\'k.P.1x\',s=e[0].2v;s.l=\'2u\';8(i<2){s.14(\'A\');s.14(\'z\');s.Z(\'A\',\'\'+16+\' > \'+a+\' ? \'+16+\' : \'+a+\' + "o"\');s.Z(\'z\',\'\'+15+\' > \'+N+\' ? \'+15+\' : \'+N+\' + "o"\')}q{m b,W;8(p&&p.1Y==1X){8(p[0]){m c=J p[0]==\'1r\'?p[0].1V():p[0].13(/o/,\'\');b=c.1U(\'%\')==-1?c+\' + (t = \'+G+\' ? \'+G+\' : \'+R+\') + "o"\':1a(c.13(/%/,\'\'))+\' * ((\'+1t+\' || \'+a+\') / 1b) + (t = \'+G+\' ? \'+G+\' : \'+R+\') + "o"\'}8(p[1]){m d=J p[1]==\'1r\'?p[1].1V():p[1].13(/o/,\'\');W=d.1U(\'%\')==-1?d+\' + (t = \'+C+\' ? \'+C+\' : \'+S+\') + "o"\':1a(d.13(/%/,\'\'))+\' * ((\'+1s+\' || \'+N+\') / 1b) + (t = \'+C+\' ? \'+C+\' : \'+S+\') + "o"\'}}q{b=\'(\'+1t+\' || \'+a+\') / 2 - (3.2n / 2) + (t = \'+G+\' ? \'+G+\' : \'+R+\') + "o"\';W=\'(\'+1s+\' || \'+N+\') / 2 - (3.2m / 2) + (t = \'+C+\' ? \'+C+\' : \'+S+\') + "o"\'}s.14(\'L\');s.14(\'B\');s.Z(\'L\',b);s.Z(\'B\',W)}}})},1k:g(){m a=$(10);m h=$.Q.2k&&$.Q.1D>\'9.5\'&&$.1P.2i<=\'1.2.6\'?k.P[\'17\']:a.A();I[h,a.z()]},19:g(){m a,B,1u=(w[0]/2)-((3.4.n.A()||3.4.j.A())/2),1p=(w[1]/2)-((3.4.n.z()||3.4.j.z())/2);8(3.7.l&&3.7.l.1Y==1X){a=3.7.l[0]||1u;B=3.7.l[1]||1p}q{a=1u;B=1p}3.4.n.E({B:B,L:a})},1R:g(){3.4.x&&3.4.x.Y();8($.1m(3.7.1e)){3.7.1e.1l(3,[3.4])}q{3.4.H.Y();3.4.n.Y();3.4.j.Y()}3.1H()},D:g(){8(!3.4.j){I K}8($.1m(3.7.1c)&&!3.1w){3.1w=1o;3.7.1c.1l(3,[3.4])}q{8(3.4.T){8(3.7.1g){3.4.j.1I().O(3.4.T)}q{3.4.j.M();3.4.21.O(3.4.T)}}q{3.4.j.M()}3.4.n.M();3.4.H.M();3.4.x&&3.4.x.M();3.4={}}3.1E()}}})(1A);',62,158,'|||this|dialog|||opts|if||||||||function|||data|document|position|var|container|px||else|simplemodal|||body|zIndex||iframe|modal|width|height|left|sl|close|css|null|st|overlay|return|typeof|false|top|remove|bcw|appendTo|documentElement|browser|bst|bsl|parentNode|extend|opacity|le|closeClass|show|setExpression|window|addClass|impl|replace|removeExpression|bsw|bsh|clientHeight|fixIE|setPosition|parseInt|100|onClose|onShow|onOpen|ieQuirks|persist|fixed|none|display|getDimensions|apply|isFunction|init|true|vCenter|div|number|cw|ch|hCenter|parent|occb|scrollTop|scrollLeft|containerId|jQuery|overlayCss|unbind|version|unbindEvents|resize|bind|bindEvents|hide|overlayId|append|click|id|attr|defaults|fn|msie|open|create|closeHTML|indexOf|toString|clientWidth|Array|constructor|dataCss|containerCss|orig|scrollWidth|size|scrollHeight|instanceof|each|object|preventDefault|50|boxModel|iframeCss|javascript|src|Close|title|XMLHttpRequest|modalCloseImg|jquery|class|opera|type|offsetWidth|offsetHeight|Unsupported|Error|SimpleModal|alert|html|1000|absolute|style|string|clone'.split('|'),0,{}))
|
|
@ -1,9 +0,0 @@
|
|||
$('document').ready(function() {
|
||||
$('a.media, a.mediamp3').append(' <sup>[PLAY]</sup>');
|
||||
$('a.mediamp3').html('').css('display', 'block').css('width', '224px').css('height','24px').flowplayer('../bin/flowplayer-3.0.5.swf');
|
||||
$('a.media').click(function() {
|
||||
$('<a id="p1i"></a>').attr('href', $(this).attr('href')).flowplayer('../bin/flowplayer-3.0.5.swf').modal({'closeHTML':'<a class="modalCloseImg" title="Close"><img src="x.png" /></a>'});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
|
@ -156,15 +156,10 @@ class Action extends HTMLOutputter // lawsuit
|
|||
{
|
||||
if (Event::handle('StartShowStyles', array($this))) {
|
||||
if (Event::handle('StartShowLaconicaStyles', array($this))) {
|
||||
|
||||
$this->element('link', array('rel' => 'stylesheet',
|
||||
'type' => 'text/css',
|
||||
'href' => theme_path('css/display.css', 'base') . '?version=' . LACONICA_VERSION,
|
||||
'media' => 'screen, projection, tv'));
|
||||
$this->element('link', array('rel' => 'stylesheet',
|
||||
'type' => 'text/css',
|
||||
'href' => theme_path('css/modal.css', 'base') . '?version=' . LACONICA_VERSION,
|
||||
'media' => 'screen, projection, tv'));
|
||||
$this->element('link', array('rel' => 'stylesheet',
|
||||
'type' => 'text/css',
|
||||
'href' => theme_path('css/display.css', null) . '?version=' . LACONICA_VERSION,
|
||||
|
@ -215,11 +210,6 @@ class Action extends HTMLOutputter // lawsuit
|
|||
$this->element('script', array('type' => 'text/javascript',
|
||||
'src' => common_path('js/jquery.form.js')),
|
||||
' ');
|
||||
|
||||
$this->element('script', array('type' => 'text/javascript',
|
||||
'src' => common_path('js/jquery.simplemodal-1.2.2.pack.js')),
|
||||
' ');
|
||||
|
||||
Event::handle('EndShowJQueryScripts', array($this));
|
||||
}
|
||||
if (Event::handle('StartShowLaconicaScripts', array($this))) {
|
||||
|
@ -232,14 +222,6 @@ class Action extends HTMLOutputter // lawsuit
|
|||
// Frame-busting code to avoid clickjacking attacks.
|
||||
$this->element('script', array('type' => 'text/javascript'),
|
||||
'if (window.top !== window.self) { window.top.location.href = window.self.location.href; }');
|
||||
|
||||
$this->element('script', array('type' => 'text/javascript',
|
||||
'src' => common_path('js/flowplayer-3.0.5.min.js')),
|
||||
' ');
|
||||
|
||||
$this->element('script', array('type' => 'text/javascript',
|
||||
'src' => common_path('js/video.js')),
|
||||
' ');
|
||||
Event::handle('EndShowLaconicaScripts', array($this));
|
||||
}
|
||||
Event::handle('EndShowScripts', array($this));
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
if (!defined('LACONICA')) { exit(1); }
|
||||
|
||||
define('LACONICA_VERSION', '0.7.1');
|
||||
define('LACONICA_VERSION', '0.7.2');
|
||||
|
||||
define('AVATAR_PROFILE_SIZE', 96);
|
||||
define('AVATAR_STREAM_SIZE', 48);
|
||||
|
@ -73,6 +73,7 @@ $config =
|
|||
'theme' => 'default',
|
||||
'path' => $_path,
|
||||
'logfile' => null,
|
||||
'logo' => null,
|
||||
'logdebug' => false,
|
||||
'fancy' => false,
|
||||
'locale_path' => INSTALLDIR.'/locale',
|
||||
|
@ -85,7 +86,8 @@ $config =
|
|||
'broughtbyurl' => null,
|
||||
'closed' => false,
|
||||
'inviteonly' => false,
|
||||
'private' => false),
|
||||
'private' => false,
|
||||
'dupelimit' => 60), # default for same person saying the same thing
|
||||
'syslog' =>
|
||||
array('appname' => 'laconica', # for syslog
|
||||
'priority' => 'debug'), # XXX: currently ignored
|
||||
|
@ -139,7 +141,8 @@ $config =
|
|||
'user' => false,
|
||||
'group' => false),
|
||||
'integration' =>
|
||||
array('source' => 'Laconica'), # source attribute for Twitter
|
||||
array('source' => 'Laconica', # source attribute for Twitter
|
||||
'taguri' => $_server.',2009'), # base for tag URIs
|
||||
'memcached' =>
|
||||
array('enabled' => false,
|
||||
'server' => 'localhost',
|
||||
|
|
|
@ -410,8 +410,8 @@ function jabber_broadcast_notice($notice)
|
|||
"ON $UT.id = notice_inbox.user_id " .
|
||||
'WHERE notice_inbox.notice_id = ' . $notice->id . ' ' .
|
||||
'AND notice_inbox.source = 2 ' .
|
||||
'AND user.jabber is not null ' .
|
||||
'AND user.jabbernotify = 1 ');
|
||||
"AND $UT.jabber is not null " .
|
||||
"AND $UT.jabbernotify = 1 ");
|
||||
|
||||
while ($user->fetch()) {
|
||||
if (!array_key_exists($user->id, $sent_to)) {
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
* @category Search
|
||||
* @package Laconica
|
||||
* @author Zach Copley <zach@controlyourself.ca>
|
||||
* @copyright 2008-2009 Control Yourself, Inc.
|
||||
* @copyright 2009 Control Yourself, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://laconi.ca/
|
||||
*/
|
||||
|
@ -62,14 +62,19 @@ class JSONSearchResultsList
|
|||
/**
|
||||
* constructor
|
||||
*
|
||||
* @param Notice $notice stream of notices from DB_DataObject
|
||||
* @param Notice $notice stream of notices from DB_DataObject
|
||||
* @param string $query the original search query
|
||||
* @param int $rpp the number of results to display per page
|
||||
* @param int $page a page offset
|
||||
* @param int $since_id only display notices newer than this
|
||||
*/
|
||||
|
||||
function __construct($notice, $query, $rpp, $page, $since_id = 0)
|
||||
{
|
||||
$this->notice = $notice;
|
||||
$this->query = urlencode($query);
|
||||
$this->results_per_page = $this->rpp = $rpp;
|
||||
$this->results_per_page = $rpp;
|
||||
$this->rpp = $rpp;
|
||||
$this->page = $page;
|
||||
$this->since_id = $since_id;
|
||||
$this->results = array();
|
||||
|
@ -78,7 +83,7 @@ class JSONSearchResultsList
|
|||
/**
|
||||
* show the list of search results
|
||||
*
|
||||
* @return int count of the search results listed.
|
||||
* @return int $count of the search results listed.
|
||||
*/
|
||||
|
||||
function show()
|
||||
|
@ -103,7 +108,7 @@ class JSONSearchResultsList
|
|||
array_push($this->results, $item);
|
||||
}
|
||||
|
||||
$time_end = microtime(true);
|
||||
$time_end = microtime(true);
|
||||
$this->completed_in = $time_end - $time_start;
|
||||
|
||||
// Set other attrs
|
||||
|
@ -197,7 +202,7 @@ class ResultItem
|
|||
|
||||
function buildResult()
|
||||
{
|
||||
$this->text = $this->notice->content;
|
||||
$this->text = $this->notice->content;
|
||||
$replier_profile = null;
|
||||
|
||||
if ($this->notice->reply_to) {
|
||||
|
@ -209,18 +214,21 @@ class ResultItem
|
|||
|
||||
$this->to_user_id = ($replier_profile) ?
|
||||
intval($replier_profile->id) : null;
|
||||
$this->to_user = ($replier_profile) ?
|
||||
$this->to_user = ($replier_profile) ?
|
||||
$replier_profile->nickname : null;
|
||||
$this->from_user = $this->profile->nickname;
|
||||
$this->id = $this->notice->id;
|
||||
|
||||
$this->from_user = $this->profile->nickname;
|
||||
$this->id = $this->notice->id;
|
||||
$this->from_user_id = $this->profile->id;
|
||||
|
||||
$user = User::staticGet('id', $this->profile->id);
|
||||
|
||||
$this->iso_language_code = $this->user->language;
|
||||
|
||||
$this->source = $this->getSourceLink($this->notice->source);
|
||||
|
||||
$avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE);
|
||||
|
||||
$this->profile_image_url = ($avatar) ?
|
||||
$avatar->displayUrl() : Avatar::defaultImage(AVATAR_STREAM_SIZE);
|
||||
|
||||
|
@ -233,27 +241,30 @@ class ResultItem
|
|||
* Either the name (and link) of the API client that posted the notice,
|
||||
* or one of other other channels.
|
||||
*
|
||||
* @return string the source of the Notice
|
||||
* @param string $source the source of the Notice
|
||||
*
|
||||
* @return string a fully rendered source of the Notice
|
||||
*/
|
||||
|
||||
function getSourceLink($source)
|
||||
{
|
||||
$source_name = _($source);
|
||||
switch ($source) {
|
||||
case 'web':
|
||||
case 'xmpp':
|
||||
case 'mail':
|
||||
case 'omb':
|
||||
case 'api':
|
||||
break;
|
||||
default:
|
||||
$ns = Notice_source::staticGet($source);
|
||||
if ($ns) {
|
||||
$source_name = '<a href="' . $ns->url . '">' . $ns->name . '</a>';
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $source_name;
|
||||
}
|
||||
function getSourceLink($source)
|
||||
{
|
||||
$source_name = _($source);
|
||||
switch ($source) {
|
||||
case 'web':
|
||||
case 'xmpp':
|
||||
case 'mail':
|
||||
case 'omb':
|
||||
case 'api':
|
||||
break;
|
||||
default:
|
||||
$ns = Notice_source::staticGet($source);
|
||||
if ($ns) {
|
||||
$source_name = '<a href="' . $ns->url . '">' . $ns->name . '</a>';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $source_name;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,10 +50,9 @@ function mail_backend()
|
|||
static $backend = null;
|
||||
|
||||
if (!$backend) {
|
||||
global $config;
|
||||
$backend = Mail::factory($config['mail']['backend'],
|
||||
($config['mail']['params']) ?
|
||||
$config['mail']['params'] :
|
||||
$backend = Mail::factory(common_config('mail', 'backend'),
|
||||
(common_config('mail', 'params')) ?
|
||||
common_config('mail', 'params') :
|
||||
array());
|
||||
if (PEAR::isError($backend)) {
|
||||
common_server_error($backend->getMessage(), 500);
|
||||
|
|
|
@ -54,16 +54,21 @@ class LaconicaOAuthDataStore extends OAuthDataStore
|
|||
}
|
||||
}
|
||||
|
||||
// http://oauth.net/core/1.0/#nonce
|
||||
// "The Consumer SHALL then generate a Nonce value that is unique for
|
||||
// all requests with that timestamp."
|
||||
|
||||
// XXX: It's not clear why the token is here
|
||||
|
||||
function lookup_nonce($consumer, $token, $nonce, $timestamp)
|
||||
{
|
||||
$n = new Nonce();
|
||||
$n->consumer_key = $consumer->key;
|
||||
$n->tok = $token->key;
|
||||
$n->ts = $timestamp;
|
||||
$n->nonce = $nonce;
|
||||
if ($n->find(true)) {
|
||||
return true;
|
||||
} else {
|
||||
$n->ts = $timestamp;
|
||||
$n->created = DB_DataObject_Cast::dateTime();
|
||||
$n->insert();
|
||||
return false;
|
||||
|
|
|
@ -251,7 +251,6 @@ function omb_broadcast_profile($profile)
|
|||
|
||||
function omb_update_profile($profile, $remote_profile, $subscription)
|
||||
{
|
||||
global $config; # for license URL
|
||||
$user = User::staticGet($profile->id);
|
||||
$con = omb_oauth_consumer();
|
||||
$token = new OAuthToken($subscription->token, $subscription->secret);
|
||||
|
@ -295,7 +294,7 @@ function omb_update_profile($profile, $remote_profile, $subscription)
|
|||
|
||||
common_debug('Got HTTP result "'.print_r($result,true).'"', __FILE__);
|
||||
|
||||
if (empty($result) || $result) {
|
||||
if (empty($result) || !$result) {
|
||||
common_debug("Unable to contact " . $req->get_normalized_http_url());
|
||||
} else if ($result->status == 403) { # not authorized, don't send again
|
||||
common_debug('403 result, deleting subscription', __FILE__);
|
||||
|
@ -306,7 +305,7 @@ function omb_update_profile($profile, $remote_profile, $subscription)
|
|||
return false;
|
||||
} else { # success!
|
||||
parse_str($result->body, $return);
|
||||
if ($return['omb_version'] == OMB_VERSION_01) {
|
||||
if (isset($return['omb_version']) && $return['omb_version'] === OMB_VERSION_01) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -160,7 +160,7 @@ function oid_authenticate($openid_url, $returnto, $immediate=false)
|
|||
$auth_request->addExtension($sreg_request);
|
||||
}
|
||||
|
||||
$trust_root = common_local_url('public');
|
||||
$trust_root = common_path('');
|
||||
$process_url = common_local_url($returnto);
|
||||
|
||||
if ($auth_request->shouldSendRedirect()) {
|
||||
|
|
|
@ -89,6 +89,7 @@ class ProfileList extends Widget
|
|||
'id' => 'profile-' . $this->profile->id));
|
||||
|
||||
$user = common_current_user();
|
||||
$is_own = !is_null($user) && isset($this->user) && ($user->id === $this->user->id);
|
||||
|
||||
$this->out->elementStart('div', 'entity_profile vcard');
|
||||
|
||||
|
@ -102,13 +103,13 @@ class ProfileList extends Widget
|
|||
'alt' =>
|
||||
($this->profile->fullname) ? $this->profile->fullname :
|
||||
$this->profile->nickname));
|
||||
$hasFN = ($this->profile->fullname) ? 'nickname' : 'fn nickname';
|
||||
$hasFN = ($this->profile->fullname !== '') ? 'nickname' : 'fn nickname';
|
||||
$this->out->elementStart('span', $hasFN);
|
||||
$this->out->raw($this->highlight($this->profile->nickname));
|
||||
$this->out->elementEnd('span');
|
||||
$this->out->elementEnd('a');
|
||||
|
||||
if ($this->profile->fullname) {
|
||||
if ($this->profile->fullname !== '') {
|
||||
$this->out->elementStart('dl', 'entity_fn');
|
||||
$this->out->element('dt', null, 'Full name');
|
||||
$this->out->elementStart('dd');
|
||||
|
@ -118,7 +119,7 @@ class ProfileList extends Widget
|
|||
$this->out->elementEnd('dd');
|
||||
$this->out->elementEnd('dl');
|
||||
}
|
||||
if ($this->profile->location) {
|
||||
if ($this->profile->location !== '') {
|
||||
$this->out->elementStart('dl', 'entity_location');
|
||||
$this->out->element('dt', null, _('Location'));
|
||||
$this->out->elementStart('dd', 'label');
|
||||
|
@ -126,7 +127,7 @@ class ProfileList extends Widget
|
|||
$this->out->elementEnd('dd');
|
||||
$this->out->elementEnd('dl');
|
||||
}
|
||||
if ($this->profile->homepage) {
|
||||
if ($this->profile->homepage !== '') {
|
||||
$this->out->elementStart('dl', 'entity_url');
|
||||
$this->out->element('dt', null, _('URL'));
|
||||
$this->out->elementStart('dd');
|
||||
|
@ -137,7 +138,7 @@ class ProfileList extends Widget
|
|||
$this->out->elementEnd('dd');
|
||||
$this->out->elementEnd('dl');
|
||||
}
|
||||
if ($this->profile->bio) {
|
||||
if ($this->profile->bio !== '') {
|
||||
$this->out->elementStart('dl', 'entity_note');
|
||||
$this->out->element('dt', null, _('Note'));
|
||||
$this->out->elementStart('dd', 'note');
|
||||
|
@ -154,7 +155,7 @@ class ProfileList extends Widget
|
|||
|
||||
$this->out->elementStart('dl', 'entity_tags');
|
||||
$this->out->elementStart('dt');
|
||||
if ($user->id == $this->owner->id) {
|
||||
if ($is_own) {
|
||||
$this->out->element('a', array('href' => common_local_url('tagother',
|
||||
array('id' => $this->profile->id))),
|
||||
_('Tags'));
|
||||
|
@ -183,7 +184,7 @@ class ProfileList extends Widget
|
|||
$this->out->elementEnd('dl');
|
||||
}
|
||||
|
||||
if ($user && $user->id == $this->owner->id) {
|
||||
if ($is_own) {
|
||||
$this->showOwnerControls($this->profile);
|
||||
}
|
||||
|
||||
|
@ -193,11 +194,11 @@ class ProfileList extends Widget
|
|||
|
||||
$this->out->elementStart('ul');
|
||||
|
||||
if ($user && $user->id != $this->profile->id) {
|
||||
if (!$is_own) {
|
||||
# XXX: special-case for user looking at own
|
||||
# subscriptions page
|
||||
$this->out->elementStart('li', 'entity_subscribe');
|
||||
if ($user->isSubscribed($this->profile)) {
|
||||
if (!is_null($user) && $user->isSubscribed($this->profile)) {
|
||||
$usf = new UnsubscribeForm($this->out, $this->profile);
|
||||
$usf->show();
|
||||
} else {
|
||||
|
@ -206,9 +207,6 @@ class ProfileList extends Widget
|
|||
}
|
||||
$this->out->elementEnd('li');
|
||||
$this->out->elementStart('li', 'entity_block');
|
||||
if ($user && $user->id == $this->owner->id) {
|
||||
$this->showBlockForm();
|
||||
}
|
||||
$this->out->elementEnd('li');
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,9 @@ class Router
|
|||
{
|
||||
var $m = null;
|
||||
static $inst = null;
|
||||
static $bare = array('requesttoken', 'accesstoken', 'userauthorization',
|
||||
'postnotice', 'updateprofile', 'finishremotesubscribe',
|
||||
'finishopenidlogin', 'finishaddopenid');
|
||||
|
||||
static function get()
|
||||
{
|
||||
|
@ -98,7 +101,7 @@ class Router
|
|||
$main = array('login', 'logout', 'register', 'subscribe',
|
||||
'unsubscribe', 'confirmaddress', 'recoverpassword',
|
||||
'invite', 'favor', 'disfavor', 'sup',
|
||||
'block');
|
||||
'block', 'subedit');
|
||||
|
||||
foreach ($main as $a) {
|
||||
$m->connect('main/'.$a, array('action' => $a));
|
||||
|
@ -118,8 +121,7 @@ class Router
|
|||
$m->connect('main/remote', array('action' => 'remotesubscribe'));
|
||||
$m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+'));
|
||||
|
||||
foreach (array('requesttoken', 'accesstoken', 'userauthorization',
|
||||
'postnotice', 'updateprofile', 'finishremotesubscribe') as $action) {
|
||||
foreach (Router::$bare as $action) {
|
||||
$m->connect('index.php?action=' . $action, array('action' => $action));
|
||||
}
|
||||
|
||||
|
@ -230,7 +232,7 @@ class Router
|
|||
$m->connect('api/statuses/:method/:argument',
|
||||
array('action' => 'api',
|
||||
'apiaction' => 'statuses'),
|
||||
array('method' => '(user_timeline|friends_timeline|show|destroy|friends|followers)'));
|
||||
array('method' => '(user_timeline|friends_timeline|replies|show|destroy|friends|followers)'));
|
||||
|
||||
// users
|
||||
|
||||
|
@ -261,7 +263,7 @@ class Router
|
|||
}
|
||||
|
||||
foreach (array('xml', 'json', 'rss', 'atom') as $e) {
|
||||
$m->connect('api/direct_message/sent.'.$e,
|
||||
$m->connect('api/direct_messages/sent.'.$e,
|
||||
array('action' => 'api',
|
||||
'apiaction' => 'direct_messages',
|
||||
'method' => 'sent.'.$e));
|
||||
|
@ -281,7 +283,7 @@ class Router
|
|||
$m->connect('api/friendships/:method',
|
||||
array('action' => 'api',
|
||||
'apiaction' => 'friendships'),
|
||||
array('method' => 'exists(\.(xml|json|rss|atom))'));
|
||||
array('method' => 'exists(\.(xml|json))'));
|
||||
|
||||
// Social graph
|
||||
|
||||
|
@ -357,7 +359,6 @@ class Router
|
|||
array('action' => 'api',
|
||||
'apiaction' => 'laconica'));
|
||||
|
||||
|
||||
// search
|
||||
$m->connect('api/search.atom', array('action' => 'twitapisearchatom'));
|
||||
$m->connect('api/search.json', array('action' => 'twitapisearchjson'));
|
||||
|
@ -365,7 +366,7 @@ class Router
|
|||
|
||||
// user stuff
|
||||
|
||||
foreach (array('subscriptions', 'subscribers',
|
||||
foreach (array('subscriptions', 'subscribers',
|
||||
'nudge', 'xrds', 'all', 'foaf',
|
||||
'replies', 'inbox', 'outbox', 'microsummary') as $a) {
|
||||
$m->connect(':nickname/'.$a,
|
||||
|
|
|
@ -110,8 +110,6 @@ class SearchAction extends Action
|
|||
|
||||
function showForm($error=null)
|
||||
{
|
||||
global $config;
|
||||
|
||||
$q = $this->trimmed('q');
|
||||
$page = $this->trimmed('page', 1);
|
||||
$this->elementStart('form', array('method' => 'get',
|
||||
|
@ -122,7 +120,7 @@ class SearchAction extends Action
|
|||
$this->element('legend', null, _('Search site'));
|
||||
$this->elementStart('ul', 'form_data');
|
||||
$this->elementStart('li');
|
||||
if (!isset($config['site']['fancy']) || !$config['site']['fancy']) {
|
||||
if (!common_config('site', 'fancy')) {
|
||||
$this->hidden('action', $this->trimmed('action'));
|
||||
}
|
||||
$this->input('q', 'Keyword(s)', $q);
|
||||
|
|
|
@ -98,7 +98,7 @@ class SubGroupNav extends Widget
|
|||
$this->user->nickname),
|
||||
$action == 'usergroups',
|
||||
'nav_usergroups');
|
||||
if ($this->user->id == $cur->id) {
|
||||
if (!is_null($cur) && $this->user->id === $cur->id) {
|
||||
$this->out->menuItem(common_local_url('invite'),
|
||||
_('Invite'),
|
||||
sprintf(_('Invite friends and colleagues to join you on %s'),
|
||||
|
|
|
@ -51,7 +51,7 @@ class TopPostersSection extends ProfileSection
|
|||
$qry = 'SELECT profile.*, count(*) as value ' .
|
||||
'FROM profile JOIN notice ON profile.id = notice.profile_id ' .
|
||||
(common_config('public', 'localonly') ? 'WHERE is_local = 1 ' : '') .
|
||||
'GROUP BY profile.id ' .
|
||||
'GROUP BY profile.id,nickname,fullname,profileurl,homepage,bio,location,profile.created,profile.modified,textsearch ' .
|
||||
'ORDER BY value DESC ';
|
||||
|
||||
$limit = PROFILES_PER_SECTION;
|
||||
|
|
|
@ -224,7 +224,6 @@ function is_twitter_bound($notice, $flink) {
|
|||
|
||||
function broadcast_twitter($notice)
|
||||
{
|
||||
global $config;
|
||||
$success = true;
|
||||
|
||||
$flink = Foreign_link::getByUserID($notice->profile_id,
|
||||
|
@ -232,7 +231,7 @@ function broadcast_twitter($notice)
|
|||
|
||||
// XXX: Not sure WHERE to check whether a notice should go to
|
||||
// Twitter. Should we even put in the queue if it shouldn't? --Zach
|
||||
if (is_twitter_bound($notice, $flink)) {
|
||||
if (!is_null($flink) && is_twitter_bound($notice, $flink)) {
|
||||
|
||||
$fuser = $flink->getForeignUser();
|
||||
$twitter_user = $fuser->nickname;
|
||||
|
@ -248,7 +247,7 @@ function broadcast_twitter($notice)
|
|||
CURLOPT_POSTFIELDS =>
|
||||
array(
|
||||
'status' => $statustxt,
|
||||
'source' => $config['integration']['source']
|
||||
'source' => common_config('integration', 'source')
|
||||
),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_FAILONERROR => true,
|
||||
|
|
|
@ -127,8 +127,6 @@ class TwitterapiAction extends Action
|
|||
{
|
||||
|
||||
$profile = $notice->getProfile();
|
||||
|
||||
$server = common_config('site', 'server');
|
||||
$entry = array();
|
||||
|
||||
# We trim() to avoid extraneous whitespace in the output
|
||||
|
@ -137,8 +135,12 @@ class TwitterapiAction extends Action
|
|||
$entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content));
|
||||
$entry['link'] = common_local_url('shownotice', array('notice' => $notice->id));
|
||||
$entry['published'] = common_date_iso8601($notice->created);
|
||||
$entry['id'] = "tag:$server,2008:$entry[link]";
|
||||
|
||||
$taguribase = common_config('integration', 'taguri');
|
||||
$entry['id'] = "tag:$taguribase:$entry[link]";
|
||||
|
||||
$entry['updated'] = $entry['published'];
|
||||
$entry['author'] = $profile->getBestName();
|
||||
|
||||
# RSS Item specific
|
||||
$entry['description'] = $entry['content'];
|
||||
|
@ -151,7 +153,6 @@ class TwitterapiAction extends Action
|
|||
function twitter_rss_dmsg_array($message)
|
||||
{
|
||||
|
||||
$server = common_config('site', 'server');
|
||||
$entry = array();
|
||||
|
||||
$entry['title'] = sprintf('Message from %s to %s',
|
||||
|
@ -160,8 +161,12 @@ class TwitterapiAction extends Action
|
|||
$entry['content'] = common_xml_safe_str(trim($message->content));
|
||||
$entry['link'] = common_local_url('showmessage', array('message' => $message->id));
|
||||
$entry['published'] = common_date_iso8601($message->created);
|
||||
$entry['id'] = "tag:$server,2008:$entry[link]";
|
||||
|
||||
$taguribase = common_config('integration', 'taguri');
|
||||
|
||||
$entry['id'] = "tag:$taguribase,:$entry[link]";
|
||||
$entry['updated'] = $entry['published'];
|
||||
$entry['author'] = $message->getFrom()->getBestName();
|
||||
|
||||
# RSS Item specific
|
||||
$entry['description'] = $entry['content'];
|
||||
|
@ -242,6 +247,9 @@ class TwitterapiAction extends Action
|
|||
$this->element('published', null, $entry['published']);
|
||||
$this->element('updated', null, $entry['updated']);
|
||||
$this->element('link', array('href' => $entry['link'], 'rel' => 'alternate', 'type' => 'text/html'), null);
|
||||
$this->elementStart('author');
|
||||
$this->element('name', null, $entry['author']);
|
||||
$this->elementEnd('author');
|
||||
$this->elementEnd('entry');
|
||||
}
|
||||
|
||||
|
@ -358,7 +366,7 @@ class TwitterapiAction extends Action
|
|||
$this->end_twitter_rss();
|
||||
}
|
||||
|
||||
function show_atom_timeline($notice, $title, $id, $link, $subtitle=null, $suplink=null)
|
||||
function show_atom_timeline($notice, $title, $id, $link, $subtitle=null, $suplink=null, $selfuri=null)
|
||||
{
|
||||
|
||||
$this->init_document('atom');
|
||||
|
@ -366,12 +374,20 @@ class TwitterapiAction extends Action
|
|||
$this->element('title', null, $title);
|
||||
$this->element('id', null, $id);
|
||||
$this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
|
||||
|
||||
if (!is_null($suplink)) {
|
||||
# For FriendFeed's SUP protocol
|
||||
$this->element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup',
|
||||
'href' => $suplink,
|
||||
'type' => 'application/json'));
|
||||
}
|
||||
|
||||
if (!is_null($selfuri)) {
|
||||
$this->element('link', array('href' => $selfuri,
|
||||
'rel' => 'self', 'type' => 'application/atom+xml'), null);
|
||||
}
|
||||
|
||||
$this->element('updated', null, common_date_iso8601('now'));
|
||||
$this->element('subtitle', null, $subtitle);
|
||||
|
||||
if (is_array($notice)) {
|
||||
|
@ -634,79 +650,4 @@ class TwitterapiAction extends Action
|
|||
return $source_name;
|
||||
}
|
||||
|
||||
function show_extended_profile($user, $apidata)
|
||||
{
|
||||
|
||||
$this->auth_user = $apidata['user'];
|
||||
|
||||
$profile = $user->getProfile();
|
||||
|
||||
if (!$profile) {
|
||||
common_server_error(_('User has no profile.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$twitter_user = $this->twitter_user_array($profile, true);
|
||||
|
||||
// Add in extended user fields offered up by this method
|
||||
$twitter_user['created_at'] = $this->date_twitter($profile->created);
|
||||
|
||||
$subbed = DB_DataObject::factory('subscription');
|
||||
$subbed->subscriber = $profile->id;
|
||||
$subbed_count = (int) $subbed->count() - 1;
|
||||
|
||||
$notices = DB_DataObject::factory('notice');
|
||||
$notices->profile_id = $profile->id;
|
||||
$notice_count = (int) $notices->count();
|
||||
|
||||
$twitter_user['friends_count'] = (is_int($subbed_count)) ? $subbed_count : 0;
|
||||
$twitter_user['statuses_count'] = (is_int($notice_count)) ? $notice_count : 0;
|
||||
|
||||
// Other fields Twitter sends...
|
||||
$twitter_user['profile_background_color'] = '';
|
||||
$twitter_user['profile_text_color'] = '';
|
||||
$twitter_user['profile_link_color'] = '';
|
||||
$twitter_user['profile_sidebar_fill_color'] = '';
|
||||
|
||||
$faves = DB_DataObject::factory('fave');
|
||||
$faves->user_id = $user->id;
|
||||
$faves_count = (int) $faves->count();
|
||||
$twitter_user['favourites_count'] = $faves_count;
|
||||
|
||||
$timezone = 'UTC';
|
||||
|
||||
if ($user->timezone) {
|
||||
$timezone = $user->timezone;
|
||||
}
|
||||
|
||||
$t = new DateTime;
|
||||
$t->setTimezone(new DateTimeZone($timezone));
|
||||
$twitter_user['utc_offset'] = $t->format('Z');
|
||||
$twitter_user['time_zone'] = $timezone;
|
||||
|
||||
$following = 'false';
|
||||
|
||||
if (isset($this->auth_user)) {
|
||||
if ($this->auth_user->isSubscribed($profile)) {
|
||||
$following = 'true';
|
||||
}
|
||||
|
||||
// Not implemented yet
|
||||
$twitter_user['notifications'] = 'false';
|
||||
}
|
||||
|
||||
$twitter_user['following'] = $following;
|
||||
|
||||
if ($apidata['content-type'] == 'xml') {
|
||||
$this->init_document('xml');
|
||||
$this->show_twitter_xml_user($twitter_user);
|
||||
$this->end_document('xml');
|
||||
} elseif ($apidata['content-type'] == 'json') {
|
||||
$this->init_document('json');
|
||||
$this->show_json_objects($twitter_user);
|
||||
$this->end_document('json');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
32
lib/util.php
32
lib/util.php
|
@ -72,8 +72,7 @@ function common_timezone()
|
|||
}
|
||||
}
|
||||
|
||||
global $config;
|
||||
return $config['site']['timezone'];
|
||||
return common_config('site', 'timezone');
|
||||
}
|
||||
|
||||
function common_language()
|
||||
|
@ -467,7 +466,7 @@ function common_replace_urls_callback($text, $callback) {
|
|||
$url = (mb_strpos($orig_url, htmlspecialchars($url)) === FALSE) ? $url:htmlspecialchars($url);
|
||||
|
||||
// Call user specified func
|
||||
$modified_url = $callback($url);
|
||||
$modified_url = call_user_func($callback, $url);
|
||||
|
||||
// Replace it!
|
||||
$start = mb_strpos($text, $url, $offset);
|
||||
|
@ -481,18 +480,12 @@ function common_replace_urls_callback($text, $callback) {
|
|||
function common_linkify($url) {
|
||||
// It comes in special'd, so we unspecial it before passing to the stringifying
|
||||
// functions
|
||||
$ext = pathinfo($url, PATHINFO_EXTENSION);
|
||||
$url = htmlspecialchars_decode($url);
|
||||
$video_ext = array('mp4', 'flv', 'avi', 'mpg', 'mp3', 'ogg');
|
||||
$display = $url;
|
||||
$url = (!preg_match('#^([a-z]+://|(mailto|aim|tel):)#i', $url)) ? 'http://'.$url : $url;
|
||||
|
||||
$attrs = array('href' => $url, 'rel' => 'external');
|
||||
|
||||
if (in_array($ext, $video_ext)) {
|
||||
$attrs['class'] = 'media';
|
||||
}
|
||||
|
||||
if ($longurl = common_longurl($url)) {
|
||||
$attrs['title'] = $longurl;
|
||||
}
|
||||
|
@ -688,7 +681,7 @@ function common_relative_profile($sender, $nickname, $dt=null)
|
|||
$recipient = new Profile();
|
||||
// XXX: use a join instead of a subquery
|
||||
$recipient->whereAdd('EXISTS (SELECT subscribed from subscription where subscriber = '.$sender->id.' and subscribed = id)', 'AND');
|
||||
$recipient->whereAdd('nickname = "' . trim($nickname) . '"', 'AND');
|
||||
$recipient->whereAdd("nickname = '" . trim($nickname) . "'", 'AND');
|
||||
if ($recipient->find(true)) {
|
||||
// XXX: should probably differentiate between profiles with
|
||||
// the same name by date of most recent update
|
||||
|
@ -698,7 +691,7 @@ function common_relative_profile($sender, $nickname, $dt=null)
|
|||
$recipient = new Profile();
|
||||
// XXX: use a join instead of a subquery
|
||||
$recipient->whereAdd('EXISTS (SELECT subscriber from subscription where subscribed = '.$sender->id.' and subscriber = id)', 'AND');
|
||||
$recipient->whereAdd('nickname = "' . trim($nickname) . '"', 'AND');
|
||||
$recipient->whereAdd("nickname = '" . trim($nickname) . "'", 'AND');
|
||||
if ($recipient->find(true)) {
|
||||
// XXX: should probably differentiate between profiles with
|
||||
// the same name by date of most recent update
|
||||
|
@ -722,21 +715,23 @@ function common_local_url($action, $args=null, $params=null, $fragment=null)
|
|||
{
|
||||
$r = Router::get();
|
||||
$path = $r->build($action, $args, $params, $fragment);
|
||||
if ($path) {
|
||||
}
|
||||
|
||||
if (common_config('site','fancy')) {
|
||||
$url = common_path(mb_substr($path, 1));
|
||||
} else {
|
||||
$url = common_path('index.php'.$path);
|
||||
if (mb_strpos($path, '/index.php') === 0) {
|
||||
$url = common_path(mb_substr($path, 1));
|
||||
} else {
|
||||
$url = common_path('index.php'.$path);
|
||||
}
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
function common_path($relative)
|
||||
{
|
||||
global $config;
|
||||
$pathpart = ($config['site']['path']) ? $config['site']['path']."/" : '';
|
||||
return "http://".$config['site']['server'].'/'.$pathpart.$relative;
|
||||
$pathpart = (common_config('site', 'path')) ? common_config('site', 'path')."/" : '';
|
||||
return "http://".common_config('site', 'server').'/'.$pathpart.$relative;
|
||||
}
|
||||
|
||||
function common_date_string($dt)
|
||||
|
@ -989,8 +984,7 @@ function common_ensure_syslog()
|
|||
{
|
||||
static $initialized = false;
|
||||
if (!$initialized) {
|
||||
global $config;
|
||||
openlog($config['syslog']['appname'], 0, LOG_USER);
|
||||
openlog(common_config('syslog', 'appname'), 0, LOG_USER);
|
||||
$initialized = true;
|
||||
}
|
||||
}
|
||||
|
|
0
local/.gitignore
vendored
Normal file
0
local/.gitignore
vendored
Normal file
1173
locale/laconica.po
1173
locale/laconica.po
File diff suppressed because it is too large
Load Diff
223
plugins/LinkbackPlugin.php
Normal file
223
plugins/LinkbackPlugin.php
Normal file
|
@ -0,0 +1,223 @@
|
|||
<?php
|
||||
/**
|
||||
* Laconica, the distributed open-source microblogging tool
|
||||
*
|
||||
* Plugin to do linkbacks for notices containing links
|
||||
*
|
||||
* 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 Plugin
|
||||
* @package Laconica
|
||||
* @author Evan Prodromou <evan@controlyourself.ca>
|
||||
* @copyright 2009 Control Yourself, Inc.
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://laconi.ca/
|
||||
*/
|
||||
|
||||
if (!defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once('Auth/Yadis/Yadis.php');
|
||||
|
||||
define('LINKBACKPLUGIN_VERSION', '0.1');
|
||||
|
||||
/**
|
||||
* Plugin to do linkbacks for notices containing URLs
|
||||
*
|
||||
* After new notices are saved, we check their text for URLs. If there
|
||||
* are URLs, we test each URL to see if it supports any
|
||||
*
|
||||
* @category Plugin
|
||||
* @package Laconica
|
||||
* @author Evan Prodromou <evan@controlyourself.ca>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://laconi.ca/
|
||||
*
|
||||
* @see Event
|
||||
*/
|
||||
|
||||
class LinkbackPlugin extends Plugin
|
||||
{
|
||||
var $notice = null;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
function onEndNoticeSave($notice)
|
||||
{
|
||||
if ($notice->is_local == 1) {
|
||||
// Try to avoid actually mucking with the
|
||||
// notice content
|
||||
$c = $notice->content;
|
||||
$this->notice = $notice;
|
||||
// Ignoring results
|
||||
common_replace_urls_callback($c,
|
||||
array($this, 'linkbackUrl'));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function linkbackUrl($url)
|
||||
{
|
||||
$orig = $url;
|
||||
$url = htmlspecialchars_decode($orig);
|
||||
$scheme = parse_url($url, PHP_URL_SCHEME);
|
||||
if (!in_array($scheme, array('http', 'https'))) {
|
||||
return $orig;
|
||||
}
|
||||
|
||||
// XXX: Do a HEAD first to save some time/bandwidth
|
||||
|
||||
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||
|
||||
$result = $fetcher->get($url,
|
||||
array('User-Agent: ' . $this->userAgent(),
|
||||
'Accept: application/html+xml,text/html'));
|
||||
|
||||
if (!in_array($result->status, array('200', '206'))) {
|
||||
return $orig;
|
||||
}
|
||||
|
||||
$pb = null;
|
||||
$tb = null;
|
||||
|
||||
if (array_key_exists('X-Pingback', $result->headers)) {
|
||||
$pb = $result->headers['X-Pingback'];
|
||||
} else if (preg_match('/<link rel="pingback" href="([^"]+)" ?/?>/',
|
||||
$result->body,
|
||||
$match)) {
|
||||
$pb = $match[1];
|
||||
}
|
||||
|
||||
$tb = $this->getTrackback($result->body, $result->final_url);
|
||||
|
||||
if (!empty($tb)) {
|
||||
$this->trackback($result->final_url, $tb);
|
||||
} else if (!empty($pb)) {
|
||||
$this->pingback($result->final_url, $pb);
|
||||
}
|
||||
|
||||
return $orig;
|
||||
}
|
||||
|
||||
function pingback($url, $endpoint)
|
||||
{
|
||||
$args = array($this->notice->uri, $url);
|
||||
|
||||
$request = xmlrpc_encode_request('pingback.ping', $args);
|
||||
$context = stream_context_create(array('http' => array('method' => "POST",
|
||||
'header' =>
|
||||
"Content-Type: text/xml\r\n".
|
||||
"User-Agent: " . $this->userAgent(),
|
||||
'content' => $request)));
|
||||
$file = file_get_contents($endpoint, false, $context);
|
||||
$response = xmlrpc_decode($file);
|
||||
if (xmlrpc_is_fault($response)) {
|
||||
common_log(LOG_WARNING,
|
||||
"Pingback error for '$url' ($endpoint): ".
|
||||
"$response[faultString] ($response[faultCode])");
|
||||
} else {
|
||||
common_log(LOG_INFO,
|
||||
"Pingback success for '$url' ($endpoint): ".
|
||||
"'$response'");
|
||||
}
|
||||
}
|
||||
|
||||
// Largely cadged from trackback_cls.php by
|
||||
// Ran Aroussi <ran@blogish.org>, GPL2
|
||||
// http://phptrackback.sourceforge.net/
|
||||
|
||||
function getTrackback($text, $url)
|
||||
{
|
||||
if (preg_match_all('/(<rdf:RDF.*?<\/rdf:RDF>)/sm', $text, $match, PREG_SET_ORDER)) {
|
||||
for ($i = 0; $i < count($match); $i++) {
|
||||
if (preg_match('|dc:identifier="' . preg_quote($url) . '"|ms', $match[$i][1])) {
|
||||
$rdf_array[] = trim($match[$i][1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through the RDFs array and extract trackback URIs
|
||||
|
||||
$tb_array = array(); // <- holds list of trackback URIs
|
||||
|
||||
if (!empty($rdf_array)) {
|
||||
|
||||
for ($i = 0; $i < count($rdf_array); $i++) {
|
||||
if (preg_match('/trackback:ping="([^"]+)"/', $rdf_array[$i], $array)) {
|
||||
$tb_array[] = trim($array[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return Trackbacks
|
||||
|
||||
if (empty($tb_array)) {
|
||||
return null;
|
||||
} else {
|
||||
return $tb_array[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match_all('/(<a[^>]*?rel=[\'"]trackback[\'"][^>]*?>)/', $text, $match)) {
|
||||
foreach ($match[1] as $atag) {
|
||||
if (preg_match('/href=[\'"]([^\'"]*?)[\'"]/', $atag, $url)) {
|
||||
return $url[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
function trackback($url, $endpoint)
|
||||
{
|
||||
$profile = $this->notice->getProfile();
|
||||
|
||||
$args = array('title' => sprintf(_('%1$s\'s status on %2$s'),
|
||||
$profile->nickname,
|
||||
common_exact_date($this->notice->created)),
|
||||
'excerpt' => $this->notice->content,
|
||||
'url' => $this->notice->uri,
|
||||
'blog_name' => $profile->nickname);
|
||||
|
||||
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||
|
||||
$result = $fetcher->post($endpoint,
|
||||
http_build_query($args),
|
||||
array('User-Agent: ' . $this->userAgent()));
|
||||
|
||||
if ($result->status != '200') {
|
||||
common_log(LOG_WARNING,
|
||||
"Trackback error for '$url' ($endpoint): ".
|
||||
"$result->body");
|
||||
} else {
|
||||
common_log(LOG_INFO,
|
||||
"Trackback success for '$url' ($endpoint): ".
|
||||
"'$result->body'");
|
||||
}
|
||||
}
|
||||
|
||||
function userAgent()
|
||||
{
|
||||
return 'LinkbackPlugin/'.LINKBACKPLUGIN_VERSION .
|
||||
' Laconica/' . LACONICA_VERSION;
|
||||
}
|
||||
}
|
1
scripts/SearchMonkey-MrH.0.txt
Normal file
1
scripts/SearchMonkey-MrH.0.txt
Normal file
|
@ -0,0 +1 @@
|
|||
b2.PXYLSJBuLkZnUQ7H.2gOSSTbVllQM_XyN0ya4sO9CNt5uBUATEGyKd3LkkR0QXTSduKb7xj7Bp1TX6.8XdIDsOVVuXe.3HPnPLtSacZm66.NLY3JGhJv1MfGvcJU42YEQiUvpAhZRe46E.lv38ZPzxvhjjAmGFyFv8UNuhdw6sNIlTwBdct3k1GKA1YSEeVJ_xLiAVa8IaU_XG8PlxMovsBPePOry8__.aAp5loHtmqX6vEVu3lcHEn5NvSZ502QoWkK_20Y.6oja.Fqqxtr6eli_AQXaVzWWDEu_D4_rDV9Mz_.EC8KzXKo.YdUaWQfS9akqNm1v54wTNM4ZDegsbKnaNUR7J0OYgzpzcPj4L57yWKFm7k2af.v2loGrUOKJGO3rBcOQSfUz5jyCKDdd29cdd2L96XA0Uy31RjvdWSsprigS94HjoE9e5TUMHHZt47Wi50DsMq5R87uznHTouuHOKyxT3c_wCejxb_A0lVM5JL4VnrjVEo0CkY5ju1fDXfXG.Q8T4WYbnW_4i4W.DAuaFiRxIsLtrSG5bpMvJWQ8MTfc39gaLkNtGACL_O8IxX7iyFjARapuIXPhu3v3ndEt6qlgcUsEBl6zhxe6WcTQssBTUSDSPQWMmbA8TCYe1xwHd3ukSdqkhkALdIri2oeLLhkbNQRKxouIWHqL5aH0r7CCKM.P87rJE1meb28ux00FrBg4Fa62RM5S5Wn24u0odvPGK9JGmzisi.zM2cULr8C03gwBEeGDXzLIPevVnoqcYvtUfs8_B9xtaFzsKEM.04JX94e5Y_qWqfJs2.vWdY02BwWNsM1ONUzsS5qBzFhNpVnvZYHkNuOGXLb7cg7sL0Zm9pGMQPJsffThK2KY5EGSxbCB_JMqZmu9UtmcFyrsny7Yo1z6uCgu7GzN9_6Tc_DGW4OyuOm6XaLZSbGApmGosEKRKIODNAUGyT6QChSEwnPxhgYVpIn1TYualgrBtwvON5d84Up5.mxTH5ZHDjQcW04oE1dp2mBQhlUHvTF.PbEuUztvA8mMuLZgERcE01oOKkDlh9gvjZNiMs6f8SPmulKmtvheecitvB4Urgkae_PiXbcD4aOL4PyjwByX4zpCw9NG8Ga.6JLv1nH.vEE5XBCzh0_rnEmv0XzNBjiE8nj_p2pntfxmdpEeSGrA8WzigZTHrjawltlC_sMGLqR0EfRqoVrs3NtH8jafQ8DoqKUnR1wiG1BFC9PiQzPbPunYvVT_vMILkfwHDS7gY4bfZAlLrfbC0n8EKumGJc1o1ag_VsfnO2O8vE9667oVTaAGMmxXA_g1qpPVJTDWHRm4kwA6SWTZ6RCSb0Scd_DTXN4cevjn3sSGOCKE5Tocd1s4JRCuDPGQ9XI1OXGSKZrUuuM8mUlZqj6A.FWgdv2c5qXoRla.Qlb6encfFh8gnBNNoIR5X4F9PFl7.Yq38Vi0BJ7zIJVYAU8UXqJqPKKN.qH4A941m8jG7trm9NBrsRIkyCJBE5W1UVZ4x4sBDSuJMT_UCYCyn_qsjHkYpJOaYt44c7PT5U_S8oVUUe0qtuvmQ5DK.cZAU1hdw9fINikie3yGxO66lWRPtmM1rqTHwEHqaJXkn4mnRhQmHE.pqB.N_Byir3AiezaA2EGi6Z7.G99XrOjxJ0tIyim39AkoPZbeWqVlLDL.4AGCzPLQyndMClwWJdjcHBaSrxGGL1OpZ9cGA6w1zLD_6uyP4gK2pGTGxvOk4TFqrpc07eX7j6agiBfkUzYykXSm_JYDVZqiivMwKVEynFsX24erN0kd379wr_QI8Ov2nfXudbZn9EY1bhmupuAtMqpW.dNGk7YJ3AD31zrfz2KcZjdQuLkTYwqW4zI3YEI0jFqk0WYXSUmCNRzbdvmoZI1kAWRw2COuUnYeMfoTiMeJGgUd.g--
|
|
@ -1 +1 @@
|
|||
BE9KrPPSJAPljm0ykS59yJQmWBFY9RPVJFhZsiF_5Wcf_tkGwf4FP2Ncjs7qGfHFCb2pv.eDr5y0zhKrF4s3ugs89DbLaA.hrbRJwglsNym5TDwDycrGw3TvjfCVmHBx4VzOZ2QzUyXBIs0T30paT6PYQfARKBdMkieKbbQ1tfl.f.ul35_kcpoXZt_lTDWFDaia2RM41uDLHJyVbCcRkqfHCLabgNeOq_MGFXGA6DjnKQx7TNyQe.2N6IyVd1quVapHn6jUOsWNkahehPMHtO72yvPpugS0UHCKBqcd.UCcbIhmtzLnKoBQAH2AJqUrmfg1XRwqFvTo6y9Z5XmDQK2hRnv97InV5he1AMIqNUotAcIrYjq5Tn42whYsznnMYhMY44UqZGoJI_ZwsSvnH6Je.AhKU3hBW9Tsmggpxgnhx_o2vhyNw2QAgPJng0FKxaevCmPnFtLntwluhxLbiTo2IiIotP4VjIVKs76hHzAsmXYuvS01OU.XB43Gmcw5yP9OUIoh5fEA1ANxOb.ba8aVxu2FdvedbOECgO6gvr.kYilrdxwwnVbHwVP6esrOiYE32dRs0_tWdgfvJfyloLjj_M5stZLEBcVfoUSQHsxX6jW5jrs4BTEHhKrxlOzdBt8f8T2E6eA9x2h7B8zK4eRtC2NBrcj1jzYCVWUT7IdbN05NZBjGYvxtmX1KRW91QwdFXgytfOINFkDk0scNCGGt4OYDzKLJ7sRBy0iKhHNfTC3noUhWf1372uXQ_yiWDWB7cTfxF9meAU3TWrTS7DjGTkwLvCVUJ43QyCxBQd1jWU6sp_VvytDeHx7cqrkNsa3JRN9dht3POqq_mVL0c5MX_XRpV8O.tPSGmfUPUrR5qnV8Z739D6PGWkOQzfzTTv6vGkf4jj1xyDEfUqr767_yL7gHeI2VlbdD2ammzwEhRK9f8ME8FbrZTTgX1OD0.v1cFULt1lew.rtCOtKf4F2MBbi90edst.TpOoUh_TvFkgKf0zgteNo2JjzrmCO.uviLCM2weGksARL70mdU6W9N932YWr6E8HbUc02S8ifSnbMpiUTHwNzMErsg7BSyRVJngGLDreBkBEIjXkNoApJR7kMPMaYVwtvjU.8wLmyFLZ.PHqqcrADTr7R50hL3UKj1mLsL19gQL3cP9J7_zJJM4Q1GDj2dPWBLhJMosUVhW8XBFPhW0aEdmgqhpsAJ_qv52xfjBeEPyPmKHu1p.1G2QEmOqFm_swbVAPHnra5zd8x4OOlHGFeL3qPLW9_LXN46chs6cOpUdYaWRzT2YuOUHHE8RDV9Mev22p7WjrbgC8hY3wK55Cpw_fCekaHcJX2jU3FPl09httjI1i5yGU04yD5MpOQF5EadbSlfjDEg8H.GoOFZcyKKDnqj_8SYdrXWq4aTqAnWgvBPh.bznvmevDgEpH.9gtzLcao4Dzmk.K0PnRgvkzyaN5IGk60TiD4Fk0u3f4bqKaPCOIKCTajbNGIasyND0J5ROtAa_IlXljCpzeEYPhSASSFV6fWmlqHFiGa2cKlLNr157MAUVQg52dJRRyid1YDcHK8ZuMc4BX8QA0olb2CDbUHKxxwv0zbIsgUTL8gvKIAPwBfF0cPfV9v6Phs1rPFXnysxdnunXnr6r6ZCN00rZeA7XbH68f.UjW0.ERGvB72kMnB8fBhfEgD22Y0ZFkwxI00UVAm5yiwkjYx86EdCpkMLBVDR1kWictX04pFLzxZK8iRflPBy74Nx2SFO19qJFnQmKEV2IN2wlrH6z14902LEI2Vuh_dVxNphr1k5mJ9x2VNGmvol.vvHmaLCufHIrOMRkAFMxeP37Mnzyk6NpxAQbdV0KwC.YTfoK9Y5knYAherlh7x8NqELX6XqutF2Lm11dikoK8Yu8u3Rkhoaef00PISV0LOb0E.D1LRjFFGzUCt5Ezcq68Tt0rFNEF1gm5Xl9Rygln_67F0KMgacpKTJ2PBIqcteyRZoRohFBRUTLkKn784KacSBsWFkogD5n7blv943wWeZFvUcKsFyYZny1WLvGEQX01kbjyaDCLDxiEQWSvlDuD.2wOra0JO5.Bo2PpPK5tbNexZDVx3k1yb48ev2UC4J4IlD6oK1KYe46Y.7NX9kDYCcdRiQOVnLJ4PSbPhDW0hcE2SNtxmFdpAf2xQOiekXY35lkuNxvebVhKuoFWxUgQmFEfreJQsC5qf_dunxGquD6u3SXnLbTg8aNWZqtR74dr3s7C5kwBK6eBGwJfkAx8Z2JKwLr5QHdISfa0wLl7aIejz5Mg_tfeATLIwW8SiIQdJiZ992Bx1k_50XQVeHOdyEc8J1Tjnkrji_ZY9PFn3AZc94wXD8erpANuJIbDIdLUp1idqF_TDtmqSK2qQgz26IkewcZnSIQnm98qxkHf9DAIbE6_GUP0ZeWqLQrn0CdhejhOPsKsovcDI7zBHMQABquMeBh9YC1.QCO9br6YmYEAV30n5IsZgl5PRiRDjkhXv8VaE.fnRNndSy7BX6kmxSmDWQfY0FvbUBWBQm7tfU3Mrk2Ojm4ALIYUZnJxl.5xj6VvD.2ZX3ug_1zo7mAiAYG_F939DMccfgCVFUcdtbF9Q9YFeC2bVMfgX5gA75nBCu5R8KopeWfTjdqZBtJoT6HxFmCSLQqIdjrxD7X2RapCI0QoDsp9xyzRZJ5NF5ygVBJqtNn2lAwFENasqFvGRSb35B.sjTQoHW5WBoBjFy5tNF1vWYJENfKRjWn5Pr0FvFdQfBKqeiWrLtStAnGrd49frviyK5VtYa.eSuRyX32SGb7.Pxo0Hf0bhER4uvPBktf3ec5UjL_xRI3eQ__F7tRiGu2tRYIUYcUkhLKAGYoDx2_gdMOXcow5W3KwtuVNtZ4UMRmuB3ccyBUzDTRWNLFISq56JXU46_v7ANub44kTLDfGuJExu.0NaOuW6isndAIYslJlcCs6NJ6j_6Ag55G.UhQFfzDKVOEt.L
|
||||
WJofugbSJAtzh.lFcHxO_WHM64iJd7qHJFbsqC9VLnpiqoBxdbE0iwoKozxZC331Svk6O9Un9jonWUrQeTndGO8hldPlxqGdFdbl6OjoucDGG_h6qBw3Ugnbethbyuew1qOaUr6ItfU4Pe9O2GtUj2KbqxZpUAvMeiS20hykHbIZ8n_1.fl.fTToLiJn58zcLc1_ryM0f5jbkIGwmGX_SG6isGJRzC4.BiNmntpJ5ZCKa80e7oUH9S6NlM5SEmXoA3zhuzkbBxF_fKLendcC7EafnTT34MbgD5s4uwTyubSBJvdmBu.mWSO9xrnojM903Vo8h3.UdG.id1LmFrj4xxd24IQqZM2v4zOfl8ugF8ChHaIzp82A1AOuk0ulkqKgiALia6bsZMbKdPZtlu_HGrF06tCt5VGvu5f0ZgjRk_kOlP_f_oppyVKRWzZpSH9GryOpoqJA2vlxgEH2BvZqaaUYEmaYJjIh7eNkLRLVorbK7rGhPf_L5qfHir.31Ar1zZxWgvhjSW7ipkvsNr_3j7oOh1LSTVWNHo.dpLT0_dib6syFopav2FqStJG0RTsWNXkQ_6h.jf439DjwuZmCLMcbUrMAMrRVcsUyR5nfqP5cP0igSx8m.oONA7O.txOXSzc3HjqvIVSTRO8BIHx.vTMJ6mq4NUmZ6_pHWapHbO9esUlIyXf5CF2qhtlM9aulr2fwCf8BqtjQctAtvYLrI_qKP95wNaB_qhcuFsZN1aiql.FtigHe2YYTix4SWg9.67VSBAyOqjWcA5jbyoomf8Gxzks9PZQfSuKbkbw_x3XOshGl4QMo6CEypV0qIYVVW77bOJc7I0WgwgxuAKg6lROlmsIdN0.SeTV4sY1ZcoLEgdNCNb81SVstXdE0iPXPZZsEStez.qluWnQoVkkWfOaBe78PxPZUvQS02RaR0FCH4bksqGWXLA8chAHIe4vPNerCLJwskKG3rA43xRDEZY7sgdEfA93iTztmIRq8dkYA6dJOxOMtvpSdh9jds04xpdmfhb23bVYp1XCp3qWka6JoKlzvQU2THqFr7gNU3KMCbr2ngHHkDTZGNO__MLa3SKlp2Vg3qHXxkivFUmWTNUz5vuW7v.FhsKOVFIFfSrTl3pjJMD3hyIlxzf_HJ60q1LRBkwqxrA9e8Fr5tqWQj.evL5kqz_r26MdHFeMTx9VwfDnHLaVx3eu5Wy2NJmLVPRLNn4t1VhexsEdLquy0ynBVBOBBRa2HbYgv0lXOP.QRl5twTKwJO6fx6J4_L_HhPv6jhcQej1LtUyGyKwLuXrHxnHCOqD.e9ZL_xnqSRthM6NvBsvbnZOPr7falUuQWPzbikXMFUvSlckeetPi2Z11l9pH.cxZDj19B38v7jwT5zZMQN9FzQvTCvzrQCpHxfIxwh3a2cx18BEMKHL7MJWDsoYVURtI3iIdR84WzXcr3kkCz.LM8tqTKVkzHLk8Grx8KT3dF5ZIVhWK5yyb05RFOZdoZQ4cWXmvsaQhDUd6Dm2KRJiypSFntLLSp34URsLaqzxaakMnywHvfmsINah4oKBBy8.NFBRULU9SqTyIOwf6_ggWG06J1WJxWV94RRCD1rMNNM9ISMOo4yB_dmfLHnXKOZm1lOnisEN36gsxniy_psLN3Ed1ozRtbbD.wALfE.92DrDA9ASPOdu08P7xer76iXVbdt_4cg5whY59Hq2N.A1mrSbetDbSu7NeziZ.nBvSUg_YpVvMo2H3elySL.1UsLPFznStGuYfbISN79MZecU.ScSCtaeZQ2cBFekXMNT77HWZmv6YLsz84ceQwL_MidvAYqPh6PUNiBl8iwFR5PCpTB54.g0m3zuALOqF9hJ598v9rh9Zlu1SfE.UfmGpUICMSuXH7C8zHMq4TFZGzCuBY9EOuBc.JQaCfBr8N3jnDq2bwlQjDmgbVW2xm2WpPMCB0OxkqoggA0i4ZZA3gSfqhlZ9mdmr_l9mUe0dOd_EkyWyu0l.Z1.VQLY2KJHl.8pZWyP6S_RAFmABOlEGP3m1E8NxaMP87o9ot_BNg21DeHbwADKbv16deRlDrKmntHoPwfsaTPNo5KembI_4nHxQ3y2B2OjsUPcLYFZZtsP4ukLBhYusR8SdtsEC._plxsMQzS9nH.zRP8nbluMlUjCKgfepGN_rCq.NeGON.L_.FowXEOcX7M1SD.IfihdAHqk0zUpuyTfe.9E77aEu26iIZOGmyOwSqRvN9HksJx.iMk5xL.xah7hl.NByMBbmkRDS52wDMxygNpPXsIHdvu8OyLxq.ZsNBGF.IHs0qiquX4.RZ2TBqTQ6O4AhzOTpFeZlG3UzdmxsEtDI2a7tY_27Gg39apf_83ijjiLaJE2RG5Mb9krZ2RCIAXaFez74o_oqli5dy6WyT4lEIKtw38T7m1bJ_xpLju5FBQUE54RpsK.ZWSAoyh3c05b2mBeN2id_NV7sM4MinY7rEsOgNVevUNrWebNrrdunUF8gsa59cXdGSKvmUw9kEKW_Z6Y14AICD43zlmVGYDYlSLqpHsu32m9aGuQFlqzBlHCKPg9uD9dyFKsZJI8l2yq91sEj9snlYhApmHLVQzGWLiOJCJUclByiPmVD_.OnHE84lBcv0bkXKFYXvUhFs2Xu1VrN1mGXz50KkG4Xovzkw0ueuWlJ2vozPUf9CkJ1mlOA4jwuFLMrQZmN04aXyR3ZWFN8pdSVITI0ADqNr9OGacCra.emTRIWdBLEj18kp39vhlR6BpxI2V0FQjC_27n2hkDUNAUNFaJ1Q84OO2mgzZnPXcN7NYz57W3lB_mMVUtKngnRlTNWn1sFdN6.WFQ1szScjtpFa68NBv5pLUOvKy49VWjn14uS4aUoDL_fbMi9tGw0tlrMEr74fm5hJNwPX1QKd3cy0jqDHLIpZTb1aNxIz_KU4XIJ2wx0gZZQH2zV7eTxsrozzo3J0TtiQC6pPOqbnsRF1TuX1..DV5LzUdKX0TUXyO8x0710.EF4z6pim0HuVKzV2VBKmG6qtzpIRCKzVk14lx2E9YkJTQusU9qZnw9Ixu_YL0Ue_4LachvurMZDYTIRcSyd4St3TmtZswmeLPS6Md6NtHwxQEy.RVLhuCmliiZcw9hB.CH6v0UX1i9E6r_FZyboWtnzX42mZDrJR4n1u2nGFzYwJrDN_kJ3uHKs4EoV9ESEd5AojHDWSkOs4lKbyRlYwhGrHgB25mZ2xRdBtkHMDupYisNjOJ.QEIXzpK291wKHWTL.VdeTOLqGxsthxoR7AOdnzalMIuUItxagzB14Gw2eZ5PcIZvcgO9C4BouZW4SAg0D0Xhmd4A0MW0an7NqzXM4VDVg6WMgeBHqL54yfHjK4oMkwDUQTvqNH13AKINKbtr5t5JJzfpSN0fmfysA1Z1PdkJROhFkyH1_ZXcoMJp2XbYSk6ZXm69U4j4QCYe_vmkp3NM802bMpohywJRxYstmfIxinJukmZOEiSClAk1MhRGVlj60w8ggV29QYa_pdqUjTm0DwYZQF1Z_NVkhVXVv7bQMpRQJPIMwf3TggPPHVAMA1tk0OdPFOCx1207E-
|
|
@ -1 +1 @@
|
|||
bzN6aQzSJBM8jb6JDpNRvtfEMpxOC6r2ffo5VJf0ah_Q3kXs8gwvDzGy0KGHkwDDijfnJAwhb.BIcFihUZVWe.vAy2xZZN8XUMxHwc16HwCsgDyOC9sH5WzOV0dvJcVZ.9ipAgI7RuXsAvZdJv90N89iOh1WmD6QwWyzyvnu.FjQDrQH1DlGwFs3ZNzY5lsd5uYhtJ3puBXrzSmGcR68_7OEw4QzAV9SyPSHSskXF56cpH8pUey8WiQieM4X_uuKsMjQfWEUdqNb6teXDplvPDRqHwP5rC6X1_oHBiocfgWiBfUfKOXE1g5J.JtqXYIBee8ROyx3sVIc7V2I0eotZCCiCJ1k74ru2OGC9UhX__ZESGrp9b0JZGZFO6w97IL1Y3BTgVXNox8L3FcFjl11s7YcYBhc_3e3WbJA4pZzczPzd0ouZiCjUcBCZXNu6fnM6XerBbsVj584ZbPdjQ6A5TcrED8dTcFdgfMWlMu7bHsE7e0QIJYmc2g5NWeur2ovAOPVxhELrvnJ6X6Z.06vAmwdJcl4hyYRVqA.9QynHlH2dezXS5y0eKXFUzg.tHNqZboEGutLL316mZQYvwZwATZFc8iO3EuiNV3MWbiCrLX.n3nbXp82A4v7Bv.PVJoeOTmiPmq8vuxts8nUEPf7gA3j6.DmlDvfZQNx9.WNmNFps0B08hzDMvKX98.ft9.GJlftPCYCk2qHRtWm3tjB2R9O6mF6XYvOLUK7aQ4rz5oCxHwmrF.n56G07MS5GS1pjV3ByUO88PI1i_rPbim_Up0jIO5q7PqXudyFE.nG0Dc8yaExZk7PryBfSHk2yMQVXcBjy3XQBVmrIwPwKs.YUFC1qLSThfA8UNckik2xuDZKvf29xpGvHlMbH67UV5HhY5CTeqlgvapRThmfrexqaBTTPPqR.Sy3Pvr9vHX.0UC6kNCTlxW3CExrx9EipRKUvRVClNLEG6M9hm5MQkdfgmd2Dmj5DmjcrZkWOqnNfLn_mtYdoD3nK4zCk.NZwXTFlx5FVr5bOzyncalNdb77qLEH9E9R8d2KUA4Axljx29kycILhZCvy3Qz22Vz_M72lVFKQAFnGlERRS7NLu2rB00e.MNeH9aB01uj.y9UHpfjTAwOJSFEBf7yKRCkVzqFpGAQo7txlC3NRgSyI3kQtW0WeWiNzghBv8c_5iXWkXqFyRfVZUCw2qqow3BF6SDtkhXAr8d.JrprGdG67U66ZD3JB10OLmmAaDiiCr81eVOkrsz3ONdigZOlP7RrYtodAORnheCZ10SAYbaru0sKmsJx4sB41QKn79dieWs5oihI5Rr3jUzPjfYgav5jeEfUYz5qHTtmps4vVJjt9s4zueRiT8AYzg.Xv.wdC1dvBr_kpGxwrkMEZJ0QfZkH.TDWBSWyxeG9gtuKd1gy9CGQTXlLvonJWHwK9gwWZwRKIanV4gnMfWz4pvhGixzEaiRU6g9giKYHpVKe.w0cwPfSDIhbBYqrlvuxkSTXfuFNyTpHM0Mgc6PMNnH6GooeLVK3IO2M8dRdDbL7EHykIJWe85pm5BxSqFTcWXn1nGuVWQV.F.J1XYJj1KheT725MVRlrE3sH.MgY6m4hKCUXT7mIgJ6xsfAQ8dWn7V1fCpqoIYub55iw_dg8yfZ7Yfgwj9bqN81lvTWQzBMLKYpG6pOh37JsTikpXa9kTSJfbJm0_GYZv_Bu10D1WqiO0UCvJXrlqkl5F610fkXkhwRXvmIQL2Mv_R71zSKqMr1g5RmnNifUv43jpXoOWmOKscg59pv8eOyb2JG26BtyVS2XOTDw6pHpHKuksqRKt62i1z83uv_ve5rO0zIKeMnWGlvXKHn8ld3fqGyJvqZ7wqYpnV_LTE3p73XCLEeniClXyk5Q_Icu1PgakYh3GIz8RVBjbjgrZIsvvkEp0Kgeo6oFY0wnboIwDlaUVHIue1nPkuAIsVk9i_IJWeqoRV0eO0wUGWgQ5.HT9RzcKdFVe2kl84RKuQkvoibiCfGlfTXpOfLOZtwsfl2TuNgZtP5oG1Wfsgi6g2a1N8B2WHYN8kZuG8pMnc4NQDIoO8BDnz89lDt7Y9JCslIM3O2Y25EGsylNKamKPJZr.ZSvvTVJG5sxftLxvF8vc8h5_pJD9Akj.84CjqQyfsnzFqc_.EqBaRVSy35wHHrdWTBsvjcobtSed8wXs.5En6HdubdATcgthozHWMEMsUdA29XkenPic2cR.8bQ5VdGZ2_YkJIXrxgt_XoEFFB8tgy061cghyL2fc2fHMQm4KBEc1vjkZjwRo9adWbg9dzAl782AwKC.C72bw--
|
||||
8Yd1y.bSJBPj3Ul5NjhQHFX0dN0NuJAfBObEdgIY2QDh0bVXkzcJbNlBamPSFBZqHojxbPzIj5GlY3XdxGMY6ttYzBHvc_FK5krngYtlLSmBtjRArxI_bBfngIxeI2kFC3wPvHIdzp8gygRp2.IlDahWHABFhAPrPrxAkAOqv6O3tKUTTzcnZX22eOdoXVXQSMiphlDQIxpfbdpLnduBvMxvI.hwrfAW4iIVY7iBjkdnrtdAVj4N2.u65khGuAwaX9i6Buca3XQJm601Atf6fjM57yOFA0.laixrOss1_F36iqrc4mOeWnYI_UDw8SgIrXroswrJ.KwXlqCdICxel3Ui0hfrOoGnCt4HHT9tiObu9VG.GxM2KQ_tjb1LwFFX0WEisFKXfQwpR_2NU..0j1icWhnmPHFJGsImvX1dFgeTqcNvoAcfZ0y9c8KQKzV5CbQeg9_oPC7uviIdxQK2lJ7SzgRfZAw4FDeDXKDxuVZRE7jZYQEWtkmp9dFlFLM_L0dPk4kiJuRrRPJ8DAyWSiex_ib1mJ3bHAxbsEcWoUunIOivWVdZDudCBath8AdoAQ0P8_f.fl8jYgbZil49Qhg.WmFD_H0w7ikdEi1iHsWnjSrDORtAjENGvoEje0h.gMBf38FbQ6vW2BbKC.RBKp.cQhVPvDHel1UFtqZ8IJm7mTP0BDSDG2iFJp1_N7L5MUoUxEwgskMxeQf99vE1UybdR4q0kbx6i_RR3_6.AQ5QKk1EBeKAfnVYDdwcLGVgWd9JxxvekkL7Scl1ncZ9ViLvlC3Hfrbtrm2Q5rr17pIG6L.a6gDKKH1NGgapyLUCVtKZ27Ysd9ExMF1yHfMAyOKL5I7KaJmZTZnxWQ9ex.hHS6tZ9WjPFhjvpve7H_mB8ZXNBdEc4bL.iRdknHNe3WSSgCLWf2RVFZ4wmVMYW4KwGpIp7iGbaUDBYdBFkcCmv6zeneEsLGJHUYipsay5unopuFyBe611pwFRNHZ3XznmqaK8TXl18k_PCmQ6g93.fXFeWKmszw_FArltYuXHNIm3zwRobvDo7Rp0znnt76Q0ha3q5WHggFd6.QmC6jywGLpki_09v0Pc6qGh4gpGt1vZeZrA1OGQ7VSB7Muo4Mr4TbQU_6342nXeG0tBImIanPZsQrew1.tOmAfn16UpzcCuEC3NWw99yipIaEZirdr4teITB7xX3x6UGRcjtoAlz8oo6VEYaFX7ybG6Bo.K4hspnJLWH6jKK4hcpYedE2qSB0W4z_ieFy6Y.2qrx8_3CL1_8h5w6PjXOFTmsyk0v0DXQVuGh8QxPgPVyrWyDzlXJZ.5_mHTohGRGCI41I6jGJcCOh3qEQZKF2VYb.3o00Le9YlXxpBIBh7rBw5vNypp9jSd.W6VPnQ53xM6QzVbYW4OV3gkVE__AgvKNUzfRHiK9dN7evwvKX66W4tUPGQapjGbRWnFc3Q_0__oMYTtSR3EMxs0Lvkjh87s0SMxy0gzvVCfNt4VrdZCzdBd8N8Tcj5Pc4KJ09ikPtNB3V8i6jtcVUuKzs32B37O6AA2iOydQdsPDEhfkDA0S_ocu3DqqBsJOYmzjRq39e1WFhlRijNftxHF6DRvzpSIIm5VzcwIg8LLl4zbHyeRXEmBDp.KvlfP_.z2IUqilhGeccCmV2XFD_VFidZclzscmceMmuBOXYmaplSW3sX94eM7mp2dyDjVXtR4igukNU8Fabaqilu98OKIv9mLdTlE9u7t25KNqMHQQOsYNM4BUocalSN1K3JOot544vz9VbWJfIljF2L6DCduJd6h9aDH5wLMjljRqrteltA3yz86ensnOZ5wmU8X3NMpPN.OCXM8zxqdhEGxE_jeXkpf4AKIFWYrn8zH84sFU60gpy3xMOleKK6NfrFDRdzxkBIJGV7yhBaDi1_0iZuwm8hVLFe2odNyEKbllCeXYpeMJwe3w_e7XQD1T2Zv4bvK69qYPhmkngPs7_nCYYUFWKlvkg0njZyxn3kNSTONHjzrLSlTWMvGobei1GV74wJzWmmZpC1yQNOe_8Stjbj5Lg47iELys5kCMWU1NyIQf16AT35xTlPCYJ_RNVy3yDNeh5kqy9AMX_HIJOHw8xwJEUoiqKzWzLV.BWmpzcny9_7P2naXBrzRlOYmYyWUtp.P0gGNxLZXtEaKKGgdQB.fYXUyuKH0ptXqj1YCl1g1KWo3.AwndjDy0PE8ZYCaCjz.EyXqx.8bvfJycccGRw5RVU74aydE0DRCC3vF6VI9qbPWK89HNTMWxPSSTHdChY6yAhNsc5UhDwsAmhZ_UIYynZ8AM_oCS232.einbw9fYI4X6yZm_fD60DJ1FsBweU9ivf3Y4X2ucLFY8xPp_8SxZ5UtaP67TKflzMYjuvhGXAUF9.1u8ZKJGOliFmMxIwJ7lPJs93HfZgLN2eyH6F.rZjREeyd0fbsRzZ_vMEs4fx6sX4eIGVoyVFpO5b11kxnBqlKnATI_nw1.exkl5sQj00clwphsecXiVLaDFCEjG94ve_1cw2foqQUoX6pUfqr_hSSpnacdacPclbxWPehuN5LpjMso_5b4v.rj4s9VtzRqxovBinIgBdTFAH560aN5f7wuN5ZZT_BT5aCFNwQBYoDfCTaU9jw3aaXU29vEJmYZcvC9WCpU0RTntciGlx8rIbagu7cUGrYFvLbU2ZekazCMklPO5Sno_z5zzxLnTpFxesNoreOj7M3Cp1RHKUmqAog04bYC9kPFvH_q.jF2g507LEN3nPL9EJUx460UG_o4HRQOhDi2YrmrHk.gLZECgnMwCN770fhL
|
34
scripts/rebuilddb_psql.sh
Executable file
34
scripts/rebuilddb_psql.sh
Executable file
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# ******************************* WARNING *********************************
|
||||
# Do not run this script until you have read and understood the information
|
||||
# below, AND backed up your database. Failure to observe these instructions
|
||||
# may result in losing all the data in your database.
|
||||
#
|
||||
# This script is used to upgrade Laconica's PostgreSQL database to the
|
||||
# latest version. It does the following:
|
||||
#
|
||||
# 1. Dumps the existing data to /tmp/rebuilddb_psql.sql
|
||||
# 2. Clears out the objects (tables, etc) in the database schema
|
||||
# 3. Reconstructs the database schema using the latest script
|
||||
# 4. Restores the data dumped in step 1
|
||||
#
|
||||
# You MUST run this script as the 'postgres' user.
|
||||
# You MUST be able to write to /tmp/rebuilddb_psql.sql
|
||||
# You MUST specify the laconica database user and database name on the
|
||||
# command line, e.g. ./rebuilddb_psql.sh myuser mydbname
|
||||
#
|
||||
|
||||
user=$1
|
||||
DB=$2
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
pg_dump -a -D --disable-trigger $DB > /tmp/rebuilddb_psql.sql
|
||||
psql -c "drop schema public cascade; create schema public;" $DB
|
||||
psql -c "grant all privileges on schema public to $user;" $DB
|
||||
psql $DB < ../db/laconica_pg.sql
|
||||
psql $DB < /tmp/rebuilddb_psql.sql
|
||||
for tab in `psql -c '\dts' $DB -tA | cut -d\| -f2`; do
|
||||
psql -c "ALTER TABLE \"$tab\" OWNER TO $user;" $DB
|
||||
done
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
# Laconica - a distributed open-source microblogging tool
|
||||
|
||||
|
@ -23,19 +23,30 @@
|
|||
SDIR=`dirname $0`
|
||||
DIR=`php $SDIR/getpiddir.php`
|
||||
|
||||
for f in jabberhandler ombhandler publichandler smshandler \
|
||||
for f in jabberhandler ombhandler publichandler smshandler pinghandler \
|
||||
xmppconfirmhandler xmppdaemon twitterhandler facebookhandler ; do
|
||||
|
||||
FILES="$DIR/$f.*.pid"
|
||||
for ff in "$FILES" ; do
|
||||
|
||||
echo -n "Stopping $f..."
|
||||
PID=`cat $ff`
|
||||
kill -3 $PID
|
||||
if kill -9 $PID ; then
|
||||
echo "DONE."
|
||||
else
|
||||
echo "FAILED."
|
||||
PID=`cat $ff 2>/dev/null`
|
||||
if [ -n "$PID" ] ; then
|
||||
echo -n "Stopping $f ($PID)..."
|
||||
if kill -3 $PID 2>/dev/null ; then
|
||||
count=0
|
||||
while kill -0 $PID 2>/dev/null ; do
|
||||
sleep 1
|
||||
count=$(($count + 1))
|
||||
if [ $count -gt 5 ]; then break; fi
|
||||
done
|
||||
if kill -9 $PID 2>/dev/null ; then
|
||||
echo "FORCIBLY TERMINATED"
|
||||
else
|
||||
echo "STOPPED CLEANLY"
|
||||
fi
|
||||
else
|
||||
echo "NOT FOUND"
|
||||
fi
|
||||
fi
|
||||
rm -f $ff
|
||||
done
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
cd `dirname $0`
|
||||
cd ..
|
||||
xgettext --from-code=UTF-8 --default-domain=laconica --output=locale/laconica.pot --language=PHP --join-existing actions/*.php classes/*.php lib/*.php scripts/*.php
|
||||
xgettext --from-code=UTF-8 --default-domain=laconica --output=locale/laconica.po --language=PHP --join-existing actions/*.php classes/*.php lib/*.php scripts/*.php
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* SimpleModal Basic Modal Dialog
|
||||
* http://www.ericmmartin.com/projects/simplemodal/
|
||||
* http://code.google.com/p/simplemodal/
|
||||
*
|
||||
* Copyright (c) 2008 Eric Martin - http://ericmmartin.com
|
||||
*
|
||||
* Licensed under the MIT license:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Revision: $Id: basic.css 162 2008-12-01 23:36:58Z emartin24 $
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* Overlay */
|
||||
#simplemodal-overlay {background-color:#000; cursor:wait;}
|
||||
|
||||
/* Container */
|
||||
#simplemodal-container {height:240px; width:320px; background-color:#fff; border:3px solid #ccc;}
|
||||
#simplemodal-container a.modalCloseImg {background:url(../images/x.png) no-repeat; width:25px; height:29px; display:inline; z-index:3200; position:absolute; top:-15px; right:-18px; cursor:pointer;}
|
||||
#simplemodal-container #basicModalContent {padding:8px;}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* SimpleModal Basic Modal Dialog
|
||||
* http://www.ericmmartin.com/projects/simplemodal/
|
||||
* http://code.google.com/p/simplemodal/
|
||||
*
|
||||
* Copyright (c) 2008 Eric Martin - http://ericmmartin.com
|
||||
*
|
||||
* Licensed under the MIT license:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Revision: $Id: basic_ie.css 162 2008-12-01 23:36:58Z emartin24 $
|
||||
*
|
||||
*/
|
||||
|
||||
/* IE 6 hacks*/
|
||||
#simplemodal-container a.modalCloseImg {background:none; right:-14px; width:22px; height:26px; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../images/x.png',sizingMethod='scale');}
|
Binary file not shown.
Before Width: | Height: | Size: 1.0 KiB |
Loading…
Reference in New Issue
Block a user