summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Shorturl_api.php2
-rw-r--r--lib/action.php41
-rw-r--r--lib/common.php9
-rw-r--r--lib/currentuserdesignaction.php12
-rw-r--r--lib/daemon.php21
-rw-r--r--lib/dbqueuemanager.php166
-rw-r--r--lib/designsettings.php111
-rw-r--r--lib/facebookaction.php14
-rw-r--r--lib/groupdesignaction.php16
-rw-r--r--lib/groupeditform.php28
-rw-r--r--lib/jsonsearchresultslist.php1
-rw-r--r--lib/language.php2
-rw-r--r--lib/noticelist.php2
-rw-r--r--lib/ownerdesignaction.php12
-rw-r--r--lib/peoplesearchresults.php80
-rw-r--r--lib/profileaction.php8
-rw-r--r--lib/profilelist.php9
-rw-r--r--lib/profileminilist.php18
-rw-r--r--lib/queuehandler.php135
-rw-r--r--lib/queuemanager.php74
-rw-r--r--lib/router.php8
-rw-r--r--lib/rssaction.php2
-rw-r--r--lib/stompqueuemanager.php129
-rw-r--r--lib/subgroupnav.php2
-rw-r--r--lib/twitterapi.php61
-rw-r--r--lib/unqueuemanager.php85
-rw-r--r--lib/util.php308
-rw-r--r--lib/xmppqueuehandler.php2
28 files changed, 923 insertions, 435 deletions
diff --git a/lib/Shorturl_api.php b/lib/Shorturl_api.php
index 29f4eb3a6..22d5b4cb5 100644
--- a/lib/Shorturl_api.php
+++ b/lib/Shorturl_api.php
@@ -40,7 +40,7 @@ class ShortUrlApi
}
private function is_long($url) {
- return strlen($url) >= $this->long_limit;
+ return strlen($url) >= common_config('site', 'shorturllength');
}
protected function http_post($data) {
diff --git a/lib/action.php b/lib/action.php
index 89a8c8f4d..928eb48c0 100644
--- a/lib/action.php
+++ b/lib/action.php
@@ -247,7 +247,6 @@ class Action extends HTMLOutputter // lawsuit
'src' => common_path('js/jquery.joverlay.min.js')),
' ');
-
Event::handle('EndShowJQueryScripts', array($this));
}
if (Event::handle('StartShowLaconicaScripts', array($this))) {
@@ -384,15 +383,18 @@ class Action extends HTMLOutputter // lawsuit
{
$this->elementStart('address', array('id' => 'site_contact',
'class' => 'vcard'));
- $this->elementStart('a', array('class' => 'url home bookmark',
- 'href' => common_local_url('public')));
- if (common_config('site', 'logo') || file_exists(theme_file('logo.png'))) {
- $this->element('img', array('class' => 'logo photo',
- 'src' => (common_config('site', 'logo')) ? common_config('site', 'logo') : theme_path('logo.png'),
- 'alt' => common_config('site', 'name')));
+ if (Event::handle('StartAddressData', array($this))) {
+ $this->elementStart('a', array('class' => 'url home bookmark',
+ 'href' => common_local_url('public')));
+ if (common_config('site', 'logo') || file_exists(theme_file('logo.png'))) {
+ $this->element('img', array('class' => 'logo photo',
+ 'src' => (common_config('site', 'logo')) ? common_config('site', 'logo') : theme_path('logo.png'),
+ 'alt' => common_config('site', 'name')));
+ }
+ $this->element('span', array('class' => 'fn org'), common_config('site', 'name'));
+ $this->elementEnd('a');
+ Event::handle('EndAddressData', array($this));
}
- $this->element('span', array('class' => 'fn org'), common_config('site', 'name'));
- $this->elementEnd('a');
$this->elementEnd('address');
}
@@ -422,11 +424,13 @@ class Action extends HTMLOutputter // lawsuit
$this->menuItem(common_local_url('smssettings'),
_('Connect'), _('Connect to SMS, Twitter'), false, 'nav_connect');
}
- $this->menuItem(common_local_url('invite'),
- _('Invite'),
- sprintf(_('Invite friends and colleagues to join you on %s'),
- common_config('site', 'name')),
- false, 'nav_invitecontact');
+ if (common_config('invite', 'enabled')) {
+ $this->menuItem(common_local_url('invite'),
+ _('Invite'),
+ sprintf(_('Invite friends and colleagues to join you on %s'),
+ common_config('site', 'name')),
+ false, 'nav_invitecontact');
+ }
$this->menuItem(common_local_url('logout'),
_('Logout'), _('Logout from the site'), false, 'nav_logout');
}
@@ -704,6 +708,11 @@ class Action extends HTMLOutputter // lawsuit
_('About'));
$this->menuItem(common_local_url('doc', array('title' => 'faq')),
_('FAQ'));
+ $bb = common_config('site', 'broughtby');
+ if (!empty($bb)) {
+ $this->menuItem(common_local_url('doc', array('title' => 'tos')),
+ _('TOS'));
+ }
$this->menuItem(common_local_url('doc', array('title' => 'privacy')),
_('Privacy'));
$this->menuItem(common_local_url('doc', array('title' => 'source')),
@@ -964,12 +973,16 @@ class Action extends HTMLOutputter // lawsuit
$action = $this->trimmed('action');
$args = $this->args;
unset($args['action']);
+ if (common_config('site', 'fancy')) {
+ unset($args['p']);
+ }
if (array_key_exists('submit', $args)) {
unset($args['submit']);
}
foreach (array_keys($_COOKIE) as $cookie) {
unset($args[$cookie]);
}
+
return common_local_url($action, $args);
}
diff --git a/lib/common.php b/lib/common.php
index bb1a4255d..14be747bc 100644
--- a/lib/common.php
+++ b/lib/common.php
@@ -120,10 +120,12 @@ $config =
'private' => false,
'ssl' => 'never',
'sslserver' => null,
+ 'shorturllength' => 30,
'dupelimit' => 60), # default for same person saying the same thing
'syslog' =>
array('appname' => 'laconica', # for syslog
- 'priority' => 'debug'), # XXX: currently ignored
+ 'priority' => 'debug', # XXX: currently ignored
+ 'facility' => LOG_USER),
'queue' =>
array('enabled' => false,
'subsystem' => 'db', # default to database, or 'stomp'
@@ -175,6 +177,8 @@ $config =
'host' => null, # only set if != server
'debug' => false, # print extra debug info
'public' => array()), # JIDs of users who want to receive the public stream
+ 'invite' =>
+ array('enabled' => true),
'sphinx' =>
array('enabled' => false,
'server' => 'localhost',
@@ -254,6 +258,9 @@ $config =
'oohembed' => array('endpoint' => 'http://oohembed.com/oohembed/'),
'search' =>
array('type' => 'fulltext'),
+ 'sessions' =>
+ array('handle' => false, // whether to handle sessions ourselves
+ 'debug' => false), // debugging output for sessions
);
$config['db'] = &PEAR::getStaticProperty('DB_DataObject','options');
diff --git a/lib/currentuserdesignaction.php b/lib/currentuserdesignaction.php
index 7c2520cf6..4c7e15a8b 100644
--- a/lib/currentuserdesignaction.php
+++ b/lib/currentuserdesignaction.php
@@ -53,14 +53,19 @@ class CurrentUserDesignAction extends Action
*
* @return nothing
*/
+
function showStylesheets()
{
parent::showStylesheets();
- $design = $this->getDesign();
+ $user = common_current_user();
+
+ if (empty($user) || $user->viewdesigns) {
+ $design = $this->getDesign();
- if (!empty($design)) {
- $design->showCSS($this);
+ if (!empty($design)) {
+ $design->showCSS($this);
+ }
}
}
@@ -84,5 +89,4 @@ class CurrentUserDesignAction extends Action
return $cur->getDesign();
}
-
}
diff --git a/lib/daemon.php b/lib/daemon.php
index a0df00bdc..9d89c63e7 100644
--- a/lib/daemon.php
+++ b/lib/daemon.php
@@ -23,6 +23,13 @@ if (!defined('LACONICA')) {
class Daemon
{
+ var $daemonize = true;
+
+ function __construct($daemonize = true)
+ {
+ $this->daemonize = $daemonize;
+ }
+
function name()
{
return null;
@@ -129,12 +136,16 @@ class Daemon
common_log(LOG_INFO, $this->name() . ' already running. Exiting.');
exit(0);
}
- if ($this->background()) {
- $this->writePidFile();
- $this->changeUser();
- $this->run();
- $this->clearPidFile();
+
+ if ($this->daemonize) {
+ common_log(LOG_INFO, 'Backgrounding daemon "'.$this->name().'"');
+ $this->background();
}
+
+ $this->writePidFile();
+ $this->changeUser();
+ $this->run();
+ $this->clearPidFile();
}
function run()
diff --git a/lib/dbqueuemanager.php b/lib/dbqueuemanager.php
new file mode 100644
index 000000000..6e7172de0
--- /dev/null
+++ b/lib/dbqueuemanager.php
@@ -0,0 +1,166 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Simple-minded queue manager for storing items in the database
+ *
+ * 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 QueueManager
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @author Sarven Capadisli <csarven@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/
+ */
+
+class DBQueueManager extends QueueManager
+{
+ var $qis = array();
+
+ function enqueue($object, $queue)
+ {
+ $notice = $object;
+
+ $qi = new Queue_item();
+
+ $qi->notice_id = $notice->id;
+ $qi->transport = $queue;
+ $qi->created = $notice->created;
+ $result = $qi->insert();
+
+ if (!$result) {
+ common_log_db_error($qi, 'INSERT', __FILE__);
+ throw new ServerException('DB error inserting queue item');
+ }
+
+ return true;
+ }
+
+ function service($queue, $handler)
+ {
+ while (true) {
+ $this->_log(LOG_DEBUG, 'Checking for notices...');
+ $notice = $this->_nextItem($queue, null);
+ if (empty($notice)) {
+ $this->_log(LOG_DEBUG, 'No notices waiting; idling.');
+ // Nothing in the queue. Do you
+ // have other tasks, like servicing your
+ // XMPP connection, to do?
+ $handler->idle(QUEUE_HANDLER_MISS_IDLE);
+ } else {
+ $this->_log(LOG_INFO, 'Got notice '. $notice->id);
+ // Yay! Got one!
+ if ($handler->handle_notice($notice)) {
+ $this->_log(LOG_INFO, 'Successfully handled notice '. $notice->id);
+ $this->_done($notice, $queue);
+ } else {
+ $this->_log(LOG_INFO, 'Failed to handle notice '. $notice->id);
+ $this->_fail($notice, $queue);
+ }
+ // Chance to e.g. service your XMPP connection
+ $this->_log(LOG_DEBUG, 'Idling after success.');
+ $handler->idle(QUEUE_HANDLER_HIT_IDLE);
+ }
+ // XXX: when do we give up?
+ }
+ }
+
+ function _nextItem($queue, $timeout=null)
+ {
+ $start = time();
+ $result = null;
+
+ do {
+ $qi = Queue_item::top($queue);
+ if (!empty($qi)) {
+ $notice = Notice::staticGet('id', $qi->notice_id);
+ if (!empty($notice)) {
+ $result = $notice;
+ } else {
+ $this->_log(LOG_INFO, 'dequeued non-existent notice ' . $notice->id);
+ $qi->delete();
+ $qi->free();
+ $qi = null;
+ }
+ }
+ } while (empty($result) && (is_null($timeout) || (time() - $start) < $timeout));
+
+ return $result;
+ }
+
+ function _done($object, $queue)
+ {
+ // XXX: right now, we only handle notices
+
+ $notice = $object;
+
+ $qi = Queue_item::pkeyGet(array('notice_id' => $notice->id,
+ 'transport' => $queue));
+
+ if (empty($qi)) {
+ $this->_log(LOG_INFO, 'Cannot find queue item for notice '.$notice->id.', queue '.$queue);
+ } else {
+ if (empty($qi->claimed)) {
+ $this->_log(LOG_WARNING, 'Reluctantly releasing unclaimed queue item '.
+ 'for '.$notice->id.', queue '.$queue);
+ }
+ $qi->delete();
+ $qi->free();
+ $qi = null;
+ }
+
+ $this->_log(LOG_INFO, 'done with notice ID = ' . $notice->id);
+
+ $notice->free();
+ $notice = null;
+ }
+
+ function _fail($object, $queue)
+ {
+ // XXX: right now, we only handle notices
+
+ $notice = $object;
+
+ $qi = Queue_item::pkeyGet(array('notice_id' => $notice->id,
+ 'transport' => $queue));
+
+ if (empty($qi)) {
+ $this->_log(LOG_INFO, 'Cannot find queue item for notice '.$notice->id.', queue '.$queue);
+ } else {
+ if (empty($qi->claimed)) {
+ $this->_log(LOG_WARNING, 'Ignoring failure for unclaimed queue item '.
+ 'for '.$notice->id.', queue '.$queue);
+ } else {
+ $orig = clone($qi);
+ $qi->claimed = null;
+ $qi->update($orig);
+ $qi = null;
+ }
+ }
+
+ $this->_log(LOG_INFO, 'done with notice ID = ' . $notice->id);
+
+ $notice->free();
+ $notice = null;
+ }
+
+ function _log($level, $msg)
+ {
+ common_log($level, 'DBQueueManager: '.$msg);
+ }
+}
diff --git a/lib/designsettings.php b/lib/designsettings.php
index 9650679ac..fbffdb208 100644
--- a/lib/designsettings.php
+++ b/lib/designsettings.php
@@ -35,6 +35,20 @@ if (!defined('LACONICA')) {
require_once INSTALLDIR . '/lib/accountsettingsaction.php';
require_once INSTALLDIR . '/lib/webcolor.php';
+/**
+ * Base class for setting a user or group design
+ *
+ * Shows the design setting form and also handles some things like saving
+ * background images, and fetching a default design
+ *
+ * @category Settings
+ * @package Laconica
+ * @author Zach Copley <zach@controlyourself.ca>
+ * @author Sarven Capadisli <csarven@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/
+ */
+
class DesignSettingsAction extends AccountSettingsAction
{
@@ -63,6 +77,14 @@ class DesignSettingsAction extends AccountSettingsAction
'with a background image and a colour palette of your choice.');
}
+ /**
+ * Shows the design settings form
+ *
+ * @param Design $design a working design to show
+ *
+ * @return nothing
+ */
+
function showDesignForm($design)
{
@@ -94,7 +116,8 @@ class DesignSettingsAction extends AccountSettingsAction
if (!empty($design->backgroundimage)) {
- $this->elementStart('li', array('id' => 'design_background-image_onoff'));
+ $this->elementStart('li', array('id' =>
+ 'design_background-image_onoff'));
$this->element('img', array('src' =>
Design::url($design->backgroundimage)));
@@ -136,7 +159,7 @@ class DesignSettingsAction extends AccountSettingsAction
$this->elementStart('li');
$this->checkbox('design_background-image_repeat',
_('Tile background image'),
- ($design->disposition & BACKGROUND_TILE) ? true : false );
+ ($design->disposition & BACKGROUND_TILE) ? true : false);
$this->elementEnd('li');
}
@@ -212,18 +235,19 @@ class DesignSettingsAction extends AccountSettingsAction
'maxlength' => '7',
'size' => '7',
'value' => '#' . $lcolor->hexValue()));
+ $this->elementEnd('li');
- $this->elementEnd('li');
+ } catch (WebColorException $e) {
+ common_log(LOG_ERR, 'Bad color values in design ID: ' .$design->id);
+ }
- } catch (WebColorException $e) {
- common_log(LOG_ERR, 'Bad color values in design ID: ' .
- $design->id);
- }
+ $this->elementEnd('ul');
+ $this->elementEnd('fieldset');
- $this->elementEnd('ul');
- $this->elementEnd('fieldset');
+ $this->submit('defaults', _('Use defaults'), 'submit form_action-default',
+ 'defaults', _('Restore default designs'));
- $this->element('input', array('id' => 'settings_design_reset',
+ $this->element('input', array('id' => 'settings_design_reset',
'type' => 'reset',
'value' => 'Reset',
'class' => 'submit form_action-primary',
@@ -271,8 +295,8 @@ class DesignSettingsAction extends AccountSettingsAction
if ($this->arg('save')) {
$this->saveDesign();
- } else if ($this->arg('reset')) {
- $this->resetDesign();
+ } else if ($this->arg('defaults')) {
+ $this->restoreDefaults();
} else {
$this->showForm(_('Unexpected form submission.'));
}
@@ -316,7 +340,7 @@ class DesignSettingsAction extends AccountSettingsAction
}
/**
- * Get a default user design
+ * Get a default design
*
* @return Design design
*/
@@ -358,7 +382,16 @@ class DesignSettingsAction extends AccountSettingsAction
return $design;
}
- function saveBackgroundImage($design) {
+ /**
+ * Save the background image, if any, and set its disposition
+ *
+ * @param Design $design a working design to attach the img to
+ *
+ * @return nothing
+ */
+
+ function saveBackgroundImage($design)
+ {
// Now that we have a Design ID we can add a file to the design.
// XXX: This is an additional DB hit, but figured having the image
@@ -371,12 +404,12 @@ class DesignSettingsAction extends AccountSettingsAction
$filepath = null;
try {
- $imagefile =
- ImageFile::fromUpload('design_background-image_file');
- } catch (Exception $e) {
- $this->showForm($e->getMessage());
- return;
- }
+ $imagefile =
+ ImageFile::fromUpload('design_background-image_file');
+ } catch (Exception $e) {
+ $this->showForm($e->getMessage());
+ return;
+ }
$filename = Design::filename($design->id,
image_type_to_extension($imagefile->type),
@@ -386,7 +419,14 @@ class DesignSettingsAction extends AccountSettingsAction
move_uploaded_file($imagefile->filepath, $filepath);
+ // delete any old backround img laying around
+
+ if (isset($design->backgroundimage)) {
+ @unlink(Design::path($design->backgroundimage));
+ }
+
$original = clone($design);
+
$design->backgroundimage = $filename;
// default to on, no tile
@@ -403,4 +443,35 @@ class DesignSettingsAction extends AccountSettingsAction
}
}
+ /**
+ * Restore the user or group design to system defaults
+ *
+ * @return nothing
+ */
+
+ function restoreDefaults()
+ {
+ $design = $this->getWorkingDesign();
+ $default = $this->defaultDesign();
+ $original = clone($design);
+
+ $design->backgroundcolor = $default->backgroundcolor;
+ $design->contentcolor = $default->contentcolor;
+ $design->sidebarcolor = $default->sidebarcolor;
+ $design->textcolor = $default->textcolor;
+ $design->linkcolor = $default->linkcolor;
+
+ $design->setDisposition(false, true, false);
+
+ $result = $design->update($original);
+
+ if ($result === false) {
+ common_log_db_error($design, 'UPDATE', __FILE__);
+ $this->showForm(_('Couldn\'t update your design.'));
+ return;
+ }
+
+ $this->showForm(_('Design defaults restored.'), true);
+ }
+
}
diff --git a/lib/facebookaction.php b/lib/facebookaction.php
index a445750f7..1ae90d53b 100644
--- a/lib/facebookaction.php
+++ b/lib/facebookaction.php
@@ -213,12 +213,14 @@ class FacebookAction extends Action
array('href' => 'index.php', 'title' => _('Home')), _('Home'));
$this->elementEnd('li');
- $this->elementStart('li',
- array('class' =>
- ($this->action == 'facebookinvite') ? 'current' : 'facebook_invite'));
- $this->element('a',
- array('href' => 'invite.php', 'title' => _('Invite')), _('Invite'));
- $this->elementEnd('li');
+ if (common_config('invite', 'enabled')) {
+ $this->elementStart('li',
+ array('class' =>
+ ($this->action == 'facebookinvite') ? 'current' : 'facebook_invite'));
+ $this->element('a',
+ array('href' => 'invite.php', 'title' => _('Invite')), _('Invite'));
+ $this->elementEnd('li');
+ }
$this->elementStart('li',
array('class' =>
diff --git a/lib/groupdesignaction.php b/lib/groupdesignaction.php
index bc95921f1..58777c283 100644
--- a/lib/groupdesignaction.php
+++ b/lib/groupdesignaction.php
@@ -34,7 +34,7 @@ if (!defined('LACONICA')) {
/**
* Base class for actions that use a group's design
*
- * Pages related to groups can be themed with a design.
+ * Pages related to groups can be themed with a design.
* This superclass returns that design.
*
* @category Action
@@ -48,7 +48,7 @@ class GroupDesignAction extends Action {
/** The group in question */
var $group = null;
-
+
/**
* Show the groups's design stylesheet
*
@@ -58,10 +58,14 @@ class GroupDesignAction extends Action {
{
parent::showStylesheets();
- $design = $this->getDesign();
+ $user = common_current_user();
+
+ if (empty($user) || $user->viewdesigns) {
+ $design = $this->getDesign();
- if (!empty($design)) {
- $design->showCSS($this);
+ if (!empty($design)) {
+ $design->showCSS($this);
+ }
}
}
@@ -76,12 +80,10 @@ class GroupDesignAction extends Action {
function getDesign()
{
-
if (empty($this->group)) {
return null;
}
return $this->group->getDesign();
}
-
}
diff --git a/lib/groupeditform.php b/lib/groupeditform.php
index 7e8d6eea3..fbb39129b 100644
--- a/lib/groupeditform.php
+++ b/lib/groupeditform.php
@@ -130,30 +130,46 @@ class GroupEditForm extends Form
function formData()
{
+ if ($this->group) {
+ $id = $this->group->id;
+ $nickname = $this->group->nickname;
+ $fullname = $this->group->fullname;
+ $homepage = $this->group->homepage;
+ $description = $this->group->description;
+ $location = $this->group->location;
+ } else {
+ $id = '';
+ $nickname = '';
+ $fullname = '';
+ $homepage = '';
+ $description = '';
+ $location = '';
+ }
+
$this->out->elementStart('ul', 'form_data');
$this->out->elementStart('li');
- $this->out->hidden('groupid', $this->group->id);
+ $this->out->hidden('groupid', $id);
$this->out->input('nickname', _('Nickname'),
- ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $this->group->nickname,
+ ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname,
_('1-64 lowercase letters or numbers, no punctuation or spaces'));
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('fullname', _('Full name'),
- ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $this->group->fullname);
+ ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname);
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('homepage', _('Homepage'),
- ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $this->group->homepage,
+ ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
_('URL of the homepage or blog of the group or topic'));
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->textarea('description', _('Description'),
- ($this->out->arg('description')) ? $this->out->arg('description') : $this->group->description,
+ ($this->out->arg('description')) ? $this->out->arg('description') : $description,
_('Describe the group or topic in 140 chars'));
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('location', _('Location'),
- ($this->out->arg('location')) ? $this->out->arg('location') : $this->group->location,
+ ($this->out->arg('location')) ? $this->out->arg('location') : $location,
_('Location for the group, if any, like "City, State (or Region), Country"'));
$this->out->elementEnd('li');
if (common_config('group', 'maxaliases') > 0) {
diff --git a/lib/jsonsearchresultslist.php b/lib/jsonsearchresultslist.php
index f786c20a8..7beea9328 100644
--- a/lib/jsonsearchresultslist.php
+++ b/lib/jsonsearchresultslist.php
@@ -89,6 +89,7 @@ class JSONSearchResultsList
function show()
{
$cnt = 0;
+ $this->max_id = 0;
$time_start = microtime(true);
diff --git a/lib/language.php b/lib/language.php
index cd6498d30..3ea3dd2aa 100644
--- a/lib/language.php
+++ b/lib/language.php
@@ -108,7 +108,7 @@ function get_all_languages() {
'el' => array('q' => 0.1, 'lang' => 'el', 'name' => 'Greek', 'direction' => 'ltr'),
'en-us' => array('q' => 1, 'lang' => 'en_US', 'name' => 'English (US)', 'direction' => 'ltr'),
'en-gb' => array('q' => 1, 'lang' => 'en_GB', 'name' => 'English (British)', 'direction' => 'ltr'),
- 'en' => array('q' => 1, 'lang' => 'en', 'name' => 'English', 'direction' => 'ltr'),
+ 'en' => array('q' => 1, 'lang' => 'en_US', 'name' => 'English (US)', 'direction' => 'ltr'),
'es' => array('q' => 1, 'lang' => 'es', 'name' => 'Spanish', 'direction' => 'ltr'),
'fi' => array('q' => 1, 'lang' => 'fi', 'name' => 'Finnish', 'direction' => 'ltr'),
'fr-fr' => array('q' => 1, 'lang' => 'fr_FR', 'name' => 'French', 'direction' => 'ltr'),
diff --git a/lib/noticelist.php b/lib/noticelist.php
index 6f05c63d6..44726a17b 100644
--- a/lib/noticelist.php
+++ b/lib/noticelist.php
@@ -432,7 +432,7 @@ class NoticeListItem extends Widget
$this->out->elementStart('dl', 'response');
$this->out->element('dt', null, _('To'));
$this->out->elementStart('dd');
- $this->out->element('a', array('href' => $convurl),
+ $this->out->element('a', array('href' => $convurl.'#notice-'.$this->notice->id),
_('in context'));
$this->out->elementEnd('dd');
$this->out->elementEnd('dl');
diff --git a/lib/ownerdesignaction.php b/lib/ownerdesignaction.php
index 424474f42..785b8a93d 100644
--- a/lib/ownerdesignaction.php
+++ b/lib/ownerdesignaction.php
@@ -61,11 +61,15 @@ class OwnerDesignAction extends Action {
{
parent::showStylesheets();
- $design = $this->getDesign();
+ $user = common_current_user();
- if (!empty($design)) {
- $design->showCSS($this);
- }
+ if (empty($user) || $user->viewdesigns) {
+ $design = $this->getDesign();
+
+ if (!empty($design)) {
+ $design->showCSS($this);
+ }
+ }
}
/**
diff --git a/lib/peoplesearchresults.php b/lib/peoplesearchresults.php
deleted file mode 100644
index 9f6696b5f..000000000
--- a/lib/peoplesearchresults.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-/**
- * People search results class
- *
- * PHP version 5
- *
- * @category Widget
- * @package Laconica
- * @author Evan Prodromou <evan@controlyourself.ca>
- * @author Robin Millette <millette@controlyourself.ca>
- * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link http://laconi.ca/
- *
- * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, Control Yourself, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/profilelist.php';
-
-/**
- * People search results class
- *
- * Derivative of ProfileList with specialization for highlighting search terms.
- *
- * @category Widget
- * @package Laconica
- * @author Evan Prodromou <evan@controlyourself.ca>
- * @author Robin Millette <millette@controlyourself.ca>
- * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link http://laconi.ca/
- *
- * @see PeoplesearchAction
- */
-
-class PeopleSearchResults extends ProfileList
-{
- var $terms = null;
- var $pattern = null;
-
- function __construct($profile, $terms, $action)
- {
- parent::__construct($profile, $action);
-
- $this->terms = array_map('preg_quote',
- array_map('htmlspecialchars', $terms));
-
- $this->pattern = '/('.implode('|',$terms).')/i';
- }
-
- function newProfileItem($profile)
- {
- return new PeopleSearchResultItem($profile, $this->action);
- }
-}
-
-class PeopleSearchResultItem extends ProfileListItem
-{
- function highlight($text)
- {
- return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text));
- }
-}
-
diff --git a/lib/profileaction.php b/lib/profileaction.php
index 2519922b2..eeb5dbe48 100644
--- a/lib/profileaction.php
+++ b/lib/profileaction.php
@@ -108,7 +108,9 @@ class ProfileAction extends OwnerDesignAction
$this->element('h2', null, _('Subscriptions'));
- if ($profile) {
+ $cnt = 0;
+
+ if (!empty($profile)) {
$pml = new ProfileMiniList($profile, $this);
$cnt = $pml->show();
if ($cnt == 0) {
@@ -137,7 +139,9 @@ class ProfileAction extends OwnerDesignAction
$this->element('h2', null, _('Subscribers'));
- if ($profile) {
+ $cnt = 0;
+
+ if (!empty($profile)) {
$pml = new ProfileMiniList($profile, $this);
$cnt = $pml->show();
if ($cnt == 0) {
diff --git a/lib/profilelist.php b/lib/profilelist.php
index a604230f8..774538a4b 100644
--- a/lib/profilelist.php
+++ b/lib/profilelist.php
@@ -248,8 +248,13 @@ class ProfileListItem extends Widget
$usf = new UnsubscribeForm($this->out, $this->profile);
$usf->show();
} else {
- $sf = new SubscribeForm($this->out, $this->profile);
- $sf->show();
+ // Is it a local user? can't remote sub from a list
+ // XXX: make that possible!
+ $other = User::staticGet('id', $this->profile->id);
+ if (!empty($other)) {
+ $sf = new SubscribeForm($this->out, $this->profile);
+ $sf->show();
+ }
}
$this->out->elementEnd('li');
}
diff --git a/lib/profileminilist.php b/lib/profileminilist.php
index 09bef6f7c..357b4a2db 100644
--- a/lib/profileminilist.php
+++ b/lib/profileminilist.php
@@ -47,6 +47,7 @@ define('PROFILES_PER_MINILIST', 27);
class ProfileMiniList extends ProfileList
{
+
function startList()
{
$this->out->elementStart('ul', 'entities users xoxo');
@@ -56,6 +57,23 @@ class ProfileMiniList extends ProfileList
{
return new ProfileMiniListItem($profile, $this->action);
}
+
+ function showProfiles()
+ {
+ $cnt = 0;
+
+ while ($this->profile->fetch()) {
+ $cnt++;
+ if ($cnt > PROFILES_PER_MINILIST) {
+ break;
+ }
+ $pli = $this->newListItem($this->profile);
+ $pli->show();
+ }
+
+ return $cnt;
+ }
+
}
class ProfileMiniListItem extends ProfileListItem
diff --git a/lib/queuehandler.php b/lib/queuehandler.php
index ae403c65e..c0f38f4e3 100644
--- a/lib/queuehandler.php
+++ b/lib/queuehandler.php
@@ -17,26 +17,34 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-define('CLAIM_TIMEOUT', 1200);
-
if (!defined('LACONICA')) { exit(1); }
require_once(INSTALLDIR.'/lib/daemon.php');
require_once(INSTALLDIR.'/classes/Queue_item.php');
require_once(INSTALLDIR.'/classes/Notice.php');
+define('CLAIM_TIMEOUT', 1200);
+define('QUEUE_HANDLER_MISS_IDLE', 10);
+define('QUEUE_HANDLER_HIT_IDLE', 0);
+
class QueueHandler extends Daemon
{
-
var $_id = 'generic';
- function QueueHandler($id=null)
+ function __construct($id=null, $daemonize=true)
{
+ parent::__construct($daemonize);
+
if ($id) {
$this->set_id($id);
}
}
+ function timeout()
+ {
+ return 60;
+ }
+
function class_name()
{
return ucfirst($this->transport()) . 'Handler';
@@ -75,110 +83,21 @@ class QueueHandler extends Daemon
return true;
}
- function db_dispatch() {
- do {
- $qi = Queue_item::top($this->transport());
- if ($qi) {
- $this->log(LOG_INFO, 'Got item enqueued '.common_exact_date($qi->created));
- $notice = Notice::staticGet($qi->notice_id);
- if ($notice) {
- $this->log(LOG_INFO, 'broadcasting notice ID = ' . $notice->id);
- # XXX: what to do if broadcast fails?
- $result = $this->handle_notice($notice);
- if (!$result) {
- $this->log(LOG_WARNING, 'Failed broadcast for notice ID = ' . $notice->id);
- $orig = $qi;
- $qi->claimed = null;
- $qi->update($orig);
- $this->log(LOG_WARNING, 'Abandoned claim for notice ID = ' . $notice->id);
- continue;
- }
- $this->log(LOG_INFO, 'finished broadcasting notice ID = ' . $notice->id);
- $notice->free();
- unset($notice);
- $notice = null;
- } else {
- $this->log(LOG_WARNING, 'queue item for notice that does not exist');
- }
- $qi->delete();
- $qi->free();
- unset($qi);
- $this->idle(0);
- } else {
- $this->clear_old_claims();
- $this->idle(5);
- }
- } while (true);
- }
-
- function stomp_dispatch() {
-
- // use an external message queue system via STOMP
- require_once("Stomp.php");
-
- $server = common_config('queue','stomp_server');
- $username = common_config('queue', 'stomp_username');
- $password = common_config('queue', 'stomp_password');
-
- $con = new Stomp($server);
-
- if (!$con->connect($username, $password)) {
- $this->log(LOG_ERR, 'Failed to connect to queue server');
- return false;
- }
-
- $queue_basename = common_config('queue','queue_basename');
- // subscribe to the relevant queue (format: basename-transport)
- $con->subscribe('/queue/'.$queue_basename.'-'.$this->transport());
-
- do {
- $frame = $con->readFrame();
- if ($frame) {
- $this->log(LOG_INFO, 'Got item enqueued '.common_exact_date($frame->headers['created']));
-
- // XXX: Now the queue handler receives only the ID of the
- // notice, and it has to get it from the DB
- // A massive improvement would be avoid DB query by transmitting
- // all the notice details via queue server...
- $notice = Notice::staticGet($frame->body);
-
- if ($notice) {
- $this->log(LOG_INFO, 'broadcasting notice ID = ' . $notice->id);
- $result = $this->handle_notice($notice);
- if ($result) {
- // if the msg has been handled positively, ack it
- // and the queue server will remove it from the queue
- $con->ack($frame);
- $this->log(LOG_INFO, 'finished broadcasting notice ID = ' . $notice->id);
- }
- else {
- // no ack
- $this->log(LOG_WARNING, 'Failed broadcast for notice ID = ' . $notice->id);
- }
- $notice->free();
- unset($notice);
- $notice = null;
- } else {
- $this->log(LOG_WARNING, 'queue item for notice that does not exist');
- }
- }
- } while (true);
-
- $con->disconnect();
- }
-
function run()
{
if (!$this->start()) {
return false;
}
+
$this->log(LOG_INFO, 'checking for queued notices');
- if (common_config('queue','subsystem') == 'stomp') {
- $this->stomp_dispatch();
- }
- else {
- $this->db_dispatch();
- }
+
+ $queue = $this->transport();
+ $timeout = $this->timeout();
+
+ $qm = QueueManager::get();
+
+ $qm->service($queue, $this);
+
if (!$this->finish()) {
return false;
}
@@ -187,21 +106,11 @@ class QueueHandler extends Daemon
function idle($timeout=0)
{
- if ($timeout>0) {
+ if ($timeout > 0) {
sleep($timeout);
}
}
- function clear_old_claims()
- {
- $qi = new Queue_item();
- $qi->transport = $this->transport();
- $qi->whereAdd('now() - claimed > '.CLAIM_TIMEOUT);
- $qi->update(DB_DATAOBJECT_WHEREADD_ONLY);
- $qi->free();
- unset($qi);
- }
-
function log($level, $msg)
{
common_log($level, $this->class_name() . ' ('. $this->get_id() .'): '.$msg);
diff --git a/lib/queuemanager.php b/lib/queuemanager.php
new file mode 100644
index 000000000..582c24790
--- /dev/null
+++ b/lib/queuemanager.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Abstract class for queue managers
+ *
+ * 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 QueueManager
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @author Sarven Capadisli <csarven@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/
+ */
+
+class QueueManager
+{
+ static $qm = null;
+
+ static function get()
+ {
+ if (empty(self::$qm)) {
+
+ if (Event::handle('StartNewQueueManager', array(&self::$qm))) {
+
+ $enabled = common_config('queue', 'enabled');
+ $type = common_config('queue', 'subsystem');
+
+ if (!$enabled) {
+ // does everything immediately
+ self::$qm = new UnQueueManager();
+ } else {
+ switch ($type) {
+ case 'db':
+ self::$qm = new DBQueueManager();
+ break;
+ case 'stomp':
+ self::$qm = new StompQueueManager();
+ break;
+ default:
+ throw new ServerException("No queue manager class for type '$type'");
+ }
+ }
+ }
+ }
+
+ return self::$qm;
+ }
+
+ function enqueue($object, $queue)
+ {
+ throw ServerException("Unimplemented function 'enqueue' called");
+ }
+
+ function service($queue, $handler)
+ {
+ throw ServerException("Unimplemented function 'service' called");
+ }
+}
diff --git a/lib/router.php b/lib/router.php
index 1f39c60dc..75e72f932 100644
--- a/lib/router.php
+++ b/lib/router.php
@@ -152,6 +152,10 @@ class Router
$m->connect('search/notice/rss?q=:q', array('action' => 'noticesearchrss'),
array('q' => '.+'));
+ $m->connect('attachment/:attachment',
+ array('action' => 'attachment'),
+ array('attachment' => '[0-9]+'));
+
$m->connect('attachment/:attachment/ajax',
array('action' => 'attachment_ajax'),
array('attachment' => '[0-9]+'));
@@ -257,7 +261,7 @@ class Router
$m->connect('api/statuses/:method',
array('action' => 'api',
'apiaction' => 'statuses'),
- array('method' => '(public_timeline|friends_timeline|user_timeline|update|replies|mentions|friends|followers|featured)(\.(atom|rss|xml|json))?'));
+ array('method' => '(public_timeline|friends_timeline|user_timeline|update|replies|mentions|show|friends|followers|featured)(\.(atom|rss|xml|json))?'));
$m->connect('api/statuses/:method/:argument',
array('action' => 'api',
@@ -313,7 +317,7 @@ class Router
$m->connect('api/friendships/:method',
array('action' => 'api',
'apiaction' => 'friendships'),
- array('method' => 'exists(\.(xml|json))'));
+ array('method' => '(show|exists)(\.(xml|json))'));
// Social graph
diff --git a/lib/rssaction.php b/lib/rssaction.php
index 6f6c9a8cb..0c8188e88 100644
--- a/lib/rssaction.php
+++ b/lib/rssaction.php
@@ -214,7 +214,7 @@ class Rss10Action extends Action
$this->element('cc:licence', array('rdf:resource' => common_config('license', 'url')));
if ($notice->reply_to) {
$replyurl = common_local_url('shownotice', array('notice' => $notice->reply_to));
- $this->element('sioc:reply_to', array('rdf:resource' => $replyurl));
+ $this->element('sioc:reply_of', array('rdf:resource' => $replyurl));
}
$this->elementEnd('item');
$this->creators[$creator_uri] = $profile;
diff --git a/lib/stompqueuemanager.php b/lib/stompqueuemanager.php
new file mode 100644
index 000000000..e7e1e00dd
--- /dev/null
+++ b/lib/stompqueuemanager.php
@@ -0,0 +1,129 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Abstract class for queue managers
+ *
+ * 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 QueueManager
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @author Sarven Capadisli <csarven@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/
+ */
+
+require_once 'Stomp.php';
+
+class StompQueueManager
+{
+ var $server = null;
+ var $username = null;
+ var $password = null;
+ var $base = null;
+ var $con = null;
+
+ function __construct()
+ {
+ $this->server = common_config('queue', 'stomp_server');
+ $this->username = common_config('queue', 'stomp_username');
+ $this->password = common_config('queue', 'stomp_password');
+ $this->base = common_config('queue', 'queue_basename');
+ }
+
+ function _connect()
+ {
+ if (empty($this->con)) {
+ $this->_log(LOG_INFO, "Connecting to '$this->server' as '$this->username'...");
+ $this->con = new Stomp($this->server);
+
+ if ($this->con->connect($this->username, $this->password)) {
+ $this->_log(LOG_INFO, "Connected.");
+ } else {
+ $this->_log(LOG_ERR, 'Failed to connect to queue server');
+ throw new ServerException('Failed to connect to queue server');
+ }
+ }
+ }
+
+ function enqueue($object, $queue)
+ {
+ $notice = $object;
+
+ $this->_connect();
+
+ // XXX: serialize and send entire notice
+
+ $result = $this->con->send($this->_queueName($queue),
+ $notice->id, // BODY of the message
+ array ('created' => $notice->created));
+
+ if (!$result) {
+ common_log(LOG_ERR, 'Error sending to '.$queue.' queue');
+ return false;
+ }
+
+ common_log(LOG_DEBUG, 'complete remote queueing notice ID = '
+ . $notice->id . ' for ' . $queue);
+ }
+
+ function service($queue, $handler)
+ {
+ $result = null;
+
+ $this->_connect();
+
+ $this->con->setReadTimeout($handler->timeout());
+
+ $this->con->subscribe($this->_queueName($queue));
+
+ while (true) {
+
+ $frame = $this->con->readFrame();
+
+ if (!empty($frame)) {
+ $notice = Notice::staticGet('id', $frame->body);
+
+ if (empty($notice)) {
+ $this->_log(LOG_WARNING, 'Got ID '. $frame->body .' for non-existent notice');
+ $this->con->ack($frame);
+ } else if ($handler->handle_notice($notice)) {
+ $this->_log(LOG_INFO, 'Successfully handled notice '. $notice->id .' posted at ' . $frame->headers['created']);
+ $this->con->ack($frame);
+ unset($notice);
+ }
+
+ unset($frame);
+ }
+
+ $handler->idle(0);
+ }
+
+ $this->con->unsubscribe($this->_queueName($queue));
+ }
+
+ function _queueName($queue)
+ {
+ return common_config('queue', 'queue_basename') . $queue;
+ }
+
+ function _log($level, $msg)
+ {
+ common_log($level, 'StompQueueManager: '.$msg);
+ }
+}
diff --git a/lib/subgroupnav.php b/lib/subgroupnav.php
index 4a9b36ae8..520991923 100644
--- a/lib/subgroupnav.php
+++ b/lib/subgroupnav.php
@@ -100,7 +100,7 @@ class SubGroupNav extends Widget
$this->user->nickname),
$action == 'usergroups',
'nav_usergroups');
- if (!is_null($cur) && $this->user->id === $cur->id) {
+ if (common_config('invite', 'enabled') && !is_null($cur) && $this->user->id === $cur->id) {
$this->out->menuItem(common_local_url('invite'),
_('Invite'),
sprintf(_('Invite friends and colleagues to join you on %s'),
diff --git a/lib/twitterapi.php b/lib/twitterapi.php
index f538a0298..40e5b5067 100644
--- a/lib/twitterapi.php
+++ b/lib/twitterapi.php
@@ -278,6 +278,67 @@ class TwitterapiAction extends Action
return $twitter_dm;
}
+ function twitter_relationship_array($source, $target)
+ {
+ $relationship = array();
+
+ $relationship['source'] =
+ $this->relationship_details_array($source, $target);
+ $relationship['target'] =
+ $this->relationship_details_array($target, $source);
+
+ return array('relationship' => $relationship);
+ }
+
+ function relationship_details_array($source, $target)
+ {
+ $details = array();
+
+ $details['screen_name'] = $source->nickname;
+ $details['followed_by'] = $target->isSubscribed($source);
+ $details['following'] = $source->isSubscribed($target);
+
+ $notifications = false;
+
+ if ($source->isSubscribed($target)) {
+
+ $sub = Subscription::pkeyGet(array('subscriber' =>
+ $source->id, 'subscribed' => $target->id));
+
+ if (!empty($sub)) {
+ $notifications = ($sub->jabber || $sub->sms);
+ }
+ }
+
+ $details['notifications_enabled'] = $notifications;
+ $details['blocking'] = $source->hasBlocked($target);
+ $details['id'] = $source->id;
+
+ return $details;
+ }
+
+ function show_twitter_xml_relationship($relationship)
+ {
+ $this->elementStart('relationship');
+
+ foreach($relationship as $element => $value) {
+ if ($element == 'source' || $element == 'target') {
+ $this->elementStart($element);
+ $this->show_xml_relationship_details($value);
+ $this->elementEnd($element);
+ }
+ }
+
+ $this->elementEnd('relationship');
+ }
+
+ function show_xml_relationship_details($details)
+ {
+ foreach($details as $element => $value) {
+ $this->element($element, null, $value);
+ }
+ }
+
function show_twitter_xml_status($twitter_status)
{
$this->elementStart('status');
diff --git a/lib/unqueuemanager.php b/lib/unqueuemanager.php
new file mode 100644
index 000000000..515461072
--- /dev/null
+++ b/lib/unqueuemanager.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * A queue manager interface for just doing things immediately
+ *
+ * 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 QueueManager
+ * @package Laconica
+ * @author Evan Prodromou <evan@controlyourself.ca>
+ * @author Sarven Capadisli <csarven@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/
+ */
+
+class UnQueueManager
+{
+ function enqueue($object, $queue)
+ {
+ $notice = $object;
+
+ switch ($queue)
+ {
+ case 'omb':
+ if ($this->_isLocal($notice)) {
+ require_once(INSTALLDIR.'/lib/omb.php');
+ omb_broadcast_remote_subscribers($notice);
+ }
+ break;
+ case 'public':
+ if ($this->_isLocal($notice)) {
+ require_once(INSTALLDIR.'/lib/jabber.php');
+ jabber_public_notice($notice);
+ }
+ break;
+ case 'twitter':
+ if ($this->_isLocal($notice)) {
+ broadcast_twitter($notice);
+ }
+ break;
+ case 'facebook':
+ if ($this->_isLocal($notice)) {
+ require_once INSTALLDIR . '/lib/facebookutil.php';
+ return facebookBroadcastNotice($notice);
+ }
+ break;
+ case 'ping':
+ if ($this->_isLocal($notice)) {
+ require_once INSTALLDIR . '/lib/ping.php';
+ return ping_broadcast_notice($notice);
+ }
+ case 'sms':
+ require_once(INSTALLDIR.'/lib/mail.php');
+ mail_broadcast_notice_sms($notice);
+ break;
+ case 'jabber':
+ require_once(INSTALLDIR.'/lib/jabber.php');
+ jabber_broadcast_notice($notice);
+ break;
+ default:
+ throw ServerException("UnQueueManager: Unknown queue: $type");
+ }
+ }
+
+ function _isLocal($notice)
+ {
+ return ($notice->is_local == NOTICE_LOCAL_PUBLIC ||
+ $notice->is_local == NOTICE_LOCAL_NONPUBLIC);
+ }
+} \ No newline at end of file
diff --git a/lib/util.php b/lib/util.php
index 30b767c3e..9e8ec41d2 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -114,7 +114,7 @@ function common_check_user($nickname, $password)
return false;
}
$user = User::staticGet('nickname', $nickname);
- if (is_null($user)) {
+ if (is_null($user) || $user === false) {
return false;
} else {
if (0 == strcmp(common_munge_password($password, $user->id),
@@ -139,8 +139,22 @@ function common_have_session()
function common_ensure_session()
{
+ $c = null;
+ if (array_key_exists(session_name, $_COOKIE)) {
+ $c = $_COOKIE[session_name()];
+ }
if (!common_have_session()) {
+ if (common_config('sessions', 'handle')) {
+ Session::setSaveHandler();
+ }
@session_start();
+ if (!isset($_SESSION['started'])) {
+ $_SESSION['started'] = time();
+ if (!empty($c)) {
+ common_log(LOG_WARNING, 'Session cookie "' . $_COOKIE[session_name()] . '" ' .
+ ' is set but started value is null');
+ }
+ }
}
}
@@ -485,44 +499,73 @@ function common_linkify($url) {
// It comes in special'd, so we unspecial it before passing to the stringifying
// functions
$url = htmlspecialchars_decode($url);
- $display = File_redirection::_canonUrl($url);
+
+ $canon = File_redirection::_canonUrl($url);
+
$longurl_data = File_redirection::where($url);
if (is_array($longurl_data)) {
$longurl = $longurl_data['url'];
} elseif (is_string($longurl_data)) {
$longurl = $longurl_data;
} else {
- die('impossible to linkify');
+ throw new ServerException("Can't linkify url '$url'");
+ }
+
+ $attrs = array('href' => $canon, 'rel' => 'external');
+
+ $is_attachment = false;
+ $attachment_id = null;
+ $has_thumb = false;
+
+ // Check to see whether there's a filename associated with this URL.
+ // If there is, it's an upload and qualifies as an attachment
+
+ $localfile = File::staticGet('url', $longurl);
+
+ if (!empty($localfile)) {
+ if (isset($localfile->filename)) {
+ $is_attachment = true;
+ $attachment_id = $localfile->id;
+ }
}
- $attrs = array('href' => $longurl, 'rel' => 'external');
+ // if this URL is an attachment, then we set class='attachment' and id='attahcment-ID'
+ // where ID is the id of the attachment for the given URL.
+ //
+ // we need a better test telling what can be shown as an attachment
+ // we're currently picking up oembeds only.
+ // I think the best option is another file_view table in the db
+ // and associated dbobject.
-// if this URL is an attachment, then we set class='attachment' and id='attahcment-ID'
-// where ID is the id of the attachment for the given URL.
-//
-// we need a better test telling what can be shown as an attachment
-// we're currently picking up oembeds only.
-// I think the best option is another file_view table in the db
-// and associated dbobject.
$query = "select file_oembed.file_id as file_id from file join file_oembed on file.id = file_oembed.file_id where file.url='$longurl'";
$file = new File;
$file->query($query);
$file->fetch();
if (!empty($file->file_id)) {
+ $is_attachment = true;
+ $attachment_id = $file->file_id;
+
$query = "select file_thumbnail.file_id as file_id from file join file_thumbnail on file.id = file_thumbnail.file_id where file.url='$longurl'";
$file2 = new File;
$file2->query($query);
$file2->fetch();
- if (empty($file2->file_id)) {
- $attrs['class'] = 'attachment';
- } else {
+ if (!empty($file2)) {
+ $has_thumb = true;
+ }
+ }
+
+ // Add clippy
+ if ($is_attachment) {
+ $attrs['class'] = 'attachment';
+ if ($has_thumb) {
$attrs['class'] = 'attachment thumbnail';
}
- $attrs['id'] = "attachment-{$file->file_id}";
+ $attrs['id'] = "attachment-{$attachment_id}";
}
- return XMLStringer::estring('a', $attrs, $display);
+
+ return XMLStringer::estring('a', $attrs, $url);
}
function common_shorten_links($text)
@@ -790,7 +833,12 @@ function common_date_iso8601($dt)
function common_sql_now()
{
- return strftime('%Y-%m-%d %H:%M:%S', time());
+ return common_sql_date(time());
+}
+
+function common_sql_date($datetime)
+{
+ return strftime('%Y-%m-%d %H:%M:%S', $datetime);
}
function common_redirect($url, $code=307)
@@ -814,165 +862,45 @@ function common_redirect($url, $code=307)
function common_broadcast_notice($notice, $remote=false)
{
- if (common_config('queue', 'enabled')) {
- // Do it later!
- return common_enqueue_notice($notice);
- } else {
- return common_real_broadcast($notice, $remote);
- }
+ return common_enqueue_notice($notice);
}
// Stick the notice on the queue
function common_enqueue_notice($notice)
{
- $transports = array('omb', 'sms', 'public', 'twitter', 'facebook', 'ping');
-
- if (common_config('xmpp', 'enabled'))
- {
- $transports[] = 'jabber';
- }
+ static $localTransports = array('omb',
+ 'twitter',
+ 'facebook',
+ 'ping');
+ static $allTransports = array('sms');
- if (common_config('queue','subsystem') == 'stomp') {
- common_enqueue_notice_stomp($notice, $transports);
- }
- else {
- common_enqueue_notice_db($notice, $transports);
- }
- return $result;
-}
-
-function common_enqueue_notice_stomp($notice, $transports)
-{
- // use an external message queue system via STOMP
- require_once("Stomp.php");
+ $transports = $allTransports;
- $server = common_config('queue','stomp_server');
- $username = common_config('queue', 'stomp_username');
- $password = common_config('queue', 'stomp_password');
+ $xmpp = common_config('xmpp', 'enabled');
- $con = new Stomp($server);
-
- if (!$con->connect($username, $password)) {
- common_log(LOG_ERR, 'Failed to connect to queue server');
- return false;
+ if ($xmpp) {
+ $transports[] = 'jabber';
}
- $queue_basename = common_config('queue','queue_basename');
-
- foreach ($transports as $transport) {
- $result = $con->send('/queue/'.$queue_basename.'-'.$transport, // QUEUE
- $notice->id, // BODY of the message
- array ('created' => $notice->created));
- if (!$result) {
- common_log(LOG_ERR, 'Error sending to '.$transport.' queue');
- return false;
- }
- common_log(LOG_DEBUG, 'complete remote queueing notice ID = ' . $notice->id . ' for ' . $transport);
- }
-
- //send tags as headers, so they can be used as JMS selectors
- common_log(LOG_DEBUG, 'searching for tags ' . $notice->id);
- $tags = array();
- $tag = new Notice_tag();
- $tag->notice_id = $notice->id;
- if ($tag->find()) {
- while ($tag->fetch()) {
- common_log(LOG_DEBUG, 'tag found = ' . $tag->tag);
- array_push($tags,$tag->tag);
+ if ($notice->is_local == NOTICE_LOCAL_PUBLIC ||
+ $notice->is_local == NOTICE_LOCAL_NONPUBLIC) {
+ $transports = array_merge($transports, $localTransports);
+ if ($xmpp) {
+ $transports[] = 'public';
}
}
- $tag->free();
- $con->send('/topic/laconica.'.$notice->profile_id,
- $notice->content,
- array(
- 'profile_id' => $notice->profile_id,
- 'created' => $notice->created,
- 'tags' => implode($tags,' - ')
- )
- );
- common_log(LOG_DEBUG, 'sent to personal topic ' . $notice->id);
- $con->send('/topic/laconica.allusers',
- $notice->content,
- array(
- 'profile_id' => $notice->profile_id,
- 'created' => $notice->created,
- 'tags' => implode($tags,' - ')
- )
- );
- common_log(LOG_DEBUG, 'sent to catch-all topic ' . $notice->id);
- $result = true;
-}
+ $qm = QueueManager::get();
-function common_enqueue_notice_db($notice, $transports)
-{
- // in any other case, 'internal'
- foreach ($transports as $transport) {
- common_enqueue_notice_transport($notice, $transport);
+ foreach ($transports as $transport)
+ {
+ $qm->enqueue($notice, $transport);
}
-}
-function common_enqueue_notice_transport($notice, $transport)
-{
- $qi = new Queue_item();
- $qi->notice_id = $notice->id;
- $qi->transport = $transport;
- $qi->created = $notice->created;
- $result = $qi->insert();
- if (!$result) {
- $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
- common_log(LOG_ERR, 'DB error inserting queue item: ' . $last_error->message);
- throw new ServerException('DB error inserting queue item: ' . $last_error->message);
- }
- common_log(LOG_DEBUG, 'complete queueing notice ID = ' . $notice->id . ' for ' . $transport);
return true;
}
-function common_real_broadcast($notice, $remote=false)
-{
- $success = true;
- if (!$remote) {
- // Make sure we have the OMB stuff
- require_once(INSTALLDIR.'/lib/omb.php');
- $success = omb_broadcast_remote_subscribers($notice);
- if (!$success) {
- common_log(LOG_ERR, 'Error in OMB broadcast for notice ' . $notice->id);
- }
- }
- if ($success) {
- require_once(INSTALLDIR.'/lib/jabber.php');
- $success = jabber_broadcast_notice($notice);
- if (!$success) {
- common_log(LOG_ERR, 'Error in jabber broadcast for notice ' . $notice->id);
- }
- }
- if ($success) {
- require_once(INSTALLDIR.'/lib/mail.php');
- $success = mail_broadcast_notice_sms($notice);
- if (!$success) {
- common_log(LOG_ERR, 'Error in sms broadcast for notice ' . $notice->id);
- }
- }
- if ($success) {
- $success = jabber_public_notice($notice);
- if (!$success) {
- common_log(LOG_ERR, 'Error in public broadcast for notice ' . $notice->id);
- }
- }
- if ($success) {
- $success = broadcast_twitter($notice);
- if (!$success) {
- common_log(LOG_ERR, 'Error in Twitter broadcast for notice ' . $notice->id);
- }
- }
-
- // XXX: Do a real-time FB broadcast here?
-
- // XXX: broadcast notices to other IM
- return $success;
-}
-
function common_broadcast_profile($profile)
{
// XXX: optionally use a queue system like http://code.google.com/p/microapps/wiki/NQDQ
@@ -1050,20 +978,26 @@ function common_ensure_syslog()
{
static $initialized = false;
if (!$initialized) {
- openlog(common_config('syslog', 'appname'), 0, LOG_USER);
+ openlog(common_config('syslog', 'appname'), 0,
+ common_config('syslog', 'facility'));
$initialized = true;
}
}
+function common_log_line($priority, $msg)
+{
+ static $syslog_priorities = array('LOG_EMERG', 'LOG_ALERT', 'LOG_CRIT', 'LOG_ERR',
+ 'LOG_WARNING', 'LOG_NOTICE', 'LOG_INFO', 'LOG_DEBUG');
+ return date('Y-m-d H:i:s') . ' ' . $syslog_priorities[$priority] . ': ' . $msg . "\n";
+}
+
function common_log($priority, $msg, $filename=null)
{
$logfile = common_config('site', 'logfile');
if ($logfile) {
$log = fopen($logfile, "a");
if ($log) {
- static $syslog_priorities = array('LOG_EMERG', 'LOG_ALERT', 'LOG_CRIT', 'LOG_ERR',
- 'LOG_WARNING', 'LOG_NOTICE', 'LOG_INFO', 'LOG_DEBUG');
- $output = date('Y-m-d H:i:s') . ' ' . $syslog_priorities[$priority] . ': ' . $msg . "\n";
+ $output = common_log_line($priority, $msg);
fwrite($log, $output);
fclose($log);
}
@@ -1094,6 +1028,9 @@ function common_log_objstring(&$object)
if (is_null($object)) {
return "null";
}
+ if (!($object instanceof DB_DataObject)) {
+ return "(unknown)";
+ }
$arr = $object->toArray();
$fields = array();
foreach ($arr as $k => $v) {
@@ -1294,18 +1231,39 @@ function common_canonical_sms($sms)
function common_error_handler($errno, $errstr, $errfile, $errline, $errcontext)
{
switch ($errno) {
+
+ case E_ERROR:
+ case E_COMPILE_ERROR:
+ case E_CORE_ERROR:
case E_USER_ERROR:
- common_log(LOG_ERR, "[$errno] $errstr ($errfile:$errline)");
- exit(1);
+ case E_PARSE:
+ case E_RECOVERABLE_ERROR:
+ common_log(LOG_ERR, "[$errno] $errstr ($errfile:$errline) [ABORT]");
+ die();
break;
+ case E_WARNING:
+ case E_COMPILE_WARNING:
+ case E_CORE_WARNING:
case E_USER_WARNING:
common_log(LOG_WARNING, "[$errno] $errstr ($errfile:$errline)");
break;
+ case E_NOTICE:
case E_USER_NOTICE:
common_log(LOG_NOTICE, "[$errno] $errstr ($errfile:$errline)");
break;
+
+ case E_STRICT:
+ case E_DEPRECATED:
+ case E_USER_DEPRECATED:
+ // XXX: config variable to log this stuff, too
+ break;
+
+ default:
+ common_log(LOG_ERR, "[$errno] $errstr ($errfile:$errline) [UNKNOWN LEVEL, die()'ing]");
+ die();
+ break;
}
// FIXME: show error page if we're on the Web
@@ -1443,4 +1401,28 @@ function common_shorten_url($long_url)
curl_close($curlh);
return $short_url;
-} \ No newline at end of file
+}
+
+function common_client_ip()
+{
+ if (!isset($_SERVER) || !array_key_exists('REQUEST_METHOD', $_SERVER)) {
+ return null;
+ }
+
+ if ($_SERVER['HTTP_X_FORWARDED_FOR']) {
+ if ($_SERVER['HTTP_CLIENT_IP']) {
+ $proxy = $_SERVER['HTTP_CLIENT_IP'];
+ } else {
+ $proxy = $_SERVER['REMOTE_ADDR'];
+ }
+ $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
+ } else {
+ if ($_SERVER['HTTP_CLIENT_IP']) {
+ $ip = $_SERVER['HTTP_CLIENT_IP'];
+ } else {
+ $ip = $_SERVER['REMOTE_ADDR'];
+ }
+ }
+
+ return array($ip, $proxy);
+}
diff --git a/lib/xmppqueuehandler.php b/lib/xmppqueuehandler.php
index 986e09c25..c8b5ad1fb 100644
--- a/lib/xmppqueuehandler.php
+++ b/lib/xmppqueuehandler.php
@@ -91,7 +91,7 @@ class XmppQueueHandler extends QueueHandler
if (common_config('xmpp', 'listener')) {
return common_config('xmpp', 'listener');
} else {
- return jabber_daemon_address() . '/' . common_config('xmpp','resource') . '-listener';
+ return jabber_daemon_address() . '/' . common_config('xmpp','resource') . 'daemon';
}
}
}