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:
Evan Prodromou 2009-03-12 11:56:23 -04:00
commit b3a0eea3b6
69 changed files with 2365 additions and 686 deletions

73
README
View File

@ -2,8 +2,8 @@
README README
------ ------
Laconica 0.7.1 ("West of the Fields") Laconica 0.7.2 ("Talk about the Passion")
6 February 2009 11 March 2009
This is the README file for Laconica, the Open Source microblogging This is the README file for Laconica, the Open Source microblogging
platform. It includes installation instructions, descriptions of platform. It includes installation instructions, descriptions of
@ -71,8 +71,47 @@ for additional terms.
New this version New this version
================ ================
This is a minor bug-fix release since version 0.7.0, released Jan 29 This is a minor bug-fix and feature release since version 0.7.1,
2009. Notable changes this version: 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. - Vast improvement in auto-linking to URLs.
- Link to group search from user's group page - 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. directory to your new directory.
9. Copy htaccess.sample to .htaccess in the new directory. Change the 9. Copy htaccess.sample to .htaccess in the new directory. Change the
RewriteBase to use the correct path. RewriteBase to use the correct path.
10. Rebuild the database. Go to your Laconica directory and run the 10. Rebuild the database. For MySQL, go to your Laconica directory and
rebuilddb.sh script like this: run the rebuilddb.sh script like this:
./scripts/rebuilddb.sh rootuser rootpassword database db/laconica.sql ./scripts/rebuilddb.sh rootuser rootpassword database db/laconica.sql
Here, rootuser and rootpassword are the username and password for a Here, rootuser and rootpassword are the username and password for a
user who can drop and create databases as well as tables; typically user who can drop and create databases as well as tables; typically
that's _not_ the user Laconica runs as. that's _not_ the user Laconica runs as.
11. Use mysql client to log into your database and make sure that the For PostgreSQL databases there is an equivalent, rebuilddb_psql.sh,
notice, user, profile, subscription etc. tables are non-empty. which operates slightly differently. Read the documentation in that
script before running it.
11. Use mysql or psql client to log into your database and make sure that
the notice, user, profile, subscription etc. tables are non-empty.
12. Turn back on the Web server, and check that things still work. 12. Turn back on the Web server, and check that things still work.
13. Turn back on XMPP bots and email maildaemon. Note that the XMPP 13. Turn back on XMPP bots and email maildaemon. Note that the XMPP
bots have changed since version 0.5; see above for details. bots have changed since version 0.5; see above for details.
@ -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 to put introductory information about your service, or info about
upgrades and outages, or other community info. Any HTML will upgrades and outages, or other community info. Any HTML will
be escaped. be escaped.
dupelimit: Time in which it's not OK for the same person to post the
same notice; default = 60 seconds.
logo: URL of an image file to use as the logo for the site. Overrides
the logo in the theme, if any.
db db
-- --
@ -1225,6 +1271,9 @@ if anyone's been overlooked in error.
* Ken Sheppardson (Trac server, man-about-town) * Ken Sheppardson (Trac server, man-about-town)
* Tiago 'gouki' Faria (i18n managerx) * Tiago 'gouki' Faria (i18n managerx)
* Sean Murphy * Sean Murphy
* Leslie Michael Orchard
* Eric Helgeson
* Ken Sedgwick
Thanks also to the developers of our upstream library code and to the Thanks also to the developers of our upstream library code and to the
thousands of people who have tried out Identi.ca, installed Laconi.ca, thousands of people who have tried out Identi.ca, installed Laconi.ca,

View File

@ -77,12 +77,12 @@ class AllAction extends Action
sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)), sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)),
new Feed(Feed::RSS2, new Feed(Feed::RSS2,
common_local_url('api', array('apiaction' => 'statuses', common_local_url('api', array('apiaction' => 'statuses',
'method' => 'friends', 'method' => 'friends_timeline',
'argument' => $this->user->nickname.'.rss')), 'argument' => $this->user->nickname.'.rss')),
sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)), sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)),
new Feed(Feed::ATOM, new Feed(Feed::ATOM,
common_local_url('api', array('apiaction' => 'statuses', common_local_url('api', array('apiaction' => 'statuses',
'method' => 'friends', 'method' => 'friends_timeline',
'argument' => $this->user->nickname.'.atom')), 'argument' => $this->user->nickname.'.atom')),
sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname))); sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname)));
} }

View File

@ -81,8 +81,9 @@ class AllrssAction extends Rss10Action
*/ */
function getNotices($limit=0) function getNotices($limit=0)
{ {
$user = $this->user; $user = $this->user;
$notice = $user->noticesWithFriends(0, $limit); $notice = $user->noticesWithFriends(0, $limit);
$notices = array();
while ($notice->fetch()) { while ($notice->fetch()) {
$notices[] = clone($notice); $notices[] = clone($notice);

View File

@ -127,7 +127,9 @@ class ApiAction extends Action
'laconica/wadl'); 'laconica/wadl');
static $bareauth = array('statuses/user_timeline', static $bareauth = array('statuses/user_timeline',
'statuses/friends_timeline',
'statuses/friends', 'statuses/friends',
'statuses/replies',
'statuses/followers', 'statuses/followers',
'favorites/favorites'); 'favorites/favorites');

View File

@ -178,7 +178,7 @@ class FavoritedAction extends Action
$qry = 'SELECT notice.*, '. $qry = 'SELECT notice.*, '.
$weightexpr . ' as weight ' . $weightexpr . ' as weight ' .
'FROM notice JOIN fave ON notice.id = fave.notice_id ' . 'FROM notice JOIN fave ON notice.id = fave.notice_id ' .
'GROUP BY fave.notice_id ' . 'GROUP BY id,profile_id,uri,content,rendered,url,created,notice.modified,reply_to,is_local,source ' .
'ORDER BY weight DESC'; 'ORDER BY weight DESC';
$offset = ($this->page - 1) * NOTICES_PER_PAGE; $offset = ($this->page - 1) * NOTICES_PER_PAGE;

View File

@ -62,9 +62,8 @@ class FinishopenidloginAction extends Action
if ($this->error) { if ($this->error) {
$this->element('div', array('class' => 'error'), $this->error); $this->element('div', array('class' => 'error'), $this->error);
} else { } else {
global $config;
$this->element('div', 'instructions', $this->element('div', 'instructions',
sprintf(_('This is the first time you\'ve logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), $config['site']['name'])); sprintf(_('This is the first time you\'ve logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name')));
} }
} }

View File

@ -136,16 +136,16 @@ class FinishremotesubscribeAction extends Action
$profile->nickname = $nickname; $profile->nickname = $nickname;
$profile->profileurl = $profile_url; $profile->profileurl = $profile_url;
if ($fullname) { if (!is_null($fullname)) {
$profile->fullname = $fullname; $profile->fullname = $fullname;
} }
if ($homepage) { if (!is_null($homepage)) {
$profile->homepage = $homepage; $profile->homepage = $homepage;
} }
if ($bio) { if (!is_null($bio)) {
$profile->bio = $bio; $profile->bio = $bio;
} }
if ($location) { if (!is_null($location)) {
$profile->location = $location; $profile->location = $location;
} }

View File

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

View File

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

View File

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

View File

@ -82,10 +82,9 @@ class NoticesearchrssAction extends Rss10Action
function getChannel() function getChannel()
{ {
global $config;
$q = $this->trimmed('q'); $q = $this->trimmed('q');
$c = array('url' => common_local_url('noticesearchrss', array('q' => $q)), $c = array('url' => common_local_url('noticesearchrss', array('q' => $q)),
'title' => $config['site']['name'] . sprintf(_(' Search Stream for "%s"'), $q), 'title' => common_config('site', 'name') . sprintf(_(' Search Stream for "%s"'), $q),
'link' => common_local_url('noticesearch', array('q' => $q)), 'link' => common_local_url('noticesearch', array('q' => $q)),
'description' => sprintf(_('All updates matching search term "%s"'), $q)); 'description' => sprintf(_('All updates matching search term "%s"'), $q));
return $c; return $c;

View File

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

View File

@ -79,7 +79,7 @@ class PostnoticeAction extends Action
} }
$notice = Notice::staticGet('uri', $notice_uri); $notice = Notice::staticGet('uri', $notice_uri);
if (!$notice) { if (!$notice) {
$notice = Notice::saveNew($remote_profile->id, $content, 'omb', false, 0, $notice_uri); $notice = Notice::saveNew($remote_profile->id, $content, 'omb', false, null, $notice_uri);
if (is_string($notice)) { if (is_string($notice)) {
common_server_serror($notice, 500); common_server_serror($notice, 500);
return false; return false;

View File

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

View File

@ -333,8 +333,6 @@ class RemotesubscribeAction extends Action
function requestAuthorization($user, $omb, $token, $secret) function requestAuthorization($user, $omb, $token, $secret)
{ {
global $config; # for license URL
$con = omb_oauth_consumer(); $con = omb_oauth_consumer();
$tok = new OAuthToken($token, $secret); $tok = new OAuthToken($token, $secret);
@ -358,7 +356,7 @@ class RemotesubscribeAction extends Action
$req->set_parameter('omb_listenee', $user->uri); $req->set_parameter('omb_listenee', $user->uri);
$req->set_parameter('omb_listenee_profile', common_profile_url($user->nickname)); $req->set_parameter('omb_listenee_profile', common_profile_url($user->nickname));
$req->set_parameter('omb_listenee_nickname', $user->nickname); $req->set_parameter('omb_listenee_nickname', $user->nickname);
$req->set_parameter('omb_listenee_license', $config['license']['url']); $req->set_parameter('omb_listenee_license', common_config('license', 'url'));
$profile = $user->getProfile(); $profile = $user->getProfile();
if (!$profile) { if (!$profile) {
@ -367,16 +365,16 @@ class RemotesubscribeAction extends Action
return; return;
} }
if ($profile->fullname) { if (!is_null($profile->fullname)) {
$req->set_parameter('omb_listenee_fullname', $profile->fullname); $req->set_parameter('omb_listenee_fullname', $profile->fullname);
} }
if ($profile->homepage) { if (!is_null($profile->homepage)) {
$req->set_parameter('omb_listenee_homepage', $profile->homepage); $req->set_parameter('omb_listenee_homepage', $profile->homepage);
} }
if ($profile->bio) { if (!is_null($profile->bio)) {
$req->set_parameter('omb_listenee_bio', $profile->bio); $req->set_parameter('omb_listenee_bio', $profile->bio);
} }
if ($profile->location) { if (!is_null($profile->location)) {
$req->set_parameter('omb_listenee_location', $profile->location); $req->set_parameter('omb_listenee_location', $profile->location);
} }
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);

View File

@ -275,10 +275,8 @@ class ShowgroupAction extends Action
$cur = common_current_user(); $cur = common_current_user();
if ($cur) { if ($cur) {
if ($cur->isMember($this->group)) { if ($cur->isMember($this->group)) {
if (!$cur->isAdmin($this->group)) { $lf = new LeaveForm($this, $this->group);
$lf = new LeaveForm($this, $this->group); $lf->show();
$lf->show();
}
} else { } else {
$jf = new JoinForm($this, $this->group); $jf = new JoinForm($this, $this->group);
$jf->show(); $jf->show();

View File

@ -65,7 +65,9 @@ class SupAction extends Action
$notice->query('SELECT profile_id, max(id) AS max_id ' . $notice->query('SELECT profile_id, max(id) AS max_id ' .
'FROM notice ' . 'FROM notice ' .
'WHERE created > (now() - ' . $seconds . ') ' . ((common_config('db','type') == 'pgsql') ?
'WHERE extract(epoch from created) > (extract(epoch from now()) - ' . $seconds . ') ' :
'WHERE created > (now() - ' . $seconds . ') ' ) .
'GROUP BY profile_id'); 'GROUP BY profile_id');
$updates = array(); $updates = array();

View File

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

View File

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

View File

@ -0,0 +1,377 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* Action for showing Twitter-like Atom search results
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Search
* @package Laconica
* @author Zach Copley <zach@controlyourself.ca>
* @copyright 2008-2009 Control Yourself, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*/
if (!defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/twitterapi.php';
/**
* Action for outputting search results in Twitter compatible Atom
* format.
*
* TODO: abstract Atom stuff into a ruseable base class like
* RSS10Action.
*
* @category Search
* @package Laconica
* @author Zach Copley <zach@controlyourself.ca>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*
* @see TwitterapiAction
*/
class TwitapisearchatomAction extends TwitterapiAction
{
var $cnt;
var $query;
var $lang;
var $rpp;
var $page;
var $since_id;
var $geocode;
/**
* Constructor
*
* Just wraps the Action constructor.
*
* @param string $output URI to output to, default = stdout
* @param boolean $indent Whether to indent output, default true
*
* @see Action::__construct
*/
function __construct($output='php://output', $indent=true)
{
parent::__construct($output, $indent);
}
/**
* Do we need to write to the database?
*
* @return boolean true
*/
function isReadonly()
{
return true;
}
/**
* Read arguments and initialize members
*
* @param array $args Arguments from $_REQUEST
*
* @return boolean success
*
*/
function prepare($args)
{
parent::prepare($args);
$this->query = $this->trimmed('q');
$this->lang = $this->trimmed('lang');
$this->rpp = $this->trimmed('rpp');
if (!$this->rpp) {
$this->rpp = 15;
}
if ($this->rpp > 100) {
$this->rpp = 100;
}
$this->page = $this->trimmed('page');
if (!$this->page) {
$this->page = 1;
}
// TODO: Suppport since_id -- we need to tweak the backend
// Search classes to support it.
$this->since_id = $this->trimmed('since_id');
$this->geocode = $this->trimmed('geocode');
// TODO: Also, language and geocode
return true;
}
/**
* Handle a request
*
* @param array $args Arguments from $_REQUEST
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$this->showAtom();
}
/**
* Get the notices to output as results. This also sets some class
* attrs so we can use them to calculate pagination, and output
* since_id and max_id.
*
* @return array an array of Notice objects sorted in reverse chron
*/
function getNotices()
{
// TODO: Support search operators like from: and to:, boolean, etc.
$notices = array();
$notice = new Notice();
// lcase it for comparison
$q = strtolower($this->query);
$search_engine = $notice->getSearchEngine('identica_notices');
$search_engine->set_sort_mode('chron');
$search_engine->limit(($this->page - 1) * $this->rpp,
$this->rpp + 1, true);
$search_engine->query($q);
$this->cnt = $notice->find();
$cnt = 0;
while ($notice->fetch()) {
++$cnt;
if (!$this->max_id) {
$this->max_id = $notice->id;
}
if ($cnt > $this->rpp) {
break;
}
$notices[] = clone($notice);
}
return $notices;
}
/**
* Output search results as an Atom feed
*
* @return void
*/
function showAtom()
{
$notices = $this->getNotices();
$this->initAtom();
$this->showFeed();
foreach ($notices as $n) {
$this->showEntry($n);
}
$this->endAtom();
}
/**
* Show feed specific Atom elements
*
* @return void
*/
function showFeed()
{
// TODO: A9 OpenSearch stuff like search.twitter.com?
$server = common_config('site', 'server');
$sitename = common_config('site', 'name');
// XXX: Use xmlns:laconica instead?
$this->elementStart('feed',
array('xmlns' => 'http://www.w3.org/2005/Atom',
// XXX: xmlns:twitter causes Atom validation to fail
// It's used for the source attr on notices
'xmlns:twitter' => 'http://api.twitter.com/',
'xml:lang' => 'en-US')); // XXX Other locales ?
$taguribase = common_config('integration', 'taguri');
$this->element('id', null, "tag:$taguribase:search/$server");
$site_uri = common_path(false);
$search_uri = $site_uri . 'api/search.atom?q=' . urlencode($this->query);
if ($this->rpp != 15) {
$search_uri .= '&rpp=' . $this->rpp;
}
// FIXME: this alternate link is not quite right because our
// web-based notice search doesn't support a rpp (responses per
// page) param yet
$this->element('link', array('type' => 'text/html',
'rel' => 'alternate',
'href' => $site_uri . 'search/notice?q=' .
urlencode($this->query)));
// self link
$self_uri = $search_uri;
$self_uri .= ($this->page > 1) ? '&page=' . $this->page : '';
$this->element('link', array('type' => 'application/atom+xml',
'rel' => 'self',
'href' => $self_uri));
$this->element('title', null, "$this->query - $sitename Search");
$this->element('updated', null, common_date_iso8601('now'));
// XXX: The below "rel" links are not valid Atom, but it's what
// Twitter does...
// refresh link
$refresh_uri = $search_uri . "&since_id=" . $this->max_id;
$this->element('link', array('type' => 'application/atom+xml',
'rel' => 'refresh',
'href' => $refresh_uri));
// pagination links
if ($this->cnt > $this->rpp) {
$next_uri = $search_uri . "&max_id=" . $this->max_id .
'&page=' . ($this->page + 1);
$this->element('link', array('type' => 'application/atom+xml',
'rel' => 'next',
'href' => $next_uri));
}
if ($this->page > 1) {
$previous_uri = $search_uri . "&max_id=" . $this->max_id .
'&page=' . ($this->page - 1);
$this->element('link', array('type' => 'application/atom+xml',
'rel' => 'previous',
'href' => $previous_uri));
}
}
/**
* Build an Atom entry similar to search.twitter.com's based on
* a given notice
*
* @param Notice $notice the notice to use
*
* @return void
*/
function showEntry($notice)
{
$server = common_config('site', 'server');
$profile = $notice->getProfile();
$nurl = common_local_url('shownotice', array('notice' => $notice->id));
$this->elementStart('entry');
$taguribase = common_config('integration', 'taguri');
$this->element('id', null, "tag:$taguribase:$notice->id");
$this->element('published', null, common_date_w3dtf($notice->created));
$this->element('link', array('type' => 'text/html',
'rel' => 'alternate',
'href' => $nurl));
$this->element('title', null, common_xml_safe_str(trim($notice->content)));
$this->element('content', array('type' => 'html'), $notice->rendered);
$this->element('updated', null, common_date_w3dtf($notice->created));
$this->element('link', array('type' => 'image/png',
// XXX: Twitter uses rel="image" (not valid)
'rel' => 'related',
'href' => $profile->avatarUrl()));
// TODO: Here is where we'd put in a link to an atom feed for threads
$this->element("twitter:source", null,
htmlentities($this->source_link($notice->source)));
$this->elementStart('author');
$name = $profile->nickname;
if ($profile->fullname) {
$name .= ' (' . $profile->fullname . ')';
}
$this->element('name', null, $name);
$this->element('uri', null, common_profile_uri($profile));
$this->elementEnd('author');
$this->elementEnd('entry');
}
/**
* Initialize the Atom output, send headers
*
* @return void
*/
function initAtom()
{
header('Content-Type: application/atom+xml; charset=utf-8');
$this->startXml();
}
/**
* End the Atom feed
*
* @return void
*/
function endAtom()
{
$this->elementEnd('feed');
}
}

View File

@ -2,7 +2,7 @@
/** /**
* Laconica, the distributed open-source microblogging tool * Laconica, the distributed open-source microblogging tool
* *
* List of replies * Action for showing Twitter-like JSON search results
* *
* PHP version 5 * PHP version 5
* *
@ -114,7 +114,7 @@ class TwitapisearchjsonAction extends TwitterapiAction
function showResults() function showResults()
{ {
// TODO: Support search operators like from: and to: // TODO: Support search operators like from: and to:, boolean, etc.
$notice = new Notice(); $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 * @return boolean true
*/ */

View File

@ -29,10 +29,12 @@ class TwitapistatusesAction extends TwitterapiAction
parent::handle($args); parent::handle($args);
$sitename = common_config('site', 'name'); $sitename = common_config('site', 'name');
$siteserver = common_config('site', 'server');
$title = sprintf(_("%s public timeline"), $sitename); $title = sprintf(_("%s public timeline"), $sitename);
$id = "tag:$siteserver:Statuses";
$taguribase = common_config('integration', 'taguri');
$id = "tag:$taguribase:PublicTimeline";
$link = common_root_url(); $link = common_root_url();
$subtitle = sprintf(_("%s updates from everyone!"), $sitename); $subtitle = sprintf(_("%s updates from everyone!"), $sitename);
// Number of public statuses to return by default -- Twitter sends 20 // Number of public statuses to return by default -- Twitter sends 20
@ -70,7 +72,8 @@ class TwitapistatusesAction extends TwitterapiAction
$this->show_rss_timeline($notice, $title, $link, $subtitle); $this->show_rss_timeline($notice, $title, $link, $subtitle);
break; break;
case 'atom': case 'atom':
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle); $selfuri = common_root_url() . 'api/statuses/public_timeline.atom';
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle, null, $selfuri);
break; break;
case 'json': case 'json':
$this->show_json_timeline($notice); $this->show_json_timeline($notice);
@ -114,17 +117,19 @@ class TwitapistatusesAction extends TwitterapiAction
} }
$since = strtotime($this->arg('since')); $since = strtotime($this->arg('since'));
$user = $this->get_user($apidata['api_arg'], $apidata);
$user = $this->get_user(null, $apidata);
$this->auth_user = $user; $this->auth_user = $user;
if (empty($user)) {
$this->clientError(_('No such user!'), 404, $apidata['content-type']);
return;
}
$profile = $user->getProfile(); $profile = $user->getProfile();
$sitename = common_config('site', 'name'); $sitename = common_config('site', 'name');
$siteserver = common_config('site', 'server');
$title = sprintf(_("%s and friends"), $user->nickname); $title = sprintf(_("%s and friends"), $user->nickname);
$id = "tag:$siteserver:friends:" . $user->id; $taguribase = common_config('integration', 'taguri');
$id = "tag:$taguribase:FriendsTimeline:" . $user->id;
$link = common_local_url('all', array('nickname' => $user->nickname)); $link = common_local_url('all', array('nickname' => $user->nickname));
$subtitle = sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, $sitename); $subtitle = sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, $sitename);
@ -138,7 +143,14 @@ class TwitapistatusesAction extends TwitterapiAction
$this->show_rss_timeline($notice, $title, $link, $subtitle); $this->show_rss_timeline($notice, $title, $link, $subtitle);
break; break;
case 'atom': case 'atom':
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle); if (isset($apidata['api_arg'])) {
$selfuri = $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; break;
case 'json': case 'json':
$this->show_json_timeline($notice); $this->show_json_timeline($notice);
@ -194,10 +206,9 @@ class TwitapistatusesAction extends TwitterapiAction
$since = strtotime($this->arg('since')); $since = strtotime($this->arg('since'));
$sitename = common_config('site', 'name'); $sitename = common_config('site', 'name');
$siteserver = common_config('site', 'server');
$title = sprintf(_("%s timeline"), $user->nickname); $title = sprintf(_("%s timeline"), $user->nickname);
$id = "tag:$siteserver:user:".$user->id; $taguribase = common_config('integration', 'taguri');
$id = "tag:$taguribase:UserTimeline:".$user->id;
$link = common_local_url('showstream', array('nickname' => $user->nickname)); $link = common_local_url('showstream', array('nickname' => $user->nickname));
$subtitle = sprintf(_('Updates from %1$s on %2$s!'), $user->nickname, $sitename); $subtitle = sprintf(_('Updates from %1$s on %2$s!'), $user->nickname, $sitename);
@ -219,7 +230,14 @@ class TwitapistatusesAction extends TwitterapiAction
$this->show_rss_timeline($notice, $title, $link, $subtitle, $suplink); $this->show_rss_timeline($notice, $title, $link, $subtitle, $suplink);
break; break;
case 'atom': case 'atom':
$this->show_atom_timeline($notice, $title, $id, $link, $subtitle, $suplink); if (isset($apidata['api_arg'])) {
$selfuri = $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; break;
case 'json': case 'json':
$this->show_json_timeline($notice); $this->show_json_timeline($notice);
@ -337,15 +355,14 @@ class TwitapistatusesAction extends TwitterapiAction
$since_id = $this->arg('since_id'); $since_id = $this->arg('since_id');
$before_id = $this->arg('before_id'); $before_id = $this->arg('before_id');
$user = $this->get_user($apidata['api_arg'], $apidata);
$this->auth_user = $apidata['user']; $this->auth_user = $apidata['user'];
$user = $this->auth_user;
$profile = $user->getProfile(); $profile = $user->getProfile();
$sitename = common_config('site', 'name'); $sitename = common_config('site', 'name');
$siteserver = common_config('site', 'server');
$title = sprintf(_('%1$s / Updates replying to %2$s'), $sitename, $user->nickname); $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)); $link = common_local_url('replies', array('nickname' => $user->nickname));
$subtitle = sprintf(_('%1$s updates that reply to updates from %2$s / %3$s.'), $sitename, $user->nickname, $profile->getBestName()); $subtitle = sprintf(_('%1$s updates that reply to updates from %2$s / %3$s.'), $sitename, $user->nickname, $profile->getBestName());
@ -383,7 +400,14 @@ class TwitapistatusesAction extends TwitterapiAction
$this->show_rss_timeline($notices, $title, $link, $subtitle); $this->show_rss_timeline($notices, $title, $link, $subtitle);
break; break;
case 'atom': case 'atom':
$this->show_atom_timeline($notices, $title, $id, $link, $subtitle); 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; break;
case 'json': case 'json':
$this->show_json_timeline($notices); $this->show_json_timeline($notices);

View File

@ -186,12 +186,12 @@ class TwittersettingsAction extends ConnectSettingsAction
$current_user = common_current_user(); $current_user = common_current_user();
$qry = 'SELECT user.* ' . $qry = 'SELECT "user".* ' .
'FROM subscription ' . 'FROM subscription ' .
'JOIN user ON subscription.subscribed = user.id ' . 'JOIN "user" ON subscription.subscribed = "user".id ' .
'JOIN foreign_link ON foreign_link.user_id = user.id ' . 'JOIN foreign_link ON foreign_link.user_id = "user".id ' .
'WHERE subscriber = %d ' . 'WHERE subscriber = %d ' .
'ORDER BY user.nickname'; 'ORDER BY "user".nickname';
$user = new User(); $user = new User();

View File

@ -34,6 +34,8 @@ class UpdateprofileAction extends Action
$server = omb_oauth_server(); $server = omb_oauth_server();
list($consumer, $token) = $server->verify_request($req); list($consumer, $token) = $server->verify_request($req);
if ($this->update_profile($req, $consumer, $token)) { if ($this->update_profile($req, $consumer, $token)) {
header('HTTP/1.1 200 OK');
header('Content-type: text/plain');
print "omb_version=".OMB_VERSION_01; print "omb_version=".OMB_VERSION_01;
} }
} catch (OAuthException $e) { } catch (OAuthException $e) {
@ -136,22 +138,24 @@ class UpdateprofileAction extends Action
$orig_profile = clone($profile); $orig_profile = clone($profile);
if ($nickname) { /* Use values even if they are an empty string. Parsing an empty string in
updateProfile is the specified way of clearing a parameter in OMB. */
if (!is_null($nickname)) {
$profile->nickname = $nickname; $profile->nickname = $nickname;
} }
if ($profile_url) { if (!is_null($profile_url)) {
$profile->profileurl = $profile_url; $profile->profileurl = $profile_url;
} }
if ($fullname) { if (!is_null($fullname)) {
$profile->fullname = $fullname; $profile->fullname = $fullname;
} }
if ($homepage) { if (!is_null($homepage)) {
$profile->homepage = $homepage; $profile->homepage = $homepage;
} }
if ($bio) { if (!is_null($bio)) {
$profile->bio = $bio; $profile->bio = $bio;
} }
if ($location) { if (!is_null($location)) {
$profile->location = $location; $profile->location = $location;
} }
@ -173,10 +177,6 @@ class UpdateprofileAction extends Action
return false; return false;
} }
} }
header('HTTP/1.1 200 OK');
header('Content-type: text/plain');
print 'Updated profile';
print "\n";
return true; return true;
} }
} }

View File

@ -113,9 +113,9 @@ class UserauthorizationAction extends Action
$this->element('a', array('href' => $profile, $this->element('a', array('href' => $profile,
'class' => 'external profile nickname'), 'class' => 'external profile nickname'),
$nickname); $nickname);
if ($fullname) { if (!is_null($fullname)) {
$this->elementStart('div', 'fullname'); $this->elementStart('div', 'fullname');
if ($homepage) { if (!is_null($homepage)) {
$this->element('a', array('href' => $homepage), $this->element('a', array('href' => $homepage),
$fullname); $fullname);
} else { } else {
@ -123,10 +123,10 @@ class UserauthorizationAction extends Action
} }
$this->elementEnd('div'); $this->elementEnd('div');
} }
if ($location) { if (!is_null($location)) {
$this->element('div', 'location', $location); $this->element('div', 'location', $location);
} }
if ($bio) { if (!is_null($bio)) {
$this->element('div', 'bio', $bio); $this->element('div', 'bio', $bio);
} }
$this->elementStart('div', 'license'); $this->elementStart('div', 'license');
@ -179,16 +179,16 @@ class UserauthorizationAction extends Action
$params['omb_listener_nickname'] = $user->nickname; $params['omb_listener_nickname'] = $user->nickname;
$params['omb_listener_profile'] = common_local_url('showstream', $params['omb_listener_profile'] = common_local_url('showstream',
array('nickname' => $user->nickname)); array('nickname' => $user->nickname));
if ($profile->fullname) { if (!is_null($profile->fullname)) {
$params['omb_listener_fullname'] = $profile->fullname; $params['omb_listener_fullname'] = $profile->fullname;
} }
if ($profile->homepage) { if (!is_null($profile->homepage)) {
$params['omb_listener_homepage'] = $profile->homepage; $params['omb_listener_homepage'] = $profile->homepage;
} }
if ($profile->bio) { if (!is_null($profile->bio)) {
$params['omb_listener_bio'] = $profile->bio; $params['omb_listener_bio'] = $profile->bio;
} }
if ($profile->location) { if (!is_null($profile->location)) {
$params['omb_listener_location'] = $profile->location; $params['omb_listener_location'] = $profile->location;
} }
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
@ -197,7 +197,7 @@ class UserauthorizationAction extends Action
} }
$parts = array(); $parts = array();
foreach ($params as $k => $v) { foreach ($params as $k => $v) {
$parts[] = $k . '=' . OAuthUtil::urlencodeRFC3986($v); $parts[] = $k . '=' . OAuthUtil::urlencode_rfc3986($v);
} }
$query_string = implode('&', $parts); $query_string = implode('&', $parts);
$parsed = parse_url($callback); $parsed = parse_url($callback);
@ -267,16 +267,16 @@ class UserauthorizationAction extends Action
$profile->nickname = $nickname; $profile->nickname = $nickname;
$profile->profileurl = $profile_url; $profile->profileurl = $profile_url;
if ($fullname) { if (!is_null($fullname)) {
$profile->fullname = $fullname; $profile->fullname = $fullname;
} }
if ($homepage) { if (!is_null($homepage)) {
$profile->homepage = $homepage; $profile->homepage = $homepage;
} }
if ($bio) { if (!is_null($bio)) {
$profile->bio = $bio; $profile->bio = $bio;
} }
if ($location) { if (!is_null($location)) {
$profile->location = $location; $profile->location = $location;
} }
@ -409,7 +409,7 @@ class UserauthorizationAction extends Action
'omb_listenee_profile', 'omb_listenee_nickname', 'omb_listenee_profile', 'omb_listenee_nickname',
'omb_listenee_license') as $param) 'omb_listenee_license') as $param)
{ {
if (!$req->get_parameter($param)) { if (is_null($req->get_parameter($param))) {
throw new OAuthException("Required parameter '$param' not found"); throw new OAuthException("Required parameter '$param' not found");
} }
} }

View File

@ -53,6 +53,7 @@ class UserrssAction extends Rss10Action
$notice = $user->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit); $notice = $user->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit);
$notices = array();
while ($notice->fetch()) { while ($notice->fetch()) {
$notices[] = clone($notice); $notices[] = clone($notice);
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -4,22 +4,21 @@
*/ */
require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Nonce extends Memcached_DataObject class Nonce extends Memcached_DataObject
{ {
###START_AUTOCODE ###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */ /* the code below is auto generated do not remove the above tag */
public $__table = 'nonce'; // table name public $__table = 'nonce'; // table name
public $consumer_key; // varchar(255) primary_key not_null 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 $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 $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
/* Static get */ /* Static get */
function staticGet($k,$v=null) function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Nonce',$k,$v); }
{ return Memcached_DataObject::staticGet('Nonce',$k,$v); }
/* the code above is auto generated do not remove the tag below */ /* the code above is auto generated do not remove the tag below */
###END_AUTOCODE ###END_AUTOCODE

View File

@ -122,6 +122,8 @@ class Notice extends Memcached_DataObject
$profile = Profile::staticGet($profile_id); $profile = Profile::staticGet($profile_id);
$final = common_shorten_links($content);
if (!$profile) { if (!$profile) {
common_log(LOG_ERR, 'Problem saving notice. Unknown user.'); common_log(LOG_ERR, 'Problem saving notice. Unknown user.');
return _('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.'); 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)) { 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)."); 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->query('BEGIN');
$notice->created = common_sql_now(); $notice->reply_to = $reply_to;
$notice->content = common_shorten_links($content); $notice->created = common_sql_now();
$notice->rendered = common_render_content($notice->content, $notice); $notice->content = $final;
$notice->source = $source; $notice->rendered = common_render_content($final, $notice);
$notice->uri = $uri; $notice->source = $source;
$notice->uri = $uri;
if (!empty($reply_to)) { if (!empty($reply_to)) {
$reply_notice = Notice::staticGet('id', $reply_to); $reply_notice = Notice::staticGet('id', $reply_to);
@ -212,6 +220,36 @@ class Notice extends Memcached_DataObject
return $notice; 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) { static function checkEditThrottle($profile_id) {
$profile = Profile::staticGet($profile_id); $profile = Profile::staticGet($profile_id);
if (!$profile) { if (!$profile) {

View File

@ -121,7 +121,8 @@ class User extends Memcached_DataObject
static $blacklist = array('rss', 'xrds', 'doc', 'main', static $blacklist = array('rss', 'xrds', 'doc', 'main',
'settings', 'notice', 'user', 'settings', 'notice', 'user',
'search', 'avatar', 'tag', 'tags', 'search', 'avatar', 'tag', 'tags',
'api', 'message', 'group', 'groups'); 'api', 'message', 'group', 'groups',
'local');
$merged = array_merge($blacklist, common_config('nickname', 'blacklist')); $merged = array_merge($blacklist, common_config('nickname', 'blacklist'));
return !in_array($nickname, $merged); return !in_array($nickname, $merged);
} }

View File

@ -145,7 +145,7 @@ id = N
[nonce] [nonce]
consumer_key = 130 consumer_key = 130
tok = 130 tok = 2
nonce = 130 nonce = 130
ts = 142 ts = 142
created = 142 created = 142
@ -153,8 +153,8 @@ modified = 384
[nonce__keys] [nonce__keys]
consumer_key = K consumer_key = K
tok = K
nonce = K nonce = K
ts = K
[notice] [notice]
id = 129 id = 129

View File

@ -37,6 +37,9 @@ $config['site']['path'] = 'laconica';
# Enables extra log information, for example full details of PEAR DB errors # Enables extra log information, for example full details of PEAR DB errors
#$config['site']['logdebug'] = true; #$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 # 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 # Set it to match your actual database
@ -159,3 +162,15 @@ $config['sphinx']['port'] = 3312;
# Add Google Analytics # Add Google Analytics
# require_once('plugins/GoogleAnalyticsPlugin.php'); # require_once('plugins/GoogleAnalyticsPlugin.php');
# $ga = new GoogleAnalyticsPlugin('your secret code'); # $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'

View File

@ -183,15 +183,14 @@ create table token (
create table nonce ( create table nonce (
consumer_key varchar(255) not null comment 'unique identifier, root URL', 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', nonce char(32) not null comment 'nonce',
ts datetime not null comment 'timestamp sent', ts datetime not null comment 'timestamp sent',
created datetime not null comment 'date this record was created', created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified', modified timestamp comment 'date this record was modified',
constraint primary key (consumer_key, tok, nonce), constraint primary key (consumer_key, ts, nonce)
constraint foreign key (consumer_key, tok) references token (consumer_key, tok)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
/* One-to-many relationship of user to openid_url */ /* One-to-many relationship of user to openid_url */

View File

@ -1,7 +1,8 @@
/* local and remote users have profiles */ /* local and remote users have profiles */
create sequence profile_seq;
create table profile ( 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' */, nickname varchar(64) not null /* comment 'nickname or username' */,
fullname varchar(255) /* comment 'display name' */, fullname varchar(255) /* comment 'display name' */,
profileurl varchar(255) /* comment 'URL, cached so we dont regenerate' */, 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 index avatar_profile_id_idx on avatar using btree(profile_id);
create sequence sms_carrier_seq;
create table sms_carrier ( 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' */, 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' */, 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' */, 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_subscriber_idx on subscription using btree(subscriber);
create index subscription_subscribed_idx on subscription using btree(subscribed); create index subscription_subscribed_idx on subscription using btree(subscribed);
create sequence notice_seq;
create table notice ( 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) , 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' */, uri varchar(255) unique /* comment 'universally unique identifier, usually a tag URI' */,
content varchar(140) /* comment 'update content' */, content varchar(140) /* comment 'update content' */,
@ -180,14 +183,13 @@ create table token (
create table nonce ( create table nonce (
consumer_key varchar(255) not null /* comment 'unique identifier, root URL' */, consumer_key varchar(255) not null /* comment 'unique identifier, root URL' */,
tok char(32) not null /* comment 'identifying value' */, 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 */, 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' */, created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */,
modified timestamp /* comment 'date this record was modified' */, modified timestamp /* comment 'date this record was modified' */,
primary key (consumer_key, tok, nonce), primary key (consumer_key, ts, nonce)
foreign key (consumer_key, tok) references token (consumer_key, tok)
); );
/* One-to-many relationship of user to openid_url */ /* 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_address_idx on invitation using btree(address,address_type);
create index invitation_user_id_idx on invitation using btree(user_id); create index invitation_user_id_idx on invitation using btree(user_id);
create sequence message_seq;
create table message ( 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' */, uri varchar(255) unique /* comment 'universally unique identifier' */,
from_profile integer not null /* comment 'who the message is from' */ references profile (id), 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), 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 ( 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' */, nickname varchar(64) unique /* comment 'nickname for addressing' */,
fullname varchar(255) /* comment 'display name' */, fullname varchar(255) /* comment 'display name' */,

397
extlib/PEAR/Exception.php Normal file
View 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 .= '&hellip;';
$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>&nbsp;</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();
}
}
?>

View File

@ -63,7 +63,7 @@ function handleError($error)
function main() function main()
{ {
global $user, $action; global $user, $action, $config;
if (!_have_config()) { if (!_have_config()) {
$msg = sprintf(_("No configuration file found. Try running ". $msg = sprintf(_("No configuration file found. Try running ".
@ -131,11 +131,11 @@ function main()
if (common_config('db', 'mirror') && $action_obj->isReadOnly()) { if (common_config('db', 'mirror') && $action_obj->isReadOnly()) {
if (is_array(common_config('db', 'mirror'))) { if (is_array(common_config('db', 'mirror'))) {
// "load balancing", ha ha // "load balancing", ha ha
$k = array_rand($config['db']['mirror']); $arr = common_config('db', 'mirror');
$k = array_rand($arr);
$mirror = $config['db']['mirror'][$k]; $mirror = $arr[$k];
} else { } else {
$mirror = $config['db']['mirror']; $mirror = common_config('db', 'mirror');
} }
$config['db']['database'] = $mirror; $config['db']['database'] = $mirror;
} }

File diff suppressed because one or more lines are too long

View File

@ -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,{}))

View File

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

View File

@ -156,15 +156,10 @@ class Action extends HTMLOutputter // lawsuit
{ {
if (Event::handle('StartShowStyles', array($this))) { if (Event::handle('StartShowStyles', array($this))) {
if (Event::handle('StartShowLaconicaStyles', array($this))) { if (Event::handle('StartShowLaconicaStyles', array($this))) {
$this->element('link', array('rel' => 'stylesheet', $this->element('link', array('rel' => 'stylesheet',
'type' => 'text/css', 'type' => 'text/css',
'href' => theme_path('css/display.css', 'base') . '?version=' . LACONICA_VERSION, 'href' => theme_path('css/display.css', 'base') . '?version=' . LACONICA_VERSION,
'media' => 'screen, projection, tv')); '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', $this->element('link', array('rel' => 'stylesheet',
'type' => 'text/css', 'type' => 'text/css',
'href' => theme_path('css/display.css', null) . '?version=' . LACONICA_VERSION, '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', $this->element('script', array('type' => 'text/javascript',
'src' => common_path('js/jquery.form.js')), '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)); Event::handle('EndShowJQueryScripts', array($this));
} }
if (Event::handle('StartShowLaconicaScripts', array($this))) { if (Event::handle('StartShowLaconicaScripts', array($this))) {
@ -232,14 +222,6 @@ class Action extends HTMLOutputter // lawsuit
// Frame-busting code to avoid clickjacking attacks. // Frame-busting code to avoid clickjacking attacks.
$this->element('script', array('type' => 'text/javascript'), $this->element('script', array('type' => 'text/javascript'),
'if (window.top !== window.self) { window.top.location.href = window.self.location.href; }'); '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('EndShowLaconicaScripts', array($this));
} }
Event::handle('EndShowScripts', array($this)); Event::handle('EndShowScripts', array($this));

View File

@ -19,7 +19,7 @@
if (!defined('LACONICA')) { exit(1); } if (!defined('LACONICA')) { exit(1); }
define('LACONICA_VERSION', '0.7.1'); define('LACONICA_VERSION', '0.7.2');
define('AVATAR_PROFILE_SIZE', 96); define('AVATAR_PROFILE_SIZE', 96);
define('AVATAR_STREAM_SIZE', 48); define('AVATAR_STREAM_SIZE', 48);
@ -73,6 +73,7 @@ $config =
'theme' => 'default', 'theme' => 'default',
'path' => $_path, 'path' => $_path,
'logfile' => null, 'logfile' => null,
'logo' => null,
'logdebug' => false, 'logdebug' => false,
'fancy' => false, 'fancy' => false,
'locale_path' => INSTALLDIR.'/locale', 'locale_path' => INSTALLDIR.'/locale',
@ -85,7 +86,8 @@ $config =
'broughtbyurl' => null, 'broughtbyurl' => null,
'closed' => false, 'closed' => false,
'inviteonly' => false, 'inviteonly' => false,
'private' => false), 'private' => false,
'dupelimit' => 60), # default for same person saying the same thing
'syslog' => 'syslog' =>
array('appname' => 'laconica', # for syslog array('appname' => 'laconica', # for syslog
'priority' => 'debug'), # XXX: currently ignored 'priority' => 'debug'), # XXX: currently ignored
@ -139,7 +141,8 @@ $config =
'user' => false, 'user' => false,
'group' => false), 'group' => false),
'integration' => 'integration' =>
array('source' => 'Laconica'), # source attribute for Twitter array('source' => 'Laconica', # source attribute for Twitter
'taguri' => $_server.',2009'), # base for tag URIs
'memcached' => 'memcached' =>
array('enabled' => false, array('enabled' => false,
'server' => 'localhost', 'server' => 'localhost',

View File

@ -410,8 +410,8 @@ function jabber_broadcast_notice($notice)
"ON $UT.id = notice_inbox.user_id " . "ON $UT.id = notice_inbox.user_id " .
'WHERE notice_inbox.notice_id = ' . $notice->id . ' ' . 'WHERE notice_inbox.notice_id = ' . $notice->id . ' ' .
'AND notice_inbox.source = 2 ' . 'AND notice_inbox.source = 2 ' .
'AND user.jabber is not null ' . "AND $UT.jabber is not null " .
'AND user.jabbernotify = 1 '); "AND $UT.jabbernotify = 1 ");
while ($user->fetch()) { while ($user->fetch()) {
if (!array_key_exists($user->id, $sent_to)) { if (!array_key_exists($user->id, $sent_to)) {

View File

@ -22,7 +22,7 @@
* @category Search * @category Search
* @package Laconica * @package Laconica
* @author Zach Copley <zach@controlyourself.ca> * @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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/ * @link http://laconi.ca/
*/ */
@ -62,14 +62,19 @@ class JSONSearchResultsList
/** /**
* constructor * 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) function __construct($notice, $query, $rpp, $page, $since_id = 0)
{ {
$this->notice = $notice; $this->notice = $notice;
$this->query = urlencode($query); $this->query = urlencode($query);
$this->results_per_page = $this->rpp = $rpp; $this->results_per_page = $rpp;
$this->rpp = $rpp;
$this->page = $page; $this->page = $page;
$this->since_id = $since_id; $this->since_id = $since_id;
$this->results = array(); $this->results = array();
@ -78,7 +83,7 @@ class JSONSearchResultsList
/** /**
* show the list of search results * show the list of search results
* *
* @return int count of the search results listed. * @return int $count of the search results listed.
*/ */
function show() function show()
@ -103,7 +108,7 @@ class JSONSearchResultsList
array_push($this->results, $item); array_push($this->results, $item);
} }
$time_end = microtime(true); $time_end = microtime(true);
$this->completed_in = $time_end - $time_start; $this->completed_in = $time_end - $time_start;
// Set other attrs // Set other attrs
@ -197,7 +202,7 @@ class ResultItem
function buildResult() function buildResult()
{ {
$this->text = $this->notice->content; $this->text = $this->notice->content;
$replier_profile = null; $replier_profile = null;
if ($this->notice->reply_to) { if ($this->notice->reply_to) {
@ -209,18 +214,21 @@ class ResultItem
$this->to_user_id = ($replier_profile) ? $this->to_user_id = ($replier_profile) ?
intval($replier_profile->id) : null; intval($replier_profile->id) : null;
$this->to_user = ($replier_profile) ? $this->to_user = ($replier_profile) ?
$replier_profile->nickname : null; $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; $this->from_user_id = $this->profile->id;
$user = User::staticGet('id', $this->profile->id); $user = User::staticGet('id', $this->profile->id);
$this->iso_language_code = $this->user->language; $this->iso_language_code = $this->user->language;
$this->source = $this->getSourceLink($this->notice->source); $this->source = $this->getSourceLink($this->notice->source);
$avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE); $avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE);
$this->profile_image_url = ($avatar) ? $this->profile_image_url = ($avatar) ?
$avatar->displayUrl() : Avatar::defaultImage(AVATAR_STREAM_SIZE); $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, * Either the name (and link) of the API client that posted the notice,
* or one of other other channels. * 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) function getSourceLink($source)
{ {
$source_name = _($source); $source_name = _($source);
switch ($source) { switch ($source) {
case 'web': case 'web':
case 'xmpp': case 'xmpp':
case 'mail': case 'mail':
case 'omb': case 'omb':
case 'api': case 'api':
break; break;
default: default:
$ns = Notice_source::staticGet($source); $ns = Notice_source::staticGet($source);
if ($ns) { if ($ns) {
$source_name = '<a href="' . $ns->url . '">' . $ns->name . '</a>'; $source_name = '<a href="' . $ns->url . '">' . $ns->name . '</a>';
} }
break; break;
} }
return $source_name;
} return $source_name;
}
} }

View File

@ -50,10 +50,9 @@ function mail_backend()
static $backend = null; static $backend = null;
if (!$backend) { if (!$backend) {
global $config; $backend = Mail::factory(common_config('mail', 'backend'),
$backend = Mail::factory($config['mail']['backend'], (common_config('mail', 'params')) ?
($config['mail']['params']) ? common_config('mail', 'params') :
$config['mail']['params'] :
array()); array());
if (PEAR::isError($backend)) { if (PEAR::isError($backend)) {
common_server_error($backend->getMessage(), 500); common_server_error($backend->getMessage(), 500);

View File

@ -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) function lookup_nonce($consumer, $token, $nonce, $timestamp)
{ {
$n = new Nonce(); $n = new Nonce();
$n->consumer_key = $consumer->key; $n->consumer_key = $consumer->key;
$n->tok = $token->key; $n->ts = $timestamp;
$n->nonce = $nonce; $n->nonce = $nonce;
if ($n->find(true)) { if ($n->find(true)) {
return true; return true;
} else { } else {
$n->ts = $timestamp;
$n->created = DB_DataObject_Cast::dateTime(); $n->created = DB_DataObject_Cast::dateTime();
$n->insert(); $n->insert();
return false; return false;

View File

@ -251,7 +251,6 @@ function omb_broadcast_profile($profile)
function omb_update_profile($profile, $remote_profile, $subscription) function omb_update_profile($profile, $remote_profile, $subscription)
{ {
global $config; # for license URL
$user = User::staticGet($profile->id); $user = User::staticGet($profile->id);
$con = omb_oauth_consumer(); $con = omb_oauth_consumer();
$token = new OAuthToken($subscription->token, $subscription->secret); $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__); 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()); common_debug("Unable to contact " . $req->get_normalized_http_url());
} else if ($result->status == 403) { # not authorized, don't send again } else if ($result->status == 403) { # not authorized, don't send again
common_debug('403 result, deleting subscription', __FILE__); common_debug('403 result, deleting subscription', __FILE__);
@ -306,7 +305,7 @@ function omb_update_profile($profile, $remote_profile, $subscription)
return false; return false;
} else { # success! } else { # success!
parse_str($result->body, $return); 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; return true;
} else { } else {
return false; return false;

View File

@ -160,7 +160,7 @@ function oid_authenticate($openid_url, $returnto, $immediate=false)
$auth_request->addExtension($sreg_request); $auth_request->addExtension($sreg_request);
} }
$trust_root = common_local_url('public'); $trust_root = common_path('');
$process_url = common_local_url($returnto); $process_url = common_local_url($returnto);
if ($auth_request->shouldSendRedirect()) { if ($auth_request->shouldSendRedirect()) {

View File

@ -89,6 +89,7 @@ class ProfileList extends Widget
'id' => 'profile-' . $this->profile->id)); 'id' => 'profile-' . $this->profile->id));
$user = common_current_user(); $user = common_current_user();
$is_own = !is_null($user) && isset($this->user) && ($user->id === $this->user->id);
$this->out->elementStart('div', 'entity_profile vcard'); $this->out->elementStart('div', 'entity_profile vcard');
@ -102,13 +103,13 @@ class ProfileList extends Widget
'alt' => 'alt' =>
($this->profile->fullname) ? $this->profile->fullname : ($this->profile->fullname) ? $this->profile->fullname :
$this->profile->nickname)); $this->profile->nickname));
$hasFN = ($this->profile->fullname) ? 'nickname' : 'fn nickname'; $hasFN = ($this->profile->fullname !== '') ? 'nickname' : 'fn nickname';
$this->out->elementStart('span', $hasFN); $this->out->elementStart('span', $hasFN);
$this->out->raw($this->highlight($this->profile->nickname)); $this->out->raw($this->highlight($this->profile->nickname));
$this->out->elementEnd('span'); $this->out->elementEnd('span');
$this->out->elementEnd('a'); $this->out->elementEnd('a');
if ($this->profile->fullname) { if ($this->profile->fullname !== '') {
$this->out->elementStart('dl', 'entity_fn'); $this->out->elementStart('dl', 'entity_fn');
$this->out->element('dt', null, 'Full name'); $this->out->element('dt', null, 'Full name');
$this->out->elementStart('dd'); $this->out->elementStart('dd');
@ -118,7 +119,7 @@ class ProfileList extends Widget
$this->out->elementEnd('dd'); $this->out->elementEnd('dd');
$this->out->elementEnd('dl'); $this->out->elementEnd('dl');
} }
if ($this->profile->location) { if ($this->profile->location !== '') {
$this->out->elementStart('dl', 'entity_location'); $this->out->elementStart('dl', 'entity_location');
$this->out->element('dt', null, _('Location')); $this->out->element('dt', null, _('Location'));
$this->out->elementStart('dd', 'label'); $this->out->elementStart('dd', 'label');
@ -126,7 +127,7 @@ class ProfileList extends Widget
$this->out->elementEnd('dd'); $this->out->elementEnd('dd');
$this->out->elementEnd('dl'); $this->out->elementEnd('dl');
} }
if ($this->profile->homepage) { if ($this->profile->homepage !== '') {
$this->out->elementStart('dl', 'entity_url'); $this->out->elementStart('dl', 'entity_url');
$this->out->element('dt', null, _('URL')); $this->out->element('dt', null, _('URL'));
$this->out->elementStart('dd'); $this->out->elementStart('dd');
@ -137,7 +138,7 @@ class ProfileList extends Widget
$this->out->elementEnd('dd'); $this->out->elementEnd('dd');
$this->out->elementEnd('dl'); $this->out->elementEnd('dl');
} }
if ($this->profile->bio) { if ($this->profile->bio !== '') {
$this->out->elementStart('dl', 'entity_note'); $this->out->elementStart('dl', 'entity_note');
$this->out->element('dt', null, _('Note')); $this->out->element('dt', null, _('Note'));
$this->out->elementStart('dd', 'note'); $this->out->elementStart('dd', 'note');
@ -154,7 +155,7 @@ class ProfileList extends Widget
$this->out->elementStart('dl', 'entity_tags'); $this->out->elementStart('dl', 'entity_tags');
$this->out->elementStart('dt'); $this->out->elementStart('dt');
if ($user->id == $this->owner->id) { if ($is_own) {
$this->out->element('a', array('href' => common_local_url('tagother', $this->out->element('a', array('href' => common_local_url('tagother',
array('id' => $this->profile->id))), array('id' => $this->profile->id))),
_('Tags')); _('Tags'));
@ -183,7 +184,7 @@ class ProfileList extends Widget
$this->out->elementEnd('dl'); $this->out->elementEnd('dl');
} }
if ($user && $user->id == $this->owner->id) { if ($is_own) {
$this->showOwnerControls($this->profile); $this->showOwnerControls($this->profile);
} }
@ -193,11 +194,11 @@ class ProfileList extends Widget
$this->out->elementStart('ul'); $this->out->elementStart('ul');
if ($user && $user->id != $this->profile->id) { if (!$is_own) {
# XXX: special-case for user looking at own # XXX: special-case for user looking at own
# subscriptions page # subscriptions page
$this->out->elementStart('li', 'entity_subscribe'); $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 = new UnsubscribeForm($this->out, $this->profile);
$usf->show(); $usf->show();
} else { } else {
@ -206,9 +207,6 @@ class ProfileList extends Widget
} }
$this->out->elementEnd('li'); $this->out->elementEnd('li');
$this->out->elementStart('li', 'entity_block'); $this->out->elementStart('li', 'entity_block');
if ($user && $user->id == $this->owner->id) {
$this->showBlockForm();
}
$this->out->elementEnd('li'); $this->out->elementEnd('li');
} }

View File

@ -49,6 +49,9 @@ class Router
{ {
var $m = null; var $m = null;
static $inst = null; static $inst = null;
static $bare = array('requesttoken', 'accesstoken', 'userauthorization',
'postnotice', 'updateprofile', 'finishremotesubscribe',
'finishopenidlogin', 'finishaddopenid');
static function get() static function get()
{ {
@ -98,7 +101,7 @@ class Router
$main = array('login', 'logout', 'register', 'subscribe', $main = array('login', 'logout', 'register', 'subscribe',
'unsubscribe', 'confirmaddress', 'recoverpassword', 'unsubscribe', 'confirmaddress', 'recoverpassword',
'invite', 'favor', 'disfavor', 'sup', 'invite', 'favor', 'disfavor', 'sup',
'block'); 'block', 'subedit');
foreach ($main as $a) { foreach ($main as $a) {
$m->connect('main/'.$a, array('action' => $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', array('action' => 'remotesubscribe'));
$m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+')); $m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+'));
foreach (array('requesttoken', 'accesstoken', 'userauthorization', foreach (Router::$bare as $action) {
'postnotice', 'updateprofile', 'finishremotesubscribe') as $action) {
$m->connect('index.php?action=' . $action, array('action' => $action)); $m->connect('index.php?action=' . $action, array('action' => $action));
} }
@ -230,7 +232,7 @@ class Router
$m->connect('api/statuses/:method/:argument', $m->connect('api/statuses/:method/:argument',
array('action' => 'api', array('action' => 'api',
'apiaction' => 'statuses'), 'apiaction' => 'statuses'),
array('method' => '(user_timeline|friends_timeline|show|destroy|friends|followers)')); array('method' => '(user_timeline|friends_timeline|replies|show|destroy|friends|followers)'));
// users // users
@ -261,7 +263,7 @@ class Router
} }
foreach (array('xml', 'json', 'rss', 'atom') as $e) { 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', array('action' => 'api',
'apiaction' => 'direct_messages', 'apiaction' => 'direct_messages',
'method' => 'sent.'.$e)); 'method' => 'sent.'.$e));
@ -281,7 +283,7 @@ class Router
$m->connect('api/friendships/:method', $m->connect('api/friendships/:method',
array('action' => 'api', array('action' => 'api',
'apiaction' => 'friendships'), 'apiaction' => 'friendships'),
array('method' => 'exists(\.(xml|json|rss|atom))')); array('method' => 'exists(\.(xml|json))'));
// Social graph // Social graph
@ -357,7 +359,6 @@ class Router
array('action' => 'api', array('action' => 'api',
'apiaction' => 'laconica')); 'apiaction' => 'laconica'));
// search // search
$m->connect('api/search.atom', array('action' => 'twitapisearchatom')); $m->connect('api/search.atom', array('action' => 'twitapisearchatom'));
$m->connect('api/search.json', array('action' => 'twitapisearchjson')); $m->connect('api/search.json', array('action' => 'twitapisearchjson'));
@ -365,7 +366,7 @@ class Router
// user stuff // user stuff
foreach (array('subscriptions', 'subscribers', foreach (array('subscriptions', 'subscribers',
'nudge', 'xrds', 'all', 'foaf', 'nudge', 'xrds', 'all', 'foaf',
'replies', 'inbox', 'outbox', 'microsummary') as $a) { 'replies', 'inbox', 'outbox', 'microsummary') as $a) {
$m->connect(':nickname/'.$a, $m->connect(':nickname/'.$a,

View File

@ -110,8 +110,6 @@ class SearchAction extends Action
function showForm($error=null) function showForm($error=null)
{ {
global $config;
$q = $this->trimmed('q'); $q = $this->trimmed('q');
$page = $this->trimmed('page', 1); $page = $this->trimmed('page', 1);
$this->elementStart('form', array('method' => 'get', $this->elementStart('form', array('method' => 'get',
@ -122,7 +120,7 @@ class SearchAction extends Action
$this->element('legend', null, _('Search site')); $this->element('legend', null, _('Search site'));
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
$this->elementStart('li'); $this->elementStart('li');
if (!isset($config['site']['fancy']) || !$config['site']['fancy']) { if (!common_config('site', 'fancy')) {
$this->hidden('action', $this->trimmed('action')); $this->hidden('action', $this->trimmed('action'));
} }
$this->input('q', 'Keyword(s)', $q); $this->input('q', 'Keyword(s)', $q);

View File

@ -98,7 +98,7 @@ class SubGroupNav extends Widget
$this->user->nickname), $this->user->nickname),
$action == 'usergroups', $action == 'usergroups',
'nav_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'), $this->out->menuItem(common_local_url('invite'),
_('Invite'), _('Invite'),
sprintf(_('Invite friends and colleagues to join you on %s'), sprintf(_('Invite friends and colleagues to join you on %s'),

View File

@ -51,7 +51,7 @@ class TopPostersSection extends ProfileSection
$qry = 'SELECT profile.*, count(*) as value ' . $qry = 'SELECT profile.*, count(*) as value ' .
'FROM profile JOIN notice ON profile.id = notice.profile_id ' . 'FROM profile JOIN notice ON profile.id = notice.profile_id ' .
(common_config('public', 'localonly') ? 'WHERE is_local = 1 ' : '') . (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 '; 'ORDER BY value DESC ';
$limit = PROFILES_PER_SECTION; $limit = PROFILES_PER_SECTION;

View File

@ -224,7 +224,6 @@ function is_twitter_bound($notice, $flink) {
function broadcast_twitter($notice) function broadcast_twitter($notice)
{ {
global $config;
$success = true; $success = true;
$flink = Foreign_link::getByUserID($notice->profile_id, $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 // 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 // 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(); $fuser = $flink->getForeignUser();
$twitter_user = $fuser->nickname; $twitter_user = $fuser->nickname;
@ -248,7 +247,7 @@ function broadcast_twitter($notice)
CURLOPT_POSTFIELDS => CURLOPT_POSTFIELDS =>
array( array(
'status' => $statustxt, 'status' => $statustxt,
'source' => $config['integration']['source'] 'source' => common_config('integration', 'source')
), ),
CURLOPT_RETURNTRANSFER => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_FAILONERROR => true, CURLOPT_FAILONERROR => true,

View File

@ -127,8 +127,6 @@ class TwitterapiAction extends Action
{ {
$profile = $notice->getProfile(); $profile = $notice->getProfile();
$server = common_config('site', 'server');
$entry = array(); $entry = array();
# We trim() to avoid extraneous whitespace in the output # 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['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content));
$entry['link'] = common_local_url('shownotice', array('notice' => $notice->id)); $entry['link'] = common_local_url('shownotice', array('notice' => $notice->id));
$entry['published'] = common_date_iso8601($notice->created); $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['updated'] = $entry['published'];
$entry['author'] = $profile->getBestName();
# RSS Item specific # RSS Item specific
$entry['description'] = $entry['content']; $entry['description'] = $entry['content'];
@ -151,7 +153,6 @@ class TwitterapiAction extends Action
function twitter_rss_dmsg_array($message) function twitter_rss_dmsg_array($message)
{ {
$server = common_config('site', 'server');
$entry = array(); $entry = array();
$entry['title'] = sprintf('Message from %s to %s', $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['content'] = common_xml_safe_str(trim($message->content));
$entry['link'] = common_local_url('showmessage', array('message' => $message->id)); $entry['link'] = common_local_url('showmessage', array('message' => $message->id));
$entry['published'] = common_date_iso8601($message->created); $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['updated'] = $entry['published'];
$entry['author'] = $message->getFrom()->getBestName();
# RSS Item specific # RSS Item specific
$entry['description'] = $entry['content']; $entry['description'] = $entry['content'];
@ -242,6 +247,9 @@ class TwitterapiAction extends Action
$this->element('published', null, $entry['published']); $this->element('published', null, $entry['published']);
$this->element('updated', null, $entry['updated']); $this->element('updated', null, $entry['updated']);
$this->element('link', array('href' => $entry['link'], 'rel' => 'alternate', 'type' => 'text/html'), null); $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'); $this->elementEnd('entry');
} }
@ -358,7 +366,7 @@ class TwitterapiAction extends Action
$this->end_twitter_rss(); $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'); $this->init_document('atom');
@ -366,12 +374,20 @@ class TwitterapiAction extends Action
$this->element('title', null, $title); $this->element('title', null, $title);
$this->element('id', null, $id); $this->element('id', null, $id);
$this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null); $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null);
if (!is_null($suplink)) { if (!is_null($suplink)) {
# For FriendFeed's SUP protocol # For FriendFeed's SUP protocol
$this->element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup', $this->element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup',
'href' => $suplink, 'href' => $suplink,
'type' => 'application/json')); '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); $this->element('subtitle', null, $subtitle);
if (is_array($notice)) { if (is_array($notice)) {
@ -634,79 +650,4 @@ class TwitterapiAction extends Action
return $source_name; 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');
}
}
} }

View File

@ -72,8 +72,7 @@ function common_timezone()
} }
} }
global $config; return common_config('site', 'timezone');
return $config['site']['timezone'];
} }
function common_language() 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); $url = (mb_strpos($orig_url, htmlspecialchars($url)) === FALSE) ? $url:htmlspecialchars($url);
// Call user specified func // Call user specified func
$modified_url = $callback($url); $modified_url = call_user_func($callback, $url);
// Replace it! // Replace it!
$start = mb_strpos($text, $url, $offset); $start = mb_strpos($text, $url, $offset);
@ -481,18 +480,12 @@ function common_replace_urls_callback($text, $callback) {
function common_linkify($url) { function common_linkify($url) {
// It comes in special'd, so we unspecial it before passing to the stringifying // It comes in special'd, so we unspecial it before passing to the stringifying
// functions // functions
$ext = pathinfo($url, PATHINFO_EXTENSION);
$url = htmlspecialchars_decode($url); $url = htmlspecialchars_decode($url);
$video_ext = array('mp4', 'flv', 'avi', 'mpg', 'mp3', 'ogg');
$display = $url; $display = $url;
$url = (!preg_match('#^([a-z]+://|(mailto|aim|tel):)#i', $url)) ? 'http://'.$url : $url; $url = (!preg_match('#^([a-z]+://|(mailto|aim|tel):)#i', $url)) ? 'http://'.$url : $url;
$attrs = array('href' => $url, 'rel' => 'external'); $attrs = array('href' => $url, 'rel' => 'external');
if (in_array($ext, $video_ext)) {
$attrs['class'] = 'media';
}
if ($longurl = common_longurl($url)) { if ($longurl = common_longurl($url)) {
$attrs['title'] = $longurl; $attrs['title'] = $longurl;
} }
@ -688,7 +681,7 @@ function common_relative_profile($sender, $nickname, $dt=null)
$recipient = new Profile(); $recipient = new Profile();
// XXX: use a join instead of a subquery // 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('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)) { if ($recipient->find(true)) {
// XXX: should probably differentiate between profiles with // XXX: should probably differentiate between profiles with
// the same name by date of most recent update // the same name by date of most recent update
@ -698,7 +691,7 @@ function common_relative_profile($sender, $nickname, $dt=null)
$recipient = new Profile(); $recipient = new Profile();
// XXX: use a join instead of a subquery // 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('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)) { if ($recipient->find(true)) {
// XXX: should probably differentiate between profiles with // XXX: should probably differentiate between profiles with
// the same name by date of most recent update // 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(); $r = Router::get();
$path = $r->build($action, $args, $params, $fragment); $path = $r->build($action, $args, $params, $fragment);
if ($path) {
}
if (common_config('site','fancy')) { if (common_config('site','fancy')) {
$url = common_path(mb_substr($path, 1)); $url = common_path(mb_substr($path, 1));
} else { } 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; return $url;
} }
function common_path($relative) function common_path($relative)
{ {
global $config; $pathpart = (common_config('site', 'path')) ? common_config('site', 'path')."/" : '';
$pathpart = ($config['site']['path']) ? $config['site']['path']."/" : ''; return "http://".common_config('site', 'server').'/'.$pathpart.$relative;
return "http://".$config['site']['server'].'/'.$pathpart.$relative;
} }
function common_date_string($dt) function common_date_string($dt)
@ -989,8 +984,7 @@ function common_ensure_syslog()
{ {
static $initialized = false; static $initialized = false;
if (!$initialized) { if (!$initialized) {
global $config; openlog(common_config('syslog', 'appname'), 0, LOG_USER);
openlog($config['syslog']['appname'], 0, LOG_USER);
$initialized = true; $initialized = true;
} }
} }

0
local/.gitignore vendored Normal file
View File

File diff suppressed because it is too large Load Diff

223
plugins/LinkbackPlugin.php Normal file
View 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;
}
}

View 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--

View File

@ -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-

View File

@ -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
View 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

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
# Laconica - a distributed open-source microblogging tool # Laconica - a distributed open-source microblogging tool
@ -23,19 +23,30 @@
SDIR=`dirname $0` SDIR=`dirname $0`
DIR=`php $SDIR/getpiddir.php` 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 xmppconfirmhandler xmppdaemon twitterhandler facebookhandler ; do
FILES="$DIR/$f.*.pid" FILES="$DIR/$f.*.pid"
for ff in "$FILES" ; do for ff in "$FILES" ; do
echo -n "Stopping $f..." PID=`cat $ff 2>/dev/null`
PID=`cat $ff` if [ -n "$PID" ] ; then
kill -3 $PID echo -n "Stopping $f ($PID)..."
if kill -9 $PID ; then if kill -3 $PID 2>/dev/null ; then
echo "DONE." count=0
else while kill -0 $PID 2>/dev/null ; do
echo "FAILED." 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 fi
rm -f $ff rm -f $ff
done done

View File

@ -1,3 +1,3 @@
cd `dirname $0` cd `dirname $0`
cd .. 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

View File

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

View File

@ -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