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

This commit is contained in:
CiaranG 2009-03-05 16:18:59 +00:00
commit 3c53e821cc
20 changed files with 544 additions and 127 deletions

97
README
View File

@ -236,21 +236,28 @@ especially if you've previously installed PHP/MySQL packages.
configure virtual hosts on your web server, you can try setting up configure virtual hosts on your web server, you can try setting up
"http://micro.example.net/" or the like. "http://micro.example.net/" or the like.
3. You should also take this moment to make your avatar subdirectory 3. Make your target directory writeable by the Web server.
chmod a+w /var/www/mublog/
On some systems, this will probably work:
chgrp www-data /var/www/mublog/
chmod g+w /var/www/mublog/
If your Web server runs as another user besides "www-data", try
that user's default group instead. As a last resort, you can create
a new group like "mublog" and add the Web server's user to the group.
4. You should also take this moment to make your avatar subdirectory
writeable by the Web server. An insecure way to do this is: writeable by the Web server. An insecure way to do this is:
chmod a+w /var/www/mublog/avatar chmod a+w /var/www/mublog/avatar
On some systems, this will probably work: You can also make the avatar directory writeable by the Web server
group, as noted above.
chgrp www-data /var/www/mublog/avatar 5. Create a database to hold your microblog data. Something like this
chmod g+w /var/www/mublog/avatar
If your Web server runs as another user besides "www-data", try
that user's default group instead. As a last resort, you can create
a new group like "avatar" and add the Web server's user to the group.
4. Create a database to hold your microblog data. Something like this
should work: should work:
mysqladmin -u "username" --password="password" create laconica mysqladmin -u "username" --password="password" create laconica
@ -263,63 +270,55 @@ especially if you've previously installed PHP/MySQL packages.
a tool like PHPAdmin to create a database. Check your hosting a tool like PHPAdmin to create a database. Check your hosting
service's documentation for how to create a new MySQL database.) service's documentation for how to create a new MySQL database.)
5. Run the laconica.sql SQL script in the db subdirectory to create
the database tables in the database. A typical system would work
like this:
mysql -u "username" --password="password" laconica < /var/www/mublog/db/laconica.sql
You may want to test by logging into the database and checking that
the tables were created. Here's an example:
SHOW TABLES;
6. Create a new database account that Laconica will use to access the 6. Create a new database account that Laconica will use to access the
database. If you have shell access, this will probably work from the database. If you have shell access, this will probably work from the
MySQL shell: MySQL shell:
GRANT SELECT,INSERT,DELETE,UPDATE on laconica.* GRANT ALL on laconica.*
TO 'lacuser'@'localhost' TO 'lacuser'@'localhost'
IDENTIFIED BY 'lacpassword'; IDENTIFIED BY 'lacpassword';
You should change 'lacuser' and 'lacpassword' to your preferred new You should change 'lacuser' and 'lacpassword' to your preferred new
username and password. You may want to test logging in as this new username and password. You may want to test logging in to MySQL as
user and testing that you can SELECT from some of the tables in the this new user.
DB (use SHOW TABLES to see which ones are there).
7. Copy the config.php.sample in the Laconica directory to config.php. 7. In a browser, navigate to the Laconica install script; something like:
8. Edit config.php to set the basic configuration for your system. http://yourserver.example.com/mublog/install.php
(See descriptions below for basic config options.) Note that there
are lots of options and if you try to do them all at once, you will
have a hard time making sure what's working and what's not. So,
stick with the basics at first. In particular, customizing the
'site' and 'db' settings will almost definitely be needed.
9. At this point, you should be able to navigate in a browser to your Enter the database connection information and your site name. The
microblog's main directory and see the "Public Timeline", which install program will configure your site and install the initial,
will be empty. If not, magic has happened! You can now register a almost-empty database.
new user, post some notices, edit your profile, etc. However, you
may want to wait to do that stuff if you think you can set up 8. You should now be able to navigate to your microblog's main directory
"fancy URLs" (see below), since some URLs are stored in the database. and see the "Public Timeline", which will be empty. If not, magic
has happened! You can now register a new user, post some notices,
edit your profile, etc. However, you may want to wait to do that stuff
if you think you can set up "fancy URLs" (see below), since some
URLs are stored in the database.
Fancy URLs Fancy URLs
---------- ----------
By default, Laconica will have big long sloppy URLs that are hard for By default, Laconica will use URLs that include the main PHP program's
people to remember or use. For example, a user's home profile might be name in them. For example, a user's home profile might be
found at: found at:
http://example.org/mublog/index.php?action=showstream&nickname=fred http://example.org/mublog/index.php/mublog/fred
On certain systems that don't support this kind of syntax, they'll
look like this:
http://example.org/mublog/index.php?p=mublog/fred
It's possible to configure the software so it looks like this instead: It's possible to configure the software so it looks like this instead:
http://example.org/mublog/fred http://example.org/mublog/fred
These "fancy URLs" are more readable and memorable for users. To use These "fancy URLs" are more readable and memorable for users. To use
fancy URLs, you must either have Apache 2.2.x with .htaccess enabled fancy URLs, you must either have Apache 2.x with .htaccess enabled and
and mod_redirect enabled, -OR- know how to configure "url redirection" mod_redirect enabled, -OR- know how to configure "url redirection" in
in your server. your server.
1. Copy the htaccess.sample file to .htaccess in your Laconica 1. Copy the htaccess.sample file to .htaccess in your Laconica
directory. Note: if you have control of your server's httpd.conf or directory. Note: if you have control of your server's httpd.conf or
@ -344,10 +343,6 @@ like:
If you changed your HTTP server configuration, you may need to restart If you changed your HTTP server configuration, you may need to restart
the server first. the server first.
If you have problems with the .htaccess file on versions of Apache
earlier than 2.2.x, try changing the regular expressions in the
htaccess.sample file that use "\w" to just use ".".
Sphinx Sphinx
------ ------
@ -557,7 +552,7 @@ Sample cron job:
# Update Twitter friends subscriptions every half hour # Update Twitter friends subscriptions every half hour
0,30 * * * * /path/to/php /path/to/laconica/scripts/synctwitterfriends.php>&/dev/null 0,30 * * * * /path/to/php /path/to/laconica/scripts/synctwitterfriends.php>&/dev/null
Built-in Facebook Application Built-in Facebook Application
----------------------------- -----------------------------
Laconica's Facebook application allows your users to automatically Laconica's Facebook application allows your users to automatically
@ -571,7 +566,7 @@ above).
Quick setup instructions*: Quick setup instructions*:
Install the Facebook Developer application on Facebook: Install the Facebook Developer application on Facebook:
http://www.facebook.com/developers/ http://www.facebook.com/developers/
@ -644,7 +639,7 @@ to these resources.
Themes Themes
------ ------
There are two themes shipped with this version of Laconica: "stoica", There are two themes shipped with this version of Laconica: "identica",
which is what the Identi.ca site uses, and "default", which is a good which is what the Identi.ca site uses, and "default", which is a good
basis for other sites. basis for other sites.

View File

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

97
actions/twitapisearch.php Normal file
View File

@ -0,0 +1,97 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* List of replies
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Search
* @package Laconica
* @author Zach Copley <zach@controlyourself.ca>
* @copyright 2008-2009 Control Yourself, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*/
if (!defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/twitterapi.php';
/**
* Action handler for Twitter-compatible API search
*
* @category Search
* @package Laconica
* @author Zach Copley <zach@controlyourself.ca>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
* @see TwitterapiAction
*/
class TwitapisearchAction extends TwitterapiAction
{
var $query;
var $limit;
var $callback;
/**
* Initialization.
*
* @param array $args Web and URL arguments
*
* @return boolean false if user doesn't exist
*/
function prepare($args)
{
parent::prepare($args);
$qeury = $this->trimmed('query');
return true;
}
/**
* Handle a request
*
* @param array $args Arguments from $_REQUEST
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$this->showResults($this->limit);
}
/**
* Show search results
*
* @param int $limit Number of notices to show
*
* @return void
*/
function showResults($limit)
{
$this->serverError(_('API method under construction.'), $code = 501);
}
}

90
actions/twitapitrends.php Normal file
View File

@ -0,0 +1,90 @@
<?php
/**
* Laconica, the distributed open-source microblogging tool
*
* List of replies
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Search
* @package Laconica
* @author Zach Copley <zach@controlyourself.ca>
* @copyright 2008-2009 Control Yourself, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*/
if (!defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/twitterapi.php';
/**
* Returns the top ten queries that are currently trending
*
* @category Search
* @package Laconica
* @author Zach Copley <zach@controlyourself.ca>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*
* @see TwitterapiAction
*/
class TwitapitrendsAction extends TwitterapiAction
{
var $callback;
/**
* Initialization.
*
* @param array $args Web and URL arguments
*
* @return boolean false if user doesn't exist
*/
function prepare($args)
{
parent::prepare($args);
return true;
}
/**
* Handle a request
*
* @param array $args Arguments from $_REQUEST
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$this->showTrends();
}
/**
* Output the trends
*
* @return void
*/
function showTrends()
{
$this->serverError(_('API method under construction.'), $code = 501);
}
}

View File

@ -25,25 +25,29 @@ class TwitapiusersAction extends TwitterapiAction
{ {
function show($args, $apidata) function show($args, $apidata)
{ {
parent::handle($args); parent::handle($args);
if (!in_array($apidata['content-type'], array('xml', 'json'))) { if (!in_array($apidata['content-type'], array('xml', 'json'))) {
$this->clientError(_('API method not found!'), $code = 404); $this->clientError(_('API method not found!'), $code = 404);
return; return;
} }
$this->auth_user = $apidata['user'];
$user = null; $user = null;
$email = $this->arg('email'); $email = $this->arg('email');
$user_id = $this->arg('user_id');
if ($email) { if ($email) {
$user = User::staticGet('email', $email); $user = User::staticGet('email', $email);
} elseif ($user_id) {
$user = $this->get_user($user_id);
} elseif (isset($apidata['api_arg'])) { } elseif (isset($apidata['api_arg'])) {
$user = $this->get_user($apidata['api_arg']); $user = $this->get_user($apidata['api_arg']);
} } elseif (isset($apidata['user'])) {
$user = $apidata['user'];
if (!$user) { }
if (!$user) {
// XXX: Twitter returns a random(?) user instead of throwing and err! -- Zach // XXX: Twitter returns a random(?) user instead of throwing and err! -- Zach
$this->client_error(_('Not found.'), 404, $apidata['content-type']); $this->client_error(_('Not found.'), 404, $apidata['content-type']);
return; return;
@ -74,9 +78,12 @@ class TwitapiusersAction extends TwitterapiAction
// Other fields Twitter sends... // Other fields Twitter sends...
$twitter_user['profile_background_color'] = ''; $twitter_user['profile_background_color'] = '';
$twitter_user['profile_background_image_url'] = '';
$twitter_user['profile_text_color'] = ''; $twitter_user['profile_text_color'] = '';
$twitter_user['profile_link_color'] = ''; $twitter_user['profile_link_color'] = '';
$twitter_user['profile_sidebar_fill_color'] = ''; $twitter_user['profile_sidebar_fill_color'] = '';
$twitter_user['profile_sidebar_border_color'] = '';
$twitter_user['profile_background_tile'] = 'false';
$faves = DB_DataObject::factory('fave'); $faves = DB_DataObject::factory('fave');
$faves->user_id = $user->id; $faves->user_id = $user->id;
@ -94,18 +101,27 @@ class TwitapiusersAction extends TwitterapiAction
$twitter_user['utc_offset'] = $t->format('Z'); $twitter_user['utc_offset'] = $t->format('Z');
$twitter_user['time_zone'] = $timezone; $twitter_user['time_zone'] = $timezone;
if (isset($this->auth_user)) { if (isset($apidata['user'])) {
if ($this->auth_user->isSubscribed($profile)) { if ($apidata['user']->isSubscribed($profile)) {
$twitter_user['following'] = 'true'; $twitter_user['following'] = 'true';
} else { } else {
$twitter_user['following'] = 'false'; $twitter_user['following'] = 'false';
} }
// Not implemented yet // Notifications on?
$twitter_user['notifications'] = 'false'; $sub = Subscription::pkeyGet(array('subscriber' =>
} $apidata['user']->id, 'subscribed' => $profile->id));
if ($sub) {
if ($sub->jabber || $sub->sms) {
$twitter_user['notifications'] = 'true';
} else {
$twitter_user['notifications'] = 'false';
}
}
}
if ($apidata['content-type'] == 'xml') { if ($apidata['content-type'] == 'xml') {
$this->init_document('xml'); $this->init_document('xml');
$this->show_twitter_xml_user($twitter_user); $this->show_twitter_xml_user($twitter_user);
@ -114,7 +130,13 @@ class TwitapiusersAction extends TwitterapiAction
$this->init_document('json'); $this->init_document('json');
$this->show_json_objects($twitter_user); $this->show_json_objects($twitter_user);
$this->end_document('json'); $this->end_document('json');
} } else {
// This is in case 'show' was called via /account/verify_credentials
// without a format (xml or json).
header('Content-Type: text/html; charset=utf-8');
print 'Authorized';
}
} }
} }

View File

@ -1,8 +1,5 @@
insert into foreign_service insert into foreign_service
(id, name, description, created) (id, name, description, created)
values values
('1','Twitter', 'Twitter Micro-blogging service', now()); ('1','Twitter', 'Twitter Micro-blogging service', now()),
insert into foreign_service ('2','Facebook', 'Facebook', now());
(id, name, description, created)
values
('2','Facebook', 'Facebook', now());

46
db/notice_source.sql Normal file
View File

@ -0,0 +1,46 @@
INSERT INTO notice_source
(code, name, url, created)
VALUES
('Do','Gnome Do','http://do.davebsd.com/wiki/index.php?title=Microblog_Plugin', now()),
('Facebook','Facebook','http://apps.facebook.com/identica/', now()),
('Gwibber','Gwibber','http://launchpad.net/gwibber', now()),
('HelloTxt','HelloTxt','http://hellotxt.com/', now()),
('IdentiFox','IdentiFox','http://www.bitbucket.org/uncryptic/identifox/', now()),
('LaTwit','LaTwit','http://latwit.mac65.com/', now()),
('Nambu','Nambu','http://www.nambu.com/', now()),
('Pikchur','Pikchur','http://www.pikchur.com/', now()),
('Ping.fm','Ping.fm','http://ping.fm/', now()),
('Twidge','Twidge','http://software.complete.org/twidge', now()),
('Updating.Me','Updating.Me','http://updating.me/', now()),
('betwittered','BeTwittered','http://www.32hours.com/betwitteredinfo/', now()),
('bti','bti','http://gregkh.github.com/bti/', now()),
('deskbar','Deskbar-Applet','http://www.gnome.org/projects/deskbar-applet/', now()),
('identicatools','Laconica Tools','http://bitbucketlabs.net/laconica-tools/', now()),
('identichat','identichat','http://identichat.prosody.im/', now()),
('identitwitch','IdentiTwitch','http://richfish.org/identitwitch/', now()),
('mbpidgin','mbpidgin','http://code.google.com/p/microblog-purple/', now()),
('moconica','Moconica','http://moconica.com/', now()),
('pocketwit','PockeTwit','http://code.google.com/p/pocketwit/', now()),
('posty','Posty','http://spreadingfunkyness.com/posty/', now()),
('royalewithcheese','Royale With Cheese','http://p.hellyeah.org/', now()),
('rssdent','rssdent','http://github.com/zcopley/rssdent/tree/master', now()),
('rygh.no','rygh.no','http://rygh.no/', now()),
('ryghsms','ryghsms','http://sms.rygh.no/', now()),
('smob','SMOB','http://smob.sioc-project.org/', now()),
('spaz','Spaz','http://funkatron.com/spaz', now()),
('tarpipe','tarpipe','http://tarpipe.com/', now()),
('tjunar','Tjunar','http://nederflash.nl/boek/titels/tjunar-air', now()),
('tr.im','tr.im','http://tr.im/', now()),
('tweenky','Tweenky','http://beta.tweenky.com/', now()),
('twhirl','Twhirl','http://www.twhirl.org/', now()),
('twibble','twibble','http://www.twibble.de/', now()),
('twidge','Twidge','http://software.complete.org/twidge', now()),
('twidroid','twidroid','http://www.twidroid.com/', now()),
('twittelator','Twittelator','http://www.stone.com/iPhone/Twittelator/', now()),
('twitterfeed','twitterfeed','http://twitterfeed.com/', now()),
('twitterphoto','TwitterPhoto','http://richfish.org/twitterphoto/', now()),
('twitterpm','Net::Twitter','http://search.cpan.org/dist/Net-Twitter/', now()),
('twittertools','Twitter Tools','http://wordpress.org/extend/plugins/twitter-tools/', now()),
('twitux','Twitux','http://live.gnome.org/DanielMorales/Twitux', now()),
('twitvim','TwitVim','http://vim.sourceforge.net/scripts/script.php?script_id=2204', now()),
('urfastr','urfastr','http://urfastr.net/', now());

View File

@ -65,6 +65,14 @@ function main()
{ {
global $user, $action; global $user, $action;
if (!_have_config()) {
$msg = sprintf(_("No configuration file found. Try running ".
"the installation program first."));
$sac = new ServerErrorAction($msg);
$sac->showPage();
return;
}
// For database errors // For database errors
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError'); PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError');

View File

@ -130,6 +130,36 @@ function handlePost()
$password = $_POST['password']; $password = $_POST['password'];
$sitename = $_POST['sitename']; $sitename = $_POST['sitename'];
if (empty($host)) {
updateStatus("No hostname specified.", true);
showForm();
return;
}
if (empty($database)) {
updateStatus("No database specified.", true);
showForm();
return;
}
if (empty($username)) {
updateStatus("No username specified.", true);
showForm();
return;
}
if (empty($password)) {
updateStatus("No password specified.", true);
showForm();
return;
}
if (empty($sitename)) {
updateStatus("No sitename specified.", true);
showForm();
return;
}
updateStatus("Starting installation..."); updateStatus("Starting installation...");
updateStatus("Checking database..."); updateStatus("Checking database...");
$conn = mysql_connect($host, $username, $password); $conn = mysql_connect($host, $username, $password);
@ -152,6 +182,18 @@ function handlePost()
showForm(); showForm();
return; return;
} }
foreach (array('sms_carrier' => 'SMS carrier',
'notice_source' => 'notice source',
'foreign_services' => 'foreign service')
as $scr => $name) {
updateStatus(sprintf("Adding %s data to database...", $name));
$res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
if ($res === false) {
updateStatus(sprintf("Can't run %d script.", $name), true);
showForm();
return;
}
}
updateStatus("Writing config file..."); updateStatus("Writing config file...");
$sqlUrl = "mysqli://$username:$password@$host/$database"; $sqlUrl = "mysqli://$username:$password@$host/$database";
$res = writeConf($sitename, $sqlUrl); $res = writeConf($sitename, $sqlUrl);

View File

@ -976,17 +976,17 @@ class Action extends HTMLOutputter // lawsuit
} }
if ($have_before) { if ($have_before) {
$pargs = array('page' => $page-1); $pargs = array('page' => $page-1);
$newargs = $args ? array_merge($args, $pargs) : $pargs;
$this->elementStart('li', array('class' => 'nav_prev')); $this->elementStart('li', array('class' => 'nav_prev'));
$this->element('a', array('href' => common_local_url($action, $newargs), 'rel' => 'prev'), $this->element('a', array('href' => common_local_url($action, $args, $pargs),
'rel' => 'prev'),
_('After')); _('After'));
$this->elementEnd('li'); $this->elementEnd('li');
} }
if ($have_after) { if ($have_after) {
$pargs = array('page' => $page+1); $pargs = array('page' => $page+1);
$newargs = $args ? array_merge($args, $pargs) : $pargs;
$this->elementStart('li', array('class' => 'nav_next')); $this->elementStart('li', array('class' => 'nav_next'));
$this->element('a', array('href' => common_local_url($action, $newargs), 'rel' => 'next'), $this->element('a', array('href' => common_local_url($action, $args, $pargs),
'rel' => 'next'),
_('Before')); _('Before'));
$this->elementEnd('li'); $this->elementEnd('li');
} }

View File

@ -49,7 +49,7 @@ class ClientErrorAction extends ErrorAction
function __construct($message='Error', $code=400) function __construct($message='Error', $code=400)
{ {
parent::__construct($message, $code); parent::__construct($message, $code);
$this->status = array(400 => 'Bad Request', $this->status = array(400 => 'Bad Request',
401 => 'Unauthorized', 401 => 'Unauthorized',
402 => 'Payment Required', 402 => 'Payment Required',
@ -72,7 +72,7 @@ class ClientErrorAction extends ErrorAction
} }
// XXX: Should these error actions even be invokable via URI? // XXX: Should these error actions even be invokable via URI?
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -84,11 +84,16 @@ class ClientErrorAction extends ErrorAction
} }
$this->message = $this->trimmed('message'); $this->message = $this->trimmed('message');
if (!$this->message) { if (!$this->message) {
$this->message = "Client Error $this->code"; $this->message = "Client Error $this->code";
} }
$this->showPage(); $this->showPage();
} }
function title()
{
return $this->status[$this->code];
}
} }

View File

@ -188,6 +188,12 @@ foreach ($_config_files as $_config_file) {
} }
} }
function _have_config()
{
global $_have_a_config;
return $_have_a_config;
}
// XXX: Throw a conniption if database not installed // XXX: Throw a conniption if database not installed
// Fixup for laconica.ini // Fixup for laconica.ini

View File

@ -54,10 +54,12 @@ class PopularNoticeSection extends NoticeSection
$weightexpr='sum(exp(-(now() - fave.modified) / %s))'; $weightexpr='sum(exp(-(now() - fave.modified) / %s))';
} }
$qry = 'SELECT notice.id, '. $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 notice.id ' . 'GROUP BY notice.id,notice.profile_id,notice.content,notice.uri,' .
'notice.rendered,notice.url,notice.created,notice.modified,' .
'notice.reply_to,notice.is_local,notice.source ' .
'ORDER BY weight DESC'; 'ORDER BY weight DESC';
$offset = 0; $offset = 0;

View File

@ -228,14 +228,15 @@ class Router
// users // users
$m->connect('api/users/show/:argument', $m->connect('api/users/:method/:argument',
array('action' => 'api', array('action' => 'api',
'apiaction' => 'users')); 'apiaction' => 'users'),
array('method' => 'show(\.(xml|json))?'));
$m->connect('api/users/:method', $m->connect('api/users/:method',
array('action' => 'api', array('action' => 'api',
'apiaction' => 'users'), 'apiaction' => 'users'),
array('method' => 'show(\.(xml|json|atom|rss))?')); array('method' => 'show(\.(xml|json))?'));
// direct messages // direct messages
@ -283,14 +284,14 @@ class Router
array('action' => 'api', array('action' => 'api',
'apiaction' => 'statuses', 'apiaction' => 'statuses',
'method' => 'friendsIDs')); 'method' => 'friendsIDs'));
foreach (array('xml', 'json') as $e) { foreach (array('xml', 'json') as $e) {
$m->connect('api/friends/ids.'.$e, $m->connect('api/friends/ids.'.$e,
array('action' => 'api', array('action' => 'api',
'apiaction' => 'statuses', 'apiaction' => 'statuses',
'method' => 'friendsIDs.'.$e)); 'method' => 'friendsIDs.'.$e));
} }
$m->connect('api/followers/ids/:argument', $m->connect('api/followers/ids/:argument',
array('action' => 'api', array('action' => 'api',
'apiaction' => 'statuses', 'apiaction' => 'statuses',
@ -351,6 +352,16 @@ class Router
array('action' => 'api', array('action' => 'api',
'apiaction' => 'laconica')); 'apiaction' => 'laconica'));
// search
foreach (array('json', 'atom') as $e) {
$m->connect('api/search.'.$e,
array('action' => 'twitapisearch'));
}
$m->connect('api/trends.json', array('action' => 'twitapitrends'));
// user stuff // user stuff
foreach (array('subscriptions', 'subscribers', foreach (array('subscriptions', 'subscribers',

View File

@ -42,7 +42,7 @@ require_once INSTALLDIR.'/lib/error.php';
* says that 500 errors should be treated similarly to 400 errors, and * says that 500 errors should be treated similarly to 400 errors, and
* it's easier to give an HTML response. Maybe we can customize these * it's easier to give an HTML response. Maybe we can customize these
* to display some funny animal cartoons. If not, we can probably role * to display some funny animal cartoons. If not, we can probably role
* these classes up into a single class. * these classes up into a single class.
* *
* See: http://tools.ietf.org/html/rfc2616#section-10 * See: http://tools.ietf.org/html/rfc2616#section-10
* *
@ -57,19 +57,19 @@ class ServerErrorAction extends ErrorAction
function __construct($message='Error', $code=500) function __construct($message='Error', $code=500)
{ {
parent::__construct($message, $code); parent::__construct($message, $code);
$this->status = array(500 => 'Internal Server Error', $this->status = array(500 => 'Internal Server Error',
501 => 'Not Implemented', 501 => 'Not Implemented',
502 => 'Bad Gateway', 502 => 'Bad Gateway',
503 => 'Service Unavailable', 503 => 'Service Unavailable',
504 => 'Gateway Timeout', 504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported'); 505 => 'HTTP Version Not Supported');
$this->default = 500; $this->default = 500;
} }
// XXX: Should these error actions even be invokable via URI? // XXX: Should these error actions even be invokable via URI?
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -81,12 +81,16 @@ class ServerErrorAction extends ErrorAction
} }
$this->message = $this->trimmed('message'); $this->message = $this->trimmed('message');
if (!$this->message) { if (!$this->message) {
$this->message = "Server Error $this->code"; $this->message = "Server Error $this->code";
} }
$this->showPage(); $this->showPage();
} }
function title()
{
return $this->status[$this->code];
}
} }

View File

@ -60,20 +60,34 @@ class TwitterapiAction extends Action
function twitter_status_array($notice, $include_user=true) function twitter_status_array($notice, $include_user=true)
{ {
$profile = $notice->getProfile(); $profile = $notice->getProfile();
$twitter_status = array(); $twitter_status = array();
$twitter_status['text'] = $notice->content; $twitter_status['text'] = $notice->content;
$twitter_status['truncated'] = 'false'; # Not possible on Laconica $twitter_status['truncated'] = 'false'; # Not possible on Laconica
$twitter_status['created_at'] = $this->date_twitter($notice->created); $twitter_status['created_at'] = $this->date_twitter($notice->created);
$twitter_status['in_reply_to_status_id'] = ($notice->reply_to) ? intval($notice->reply_to) : null; $twitter_status['in_reply_to_status_id'] = ($notice->reply_to) ?
intval($notice->reply_to) : null;
$twitter_status['source'] = $this->source_link($notice->source); $twitter_status['source'] = $this->source_link($notice->source);
$twitter_status['id'] = intval($notice->id); $twitter_status['id'] = intval($notice->id);
$twitter_status['in_reply_to_user_id'] = ($notice->reply_to) ? $this->replier_by_reply(intval($notice->reply_to)) : null;
$replier_profile = null;
if ($notice->reply_to) {
$reply = Notice::staticGet(intval($notice->reply_to));
if ($reply) {
$replier_profile = $reply->getProfile();
}
}
$twitter_status['in_reply_to_user_id'] =
($replier_profile) ? intval($replier_profile->id) : null;
$twitter_status['in_reply_to_screen_name'] =
($replier_profile) ? $replier_profile->nickname : null;
if (isset($this->auth_user)) { if (isset($this->auth_user)) {
$twitter_status['favorited'] = ($this->auth_user->hasFave($notice)) ? 'true' : 'false'; $twitter_status['favorited'] =
($this->auth_user->hasFave($notice)) ? 'true' : 'false';
} else { } else {
$twitter_status['favorited'] = 'false'; $twitter_status['favorited'] = 'false';
} }
@ -137,7 +151,6 @@ class TwitterapiAction extends Action
function twitter_dmsg_array($message) function twitter_dmsg_array($message)
{ {
$twitter_dm = array(); $twitter_dm = array();
$from_profile = $message->getFrom(); $from_profile = $message->getFrom();
@ -386,23 +399,7 @@ class TwitterapiAction extends Action
$t = strtotime($dt); $t = strtotime($dt);
return date("D M d G:i:s O Y", $t); return date("D M d G:i:s O Y", $t);
} }
function replier_by_reply($reply_id)
{
$notice = Notice::staticGet($reply_id);
if ($notice) {
$profile = $notice->getProfile();
if ($profile) {
return intval($profile->id);
} else {
common_debug('Can\'t find a profile for notice: ' . $notice->id, __FILE__);
}
} else {
common_debug("Can't get notice: $reply_id", __FILE__);
}
return null;
}
// XXX: Candidate for a general utility method somewhere? // XXX: Candidate for a general utility method somewhere?
function count_subscriptions($profile) function count_subscriptions($profile)
{ {

View File

@ -81,7 +81,7 @@ function common_language()
// If there is a user logged in and they've set a language preference // If there is a user logged in and they've set a language preference
// then return that one... // then return that one...
if (common_logged_in()) { if (_have_config() && common_logged_in()) {
$user = common_current_user(); $user = common_current_user();
$user_language = $user->language; $user_language = $user->language;
if ($user_language) if ($user_language)
@ -315,6 +315,10 @@ function common_current_user()
{ {
global $_cur; global $_cur;
if (!_have_config()) {
return null;
}
if ($_cur === false) { if ($_cur === false) {
if (isset($_REQUEST[session_name()]) || (isset($_SESSION['userid']) && $_SESSION['userid'])) { if (isset($_REQUEST[session_name()]) || (isset($_SESSION['userid']) && $_SESSION['userid'])) {

View File

@ -0,0 +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

View File

@ -0,0 +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--

87
scripts/laconica.spec Normal file
View File

@ -0,0 +1,87 @@
BuildRequires: php-pear
BuildRequires: httpd-devel
Name: laconica
Version: 0.7.2
Release: 1%{?dist}
License: GAGPL v3 or later
Source: laconica-0.7.2.tar.gz
Group: Applications/Internet
Summary: Laconica, the Open Source microblogging platform
BuildArch: noarch
Requires: httpd
Requires: php >= 5
Requires: php-pear-Mail-Mime
Requires: php-curl
Requires: php-mysql
Requires: php-mbstring
Requires: php-gettext
Requires: php-xml
Requires: php-gd
BuildRoot: %{_tmppath}/%{name}-%{version}-build
%define apache_serverroot %(/usr/sbin/apxs -q DATADIR)
%define apache_sysconfdir %(/usr/sbin/apxs -q SYSCONFDIR)
%define wwwpath %{apache_serverroot}/%{name}
%define confpath %{_sysconfdir}/%{name}
%description
From the ABOUT file: Laconica (pronounced "luh-KAWN-ih-kuh") is a Free
and Open Source microblogging platform. It helps people in a
community, company or group to exchange short (140 character) messages
over the Web. Users can choose which people to "follow" and receive
only their friends' or colleagues' status messages. It provides a
similar service to sites like Twitter, Jaiku, and Plurk.
%prep
%setup -q
%build
%install
mkdir -p %{buildroot}%{wwwpath}
cp -a * %{buildroot}%{wwwpath}
mkdir -p %{buildroot}%{_datadir}/laconica
cp -a db %{buildroot}%{_datadir}/laconica/db
mkdir -p %{buildroot}%{_sysconfdir}/httpd/conf.d
cat > %{buildroot}%{_sysconfdir}/httpd/conf.d/laconica.conf <<"EOF"
Alias /laconica/ "/var/www/laconica/"
<Directory "/var/www/laconica">
Options Indexes FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
EOF
%clean
rm -rf %buildroot
%files
%defattr(-,root,root)
%dir %{wwwpath}
%{wwwpath}/*
%{_datadir}/laconica/*
%attr(-,apache,apache) %dir %{_datadir}/laconica/avatar
%doc COPYING README doc-src/*
%config(noreplace) %{_sysconfdir}/httpd/conf.d/laconica.conf
%changelog
* Wed Mar 03 2009 Zach Copley <zach@controlyourself.ca> - 0.7.2
- Changed version number to 0.7.2.
* Sat Feb 28 2009 Ken Sedgwick <ken@bonsai.com> - 0.7.1-1
- Modified RPM for Fedora.
* Thu Feb 13 2009 tuukka.pasanen@ilmi.fi
- packaged laconica version 0.7.1
* Wed Feb 04 2009 tuukka.pasanen@ilmi.fi
- packaged laconica version 0.7.0 using the buildservice spec file wizard