From 32f81b3c0e7be3da35d5813e5409fa3b65dc220e Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 24 Jan 2009 18:38:12 +0100 Subject: Initial support for ping service It makes sense to use the weblogs.com ping service to alert people to changes on the site. So, we do. Includes an extra ping queue handler. --- lib/common.php | 2 ++ lib/ping.php | 79 ++++++++++++++++++++++++++++++++++++++++++++ lib/util.php | 2 +- scripts/pingqueuehandler.php | 64 +++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 lib/ping.php create mode 100644 scripts/pingqueuehandler.php diff --git a/lib/common.php b/lib/common.php index a2f9b9bfe..cc82f8bd7 100644 --- a/lib/common.php +++ b/lib/common.php @@ -128,6 +128,8 @@ $config = array('enabled' => false, 'server' => 'localhost', 'port' => 11211), + 'ping' => + array('notify' => array()), 'inboxes' => array('enabled' => true), # on by default for new sites ); diff --git a/lib/ping.php b/lib/ping.php new file mode 100644 index 000000000..32c0b9806 --- /dev/null +++ b/lib/ping.php @@ -0,0 +1,79 @@ +. + */ + +if (!defined('LACONICA')) { exit(1); } + +function ping_broadcast_notice($notice) { + if (!$notice->is_local) { + return; + } + + # Array of servers, URL => type + $notify = common_config('ping', 'notify'); + $profile = $notice->getProfile(); + $tags = ping_notice_tags($notice); + + foreach ($notify as $notify_url => $type) { + switch ($type) { + case 'xmlrpc': + case 'extended': + $req = xmlrpc_encode_request('weblogUpdates.ping', + array($profile->nickname, # site name + common_local_url('showstream', + array('nickname' => $profile->nickname)), + common_local_url('shownotice', + array('notice' => $notice->id)), + common_local_url('userrss', + array('nickname' => $profile->nickname)), + $tags)); + + # We re-use this tool's fetcher, since it's pretty good + + $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); + + if (!$fetcher) { + common_log(LOG_WARNING, 'Failed to initialize Yadis fetcher.', __FILE__); + return false; + } + + $result = $fetcher->post($notify_url, + $req); + + case 'get': + case 'post': + default: + common_log(LOG_WARNING, 'Unknown notify type for ' . $notify_url . ': ' . $type); + } + } +} + +function ping_notice_tags($notice) { + $tag = new Notice_tag(); + $tag->notice_id = $notice->id; + $tags = array(); + if ($tag->find()) { + while ($tag->fetch()) { + $tags[] = $tag->tag; + } + $tag->free(); + unset($tag); + return implode('|', $tags); + } + return NULL; +} \ No newline at end of file diff --git a/lib/util.php b/lib/util.php index b5b194519..419e21e82 100644 --- a/lib/util.php +++ b/lib/util.php @@ -1124,7 +1124,7 @@ function common_twitter_broadcast($notice, $flink) function common_enqueue_notice($notice) { - foreach (array('jabber', 'omb', 'sms', 'public') as $transport) { + foreach (array('jabber', 'omb', 'sms', 'public', 'ping') as $transport) { $qi = new Queue_item(); $qi->notice_id = $notice->id; $qi->transport = $transport; diff --git a/scripts/pingqueuehandler.php b/scripts/pingqueuehandler.php new file mode 100644 index 000000000..55a266e4a --- /dev/null +++ b/scripts/pingqueuehandler.php @@ -0,0 +1,64 @@ +#!/usr/bin/env php +. + */ + +# Abort if called from a web server +if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { + print "This script must be run from the command line\n"; + exit(); +} + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); +define('LACONICA', true); + +require_once(INSTALLDIR . '/lib/common.php'); +require_once(INSTALLDIR . '/lib/ping.php'); +require_once(INSTALLDIR . '/lib/queuehandler.php'); + +set_error_handler('common_error_handler'); + +class PingQueueHandler extends QueueHandler { + + function transport() { + return 'ping'; + } + + function start() { + $this->log(LOG_INFO, "INITIALIZE"); + return true; + } + + function handle_notice($notice) { + return ping_broadcast_notice($notice); + } + + function finish() { + } +} + +ini_set("max_execution_time", "0"); +ini_set("max_input_time", "0"); +set_time_limit(0); +mb_internal_encoding('UTF-8'); + +$id = ($argc > 1) ? $argv[1] : NULL; + +$handler = new PingQueueHandler($id); + +$handler->runOnce(); -- cgit v1.2.3-54-g00ecf From 83b084e4ce2dea27c5f0ab3edfe2436fcf71e230 Mon Sep 17 00:00:00 2001 From: "ken.sedgwick" Date: Sat, 28 Feb 2009 17:33:20 -0800 Subject: Added RPM spec file. --- scripts/laconica.spec | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 scripts/laconica.spec diff --git a/scripts/laconica.spec b/scripts/laconica.spec new file mode 100644 index 000000000..2e65b4549 --- /dev/null +++ b/scripts/laconica.spec @@ -0,0 +1,81 @@ +BuildRequires: php-pear +BuildRequires: httpd-devel + +Name: laconica +Version: 0.7.1 +Release: 1%{?dist} +License: GAGPL v3 or later +Source: laconica-0.7.1.tar.bz2 +Group: Applications/Internet +Summary: Laconica, the Open Source microblogging platform +BuildArch: noarch + +Requires: httpd +Requires: php >= 5 +Requires: php-pear-Mail-Mime +Requires: php-curl +Requires: php-mysql +Requires: php-mbstring +Requires: php-gettext + +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +%define apache_serverroot %(/usr/sbin/apxs -q DATADIR) +%define apache_sysconfdir %(/usr/sbin/apxs -q SYSCONFDIR) +%define wwwpath %{apache_serverroot}/%{name} +%define confpath %{_sysconfdir}/%{name} + +%description +From the ABOUT file: Laconica (pronounced "luh-KAWN-ih-kuh") is a Free +and Open Source microblogging platform. It helps people in a +community, company or group to exchange short (140 character) messages +over the Web. Users can choose which people to "follow" and receive +only their friends' or colleagues' status messages. It provides a +similar service to sites like Twitter, Jaiku, and Plurk. + + +%prep +%setup -q + +%build + + +%install +mkdir -p %{buildroot}%{wwwpath} +cp -a * %{buildroot}%{wwwpath} + +mkdir -p %{buildroot}%{_datadir}/laconica +cp -a db %{buildroot}%{_datadir}/laconica/db + +mkdir -p %{buildroot}%{_sysconfdir}/httpd/conf.d +cat > %{buildroot}%{_sysconfdir}/httpd/conf.d/laconica.conf <<"EOF" +Alias /laconica/ "/var/www/laconica/" + + + Options Indexes FollowSymLinks + AllowOverride All + Order allow,deny + Allow from all + +EOF + +%clean +rm -rf %buildroot + +%files +%defattr(-,root,root) +%dir %{wwwpath} +%{wwwpath}/* +%{_datadir}/laconica/* +%doc COPYING README doc-src/* +%config(noreplace) %{_sysconfdir}/httpd/conf.d/laconica.conf + +%changelog +* Sat Feb 28 2009 Ken Sedgwick - 0.7.1-1 +- Modified RPM for Fedora. + +* Thu Feb 13 2009 tuukka.pasanen@ilmi.fi +- packaged laconica version 0.7.1 + +* Wed Feb 04 2009 tuukka.pasanen@ilmi.fi +- packaged laconica version 0.7.0 using the buildservice spec file wizard -- cgit v1.2.3-54-g00ecf From 3bf485ec95f3da56c779671b7a26f36085d93099 Mon Sep 17 00:00:00 2001 From: "ken.sedgwick" Date: Sat, 28 Feb 2009 17:35:36 -0800 Subject: Switched tarball ext ".bz2" -> ".gz" in spec file. --- scripts/laconica.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/laconica.spec b/scripts/laconica.spec index 2e65b4549..f3fa61904 100644 --- a/scripts/laconica.spec +++ b/scripts/laconica.spec @@ -5,7 +5,7 @@ Name: laconica Version: 0.7.1 Release: 1%{?dist} License: GAGPL v3 or later -Source: laconica-0.7.1.tar.bz2 +Source: laconica-0.7.1.tar.gz Group: Applications/Internet Summary: Laconica, the Open Source microblogging platform BuildArch: noarch -- cgit v1.2.3-54-g00ecf From 7fa6bb07d859d917080c6b110084ddedd5a772e8 Mon Sep 17 00:00:00 2001 From: "ken.sedgwick" Date: Sat, 28 Feb 2009 20:13:37 -0800 Subject: Added some missing php dependencies. Made the avatar directory apache owned. --- scripts/laconica.spec | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/laconica.spec b/scripts/laconica.spec index f3fa61904..2df41c932 100644 --- a/scripts/laconica.spec +++ b/scripts/laconica.spec @@ -17,6 +17,8 @@ Requires: php-curl Requires: php-mysql Requires: php-mbstring Requires: php-gettext +Requires: php-xml +Requires: php-gd BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -67,6 +69,7 @@ rm -rf %buildroot %dir %{wwwpath} %{wwwpath}/* %{_datadir}/laconica/* +%attr(-,apache,apache) %dir %{_datadir}/laconica/avatar %doc COPYING README doc-src/* %config(noreplace) %{_sysconfdir}/httpd/conf.d/laconica.conf -- cgit v1.2.3-54-g00ecf From 8b9e559167e27c490648cc55c4f617c2e9122a18 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 2 Mar 2009 00:06:29 +0000 Subject: Updated typography and layout --- theme/base/css/mobile.css | 160 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 119 insertions(+), 41 deletions(-) diff --git a/theme/base/css/mobile.css b/theme/base/css/mobile.css index 3d0455a67..eee98317c 100644 --- a/theme/base/css/mobile.css +++ b/theme/base/css/mobile.css @@ -2,14 +2,21 @@ * * @package Laconica * @author Meitar Moscovitz + * @author Sarven Capadisli * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://laconi.ca/ */ -/* Go linear. */ +body { +font-size:2.5em; +} + +#wrap { +width:95%; +} + #header, #header address, -#site_nav_global_primary, #anon_notice, #site_nav_local_views .nav, #form_notice, @@ -24,49 +31,120 @@ .notice .notice-options a, .pagination, .pagination .nav, -.aside .section { float: none; } +.aside .section { +float:none; +} .notice-options .notice_reply, .notice-options .notice_delete, .notice-options .form_favor, -.notice-options .form_disfavor { position: static; } +.notice-options .form_disfavor { +position:static; +} #form_notice, #anon_notice, -#content_inner, -#footer { width: auto; } - -/* And liquid. */ -#wrap { width: 95%; } - -/* Make things bigger on smaller screens. */ -body { font-size: 2em; } -.notices { font-size: 1.5em; } - -#site_nav_global_primary, #site_nav_global_secondary { text-align: center; } - -.notice div.entry-content { margin-left: 0; } -address { margin: 0; } - -#anon_notice, #footer { clear: left; font-size: .5em; } - -#form_notice textarea { width: 80%; height: 5em; } -#form_notice .form_note { right: 20%; top: 6em; } -#form_notice .form_actions input.submit { width: auto; } - -#content { padding: 18px 0; width: 100%; } -#content h1, #page_notice, #content_inner { padding: 0 18px; } -.notices .entry-title, .notices div.entry-content { width: 90%; } -.notice .author .photo { height: 4.5em; width: 4.5em; } /* about double physical size; TODO: do this scaling better */ -.notice-options { position: absolute; top: 0; right: 0; padding-left: 7%; width: 3%; } -.notice-options .notice_delete a { float: left; } /* Works, but feels like it shouldn't. */ -/* TODO: Make the icons of the notice options bigger. Probably with mobile-specific images. */ -.pagination .nav { overflow: auto; } - -#aside_primary { margin: 10px 0 0 0; border: none; padding: 0; width: 100%; } -#popular_notices { float: none; width: auto; } -/* Columns for supplemental info. */ -.aside .section { clear: none; padding: 9px; width: 45%; } -#top_groups_by_post { float: left; } -#featured_users { float: right; } -#export_data { display: none; } +#footer, +#form_notice .form_actions input.submit { +width:auto; +} + +.form_settings label { +width:25%; +} +.form_settings .form_data p.form_guide { +margin-left:26%; +} + +#site_nav_global_primary { +width:75%; +} + +.entity_profile { +width:65%; +} +.entity_actions { +margin-left:0; +} + +#form_notice, +#anon_notice { +clear:both; +} + +#content, +#aside_primary { +width:96%; +padding-left:2%; +padding-right:2%; +} + +#site_notice { +position:static; +float:right; +clear:right; +width:75%; +margin-right:0; +margin-bottom:11px; +} + +.notices { +font-size:1.5em; +} + +#form_notice textarea { +width:80%; +height:5em; +} +#form_notice .form_note { +right:20%; +top:6em; +} + + +.vcard .photo, +.section .vcard .photo { +margin-right:18px; +} +.notice, +.profile { +margin-bottom:18px; +} + +.notices .entry-title, +.notices div.entry-content { +width:90%; +} +.notice div.entry-content { +margin-left:0; +} + +.notice .author .photo { +height:4.5em; +width:4.5em; +} +.notice-options { +position:absolute; +top:0; +right:0; +padding-left:7%; +width:3%; +} + +.notice-options .notice_delete a { +float:left; +} +.pagination .nav { +overflow:auto; +} + +#export_data { +display:none; +} + +#site_nav_local_views li { +margin-right:4px; +} +#site_nav_local_views a { +padding:18px 11px; +} -- cgit v1.2.3-54-g00ecf From 8408e82f928d1b159a6c9f2dd8fabd269590be61 Mon Sep 17 00:00:00 2001 From: CiaranG Date: Tue, 3 Mar 2009 20:18:26 +0000 Subject: PostgreSQL - added new emailnotifyattn field to user table, to match MySQL version --- db/laconica_pg.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/db/laconica_pg.sql b/db/laconica_pg.sql index 9882d091a..cacdfa97e 100644 --- a/db/laconica_pg.sql +++ b/db/laconica_pg.sql @@ -50,6 +50,7 @@ create table "user" ( emailnotifyfav integer default 1 /* comment 'Notify by email of favorites' */, emailnotifynudge integer default 1 /* comment 'Notify by email of nudges' */, emailnotifymsg integer default 1 /* comment 'Notify by email of direct messages' */, + emailnotifyattn integer default 1 /* command 'Notify by email of @-replies' */, emailmicroid integer default 1 /* comment 'whether to publish email microid' */, language varchar(50) /* comment 'preferred language' */, timezone varchar(50) /* comment 'timezone' */, -- cgit v1.2.3-54-g00ecf From 3fd877c4cecd80fbe65043dd4612d9688e16ecd8 Mon Sep 17 00:00:00 2001 From: CiaranG Date: Tue, 3 Mar 2009 21:32:47 +0000 Subject: Use single quotes for data literals on inserts to notice_index, so it works on pgsql as well as mysql --- classes/Notice.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/Notice.php b/classes/Notice.php index 8300667fa..907239b08 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -585,7 +585,7 @@ class Notice extends Memcached_DataObject $inbox = new Notice_inbox(); $UT = common_config('db','type')=='pgsql'?'"user"':'user'; $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created) ' . - "SELECT $UT.id, " . $this->id . ', "' . $this->created . '" ' . + "SELECT $UT.id, " . $this->id . ", '" . $this->created . "' " . "FROM $UT JOIN subscription ON $UT.id = subscription.subscriber " . 'WHERE subscription.subscribed = ' . $this->profile_id . ' ' . 'AND NOT EXISTS (SELECT user_id, notice_id ' . @@ -655,7 +655,7 @@ class Notice extends Memcached_DataObject $inbox = new Notice_inbox(); $UT = common_config('db','type')=='pgsql'?'"user"':'user'; $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created, source) ' . - "SELECT $UT.id, " . $this->id . ', "' . $this->created . '", 2 ' . + "SELECT $UT.id, " . $this->id . ", '" . $this->created . "', 2 " . "FROM $UT JOIN group_member ON $UT.id = group_member.profile_id " . 'WHERE group_member.group_id = ' . $group->id . ' ' . 'AND NOT EXISTS (SELECT user_id, notice_id ' . -- cgit v1.2.3-54-g00ecf From 35677336de4c01c4f6b02840222075c6ac963988 Mon Sep 17 00:00:00 2001 From: CiaranG Date: Tue, 3 Mar 2009 21:33:52 +0000 Subject: Catch bad replyto IDs before saving a new notice to avoid a constraint violation. This happens, for example, when posting for the first time on a fresh install --- actions/newnotice.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/actions/newnotice.php b/actions/newnotice.php index 9f44d2516..cbd04c58b 100644 --- a/actions/newnotice.php +++ b/actions/newnotice.php @@ -152,6 +152,11 @@ class NewnoticeAction extends Action } $replyto = $this->trimmed('inreplyto'); + #If an ID of 0 is wrongly passed here, it will cause a database error, + #so override it... + if ($replyto == 0) { + $replyto = 'false'; + } $notice = Notice::saveNew($user->id, $content, 'web', 1, ($replyto == 'false') ? null : $replyto); -- cgit v1.2.3-54-g00ecf From 7279554681da728deb74a87230de3a1021182f71 Mon Sep 17 00:00:00 2001 From: CiaranG Date: Wed, 4 Mar 2009 00:23:34 +0000 Subject: Additional (optional, defaults to off) logging of PEAR error details, which allows database issues to be more easily diagnosed. --- config.php.sample | 3 +++ index.php | 6 +++++- lib/common.php | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/config.php.sample b/config.php.sample index 6e55eaffc..a6cada77a 100644 --- a/config.php.sample +++ b/config.php.sample @@ -34,6 +34,9 @@ $config['site']['path'] = 'laconica'; # If you want logging sent to a file instead of syslog #$config['site']['logfile'] = '/tmp/laconica.log'; +# Enables extra log information, for example full details of PEAR DB errors +#$config['site']['logdebug'] = true; + # This is a PEAR DB DSN, see http://pear.php.net/manual/en/package.database.db.intro-dsn.php # Set it to match your actual database diff --git a/index.php b/index.php index 914ba5bde..03c044415 100644 --- a/index.php +++ b/index.php @@ -43,7 +43,11 @@ function handleError($error) return; } - common_log(LOG_ERR, "PEAR error: " . $error->getMessage()); + $logmsg = "PEAR error: " . $error->getMessage(); + if(common_config('site', 'logdebug')) { + $logmsg .= " : ". $error->getDebugInfo(); + } + common_log(LOG_ERR, $logmsg); $msg = sprintf(_('The database for %s isn\'t responding correctly, '. 'so the site won\'t work properly. '. 'The site admins probably know about the problem, '. diff --git a/lib/common.php b/lib/common.php index 0fff3af2e..3df68d98a 100644 --- a/lib/common.php +++ b/lib/common.php @@ -73,6 +73,7 @@ $config = 'theme' => 'default', 'path' => $_path, 'logfile' => null, + 'logdebug' => false, 'fancy' => false, 'locale_path' => INSTALLDIR.'/locale', 'language' => 'en_US', -- cgit v1.2.3-54-g00ecf From dcdf47cff2dc62996df042aa0e5d19948b265686 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 4 Mar 2009 05:11:18 +0000 Subject: Avatar crop fix for shrinking/stretching of images when user doesn't scale it with the Jcrop tool (but hits Crop). This will use the lower value from width/height. --- actions/avatarsettings.php | 8 +++++--- js/jcrop/jquery.Jcrop.go.js | 7 ------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/actions/avatarsettings.php b/actions/avatarsettings.php index 6545d9489..c2bb35a39 100644 --- a/actions/avatarsettings.php +++ b/actions/avatarsettings.php @@ -324,11 +324,13 @@ class AvatarsettingsAction extends AccountSettingsAction return; } - // If image is not being cropped assume pos & dimensions of original. + $file_d = ($filedata['width'] > $filedata['height']) + ? $filedata['height'] : $filedata['width']; + $dest_x = $this->arg('avatar_crop_x') ? $this->arg('avatar_crop_x'):0; $dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0; - $dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$filedata['width']; - $dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$filedata['height']; + $dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$file_d; + $dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$file_d; $size = min($dest_w, $dest_h, MAX_ORIGINAL); $user = common_current_user(); diff --git a/js/jcrop/jquery.Jcrop.go.js b/js/jcrop/jquery.Jcrop.go.js index b2737407b..a0399d540 100644 --- a/js/jcrop/jquery.Jcrop.go.js +++ b/js/jcrop/jquery.Jcrop.go.js @@ -37,10 +37,3 @@ $('#avatar_crop_w').val(c.w); $('#avatar_crop_h').val(c.h); }; - - function checkCoords() { - if (parseInt($('#avatar_crop_w').val())) return true; - alert('Please select a crop region then press submit.'); - return false; - }; - -- cgit v1.2.3-54-g00ecf From 78a715bc37041a852d9e0eb6dc3e3dc0a0b9e279 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 4 Mar 2009 05:23:41 +0000 Subject: Hooks for: Public group nav in order to place a list-anchor item at the start or end of the list. --- EVENTS.txt | 7 ++++++- lib/publicgroupnav.php | 28 ++++++++++++++++------------ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/EVENTS.txt b/EVENTS.txt index ed461ee9f..2f33b2d5d 100644 --- a/EVENTS.txt +++ b/EVENTS.txt @@ -88,9 +88,14 @@ StartShowLocalNavBlock: Showing the local nav menu EndShowLocalNavBlock: At the end of the local nav menu - $action: the current action -StartShowHTML: Chance to set document content type, charset, language, DOCTYPE and html element properties +StartShowHTML: Chance to set document headers (e.g., content type, charset, language), DOCTYPE and html element properties - $action: the current action EndShowHTML: Showing after the html element - $action: the current action +StartPublicGroupNav: Showing the public group nav menu +- $action: the current action + +EndPublicGroupNav: At the end of the public group nav menu +- $action: the current action diff --git a/lib/publicgroupnav.php b/lib/publicgroupnav.php index d72475e20..485d25e20 100644 --- a/lib/publicgroupnav.php +++ b/lib/publicgroupnav.php @@ -39,6 +39,7 @@ require_once INSTALLDIR.'/lib/widget.php'; * @category Output * @package Laconica * @author Evan Prodromou + * @author Sarven Capadisli * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://laconi.ca/ * @@ -73,23 +74,26 @@ class PublicGroupNav extends Widget $this->action->elementStart('ul', array('class' => 'nav')); - $this->out->menuItem(common_local_url('public'), _('Public'), - _('Public timeline'), $action_name == 'public', 'nav_timeline_public'); + if (Event::handle('StartPublicGroupNav', array($this))) { + $this->out->menuItem(common_local_url('public'), _('Public'), + _('Public timeline'), $action_name == 'public', 'nav_timeline_public'); - $this->out->menuItem(common_local_url('groups'), _('Groups'), - _('User groups'), $action_name == 'groups', 'nav_groups'); + $this->out->menuItem(common_local_url('groups'), _('Groups'), + _('User groups'), $action_name == 'groups', 'nav_groups'); - $this->out->menuItem(common_local_url('publictagcloud'), _('Recent tags'), - _('Recent tags'), $action_name == 'publictagcloud', 'nav_recent-tags'); + $this->out->menuItem(common_local_url('publictagcloud'), _('Recent tags'), + _('Recent tags'), $action_name == 'publictagcloud', 'nav_recent-tags'); - if (count(common_config('nickname', 'featured')) > 0) { - $this->out->menuItem(common_local_url('featured'), _('Featured'), - _('Featured users'), $action_name == 'featured', 'nav_featured'); - } + if (count(common_config('nickname', 'featured')) > 0) { + $this->out->menuItem(common_local_url('featured'), _('Featured'), + _('Featured users'), $action_name == 'featured', 'nav_featured'); + } - $this->out->menuItem(common_local_url('favorited'), _('Popular'), - _("Popular notices"), $action_name == 'favorited', 'nav_timeline_favorited'); + $this->out->menuItem(common_local_url('favorited'), _('Popular'), + _("Popular notices"), $action_name == 'favorited', 'nav_timeline_favorited'); + Event::handle('EndPublicGroupNav', array($this)); + } $this->action->elementEnd('ul'); } } -- cgit v1.2.3-54-g00ecf From c0115bf3bc8957fd2db2dd135ad885ed1b8d6158 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 4 Mar 2009 05:29:21 -0800 Subject: fix pagination links with new URL mapper --- lib/action.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/action.php b/lib/action.php index 9c71a153d..812df635e 100644 --- a/lib/action.php +++ b/lib/action.php @@ -976,17 +976,17 @@ class Action extends HTMLOutputter // lawsuit } if ($have_before) { $pargs = array('page' => $page-1); - $newargs = $args ? array_merge($args, $pargs) : $pargs; $this->elementStart('li', array('class' => 'nav_prev')); - $this->element('a', array('href' => common_local_url($action, $newargs), 'rel' => 'prev'), + $this->element('a', array('href' => common_local_url($action, $args, $pargs), + 'rel' => 'prev'), _('After')); $this->elementEnd('li'); } if ($have_after) { $pargs = array('page' => $page+1); - $newargs = $args ? array_merge($args, $pargs) : $pargs; $this->elementStart('li', array('class' => 'nav_next')); - $this->element('a', array('href' => common_local_url($action, $newargs), 'rel' => 'next'), + $this->element('a', array('href' => common_local_url($action, $args, $pargs), + 'rel' => 'next'), _('Before')); $this->elementEnd('li'); } -- cgit v1.2.3-54-g00ecf From 18f7749995c2fb4281839df8f8aa2b0a0c545260 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 4 Mar 2009 05:42:03 -0800 Subject: run sms carrier script on install --- install.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/install.php b/install.php index 18fc362b6..3d76dace1 100644 --- a/install.php +++ b/install.php @@ -152,6 +152,13 @@ function handlePost() showForm(); return; } + updateStatus("Adding SMS carrier data to database..."); + $res = runDbScript(INSTALLDIR.'/db/sms_carrier.sql', $conn); + if ($res === false) { + updateStatus("Can't run SMS carrier script.", true); + showForm(); + return; + } updateStatus("Writing config file..."); $sqlUrl = "mysqli://$username:$password@$host/$database"; $res = writeConf($sitename, $sqlUrl); -- cgit v1.2.3-54-g00ecf From 6070d98aa6ed53cfafa9f4bcc6ecd24977b8866a Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 1 Mar 2009 10:12:16 -0800 Subject: check posted parameters --- install.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/install.php b/install.php index 3d76dace1..29d909417 100644 --- a/install.php +++ b/install.php @@ -130,6 +130,36 @@ function handlePost() $password = $_POST['password']; $sitename = $_POST['sitename']; + if (empty($host)) { + updateStatus("No hostname specified.", true); + showForm(); + return; + } + + if (empty($database)) { + updateStatus("No database specified.", true); + showForm(); + return; + } + + if (empty($username)) { + updateStatus("No username specified.", true); + showForm(); + return; + } + + if (empty($password)) { + updateStatus("No password specified.", true); + showForm(); + return; + } + + if (empty($sitename)) { + updateStatus("No sitename specified.", true); + showForm(); + return; + } + updateStatus("Starting installation..."); updateStatus("Checking database..."); $conn = mysql_connect($host, $username, $password); -- cgit v1.2.3-54-g00ecf From f53b6470cf6a8da02d44c85ce30214414d866646 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 2 Mar 2009 18:25:10 -0800 Subject: add notice source to default install --- db/notice_source.sql | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 db/notice_source.sql diff --git a/db/notice_source.sql b/db/notice_source.sql new file mode 100644 index 000000000..ea04862e0 --- /dev/null +++ b/db/notice_source.sql @@ -0,0 +1,46 @@ +INSERT INTO notice_source + (code, name, url, created) +VALUES + ('Do','Gnome Do','http://do.davebsd.com/wiki/index.php?title=Microblog_Plugin', now()), + ('Facebook','Facebook','http://apps.facebook.com/identica/', now()), + ('Gwibber','Gwibber','http://launchpad.net/gwibber', now()), + ('HelloTxt','HelloTxt','http://hellotxt.com/', now()), + ('IdentiFox','IdentiFox','http://www.bitbucket.org/uncryptic/identifox/', now()), + ('LaTwit','LaTwit','http://latwit.mac65.com/', now()), + ('Nambu','Nambu','http://www.nambu.com/', now()), + ('Pikchur','Pikchur','http://www.pikchur.com/', now()), + ('Ping.fm','Ping.fm','http://ping.fm/', now()), + ('Twidge','Twidge','http://software.complete.org/twidge', now()), + ('Updating.Me','Updating.Me','http://updating.me/', now()), + ('betwittered','BeTwittered','http://www.32hours.com/betwitteredinfo/', now()), + ('bti','bti','http://gregkh.github.com/bti/', now()), + ('deskbar','Deskbar-Applet','http://www.gnome.org/projects/deskbar-applet/', now()), + ('identicatools','Laconica Tools','http://bitbucketlabs.net/laconica-tools/', now()), + ('identichat','identichat','http://identichat.prosody.im/', now()), + ('identitwitch','IdentiTwitch','http://richfish.org/identitwitch/', now()), + ('mbpidgin','mbpidgin','http://code.google.com/p/microblog-purple/', now()), + ('moconica','Moconica','http://moconica.com/', now()), + ('pocketwit','PockeTwit','http://code.google.com/p/pocketwit/', now()), + ('posty','Posty','http://spreadingfunkyness.com/posty/', now()), + ('royalewithcheese','Royale With Cheese','http://p.hellyeah.org/', now()), + ('rssdent','rssdent','http://github.com/zcopley/rssdent/tree/master', now()), + ('rygh.no','rygh.no','http://rygh.no/', now()), + ('ryghsms','ryghsms','http://sms.rygh.no/', now()), + ('smob','SMOB','http://smob.sioc-project.org/', now()), + ('spaz','Spaz','http://funkatron.com/spaz', now()), + ('tarpipe','tarpipe','http://tarpipe.com/', now()), + ('tjunar','Tjunar','http://nederflash.nl/boek/titels/tjunar-air', now()), + ('tr.im','tr.im','http://tr.im/', now()), + ('tweenky','Tweenky','http://beta.tweenky.com/', now()), + ('twhirl','Twhirl','http://www.twhirl.org/', now()), + ('twibble','twibble','http://www.twibble.de/', now()), + ('twidge','Twidge','http://software.complete.org/twidge', now()), + ('twidroid','twidroid','http://www.twidroid.com/', now()), + ('twittelator','Twittelator','http://www.stone.com/iPhone/Twittelator/', now()), + ('twitterfeed','twitterfeed','http://twitterfeed.com/', now()), + ('twitterphoto','TwitterPhoto','http://richfish.org/twitterphoto/', now()), + ('twitterpm','Net::Twitter','http://search.cpan.org/dist/Net-Twitter/', now()), + ('twittertools','Twitter Tools','http://wordpress.org/extend/plugins/twitter-tools/', now()), + ('twitux','Twitux','http://live.gnome.org/DanielMorales/Twitux', now()), + ('twitvim','TwitVim','http://vim.sourceforge.net/scripts/script.php?script_id=2204', now()), + ('urfastr','urfastr','http://urfastr.net/', now()); -- cgit v1.2.3-54-g00ecf From 07efccd7ef274f6d6f2b28781a87ec7271702a7f Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 4 Mar 2009 05:44:29 -0800 Subject: add notice source data on install --- install.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/install.php b/install.php index 29d909417..fbfd5e4d1 100644 --- a/install.php +++ b/install.php @@ -189,6 +189,13 @@ function handlePost() showForm(); return; } + updateStatus("Adding notice source data to database..."); + $res = runDbScript(INSTALLDIR.'/db/notice_source.sql', $conn); + if ($res === false) { + updateStatus("Can't run notice source script.", true); + showForm(); + return; + } updateStatus("Writing config file..."); $sqlUrl = "mysqli://$username:$password@$host/$database"; $res = writeConf($sitename, $sqlUrl); -- cgit v1.2.3-54-g00ecf From 43ef29ba0c64f19b93a2389ab7cd26158964a20b Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 4 Mar 2009 05:47:24 -0800 Subject: all inserts in one statement for foreign services --- db/foreign_services.sql | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/db/foreign_services.sql b/db/foreign_services.sql index 512d42513..557ede024 100644 --- a/db/foreign_services.sql +++ b/db/foreign_services.sql @@ -1,8 +1,5 @@ insert into foreign_service (id, name, description, created) values - ('1','Twitter', 'Twitter Micro-blogging service', now()); -insert into foreign_service - (id, name, description, created) -values - ('2','Facebook', 'Facebook', now()); + ('1','Twitter', 'Twitter Micro-blogging service', now()), + ('2','Facebook', 'Facebook', now()); -- cgit v1.2.3-54-g00ecf From 8d05768e2cfcc1a4534307ae9a3be333376d4cfe Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 4 Mar 2009 05:47:37 -0800 Subject: run foreign services script --- install.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/install.php b/install.php index fbfd5e4d1..7aa8836c2 100644 --- a/install.php +++ b/install.php @@ -196,6 +196,13 @@ function handlePost() showForm(); return; } + updateStatus("Adding foreign service data to database..."); + $res = runDbScript(INSTALLDIR.'/db/foreign_services.sql', $conn); + if ($res === false) { + updateStatus("Can't run foreign service script.", true); + showForm(); + return; + } updateStatus("Writing config file..."); $sqlUrl = "mysqli://$username:$password@$host/$database"; $res = writeConf($sitename, $sqlUrl); -- cgit v1.2.3-54-g00ecf From aa1bc6216e61faf5d5000f875f65b86bdb097c03 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 4 Mar 2009 05:53:04 -0800 Subject: Make a loop instead of repeating almost identical text in install --- install.php | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/install.php b/install.php index 7aa8836c2..0240349bb 100644 --- a/install.php +++ b/install.php @@ -182,26 +182,17 @@ function handlePost() showForm(); return; } - updateStatus("Adding SMS carrier data to database..."); - $res = runDbScript(INSTALLDIR.'/db/sms_carrier.sql', $conn); - if ($res === false) { - updateStatus("Can't run SMS carrier script.", true); - showForm(); - return; - } - updateStatus("Adding notice source data to database..."); - $res = runDbScript(INSTALLDIR.'/db/notice_source.sql', $conn); - if ($res === false) { - updateStatus("Can't run notice source script.", true); - showForm(); - return; - } - updateStatus("Adding foreign service data to database..."); - $res = runDbScript(INSTALLDIR.'/db/foreign_services.sql', $conn); - if ($res === false) { - updateStatus("Can't run foreign service script.", true); - showForm(); - return; + foreach (array('sms_carrier' => 'SMS carrier', + 'notice_source' => 'notice source', + 'foreign_services' => 'foreign service') + as $scr => $name) { + updateStatus(sprintf("Adding %s data to database...", $name)); + $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn); + if ($res === false) { + updateStatus(sprintf("Can't run %d script.", $name), true); + showForm(); + return; + } } updateStatus("Writing config file..."); $sqlUrl = "mysqli://$username:$password@$host/$database"; -- cgit v1.2.3-54-g00ecf From 09ebc965d58df9cc087568d99c3f4c5ba41496e2 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 4 Mar 2009 06:13:05 -0800 Subject: update README with new install instructions --- README | 97 +++++++++++++++++++++++++++++++----------------------------------- 1 file changed, 46 insertions(+), 51 deletions(-) diff --git a/README b/README index 67dc9a66b..388d67ed2 100644 --- a/README +++ b/README @@ -236,21 +236,28 @@ especially if you've previously installed PHP/MySQL packages. configure virtual hosts on your web server, you can try setting up "http://micro.example.net/" or the like. -3. You should also take this moment to make your avatar subdirectory - writeable by the Web server. An insecure way to do this is: +3. Make your target directory writeable by the Web server. - chmod a+w /var/www/mublog/avatar + chmod a+w /var/www/mublog/ On some systems, this will probably work: - chgrp www-data /var/www/mublog/avatar - chmod g+w /var/www/mublog/avatar + chgrp www-data /var/www/mublog/ + chmod g+w /var/www/mublog/ If your Web server runs as another user besides "www-data", try that user's default group instead. As a last resort, you can create - a new group like "avatar" and add the Web server's user to the group. + a new group like "mublog" and add the Web server's user to the group. -4. Create a database to hold your microblog data. Something like this +4. You should also take this moment to make your avatar subdirectory + writeable by the Web server. An insecure way to do this is: + + chmod a+w /var/www/mublog/avatar + + You can also make the avatar directory writeable by the Web server + group, as noted above. + +5. Create a database to hold your microblog data. Something like this should work: mysqladmin -u "username" --password="password" create laconica @@ -263,63 +270,55 @@ especially if you've previously installed PHP/MySQL packages. a tool like PHPAdmin to create a database. Check your hosting service's documentation for how to create a new MySQL database.) -5. Run the laconica.sql SQL script in the db subdirectory to create - the database tables in the database. A typical system would work - like this: - - mysql -u "username" --password="password" laconica < /var/www/mublog/db/laconica.sql - - You may want to test by logging into the database and checking that - the tables were created. Here's an example: - - SHOW TABLES; - 6. Create a new database account that Laconica will use to access the database. If you have shell access, this will probably work from the MySQL shell: - GRANT SELECT,INSERT,DELETE,UPDATE on laconica.* + GRANT ALL on laconica.* TO 'lacuser'@'localhost' IDENTIFIED BY 'lacpassword'; You should change 'lacuser' and 'lacpassword' to your preferred new - username and password. You may want to test logging in as this new - user and testing that you can SELECT from some of the tables in the - DB (use SHOW TABLES to see which ones are there). - -7. Copy the config.php.sample in the Laconica directory to config.php. - -8. Edit config.php to set the basic configuration for your system. - (See descriptions below for basic config options.) Note that there - are lots of options and if you try to do them all at once, you will - have a hard time making sure what's working and what's not. So, - stick with the basics at first. In particular, customizing the - 'site' and 'db' settings will almost definitely be needed. - -9. At this point, you should be able to navigate in a browser to your - microblog's main directory and see the "Public Timeline", which - will be empty. If not, magic has happened! You can now register a - new user, post some notices, edit your profile, etc. However, you - may want to wait to do that stuff if you think you can set up - "fancy URLs" (see below), since some URLs are stored in the database. + username and password. You may want to test logging in to MySQL as + this new user. + +7. In a browser, navigate to the Laconica install script; something like: + + http://yourserver.example.com/mublog/install.php + + Enter the database connection information and your site name. The + install program will configure your site and install the initial, + almost-empty database. + +8. You should now be able to navigate to your microblog's main directory + and see the "Public Timeline", which will be empty. If not, magic + has happened! You can now register a new user, post some notices, + edit your profile, etc. However, you may want to wait to do that stuff + if you think you can set up "fancy URLs" (see below), since some + URLs are stored in the database. Fancy URLs ---------- -By default, Laconica will have big long sloppy URLs that are hard for -people to remember or use. For example, a user's home profile might be +By default, Laconica will use URLs that include the main PHP program's +name in them. For example, a user's home profile might be found at: - http://example.org/mublog/index.php?action=showstream&nickname=fred + http://example.org/mublog/index.php/mublog/fred + +On certain systems that don't support this kind of syntax, they'll +look like this: + + http://example.org/mublog/index.php?p=mublog/fred It's possible to configure the software so it looks like this instead: http://example.org/mublog/fred These "fancy URLs" are more readable and memorable for users. To use -fancy URLs, you must either have Apache 2.2.x with .htaccess enabled -and mod_redirect enabled, -OR- know how to configure "url redirection" -in your server. +fancy URLs, you must either have Apache 2.x with .htaccess enabled and +mod_redirect enabled, -OR- know how to configure "url redirection" in +your server. 1. Copy the htaccess.sample file to .htaccess in your Laconica directory. Note: if you have control of your server's httpd.conf or @@ -344,10 +343,6 @@ like: If you changed your HTTP server configuration, you may need to restart the server first. -If you have problems with the .htaccess file on versions of Apache -earlier than 2.2.x, try changing the regular expressions in the -htaccess.sample file that use "\w" to just use ".". - Sphinx ------ @@ -557,7 +552,7 @@ Sample cron job: # Update Twitter friends subscriptions every half hour 0,30 * * * * /path/to/php /path/to/laconica/scripts/synctwitterfriends.php>&/dev/null -Built-in Facebook Application +Built-in Facebook Application ----------------------------- Laconica's Facebook application allows your users to automatically @@ -571,7 +566,7 @@ above). Quick setup instructions*: -Install the Facebook Developer application on Facebook: +Install the Facebook Developer application on Facebook: http://www.facebook.com/developers/ @@ -644,7 +639,7 @@ to these resources. Themes ------ -There are two themes shipped with this version of Laconica: "stoica", +There are two themes shipped with this version of Laconica: "identica", which is what the Identi.ca site uses, and "default", which is a good basis for other sites. -- cgit v1.2.3-54-g00ecf From f9babf6a7d4215e763a8c1766a2e6592fe274953 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 4 Mar 2009 06:24:33 -0800 Subject: Check for config file when running When running the full system, check for a config file, and throw an error if none is found. --- index.php | 8 ++++++++ lib/common.php | 6 ++++++ lib/util.php | 6 +++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/index.php b/index.php index 914ba5bde..7f580b836 100644 --- a/index.php +++ b/index.php @@ -61,6 +61,14 @@ function main() { global $user, $action; + if (!_have_config()) { + $msg = sprintf(_("No configuration file found. Try running ". + "the installation program first.")); + $sac = new ServerErrorAction($msg); + $sac->showPage(); + return; + } + // For database errors PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError'); diff --git a/lib/common.php b/lib/common.php index 0fff3af2e..ca8dedeef 100644 --- a/lib/common.php +++ b/lib/common.php @@ -187,6 +187,12 @@ foreach ($_config_files as $_config_file) { } } +function _have_config() +{ + global $_have_a_config; + return $_have_a_config; +} + // XXX: Throw a conniption if database not installed // Fixup for laconica.ini diff --git a/lib/util.php b/lib/util.php index 18e4f310c..f9a787d47 100644 --- a/lib/util.php +++ b/lib/util.php @@ -81,7 +81,7 @@ function common_language() // If there is a user logged in and they've set a language preference // then return that one... - if (common_logged_in()) { + if (_have_config() && common_logged_in()) { $user = common_current_user(); $user_language = $user->language; if ($user_language) @@ -315,6 +315,10 @@ function common_current_user() { global $_cur; + if (!_have_config()) { + return null; + } + if ($_cur === false) { if (isset($_REQUEST[session_name()]) || (isset($_SESSION['userid']) && $_SESSION['userid'])) { -- cgit v1.2.3-54-g00ecf From cf4e1872ab8109ea6a3230e43cc70899a17dd075 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 4 Mar 2009 06:27:30 -0800 Subject: Error actions use HTTP code name for title Change the title of error actions to the HTTP code name, like 'internal server error'. --- lib/clienterroraction.php | 15 ++++++++++----- lib/servererroraction.php | 20 ++++++++++++-------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/lib/clienterroraction.php b/lib/clienterroraction.php index 5019dc06d..0c48414d5 100644 --- a/lib/clienterroraction.php +++ b/lib/clienterroraction.php @@ -49,7 +49,7 @@ class ClientErrorAction extends ErrorAction function __construct($message='Error', $code=400) { parent::__construct($message, $code); - + $this->status = array(400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', @@ -72,7 +72,7 @@ class ClientErrorAction extends ErrorAction } // XXX: Should these error actions even be invokable via URI? - + function handle($args) { parent::handle($args); @@ -84,11 +84,16 @@ class ClientErrorAction extends ErrorAction } $this->message = $this->trimmed('message'); - + if (!$this->message) { - $this->message = "Client Error $this->code"; - } + $this->message = "Client Error $this->code"; + } $this->showPage(); } + + function title() + { + return $this->status[$this->code]; + } } diff --git a/lib/servererroraction.php b/lib/servererroraction.php index 80a3fdd7b..595dcf147 100644 --- a/lib/servererroraction.php +++ b/lib/servererroraction.php @@ -42,7 +42,7 @@ require_once INSTALLDIR.'/lib/error.php'; * says that 500 errors should be treated similarly to 400 errors, and * it's easier to give an HTML response. Maybe we can customize these * to display some funny animal cartoons. If not, we can probably role - * these classes up into a single class. + * these classes up into a single class. * * See: http://tools.ietf.org/html/rfc2616#section-10 * @@ -57,19 +57,19 @@ class ServerErrorAction extends ErrorAction function __construct($message='Error', $code=500) { parent::__construct($message, $code); - + $this->status = array(500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported'); - + $this->default = 500; } // XXX: Should these error actions even be invokable via URI? - + function handle($args) { parent::handle($args); @@ -81,12 +81,16 @@ class ServerErrorAction extends ErrorAction } $this->message = $this->trimmed('message'); - + if (!$this->message) { - $this->message = "Server Error $this->code"; - } + $this->message = "Server Error $this->code"; + } $this->showPage(); } - + + function title() + { + return $this->status[$this->code]; + } } -- cgit v1.2.3-54-g00ecf From c02a2f189139bd3588b04e557239e243805aaeac Mon Sep 17 00:00:00 2001 From: CiaranG Date: Wed, 4 Mar 2009 15:30:17 +0000 Subject: PostgreSQL - added defaults for 'created' fields, so they work the same as the MySQL version, and made sure all 'modified' fields are not null --- db/laconica_pg.sql | 56 +++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/db/laconica_pg.sql b/db/laconica_pg.sql index cacdfa97e..4ef2330f4 100644 --- a/db/laconica_pg.sql +++ b/db/laconica_pg.sql @@ -8,7 +8,7 @@ create table profile ( homepage varchar(255) /* comment 'identifying URL' */, bio varchar(140) /* comment 'descriptive biography' */, location varchar(255) /* comment 'physical location' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */, textsearch tsvector @@ -23,7 +23,7 @@ create table avatar ( mediatype varchar(32) not null /* comment 'file type' */, filename varchar(255) null /* comment 'local filename, if local' */, url varchar(255) unique /* comment 'avatar location' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */, primary key(profile_id, width, height) @@ -34,7 +34,7 @@ create table sms_carrier ( id serial primary key /* comment 'primary key for SMS carrier' */, name varchar(64) unique /* comment 'name of the carrier' */, email_pattern varchar(255) not null /* comment 'sprintf pattern for making an email address from a phone number' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified ' */ ); @@ -69,7 +69,7 @@ create table "user" ( autosubscribe integer default 0 /* comment 'automatically subscribe to users who subscribe to us' */, urlshorteningservice varchar(50) default 'ur1.ca' /* comment 'service to use for auto-shortening URLs' */, inboxed integer default 0 /* comment 'has an inbox been created for this user?' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */ ); @@ -82,7 +82,7 @@ create table remote_profile ( uri varchar(255) unique /* comment 'universally unique identifier, usually a tag URI' */, postnoticeurl varchar(255) /* comment 'URL we use for posting notices' */, updateprofileurl varchar(255) /* comment 'URL we use for updates to this profile' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */ ); @@ -93,7 +93,7 @@ create table subscription ( sms integer default 1 /* comment 'deliver sms messages' */, token varchar(255) /* comment 'authorization token' */, secret varchar(255) /* comment 'token secret' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */, primary key (subscriber, subscribed) @@ -109,7 +109,7 @@ create table notice ( content varchar(140) /* comment 'update content' */, rendered text /* comment 'HTML version of the content' */, url varchar(255) /* comment 'URL of any attachment (image, video, bookmark, whatever)' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */, reply_to integer /* comment 'notice replied to (usually a guess)' */ references notice (id) , is_local integer default 0 /* comment 'notice was generated by a user' */, @@ -124,7 +124,7 @@ create table notice_source ( code varchar(32) primary key not null /* comment 'source code' */, name varchar(255) not null /* comment 'name of the source' */, url varchar(255) not null /* comment 'url to link to' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */ ); @@ -132,7 +132,7 @@ create table reply ( notice_id integer not null /* comment 'notice that is the reply' */ references notice (id) , profile_id integer not null /* comment 'profile replied to' */ references profile (id) , - modified timestamp not null default 'now' /* comment 'date this record was modified' */, + modified timestamp /* comment 'date this record was modified' */, replied_id integer /* comment 'notice replied to (not used, see notice.reply_to)' */, primary key (notice_id, profile_id) @@ -146,7 +146,7 @@ create table fave ( notice_id integer not null /* comment 'notice that is the favorite' */ references notice (id), user_id integer not null /* comment 'user who likes this notice' */ references "user" (id) , - modified timestamp not null /* comment 'date this record was modified' */, + modified timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was modified' */, primary key (notice_id, user_id) ); @@ -160,7 +160,7 @@ create table consumer ( consumer_key varchar(255) primary key /* comment 'unique identifier, root URL' */, seed char(32) not null /* comment 'seed for new tokens by this consumer' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */ ); @@ -171,7 +171,7 @@ create table token ( type integer not null default 0 /* comment 'request or access' */, state integer default 0 /* comment 'for requests; 0 = initial, 1 = authorized, 2 = used' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */, primary key (consumer_key, tok) @@ -183,7 +183,7 @@ create table nonce ( nonce char(32) not null /* comment 'nonce' */, ts timestamp not null /* comment 'timestamp sent' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */, primary key (consumer_key, tok, nonce), @@ -196,7 +196,7 @@ create table user_openid ( canonical varchar(255) primary key /* comment 'Canonical true URL' */, display varchar(255) not null unique /* comment 'URL for viewing, may be different from canonical' */, user_id integer not null /* comment 'user owning this URL' */ references "user" (id) , - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */ ); @@ -242,7 +242,7 @@ create table queue_item ( notice_id integer not null /* comment 'notice queued' */ references notice (id) , transport varchar(8) not null /* comment 'queue for what? "email", "jabber", "sms", "irc", ...' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, claimed timestamp /* comment 'date this item was claimed' */, primary key (notice_id, transport) @@ -254,7 +254,7 @@ create index queue_item_created_idx on queue_item using btree(created); create table notice_tag ( tag varchar( 64 ) not null /* comment 'hash tag associated with this notice' */, notice_id integer not null /* comment 'notice tagged' */ references notice (id) , - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, primary key (tag, notice_id) ); @@ -266,7 +266,7 @@ create table foreign_service ( id int not null primary key /* comment 'numeric key for service' */, name varchar(32) not null unique /* comment 'name of the service' */, description varchar(255) /* comment 'description' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */ ); @@ -275,7 +275,7 @@ create table foreign_user ( service int not null /* comment 'foreign key to service' */ references foreign_service(id) , uri varchar(255) not null unique /* comment 'identifying URI' */, nickname varchar(255) /* comment 'nickname on foreign service' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */, primary key (id, service) @@ -289,8 +289,8 @@ create table foreign_link ( noticesync int not null default 1 /* comment 'notice synchronisation, bit 1 = sync outgoing, bit 2 = sync incoming, bit 3 = filter local replies' */, friendsync int not null default 2 /* comment 'friend synchronisation, bit 1 = sync outgoing, bit 2 = sync incoming */, profilesync int not null default 1 /* comment 'profile synchronization, bit 1 = sync outgoing, bit 2 = sync incoming' */, - created timestamp not null /* comment 'date this record was created' */, - modified timestamp not null /* comment 'date this record was modified' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, + modified timestamp /* comment 'date this record was modified' */, primary key (user_id,foreign_id,service) ); @@ -300,7 +300,7 @@ create table foreign_subscription ( service int not null /* comment 'service where relationship happens' */ references foreign_service(id) , subscriber int not null /* comment 'subscriber on foreign service' */ , subscribed int not null /* comment 'subscribed user' */ , - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, primary key (service, subscriber, subscribed) ); @@ -312,7 +312,7 @@ create table invitation ( user_id int not null /* comment 'who sent the invitation' */ references "user" (id), address varchar(255) not null /* comment 'invitation sent to' */, address_type varchar(8) not null /* comment 'address type ("email", "jabber", "sms") '*/, - created timestamp not null /* comment 'date this record was created' */ + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */ ); create index invitation_address_idx on invitation using btree(address,address_type); @@ -327,7 +327,7 @@ create table message ( content varchar(140) /* comment 'message content' */, rendered text /* comment 'HTML version of the content' */, url varchar(255) /* comment 'URL of any attachment (image, video, bookmark, whatever)' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */, source varchar(32) /* comment 'source of comment, like "web", "im", or "clientname"' */ @@ -340,7 +340,7 @@ create table notice_inbox ( user_id integer not null /* comment 'user receiving the message' */ references "user" (id), notice_id integer not null /* comment 'notice received' */ references notice (id), - created timestamp not null /* comment 'date the notice was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date the notice was created' */, source integer default 1 /* comment 'reason it is in the inbox; 1=subscription' */, primary key (user_id, notice_id) @@ -383,7 +383,7 @@ create table user_group ( stream_logo varchar(255) /* comment 'stream-sized logo' */, mini_logo varchar(255) /* comment 'mini logo' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */ ); @@ -395,7 +395,7 @@ create table group_member ( profile_id integer not null /* comment 'foreign key to profile table' */ references profile (id), is_admin integer default 0 /* comment 'is this user an admin?' */, - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */, primary key (group_id, profile_id) @@ -406,7 +406,7 @@ create table related_group ( group_id integer not null /* comment 'foreign key to user_group' */ references user_group (id) , related_group_id integer not null /* comment 'foreign key to user_group' */ references user_group (id), - created timestamp not null /* comment 'date this record was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, primary key (group_id, related_group_id) @@ -415,7 +415,7 @@ create table related_group ( create table group_inbox ( group_id integer not null /* comment 'group receiving the message' references user_group (id) */, notice_id integer not null /* comment 'notice received' references notice (id) */, - created timestamp not null /* comment 'date the notice was created' */, + created timestamp not null default CURRENT_TIMESTAMP /* comment 'date the notice was created' */, primary key (group_id, notice_id) ); -- cgit v1.2.3-54-g00ecf From 115519a5e7e84e57656c653918efb39ab4107fe9 Mon Sep 17 00:00:00 2001 From: CiaranG Date: Wed, 4 Mar 2009 15:32:26 +0000 Subject: PostgreSQL - made all 'weight' calculating SQL expressions compatible with both databases, and made some GROUP BY queries more explicit about the fields they are selecting, for the same reason. --- actions/favorited.php | 8 +++++++- lib/groupsbymemberssection.php | 2 +- lib/groupsbypostssection.php | 2 +- lib/grouptagcloudsection.php | 8 +++++++- lib/personaltagcloudsection.php | 10 ++++++++-- lib/popularnoticesection.php | 12 +++++++++--- 6 files changed, 33 insertions(+), 9 deletions(-) diff --git a/actions/favorited.php b/actions/favorited.php index fd5ff413c..5082f4a4e 100644 --- a/actions/favorited.php +++ b/actions/favorited.php @@ -169,8 +169,14 @@ class FavoritedAction extends Action function showContent() { + if (common_config('db', 'type') == 'pgsql') { + $weightexpr='sum(exp(-extract(epoch from (now() - fave.modified)) / %s))'; + } else { + $weightexpr='sum(exp(-(now() - fave.modified) / %s))'; + } + $qry = 'SELECT notice.*, '. - 'sum(exp(-(now() - fave.modified) / %s)) as weight ' . + $weightexpr . ' as weight ' . 'FROM notice JOIN fave ON notice.id = fave.notice_id ' . 'GROUP BY fave.notice_id ' . 'ORDER BY weight DESC'; diff --git a/lib/groupsbymemberssection.php b/lib/groupsbymemberssection.php index 4fa07a244..5f26c6626 100644 --- a/lib/groupsbymemberssection.php +++ b/lib/groupsbymemberssection.php @@ -45,7 +45,7 @@ class GroupsByMembersSection extends GroupSection { function getGroups() { - $qry = 'SELECT user_group.*, count(*) as value ' . + $qry = 'SELECT user_group.id, count(*) as value ' . 'FROM user_group JOIN group_member '. 'ON user_group.id = group_member.group_id ' . 'GROUP BY user_group.id ' . diff --git a/lib/groupsbypostssection.php b/lib/groupsbypostssection.php index a5e33a93d..1a60ddb4f 100644 --- a/lib/groupsbypostssection.php +++ b/lib/groupsbypostssection.php @@ -45,7 +45,7 @@ class GroupsByPostsSection extends GroupSection { function getGroups() { - $qry = 'SELECT user_group.*, count(*) as value ' . + $qry = 'SELECT user_group.id, count(*) as value ' . 'FROM user_group JOIN group_inbox '. 'ON user_group.id = group_inbox.group_id ' . 'GROUP BY user_group.id ' . diff --git a/lib/grouptagcloudsection.php b/lib/grouptagcloudsection.php index f05be85cb..5d68af28b 100644 --- a/lib/grouptagcloudsection.php +++ b/lib/grouptagcloudsection.php @@ -58,8 +58,14 @@ class GroupTagCloudSection extends TagCloudSection function getTags() { + if (common_config('db', 'type') == 'pgsql') { + $weightexpr='sum(exp(-extract(epoch from (now() - notice_tag.created)) / %s))'; + } else { + $weightexpr='sum(exp(-(now() - notice_tag.created) / %s))'; + } + $qry = 'SELECT notice_tag.tag, '. - 'sum(exp(-(now() - notice_tag.created)/%s)) as weight ' . + $weightexpr . ' as weight ' . 'FROM notice_tag JOIN notice ' . 'ON notice_tag.notice_id = notice.id ' . 'JOIN group_inbox on group_inbox.notice_id = notice.id ' . diff --git a/lib/personaltagcloudsection.php b/lib/personaltagcloudsection.php index 0882822db..978153a84 100644 --- a/lib/personaltagcloudsection.php +++ b/lib/personaltagcloudsection.php @@ -58,8 +58,14 @@ class PersonalTagCloudSection extends TagCloudSection function getTags() { - $qry = 'SELECT notice_tag.tag, '. - 'sum(exp(-(now() - notice_tag.created)/%s)) as weight ' . + if (common_config('db', 'type') == 'pgsql') { + $weightexpr='sum(exp(-extract(epoch from (now() - notice_tag.created)) / %s))'; + } else { + $weightexpr='sum(exp(-(now() - notice_tag.created) / %s))'; + } + + $qry = 'SELECT notice_tag.tag, '. + $weightexpr . ' as weight ' . 'FROM notice_tag JOIN notice ' . 'ON notice_tag.notice_id = notice.id ' . 'WHERE notice.profile_id = %d ' . diff --git a/lib/popularnoticesection.php b/lib/popularnoticesection.php index c7c7f0215..f7fb93554 100644 --- a/lib/popularnoticesection.php +++ b/lib/popularnoticesection.php @@ -48,10 +48,16 @@ class PopularNoticeSection extends NoticeSection { function getNotices() { - $qry = 'SELECT notice.*, '. - 'sum(exp(-(now() - fave.modified) / %s)) as weight ' . + if (common_config('db', 'type') == 'pgsql') { + $weightexpr='sum(exp(-extract(epoch from (now() - fave.modified)) / %s))'; + } else { + $weightexpr='sum(exp(-(now() - fave.modified) / %s))'; + } + + $qry = 'SELECT notice.id, '. + $weightexpr . ' as weight ' . 'FROM notice JOIN fave ON notice.id = fave.notice_id ' . - 'GROUP BY fave.notice_id ' . + 'GROUP BY notice.id ' . 'ORDER BY weight DESC'; $offset = 0; -- cgit v1.2.3-54-g00ecf From a7efd4ff556bbf6bafcfc81db758ab192b8802ad Mon Sep 17 00:00:00 2001 From: CiaranG Date: Wed, 4 Mar 2009 15:34:04 +0000 Subject: Plugins - added a new event (RouterInitialized) which allows a plugin to register new paths to be routed --- EVENTS.txt | 4 ++++ lib/router.php | 2 ++ 2 files changed, 6 insertions(+) diff --git a/EVENTS.txt b/EVENTS.txt index 2f33b2d5d..5edf59245 100644 --- a/EVENTS.txt +++ b/EVENTS.txt @@ -99,3 +99,7 @@ StartPublicGroupNav: Showing the public group nav menu EndPublicGroupNav: At the end of the public group nav menu - $action: the current action + +RouterInitialized: After the router instance has been initialized +- $m: the Net_URL_Mapper that has just been set up + diff --git a/lib/router.php b/lib/router.php index 4b70c0150..d4a4d2ca9 100644 --- a/lib/router.php +++ b/lib/router.php @@ -393,6 +393,8 @@ class Router array('action' => 'showstream'), array('nickname' => '[a-zA-Z0-9]{1,64}')); + Event::handle('RouterInitialized', array($m)); + return $m; } -- cgit v1.2.3-54-g00ecf From 00c358956fcece251b8d78f2c6a41098571472c7 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 4 Mar 2009 12:07:53 -0800 Subject: check for profile record --- lib/noticesection.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/noticesection.php b/lib/noticesection.php index b31f18744..94c2738ef 100644 --- a/lib/noticesection.php +++ b/lib/noticesection.php @@ -73,6 +73,11 @@ class NoticeSection extends Section function showNotice($notice) { $profile = $notice->getProfile(); + if (empty($profile)) { + common_log(LOG_WARNING, sprintf("Notice %d has no profile", + $notice->id)); + return; + } $this->out->elementStart('li', 'hentry notice'); $this->out->elementStart('div', 'entry-title'); $avatar = $profile->getAvatar(AVATAR_MINI_SIZE); -- cgit v1.2.3-54-g00ecf From e239a5529abc0958c4f874811ff704f1e5d6ba62 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 4 Mar 2009 21:05:26 +0000 Subject: Yahoo! SearchMonkey applications: * Displays user profile information http://identi.ca * Displays user's XFN on http://identi.ca --- scripts/SearchMonkey-Om3.0.txt | 1 + scripts/SearchMonkey-yQP.0.txt | 1 + 2 files changed, 2 insertions(+) create mode 100644 scripts/SearchMonkey-Om3.0.txt create mode 100644 scripts/SearchMonkey-yQP.0.txt diff --git a/scripts/SearchMonkey-Om3.0.txt b/scripts/SearchMonkey-Om3.0.txt new file mode 100644 index 000000000..45b782d0b --- /dev/null +++ b/scripts/SearchMonkey-Om3.0.txt @@ -0,0 +1 @@ +BE9KrPPSJAPljm0ykS59yJQmWBFY9RPVJFhZsiF_5Wcf_tkGwf4FP2Ncjs7qGfHFCb2pv.eDr5y0zhKrF4s3ugs89DbLaA.hrbRJwglsNym5TDwDycrGw3TvjfCVmHBx4VzOZ2QzUyXBIs0T30paT6PYQfARKBdMkieKbbQ1tfl.f.ul35_kcpoXZt_lTDWFDaia2RM41uDLHJyVbCcRkqfHCLabgNeOq_MGFXGA6DjnKQx7TNyQe.2N6IyVd1quVapHn6jUOsWNkahehPMHtO72yvPpugS0UHCKBqcd.UCcbIhmtzLnKoBQAH2AJqUrmfg1XRwqFvTo6y9Z5XmDQK2hRnv97InV5he1AMIqNUotAcIrYjq5Tn42whYsznnMYhMY44UqZGoJI_ZwsSvnH6Je.AhKU3hBW9Tsmggpxgnhx_o2vhyNw2QAgPJng0FKxaevCmPnFtLntwluhxLbiTo2IiIotP4VjIVKs76hHzAsmXYuvS01OU.XB43Gmcw5yP9OUIoh5fEA1ANxOb.ba8aVxu2FdvedbOECgO6gvr.kYilrdxwwnVbHwVP6esrOiYE32dRs0_tWdgfvJfyloLjj_M5stZLEBcVfoUSQHsxX6jW5jrs4BTEHhKrxlOzdBt8f8T2E6eA9x2h7B8zK4eRtC2NBrcj1jzYCVWUT7IdbN05NZBjGYvxtmX1KRW91QwdFXgytfOINFkDk0scNCGGt4OYDzKLJ7sRBy0iKhHNfTC3noUhWf1372uXQ_yiWDWB7cTfxF9meAU3TWrTS7DjGTkwLvCVUJ43QyCxBQd1jWU6sp_VvytDeHx7cqrkNsa3JRN9dht3POqq_mVL0c5MX_XRpV8O.tPSGmfUPUrR5qnV8Z739D6PGWkOQzfzTTv6vGkf4jj1xyDEfUqr767_yL7gHeI2VlbdD2ammzwEhRK9f8ME8FbrZTTgX1OD0.v1cFULt1lew.rtCOtKf4F2MBbi90edst.TpOoUh_TvFkgKf0zgteNo2JjzrmCO.uviLCM2weGksARL70mdU6W9N932YWr6E8HbUc02S8ifSnbMpiUTHwNzMErsg7BSyRVJngGLDreBkBEIjXkNoApJR7kMPMaYVwtvjU.8wLmyFLZ.PHqqcrADTr7R50hL3UKj1mLsL19gQL3cP9J7_zJJM4Q1GDj2dPWBLhJMosUVhW8XBFPhW0aEdmgqhpsAJ_qv52xfjBeEPyPmKHu1p.1G2QEmOqFm_swbVAPHnra5zd8x4OOlHGFeL3qPLW9_LXN46chs6cOpUdYaWRzT2YuOUHHE8RDV9Mev22p7WjrbgC8hY3wK55Cpw_fCekaHcJX2jU3FPl09httjI1i5yGU04yD5MpOQF5EadbSlfjDEg8H.GoOFZcyKKDnqj_8SYdrXWq4aTqAnWgvBPh.bznvmevDgEpH.9gtzLcao4Dzmk.K0PnRgvkzyaN5IGk60TiD4Fk0u3f4bqKaPCOIKCTajbNGIasyND0J5ROtAa_IlXljCpzeEYPhSASSFV6fWmlqHFiGa2cKlLNr157MAUVQg52dJRRyid1YDcHK8ZuMc4BX8QA0olb2CDbUHKxxwv0zbIsgUTL8gvKIAPwBfF0cPfV9v6Phs1rPFXnysxdnunXnr6r6ZCN00rZeA7XbH68f.UjW0.ERGvB72kMnB8fBhfEgD22Y0ZFkwxI00UVAm5yiwkjYx86EdCpkMLBVDR1kWictX04pFLzxZK8iRflPBy74Nx2SFO19qJFnQmKEV2IN2wlrH6z14902LEI2Vuh_dVxNphr1k5mJ9x2VNGmvol.vvHmaLCufHIrOMRkAFMxeP37Mnzyk6NpxAQbdV0KwC.YTfoK9Y5knYAherlh7x8NqELX6XqutF2Lm11dikoK8Yu8u3Rkhoaef00PISV0LOb0E.D1LRjFFGzUCt5Ezcq68Tt0rFNEF1gm5Xl9Rygln_67F0KMgacpKTJ2PBIqcteyRZoRohFBRUTLkKn784KacSBsWFkogD5n7blv943wWeZFvUcKsFyYZny1WLvGEQX01kbjyaDCLDxiEQWSvlDuD.2wOra0JO5.Bo2PpPK5tbNexZDVx3k1yb48ev2UC4J4IlD6oK1KYe46Y.7NX9kDYCcdRiQOVnLJ4PSbPhDW0hcE2SNtxmFdpAf2xQOiekXY35lkuNxvebVhKuoFWxUgQmFEfreJQsC5qf_dunxGquD6u3SXnLbTg8aNWZqtR74dr3s7C5kwBK6eBGwJfkAx8Z2JKwLr5QHdISfa0wLl7aIejz5Mg_tfeATLIwW8SiIQdJiZ992Bx1k_50XQVeHOdyEc8J1Tjnkrji_ZY9PFn3AZc94wXD8erpANuJIbDIdLUp1idqF_TDtmqSK2qQgz26IkewcZnSIQnm98qxkHf9DAIbE6_GUP0ZeWqLQrn0CdhejhOPsKsovcDI7zBHMQABquMeBh9YC1.QCO9br6YmYEAV30n5IsZgl5PRiRDjkhXv8VaE.fnRNndSy7BX6kmxSmDWQfY0FvbUBWBQm7tfU3Mrk2Ojm4ALIYUZnJxl.5xj6VvD.2ZX3ug_1zo7mAiAYG_F939DMccfgCVFUcdtbF9Q9YFeC2bVMfgX5gA75nBCu5R8KopeWfTjdqZBtJoT6HxFmCSLQqIdjrxD7X2RapCI0QoDsp9xyzRZJ5NF5ygVBJqtNn2lAwFENasqFvGRSb35B.sjTQoHW5WBoBjFy5tNF1vWYJENfKRjWn5Pr0FvFdQfBKqeiWrLtStAnGrd49frviyK5VtYa.eSuRyX32SGb7.Pxo0Hf0bhER4uvPBktf3ec5UjL_xRI3eQ__F7tRiGu2tRYIUYcUkhLKAGYoDx2_gdMOXcow5W3KwtuVNtZ4UMRmuB3ccyBUzDTRWNLFISq56JXU46_v7ANub44kTLDfGuJExu.0NaOuW6isndAIYslJlcCs6NJ6j_6Ag55G.UhQFfzDKVOEt.L \ No newline at end of file diff --git a/scripts/SearchMonkey-yQP.0.txt b/scripts/SearchMonkey-yQP.0.txt new file mode 100644 index 000000000..e42a04e16 --- /dev/null +++ b/scripts/SearchMonkey-yQP.0.txt @@ -0,0 +1 @@ +bzN6aQzSJBM8jb6JDpNRvtfEMpxOC6r2ffo5VJf0ah_Q3kXs8gwvDzGy0KGHkwDDijfnJAwhb.BIcFihUZVWe.vAy2xZZN8XUMxHwc16HwCsgDyOC9sH5WzOV0dvJcVZ.9ipAgI7RuXsAvZdJv90N89iOh1WmD6QwWyzyvnu.FjQDrQH1DlGwFs3ZNzY5lsd5uYhtJ3puBXrzSmGcR68_7OEw4QzAV9SyPSHSskXF56cpH8pUey8WiQieM4X_uuKsMjQfWEUdqNb6teXDplvPDRqHwP5rC6X1_oHBiocfgWiBfUfKOXE1g5J.JtqXYIBee8ROyx3sVIc7V2I0eotZCCiCJ1k74ru2OGC9UhX__ZESGrp9b0JZGZFO6w97IL1Y3BTgVXNox8L3FcFjl11s7YcYBhc_3e3WbJA4pZzczPzd0ouZiCjUcBCZXNu6fnM6XerBbsVj584ZbPdjQ6A5TcrED8dTcFdgfMWlMu7bHsE7e0QIJYmc2g5NWeur2ovAOPVxhELrvnJ6X6Z.06vAmwdJcl4hyYRVqA.9QynHlH2dezXS5y0eKXFUzg.tHNqZboEGutLL316mZQYvwZwATZFc8iO3EuiNV3MWbiCrLX.n3nbXp82A4v7Bv.PVJoeOTmiPmq8vuxts8nUEPf7gA3j6.DmlDvfZQNx9.WNmNFps0B08hzDMvKX98.ft9.GJlftPCYCk2qHRtWm3tjB2R9O6mF6XYvOLUK7aQ4rz5oCxHwmrF.n56G07MS5GS1pjV3ByUO88PI1i_rPbim_Up0jIO5q7PqXudyFE.nG0Dc8yaExZk7PryBfSHk2yMQVXcBjy3XQBVmrIwPwKs.YUFC1qLSThfA8UNckik2xuDZKvf29xpGvHlMbH67UV5HhY5CTeqlgvapRThmfrexqaBTTPPqR.Sy3Pvr9vHX.0UC6kNCTlxW3CExrx9EipRKUvRVClNLEG6M9hm5MQkdfgmd2Dmj5DmjcrZkWOqnNfLn_mtYdoD3nK4zCk.NZwXTFlx5FVr5bOzyncalNdb77qLEH9E9R8d2KUA4Axljx29kycILhZCvy3Qz22Vz_M72lVFKQAFnGlERRS7NLu2rB00e.MNeH9aB01uj.y9UHpfjTAwOJSFEBf7yKRCkVzqFpGAQo7txlC3NRgSyI3kQtW0WeWiNzghBv8c_5iXWkXqFyRfVZUCw2qqow3BF6SDtkhXAr8d.JrprGdG67U66ZD3JB10OLmmAaDiiCr81eVOkrsz3ONdigZOlP7RrYtodAORnheCZ10SAYbaru0sKmsJx4sB41QKn79dieWs5oihI5Rr3jUzPjfYgav5jeEfUYz5qHTtmps4vVJjt9s4zueRiT8AYzg.Xv.wdC1dvBr_kpGxwrkMEZJ0QfZkH.TDWBSWyxeG9gtuKd1gy9CGQTXlLvonJWHwK9gwWZwRKIanV4gnMfWz4pvhGixzEaiRU6g9giKYHpVKe.w0cwPfSDIhbBYqrlvuxkSTXfuFNyTpHM0Mgc6PMNnH6GooeLVK3IO2M8dRdDbL7EHykIJWe85pm5BxSqFTcWXn1nGuVWQV.F.J1XYJj1KheT725MVRlrE3sH.MgY6m4hKCUXT7mIgJ6xsfAQ8dWn7V1fCpqoIYub55iw_dg8yfZ7Yfgwj9bqN81lvTWQzBMLKYpG6pOh37JsTikpXa9kTSJfbJm0_GYZv_Bu10D1WqiO0UCvJXrlqkl5F610fkXkhwRXvmIQL2Mv_R71zSKqMr1g5RmnNifUv43jpXoOWmOKscg59pv8eOyb2JG26BtyVS2XOTDw6pHpHKuksqRKt62i1z83uv_ve5rO0zIKeMnWGlvXKHn8ld3fqGyJvqZ7wqYpnV_LTE3p73XCLEeniClXyk5Q_Icu1PgakYh3GIz8RVBjbjgrZIsvvkEp0Kgeo6oFY0wnboIwDlaUVHIue1nPkuAIsVk9i_IJWeqoRV0eO0wUGWgQ5.HT9RzcKdFVe2kl84RKuQkvoibiCfGlfTXpOfLOZtwsfl2TuNgZtP5oG1Wfsgi6g2a1N8B2WHYN8kZuG8pMnc4NQDIoO8BDnz89lDt7Y9JCslIM3O2Y25EGsylNKamKPJZr.ZSvvTVJG5sxftLxvF8vc8h5_pJD9Akj.84CjqQyfsnzFqc_.EqBaRVSy35wHHrdWTBsvjcobtSed8wXs.5En6HdubdATcgthozHWMEMsUdA29XkenPic2cR.8bQ5VdGZ2_YkJIXrxgt_XoEFFB8tgy061cghyL2fc2fHMQm4KBEc1vjkZjwRo9adWbg9dzAl782AwKC.C72bw-- \ No newline at end of file -- cgit v1.2.3-54-g00ecf From 36bb33fb1d7b4befe2fb68c2eef0712619359293 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 4 Mar 2009 16:17:40 -0800 Subject: Made /api/account/verify_credentials.format return an extended user object. Updates to status and user API objects. --- actions/twitapiaccount.php | 20 ++++++++++-------- actions/twitapiusers.php | 52 +++++++++++++++++++++++++++++++++------------- lib/router.php | 11 +++++----- lib/twitterapi.php | 41 +++++++++++++++++------------------- 4 files changed, 73 insertions(+), 51 deletions(-) diff --git a/actions/twitapiaccount.php b/actions/twitapiaccount.php index c19cd370d..68a18cb57 100644 --- a/actions/twitapiaccount.php +++ b/actions/twitapiaccount.php @@ -23,22 +23,24 @@ require_once(INSTALLDIR.'/lib/twitterapi.php'); class TwitapiaccountAction extends TwitterapiAction { - function verify_credentials($args, $apidata) { - if ($apidata['content-type'] == 'xml') { - header('Content-Type: application/xml; charset=utf-8'); - print 'true'; - } elseif ($apidata['content-type'] == 'json') { - header('Content-Type: application/json; charset=utf-8'); - print '{"authorized":true}'; - } else { + parent::handle($args); + + switch ($apidata['content-type']) { + case 'xml': + case 'json': + $action_obj = new TwitapiusersAction(); + $action_obj->prepare($args); + call_user_func(array($action_obj, 'show'), $args, $apidata); + break; + default: header('Content-Type: text/html; charset=utf-8'); print 'Authorized'; } } - function end_session($args, $apidata) + function end_session($args, $apidata) { parent::handle($args); $this->serverError(_('API method under construction.'), $code=501); diff --git a/actions/twitapiusers.php b/actions/twitapiusers.php index 8f16e5613..2894b7486 100644 --- a/actions/twitapiusers.php +++ b/actions/twitapiusers.php @@ -25,25 +25,29 @@ class TwitapiusersAction extends TwitterapiAction { function show($args, $apidata) - { + { parent::handle($args); - if (!in_array($apidata['content-type'], array('xml', 'json'))) { + if (!in_array($apidata['content-type'], array('xml', 'json'))) { $this->clientError(_('API method not found!'), $code = 404); return; } - - $this->auth_user = $apidata['user']; + $user = null; $email = $this->arg('email'); + $user_id = $this->arg('user_id'); if ($email) { $user = User::staticGet('email', $email); + } elseif ($user_id) { + $user = $this->get_user($user_id); } elseif (isset($apidata['api_arg'])) { $user = $this->get_user($apidata['api_arg']); - } - - if (!$user) { + } elseif (isset($apidata['user'])) { + $user = $apidata['user']; + } + + if (!$user) { // XXX: Twitter returns a random(?) user instead of throwing and err! -- Zach $this->client_error(_('Not found.'), 404, $apidata['content-type']); return; @@ -74,9 +78,12 @@ class TwitapiusersAction extends TwitterapiAction // Other fields Twitter sends... $twitter_user['profile_background_color'] = ''; + $twitter_user['profile_background_image_url'] = ''; $twitter_user['profile_text_color'] = ''; $twitter_user['profile_link_color'] = ''; $twitter_user['profile_sidebar_fill_color'] = ''; + $twitter_user['profile_sidebar_border_color'] = ''; + $twitter_user['profile_background_tile'] = 'false'; $faves = DB_DataObject::factory('fave'); $faves->user_id = $user->id; @@ -94,18 +101,27 @@ class TwitapiusersAction extends TwitterapiAction $twitter_user['utc_offset'] = $t->format('Z'); $twitter_user['time_zone'] = $timezone; - if (isset($this->auth_user)) { + if (isset($apidata['user'])) { - if ($this->auth_user->isSubscribed($profile)) { + if ($apidata['user']->isSubscribed($profile)) { $twitter_user['following'] = 'true'; } else { $twitter_user['following'] = 'false'; } - - // Not implemented yet - $twitter_user['notifications'] = 'false'; - } - + + // Notifications on? + $sub = Subscription::pkeyGet(array('subscriber' => + $apidata['user']->id, 'subscribed' => $profile->id)); + + if ($sub) { + if ($sub->jabber || $sub->sms) { + $twitter_user['notifications'] = 'true'; + } else { + $twitter_user['notifications'] = 'false'; + } + } + } + if ($apidata['content-type'] == 'xml') { $this->init_document('xml'); $this->show_twitter_xml_user($twitter_user); @@ -114,7 +130,13 @@ class TwitapiusersAction extends TwitterapiAction $this->init_document('json'); $this->show_json_objects($twitter_user); $this->end_document('json'); - } + } else { + + // This is in case 'show' was called via /account/verify_credentials + // without a format (xml or json). + header('Content-Type: text/html; charset=utf-8'); + print 'Authorized'; + } } } diff --git a/lib/router.php b/lib/router.php index 4b70c0150..a36cd2691 100644 --- a/lib/router.php +++ b/lib/router.php @@ -228,14 +228,15 @@ class Router // users - $m->connect('api/users/show/:argument', + $m->connect('api/users/:method/:argument', array('action' => 'api', - 'apiaction' => 'users')); + 'apiaction' => 'users'), + array('method' => 'show(\.(xml|json))?')); $m->connect('api/users/:method', array('action' => 'api', 'apiaction' => 'users'), - array('method' => 'show(\.(xml|json|atom|rss))?')); + array('method' => 'show(\.(xml|json))?')); // direct messages @@ -304,11 +305,11 @@ class Router } // account - + $m->connect('api/account/:method', array('action' => 'api', 'apiaction' => 'account')); - + // favorites $m->connect('api/favorites/:method/:argument', diff --git a/lib/twitterapi.php b/lib/twitterapi.php index a4d183fcd..74f265cbb 100644 --- a/lib/twitterapi.php +++ b/lib/twitterapi.php @@ -60,20 +60,34 @@ class TwitterapiAction extends Action function twitter_status_array($notice, $include_user=true) { - $profile = $notice->getProfile(); $twitter_status = array(); $twitter_status['text'] = $notice->content; $twitter_status['truncated'] = 'false'; # Not possible on Laconica $twitter_status['created_at'] = $this->date_twitter($notice->created); - $twitter_status['in_reply_to_status_id'] = ($notice->reply_to) ? intval($notice->reply_to) : null; + $twitter_status['in_reply_to_status_id'] = ($notice->reply_to) ? + intval($notice->reply_to) : null; $twitter_status['source'] = $this->source_link($notice->source); $twitter_status['id'] = intval($notice->id); - $twitter_status['in_reply_to_user_id'] = ($notice->reply_to) ? $this->replier_by_reply(intval($notice->reply_to)) : null; + + $replier_profile = null; + + if ($notice->reply_to) { + $reply = Notice::staticGet(intval($notice->reply_to)); + if ($reply) { + $replier_profile = $reply->getProfile(); + } + } + + $twitter_status['in_reply_to_user_id'] = + ($replier_profile) ? intval($replier_profile->id) : null; + $twitter_status['in_reply_to_screen_name'] = + ($replier_profile) ? $replier_profile->nickname : null; if (isset($this->auth_user)) { - $twitter_status['favorited'] = ($this->auth_user->hasFave($notice)) ? 'true' : 'false'; + $twitter_status['favorited'] = + ($this->auth_user->hasFave($notice)) ? 'true' : 'false'; } else { $twitter_status['favorited'] = 'false'; } @@ -137,7 +151,6 @@ class TwitterapiAction extends Action function twitter_dmsg_array($message) { - $twitter_dm = array(); $from_profile = $message->getFrom(); @@ -386,23 +399,7 @@ class TwitterapiAction extends Action $t = strtotime($dt); return date("D M d G:i:s O Y", $t); } - - function replier_by_reply($reply_id) - { - $notice = Notice::staticGet($reply_id); - if ($notice) { - $profile = $notice->getProfile(); - if ($profile) { - return intval($profile->id); - } else { - common_debug('Can\'t find a profile for notice: ' . $notice->id, __FILE__); - } - } else { - common_debug("Can't get notice: $reply_id", __FILE__); - } - return null; - } - + // XXX: Candidate for a general utility method somewhere? function count_subscriptions($profile) { -- cgit v1.2.3-54-g00ecf From 8ba8eaa330550cbb70ba46f81788644f6d7ed168 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 4 Mar 2009 16:55:33 -0800 Subject: Bumped the version number in laconica.spec in prep for a 0.7.2. RPM --- scripts/laconica.spec | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/laconica.spec b/scripts/laconica.spec index 2df41c932..afa2766a4 100644 --- a/scripts/laconica.spec +++ b/scripts/laconica.spec @@ -2,10 +2,10 @@ BuildRequires: php-pear BuildRequires: httpd-devel Name: laconica -Version: 0.7.1 +Version: 0.7.2 Release: 1%{?dist} License: GAGPL v3 or later -Source: laconica-0.7.1.tar.gz +Source: laconica-0.7.2.tar.gz Group: Applications/Internet Summary: Laconica, the Open Source microblogging platform BuildArch: noarch @@ -74,6 +74,9 @@ rm -rf %buildroot %config(noreplace) %{_sysconfdir}/httpd/conf.d/laconica.conf %changelog +* Sat Feb 28 2009 Zach Copley - 0.7.2 +- Changed version number to 0.7.2. + * Sat Feb 28 2009 Ken Sedgwick - 0.7.1-1 - Modified RPM for Fedora. -- cgit v1.2.3-54-g00ecf From 344f7194f0b7be60c8cfc616db39f748a914d0ed Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 4 Mar 2009 17:03:01 -0800 Subject: Fixed wrong date on my msg in the laconica.spec changelog section --- scripts/laconica.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/laconica.spec b/scripts/laconica.spec index afa2766a4..5f0ed5fa9 100644 --- a/scripts/laconica.spec +++ b/scripts/laconica.spec @@ -74,7 +74,7 @@ rm -rf %buildroot %config(noreplace) %{_sysconfdir}/httpd/conf.d/laconica.conf %changelog -* Sat Feb 28 2009 Zach Copley - 0.7.2 +* Wed Mar 03 2009 Zach Copley - 0.7.2 - Changed version number to 0.7.2. * Sat Feb 28 2009 Ken Sedgwick - 0.7.1-1 -- cgit v1.2.3-54-g00ecf From 38b6946349d39359ce9f4b5ec37967a48e192862 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 4 Mar 2009 18:14:52 -0800 Subject: Stubs for Twitter-compatible API search methods --- actions/twitapisearch.php | 97 +++++++++++++++++++++++++++++++++++++++++++++++ actions/twitapitrends.php | 90 +++++++++++++++++++++++++++++++++++++++++++ lib/router.php | 20 +++++++--- 3 files changed, 202 insertions(+), 5 deletions(-) create mode 100644 actions/twitapisearch.php create mode 100644 actions/twitapitrends.php diff --git a/actions/twitapisearch.php b/actions/twitapisearch.php new file mode 100644 index 000000000..822ee77e1 --- /dev/null +++ b/actions/twitapisearch.php @@ -0,0 +1,97 @@ +. + * + * @category Search + * @package Laconica + * @author Zach Copley + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/twitterapi.php'; + +/** + * Action handler for Twitter-compatible API search + * + * @category Search + * @package Laconica + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + * @see TwitterapiAction + */ + +class TwitapisearchAction extends TwitterapiAction +{ + + var $query; + var $limit; + var $callback; + + /** + * Initialization. + * + * @param array $args Web and URL arguments + * + * @return boolean false if user doesn't exist + */ + + function prepare($args) + { + parent::prepare($args); + $qeury = $this->trimmed('query'); + + return true; + } + + /** + * Handle a request + * + * @param array $args Arguments from $_REQUEST + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + $this->showResults($this->limit); + } + + /** + * Show search results + * + * @param int $limit Number of notices to show + * + * @return void + */ + + function showResults($limit) + { + $this->serverError(_('API method under construction.'), $code = 501); + } + +} diff --git a/actions/twitapitrends.php b/actions/twitapitrends.php new file mode 100644 index 000000000..c73d89446 --- /dev/null +++ b/actions/twitapitrends.php @@ -0,0 +1,90 @@ +. + * + * @category Search + * @package Laconica + * @author Zach Copley + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/twitterapi.php'; + +/** + * Returns the top ten queries that are currently trending + * + * @category Search + * @package Laconica + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + * + * @see TwitterapiAction + */ + +class TwitapitrendsAction extends TwitterapiAction +{ + + var $callback; + + /** + * Initialization. + * + * @param array $args Web and URL arguments + * + * @return boolean false if user doesn't exist + */ + function prepare($args) + { + parent::prepare($args); + return true; + } + + /** + * Handle a request + * + * @param array $args Arguments from $_REQUEST + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + $this->showTrends(); + } + + /** + * Output the trends + * + * @return void + */ + function showTrends() + { + $this->serverError(_('API method under construction.'), $code = 501); + } + +} \ No newline at end of file diff --git a/lib/router.php b/lib/router.php index da57f8417..41c376a72 100644 --- a/lib/router.php +++ b/lib/router.php @@ -230,7 +230,7 @@ class Router $m->connect('api/users/:method/:argument', array('action' => 'api', - 'apiaction' => 'users'), + 'apiaction' => 'users'), array('method' => 'show(\.(xml|json))?')); $m->connect('api/users/:method', @@ -284,14 +284,14 @@ class Router array('action' => 'api', 'apiaction' => 'statuses', 'method' => 'friendsIDs')); - + foreach (array('xml', 'json') as $e) { $m->connect('api/friends/ids.'.$e, array('action' => 'api', 'apiaction' => 'statuses', 'method' => 'friendsIDs.'.$e)); } - + $m->connect('api/followers/ids/:argument', array('action' => 'api', 'apiaction' => 'statuses', @@ -305,11 +305,11 @@ class Router } // account - + $m->connect('api/account/:method', array('action' => 'api', 'apiaction' => 'account')); - + // favorites $m->connect('api/favorites/:method/:argument', @@ -352,6 +352,16 @@ class Router array('action' => 'api', 'apiaction' => 'laconica')); + + // search + + foreach (array('json', 'atom') as $e) { + $m->connect('api/search.'.$e, + array('action' => 'twitapisearch')); + } + + $m->connect('api/trends.json', array('action' => 'twitapitrends')); + // user stuff foreach (array('subscriptions', 'subscribers', -- cgit v1.2.3-54-g00ecf From 0c066db428843a6ca969c8523f0be2bcdfa278f7 Mon Sep 17 00:00:00 2001 From: CiaranG Date: Thu, 5 Mar 2009 14:35:50 +0000 Subject: Undo my previous change that breaks the Popular Notices section on the public timeline under MySQL --- lib/popularnoticesection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/popularnoticesection.php b/lib/popularnoticesection.php index f7fb93554..cbf458c34 100644 --- a/lib/popularnoticesection.php +++ b/lib/popularnoticesection.php @@ -54,7 +54,7 @@ class PopularNoticeSection extends NoticeSection $weightexpr='sum(exp(-(now() - fave.modified) / %s))'; } - $qry = 'SELECT notice.id, '. + $qry = 'SELECT notice.*, '. $weightexpr . ' as weight ' . 'FROM notice JOIN fave ON notice.id = fave.notice_id ' . 'GROUP BY notice.id ' . -- cgit v1.2.3-54-g00ecf From ea0c5f565c9ca4b34c1071a51333f0f842a954b9 Mon Sep 17 00:00:00 2001 From: CiaranG Date: Thu, 5 Mar 2009 14:52:35 +0000 Subject: The correct version of the bad fix I undid in the previous commit. Must explicitly specify all relevant columns in the GROUP BY. --- lib/popularnoticesection.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/popularnoticesection.php b/lib/popularnoticesection.php index cbf458c34..0505f0fa9 100644 --- a/lib/popularnoticesection.php +++ b/lib/popularnoticesection.php @@ -57,7 +57,9 @@ class PopularNoticeSection extends NoticeSection $qry = 'SELECT notice.*, '. $weightexpr . ' as weight ' . 'FROM notice JOIN fave ON notice.id = fave.notice_id ' . - 'GROUP BY notice.id ' . + 'GROUP BY notice.id,notice.profile_id,notice.content,notice.uri,' . + 'notice.rendered,notice.url,notice.created,notice.modified,' . + 'notice.reply_to,notice.is_local,notice.source ' . 'ORDER BY weight DESC'; $offset = 0; -- cgit v1.2.3-54-g00ecf From 97bc187e3132663ceadd2a779ce43ca67a7f3a02 Mon Sep 17 00:00:00 2001 From: CiaranG Date: Thu, 5 Mar 2009 16:15:29 +0000 Subject: PostgreSQL - the ts field in the OAuth nonce table needed to be an integer. (fix submitted by oxygene) --- db/laconica_pg.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/laconica_pg.sql b/db/laconica_pg.sql index 4ef2330f4..2d83f784a 100644 --- a/db/laconica_pg.sql +++ b/db/laconica_pg.sql @@ -181,7 +181,7 @@ create table nonce ( consumer_key varchar(255) not null /* comment 'unique identifier, root URL' */, tok char(32) not null /* comment 'identifying value' */, nonce char(32) not null /* comment 'nonce' */, - ts timestamp not null /* comment 'timestamp sent' */, + ts integer not null /* comment 'timestamp sent' values are epoch, and only used internally */, created timestamp not null default CURRENT_TIMESTAMP /* comment 'date this record was created' */, modified timestamp /* comment 'date this record was modified' */, -- cgit v1.2.3-54-g00ecf From e5345d8d7a5b519cfb24c9e2a971b485b3c1c872 Mon Sep 17 00:00:00 2001 From: CiaranG Date: Thu, 5 Mar 2009 16:18:31 +0000 Subject: PostgreSQL - fixed a couple more quoting issues --- classes/Notice_tag.php | 2 +- classes/User.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/classes/Notice_tag.php b/classes/Notice_tag.php index 0365973f5..f2247299a 100644 --- a/classes/Notice_tag.php +++ b/classes/Notice_tag.php @@ -40,7 +40,7 @@ class Notice_tag extends Memcached_DataObject $qry = 'SELECT notice.* ' . 'FROM notice JOIN notice_tag ON notice.id = notice_tag.notice_id ' . - 'WHERE notice_tag.tag = "%s" '; + "WHERE notice_tag.tag = '%s' "; return Notice::getStream(sprintf($qry, $tag), 'notice_tag:notice_stream:' . common_keyize($tag), diff --git a/classes/User.php b/classes/User.php index 40cf18df6..8b0b9acd5 100644 --- a/classes/User.php +++ b/classes/User.php @@ -589,7 +589,7 @@ class User extends Memcached_DataObject 'JOIN profile_tag ON (profile_tag.tagged = subscription.subscriber ' . 'AND profile_tag.tagger = subscription.subscribed) ' . 'WHERE subscription.subscribed = %d ' . - 'AND profile_tag.tag = "%s" ' . + "AND profile_tag.tag = '%s' " . 'AND subscription.subscribed != subscription.subscriber ' . 'ORDER BY subscription.created DESC '; @@ -617,7 +617,7 @@ class User extends Memcached_DataObject 'JOIN profile_tag on (profile_tag.tagged = subscription.subscribed ' . 'AND profile_tag.tagger = subscription.subscriber) ' . 'WHERE subscription.subscriber = %d ' . - 'AND profile_tag.tag = "%s" ' . + "AND profile_tag.tag = '%s' " . 'AND subscription.subscribed != subscription.subscriber ' . 'ORDER BY subscription.created DESC '; -- cgit v1.2.3-54-g00ecf From 3087e4ad5dfbccb7218c12ca747e1dbcf3a6415c Mon Sep 17 00:00:00 2001 From: CiaranG Date: Thu, 5 Mar 2009 16:23:39 +0000 Subject: Fixed bad field name in oauthstore. (fix submitted by oxygene) --- lib/oauthstore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/oauthstore.php b/lib/oauthstore.php index 7ad3be20e..9af05ea2d 100644 --- a/lib/oauthstore.php +++ b/lib/oauthstore.php @@ -63,7 +63,7 @@ class LaconicaOAuthDataStore extends OAuthDataStore if ($n->find(true)) { return true; } else { - $n->timestamp = $timestamp; + $n->ts = $timestamp; $n->created = DB_DataObject_Cast::dateTime(); $n->insert(); return false; -- cgit v1.2.3-54-g00ecf From b9781258bbfaac0e7fc91af4f77f1f340274a88e Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 5 Mar 2009 11:03:42 -0800 Subject: @-links go to permalinks for local users --- lib/util.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/util.php b/lib/util.php index f9a787d47..167508d2b 100644 --- a/lib/util.php +++ b/lib/util.php @@ -622,9 +622,15 @@ function common_at_link($sender_id, $nickname) $sender = Profile::staticGet($sender_id); $recipient = common_relative_profile($sender, common_canonical_nickname($nickname)); if ($recipient) { + $user = User::staticGet('id', $recipient->id); + if ($user) { + $url = common_local_url('userbyid', array('id' => $user->id)); + } else { + $url = $recipient->profileurl; + } $xs = new XMLStringer(false); $xs->elementStart('span', 'vcard'); - $xs->elementStart('a', array('href' => $recipient->profileurl, + $xs->elementStart('a', array('href' => $url, 'class' => 'url')); $xs->element('span', 'fn nickname', $nickname); $xs->elementEnd('a'); -- cgit v1.2.3-54-g00ecf From 87e5badcb6e8d89e2c85e2bf4be3748b8cd5f940 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 5 Mar 2009 14:37:27 -0800 Subject: add ping daemon to daemons --- scripts/startdaemons.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/startdaemons.sh b/scripts/startdaemons.sh index a3256966d..c3729761d 100755 --- a/scripts/startdaemons.sh +++ b/scripts/startdaemons.sh @@ -24,7 +24,7 @@ DIR=`dirname $0` for f in xmppdaemon.php jabberqueuehandler.php publicqueuehandler.php \ xmppconfirmhandler.php smsqueuehandler.php ombqueuehandler.php \ - twitterqueuehandler.php facebookqueuehandler.php; do + twitterqueuehandler.php facebookqueuehandler.php pingqueuehandler.php; do echo -n "Starting $f..."; php $DIR/$f -- cgit v1.2.3-54-g00ecf From 896f0340bf3710b79b117236e940b487a4b56460 Mon Sep 17 00:00:00 2001 From: CiaranG Date: Fri, 6 Mar 2009 19:28:15 +0000 Subject: Fixed problem with group lists not display all details - broken (by me) in 115519a5e7e84e57656c653918efb39ab4107fe9 --- lib/groupsbymemberssection.php | 4 ++-- lib/groupsbypostssection.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/groupsbymemberssection.php b/lib/groupsbymemberssection.php index 5f26c6626..963e21f15 100644 --- a/lib/groupsbymemberssection.php +++ b/lib/groupsbymemberssection.php @@ -45,10 +45,10 @@ class GroupsByMembersSection extends GroupSection { function getGroups() { - $qry = 'SELECT user_group.id, count(*) as value ' . + $qry = 'SELECT user_group.*, count(*) as value ' . 'FROM user_group JOIN group_member '. 'ON user_group.id = group_member.group_id ' . - 'GROUP BY user_group.id ' . + 'GROUP BY user_group.id,user_group.nickname,user_group.fullname,user_group.homepage,user_group.description,user_group.location,user_group.original_logo,user_group.homepage_logo,user_group.stream_logo,user_group.mini_logo,user_group.created,user_group.modified ' . 'ORDER BY value DESC '; $limit = GROUPS_PER_SECTION; diff --git a/lib/groupsbypostssection.php b/lib/groupsbypostssection.php index 1a60ddb4f..325b4033f 100644 --- a/lib/groupsbypostssection.php +++ b/lib/groupsbypostssection.php @@ -45,10 +45,10 @@ class GroupsByPostsSection extends GroupSection { function getGroups() { - $qry = 'SELECT user_group.id, count(*) as value ' . + $qry = 'SELECT user_group.*, count(*) as value ' . 'FROM user_group JOIN group_inbox '. 'ON user_group.id = group_inbox.group_id ' . - 'GROUP BY user_group.id ' . + 'GROUP BY user_group.id,user_group.nickname,user_group.fullname,user_group.homepage,user_group.description,user_group.location,user_group.original_logo,user_group.homepage_logo,user_group.stream_logo,user_group.mini_logo,user_group.created,user_group.modified ' . 'ORDER BY value DESC '; $limit = GROUPS_PER_SECTION; -- cgit v1.2.3-54-g00ecf From b1f337fe018dd3910c68589a98b0242470743a4c Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 6 Mar 2009 13:33:47 -0800 Subject: First crack at Twitter-like JSON search results for the API --- actions/twitapisearch.php | 97 ---------------- actions/twitapisearchjson.php | 149 ++++++++++++++++++++++++ lib/jsonsearchresultslist.php | 259 ++++++++++++++++++++++++++++++++++++++++++ lib/router.php | 10 +- lib/twitterapi.php | 28 ++++- 5 files changed, 436 insertions(+), 107 deletions(-) delete mode 100644 actions/twitapisearch.php create mode 100644 actions/twitapisearchjson.php create mode 100644 lib/jsonsearchresultslist.php diff --git a/actions/twitapisearch.php b/actions/twitapisearch.php deleted file mode 100644 index 822ee77e1..000000000 --- a/actions/twitapisearch.php +++ /dev/null @@ -1,97 +0,0 @@ -. - * - * @category Search - * @package Laconica - * @author Zach Copley - * @copyright 2008-2009 Control Yourself, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://laconi.ca/ - */ - -if (!defined('LACONICA')) { - exit(1); -} - -require_once INSTALLDIR.'/lib/twitterapi.php'; - -/** - * Action handler for Twitter-compatible API search - * - * @category Search - * @package Laconica - * @author Zach Copley - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://laconi.ca/ - * @see TwitterapiAction - */ - -class TwitapisearchAction extends TwitterapiAction -{ - - var $query; - var $limit; - var $callback; - - /** - * Initialization. - * - * @param array $args Web and URL arguments - * - * @return boolean false if user doesn't exist - */ - - function prepare($args) - { - parent::prepare($args); - $qeury = $this->trimmed('query'); - - return true; - } - - /** - * Handle a request - * - * @param array $args Arguments from $_REQUEST - * - * @return void - */ - - function handle($args) - { - parent::handle($args); - $this->showResults($this->limit); - } - - /** - * Show search results - * - * @param int $limit Number of notices to show - * - * @return void - */ - - function showResults($limit) - { - $this->serverError(_('API method under construction.'), $code = 501); - } - -} diff --git a/actions/twitapisearchjson.php b/actions/twitapisearchjson.php new file mode 100644 index 000000000..b50aa86b7 --- /dev/null +++ b/actions/twitapisearchjson.php @@ -0,0 +1,149 @@ +. + * + * @category Search + * @package Laconica + * @author Zach Copley + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/twitterapi.php'; +require_once INSTALLDIR.'/lib/jsonsearchresultslist.php'; + +/** + * Action handler for Twitter-compatible API search + * + * @category Search + * @package Laconica + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + * @see TwitterapiAction + */ + +class TwitapisearchjsonAction extends TwitterapiAction +{ + var $query; + var $lang; + var $rpp; + var $page; + var $since_id; + var $limit; + var $geocode; + + /** + * Initialization. + * + * @param array $args Web and URL arguments + * + * @return boolean true if nothing goes wrong + */ + + function prepare($args) + { + parent::prepare($args); + + $this->query = $this->trimmed('q'); + $this->lang = $this->trimmed('lang'); + $this->rpp = $this->trimmed('rpp'); + + if (!$this->rpp) { + $this->rpp = 15; + } + + if ($this->rpp > 100) { + $this->rpp = 100; + } + + $this->page = $this->trimmed('page'); + + if (!$this->page) { + $this->page = 1; + } + + $this->since_id = $this->trimmed('since_id'); + $this->geocode = $this->trimmed('geocode'); + + return true; + } + + /** + * Handle a request + * + * @param array $args Arguments from $_REQUEST + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + $this->showResults(); + } + + /** + * Show search results + * + * @return void + */ + + function showResults() + { + + // TODO: Support search operators like from: and to: + + $notice = new Notice(); + + // lcase it for comparison + $q = strtolower($this->query); + + $search_engine = $notice->getSearchEngine('identica_notices'); + $search_engine->set_sort_mode('chron'); + $search_engine->limit(($this->page - 1) * $this->rpp, $this->rpp + 1, true); + $search_engine->query($q); + $cnt = $notice->find(); + + // TODO: since_id, lang, geocode + + $results = new JSONSearchResultsList($notice, $q, $this->rpp, $this->page); + + $this->init_document('json'); + $results->show(); + $this->end_document('json'); + } + + /** + * This is a read-only action + * + * @return boolean true + */ + + function isReadOnly() + { + return true; + } +} \ No newline at end of file diff --git a/lib/jsonsearchresultslist.php b/lib/jsonsearchresultslist.php new file mode 100644 index 000000000..171e1db4d --- /dev/null +++ b/lib/jsonsearchresultslist.php @@ -0,0 +1,259 @@ +. + * + * @category Search + * @package Laconica + * @author Zach Copley + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +/** + * widget-like class for showing JSON search results + * + * @category Search + * @package Laconica + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + * + */ + +class JSONSearchResultsList +{ + protected $notice; // protected attrs invisible to json_encode() + protected $rpp; + + // The below attributes are carefully named so the JSON output from + // this obj matches the output from search.twitter.com + + var $results; + var $since_id; + var $max_id; + var $refresh_url; + var $results_per_page; + var $completed_in; + var $page; + var $query; + + /** + * constructor + * + * @param Notice $notice stream of notices from DB_DataObject + */ + + function __construct($notice, $query, $rpp, $page, $since_id = 0) + { + $this->notice = $notice; + $this->query = urlencode($query); + $this->results_per_page = $this->rpp = $rpp; + $this->page = $page; + $this->since_id = $since_id; + $this->results = array(); + } + + /** + * show the list of search results + * + * @return int count of the search results listed. + */ + + function show() + { + $cnt = 0; + + $time_start = microtime(true); + + while ($this->notice->fetch() && $cnt <= $this->rpp) { + $cnt++; + + // XXX: Hmmm. this depends on desc sort order + if (!$this->max_id) { + $this->max_id = (int)$this->notice->id; + } + + if ($cnt > $this->rpp) { + break; + } + + $item = new ResultItem($this->notice); + array_push($this->results, $item); + } + + $time_end = microtime(true); + $this->completed_in = $time_end - $time_start; + + // Set other attrs + + $this->refresh_url = '?since_id=' . $this->max_id . + '&q=' . $this->query; + + // pagination stuff + + if ($cnt > $this->rpp) { + $this->next_page = '?page=' . ($this->page + 1) . + '&max_id=' . $this->max_id; + if ($this->rpp != 15) { + $this->next_page .= '&rpp=' . $this->rpp; + } + $this->next_page .= '&q=' . $this->query; + } + + if ($this->page > 1) { + $this->previous_page = '?page=' . ($this->page - 1) . + '&max_id=' . $this->max_id; + if ($this->rpp != 15) { + $this->previous_page .= '&rpp=' . $this->rpp; + } + $this->previous_page .= '&q=' . $this->query; + } + + print json_encode($this); + + return $cnt; + } +} + +/** + * widget for displaying a single JSON search result + * + * @category UI + * @package Laconica + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + * @see JSONSearchResultsList + */ + +class ResultItem +{ + /** The notice this item is based on. */ + + protected $notice; // protected attrs invisible to json_encode() + + /** The profile associated with the notice. */ + + protected $profile; + + // The below attributes are carefully named so the JSON output from + // this obj matches the output from search.twitter.com + + var $text; + var $to_user_id; + var $to_user; + var $from_user; + var $id; + var $from_user_id; + var $iso_language_code; + var $source; + var $profile_image_url; + var $created_at; + + /** + * constructor + * + * Also initializes the profile attribute. + * + * @param Notice $notice The notice we'll display + */ + + function __construct($notice) + { + $this->notice = $notice; + $this->profile = $notice->getProfile(); + $this->buildResult(); + } + + /** + * Build a search result object + * + * This populates the the result in preparation for JSON encoding. + * + * @return void + */ + + function buildResult() + { + $this->text = $this->notice->content; + $replier_profile = null; + + if ($this->notice->reply_to) { + $reply = Notice::staticGet(intval($notice->reply_to)); + if ($reply) { + $replier_profile = $reply->getProfile(); + } + } + + $this->to_user_id = ($replier_profile) ? + intval($replier_profile->id) : null; + $this->to_user = ($replier_profile) ? + $replier_profile->nickname : null; + $this->from_user = $this->profile->nickname; + $this->id = $this->notice->id; + $this->from_user_id = $this->profile->id; + + $user = User::staticGet('id', $this->profile->id); + $this->iso_language_code = $this->user->language; + + $this->source = $this->getSourceLink($this->notice->source); + + $avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE); + $this->profile_image_url = ($avatar) ? + $avatar->displayUrl() : Avatar::defaultImage(AVATAR_STREAM_SIZE); + + $this->created_at = date('r', $this->notice->created); + } + + /** + * Show the source of the notice + * + * Either the name (and link) of the API client that posted the notice, + * or one of other other channels. + * + * @return string the source of the Notice + */ + + function getSourceLink($source) + { + $source_name = _($source); + switch ($source) { + case 'web': + case 'xmpp': + case 'mail': + case 'omb': + case 'api': + break; + default: + $ns = Notice_source::staticGet($source); + if ($ns) { + $source_name = '' . $ns->name . ''; + } + break; + } + return $source_name; + } + +} diff --git a/lib/router.php b/lib/router.php index 41c376a72..516b48122 100644 --- a/lib/router.php +++ b/lib/router.php @@ -354,17 +354,13 @@ class Router // search - - foreach (array('json', 'atom') as $e) { - $m->connect('api/search.'.$e, - array('action' => 'twitapisearch')); - } - + $m->connect('api/search.atom', array('action' => 'twitapisearchatom')); + $m->connect('api/search.json', array('action' => 'twitapisearchjson')); $m->connect('api/trends.json', array('action' => 'twitapitrends')); // user stuff - foreach (array('subscriptions', 'subscribers', + foreach (array('subscriptions', 'subscribers', 'nudge', 'xrds', 'all', 'foaf', 'replies', 'inbox', 'outbox', 'microsummary') as $a) { $m->connect(':nickname/'.$a, diff --git a/lib/twitterapi.php b/lib/twitterapi.php index 74f265cbb..1de169a0b 100644 --- a/lib/twitterapi.php +++ b/lib/twitterapi.php @@ -24,11 +24,33 @@ class TwitterapiAction extends Action var $auth_user; + /** + * Initialization. + * + * @param array $args Web and URL arguments + * + * @return boolean false if user doesn't exist + */ + + function prepare($args) + { + parent::prepare($args); + return true; + } + + /** + * Handle a request + * + * @param array $args Arguments from $_REQUEST + * + * @return void + */ + function handle($args) { parent::handle($args); } - + function twitter_user_array($profile, $get_notice=false) { @@ -86,7 +108,7 @@ class TwitterapiAction extends Action ($replier_profile) ? $replier_profile->nickname : null; if (isset($this->auth_user)) { - $twitter_status['favorited'] = + $twitter_status['favorited'] = ($this->auth_user->hasFave($notice)) ? 'true' : 'false'; } else { $twitter_status['favorited'] = 'false'; @@ -399,7 +421,7 @@ class TwitterapiAction extends Action $t = strtotime($dt); return date("D M d G:i:s O Y", $t); } - + // XXX: Candidate for a general utility method somewhere? function count_subscriptions($profile) { -- cgit v1.2.3-54-g00ecf