diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Shorturl_api.php | 2 | ||||
-rw-r--r-- | lib/action.php | 41 | ||||
-rw-r--r-- | lib/common.php | 9 | ||||
-rw-r--r-- | lib/currentuserdesignaction.php | 12 | ||||
-rw-r--r-- | lib/daemon.php | 21 | ||||
-rw-r--r-- | lib/dbqueuemanager.php | 166 | ||||
-rw-r--r-- | lib/designsettings.php | 111 | ||||
-rw-r--r-- | lib/facebookaction.php | 14 | ||||
-rw-r--r-- | lib/groupdesignaction.php | 16 | ||||
-rw-r--r-- | lib/groupeditform.php | 28 | ||||
-rw-r--r-- | lib/jsonsearchresultslist.php | 1 | ||||
-rw-r--r-- | lib/language.php | 2 | ||||
-rw-r--r-- | lib/noticelist.php | 2 | ||||
-rw-r--r-- | lib/ownerdesignaction.php | 12 | ||||
-rw-r--r-- | lib/peoplesearchresults.php | 80 | ||||
-rw-r--r-- | lib/profileaction.php | 8 | ||||
-rw-r--r-- | lib/profilelist.php | 9 | ||||
-rw-r--r-- | lib/profileminilist.php | 18 | ||||
-rw-r--r-- | lib/queuehandler.php | 135 | ||||
-rw-r--r-- | lib/queuemanager.php | 74 | ||||
-rw-r--r-- | lib/router.php | 8 | ||||
-rw-r--r-- | lib/rssaction.php | 2 | ||||
-rw-r--r-- | lib/stompqueuemanager.php | 129 | ||||
-rw-r--r-- | lib/subgroupnav.php | 2 | ||||
-rw-r--r-- | lib/twitterapi.php | 61 | ||||
-rw-r--r-- | lib/unqueuemanager.php | 85 | ||||
-rw-r--r-- | lib/util.php | 308 | ||||
-rw-r--r-- | lib/xmppqueuehandler.php | 2 |
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'; } } } |