]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.7.x' into 0.8.x
authorSarven Capadisli <csarven@controlyourself.ca>
Wed, 8 Apr 2009 22:55:13 +0000 (22:55 +0000)
committerSarven Capadisli <csarven@controlyourself.ca>
Wed, 8 Apr 2009 22:55:13 +0000 (22:55 +0000)
35 files changed:
README
actions/all.php
actions/favorited.php
actions/groupsearch.php
actions/noticesearch.php
actions/peoplesearch.php
actions/public.php
actions/publictagcloud.php
actions/replies.php
actions/showfavorites.php
actions/showstream.php
actions/subscribers.php
actions/subscriptions.php
actions/userauthorization.php
actions/usergroups.php
config.php.sample
db/notice_source.sql
doc-src/bookmarklet [new file with mode: 0644]
doc-src/help
lib/action.php
lib/common.php
lib/galleryaction.php
lib/groupminilist.php
lib/mailbox.php
lib/profileaction.php [new file with mode: 0644]
lib/profileminilist.php
lib/searchaction.php
lib/twitter.php
plugins/TemplatePlugin.php [new file with mode: 0644]
scripts/laconica.spec
scripts/synctwitterfriends.php
theme/base/css/display.css
theme/default/css/display.css
theme/identica/css/display.css
tpl/index.php [new file with mode: 0644]

diff --git a/README b/README
index 2053bb8d25fec093f96ad19536ff081e7ce24665..dcd5f6a953efc748769822d1b15d0ee64e4ab907 100644 (file)
--- a/README
+++ b/README
@@ -2,8 +2,8 @@
 README
 ------
 
-Laconica 0.7.2.1 ("Talk about the Passion")
-11 March 2009
+Laconica 0.7.3 ("You Are The Everything")
+7 April 2009
 
 This is the README file for Laconica, the Open Source microblogging
 platform. It includes installation instructions, descriptions of
@@ -71,93 +71,29 @@ for additional terms.
 New this version
 ================
 
-This is a minor bug-fix and feature release since version 0.7.1,
-released Feb 9 2009. Notable changes this version:
-
-- First version of a web-based installer
-- Use Net_URL_Mapper instead of mod_rewrite to map "fancy URLs",
-  for a much simpler installation and use of PATH_INFO on sites
-  that don't have mod_rewrite.
-- A plugin framework for system events, to make it easier to build
-  server-side plugins.
-- A plugin for Google Analytics
-- A plugin to use blogspam.net to check notices for spam
-- A plugin to send linkbacks for notices about blog posts
-- Configurable check for duplicate notices in a specific time
-  period
-- Better Atom feeds
-- First implementation of Twitter Search API
-- Add streamlined mobile device-friendly styles when enabled in config.
-- A queue server for sending notices to Twitter
-- A queue server for sending notices to Facebook
-- A queue server for sending notices to a ping server
-- Fixed a bug in nonces for OAuth in OpenMicroBlogging
-- Fixed bugs in transfer of avatars in OpenMicroBlogging
-- @-links go to permalinks for local users
-- Better handling of DB errors (instead of dreaded DB_DataObject blank
-  screen)
-- Initial version of an RPM spec file
-- More consistent display of notices in notice search
-- A stylesheet for printed output
-- "Social graph" methods for Twitter API
-- Documentation for the JavaScript badge
-- Debugged a ton of problems that happened with E_NOTICE on
-- Better caching in RSS feeds
-- Optionally send email when an @-message is received
-- Automatically add tags for every group message
-- Add framebusting JavaScript to help avoid clickjacking attacks.
-- Optionally ignore some notice sources for public page.
-- Add default SMS carriers and notice sources to distribution file.
-- Change titles to use mixed case instead of all uppercase.
-- Use exceptions for error handling.
-
-Changes in version 0.7.1:
-
-- Vast improvement in auto-linking to URLs.
-- Link to group search from user's group page
-- Improved interface in Facebook application
-- Fix bad redirects in delete notice
-- Updated PostgreSQL database creation script
-- Show filesize in avatar/logo upload
-- Vastly improved avatar/logo upload
-- Allow re-authentication with OpenID
-- Correctly link hashtabs inside parens and brackets
-- Group and avatar image transparency works
-- Better handling of commands through the Web and Ajax channels
-- Fix links for profile page feeds
-- Fixed destroy method in API
-- Fix endpoint of Connect menu when XMPP disabled
-- Show number of group members
-- Enable configuration files in /etc/laconica/
-
-Changes in version 0.7.0:
-
-- Support for groups. Users can join groups and send themed notices
-  to those groups. All other members of the group receive the notices.
-- Laconica-specific extensions to the Twitter API.
-- A Facebook application.
-- A massive UI redesign. The HTML generated by Laconica has changed
-  significantly, to make theming easier and to give a more open look
-  by default. Also, sidebar.
-- Massive code hygiene changes to move towards compliance with the PEAR
-  coding standards and to support the new UI redesign.
-- Began the breakup of util.php -- moved about 30% of code to a views
-  hierarchy.
-- UI elements for statistical information (like top posters or most
-  popular groups) added in a sidebar.
-- include Javascript badge by Kent Brewster.
-- Updated online documentation.
-- Cropping of user avatars using Jcrop.
-- fix for Twitter bridge to not send "Expect:" headers.
-- add 'dm' as a synonym for 'd' in commands.
-- Upgrade upstream version of jQuery to 1.3.
-- Upgrade upstream version of PHP-OpenID to 2.1.2.
-- Move OpenMicroBlogging specification to its own repository.
-- Make tag-based RSS streams work.
-- Additional locales: Bulgarian, Catalan, Greek, Hebrew, simplified
-  Chinese, Telugu, Taiwanese Chinese, Vietnamese,
-- PostgreSQL updates.
-- Nasty bug in Twitter bridge that wouldn't verify with Twitter
+This is a minor bug-fix and feature release since version 0.7.2.1,
+released Mar 11 2009. Notable changes this version:
+
+- A plugin to allow a templating language for customization
+- A plugin for Piwik Analytics engine
+- A bookmarklet for posting a notice about a Web page you're reading
+- A welcome notice ('welcomebot') and default subscription for new users
+- Support for SSL for some or all pages on the site
+- Better handling of empty notice lists on many pages
+- Major improvements to the Twitter friend-sync offline processing
+- subscribers, subscriptions, groups are listed on the Personal page.
+- "Invite" link restored to main menu
+- Better memory handling in FOAF output
+- Fix for SUP support (FriendFeed)
+- Correct and intelligent redirect HTTP status codes
+- Fix DB collations for search and sort
+- Better H1s and Titles using user full names
+- Fixes to make the linkback plugin operational
+- Better indication that a notice is being published by Ajax (spinner)
+- Better and unified Atom output
+- Hiding "register" and "join now" messages when site is closed
+- ping, twitter and facebook queuehandlers working better
+- Updated RPM spec
 
 Prerequisites
 =============
@@ -261,9 +197,9 @@ especially if you've previously installed PHP/MySQL packages.
 1. Unpack the tarball you downloaded on your Web server. Usually a
    command like this will work:
 
-          tar zxf laconica-0.7.2.1.tar.gz
+          tar zxf laconica-0.7.3.tar.gz
 
-   ...which will make a laconica-0.7.2.1 subdirectory in your current
+   ...which will make a laconica-0.7.3 subdirectory in your current
    directory. (If you don't have shell access on your Web server, you
    may have to unpack the tarball on your local computer and FTP the
    files to the server.)
@@ -271,7 +207,7 @@ especially if you've previously installed PHP/MySQL packages.
 2. Move the tarball to a directory of your choosing in your Web root
    directory. Usually something like this will work:
 
-          mv laconica-0.7.2.1 /var/www/mublog
+          mv laconica-0.7.3 /var/www/mublog
 
    This will make your Laconica instance available in the mublog path of
    your server, like "http://example.net/mublog". "microblog" or
@@ -761,7 +697,7 @@ Upgrading
 If you've been using Laconica 0.6, 0.5 or lower, or if you've been
 tracking the "git" version of the software, you will probably want
 to upgrade and keep your existing data. There is no automated upgrade
-procedure in Laconica 0.7.2.1. Try these step-by-step instructions; read
+procedure in Laconica 0.7.3. Try these step-by-step instructions; read
 to the end first before trying them.
 
 0. Download Laconica and set up all the prerequisites as if you were
@@ -1209,7 +1145,7 @@ repository (see below), and you get a compilation error ("unexpected
 T_STRING") in the browser, check to see that you don't have any
 conflicts in your code.
 
-If you upgraded to Laconica 0.7.2.1 without reading the "Notice inboxes"
+If you upgraded to Laconica 0.7.3 without reading the "Notice inboxes"
 section above, and all your users' 'Personal' tabs are empty, read the
 "Notice inboxes" section above.
 
@@ -1298,6 +1234,8 @@ if anyone's been overlooked in error.
 * Leslie Michael Orchard
 * Eric Helgeson
 * Ken Sedgwick
+* Brian Hendrickson
+* Tobias Diekershoff
 
 Thanks also to the developers of our upstream library code and to the
 thousands of people who have tried out Identi.ca, installed Laconi.ca,
index 8e67ec0f3b49eff9db6b6b7020762b952f9843da..f5bbfe2e395c00138bdc990da56f01a9326a5bb8 100644 (file)
@@ -23,31 +23,13 @@ require_once INSTALLDIR.'/lib/personalgroupnav.php';
 require_once INSTALLDIR.'/lib/noticelist.php';
 require_once INSTALLDIR.'/lib/feedlist.php';
 
-class AllAction extends Action
+class AllAction extends ProfileAction
 {
-    var $user = null;
-    var $page = null;
-
     function isReadOnly()
     {
         return true;
     }
 
-    function prepare($args)
-    {
-        parent::prepare($args);
-        $nickname = common_canonical_nickname($this->arg('nickname'));
-        $this->user = User::staticGet('nickname', $nickname);
-        $this->page = $this->trimmed('page');
-        if (!$this->page) {
-            $this->page = 1;
-        }
-
-        common_set_returnto($this->selfUrl());
-
-        return true;
-    }
-
     function handle($args)
     {
         parent::handle($args);
@@ -93,6 +75,27 @@ class AllAction extends Action
         $nav->show();
     }
 
+    function showEmptyListMessage()
+    {
+        $message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->user->nickname) . ' ';
+
+        if (common_logged_in()) {
+            $current_user = common_current_user();
+            if ($this->user->id === $current_user->id) {
+                $message .= _('Try subscribing to more people, [join a group](%%action.groups) or post something yourself.');
+            } else {
+                $message .= sprintf(_('You can try to [nudge %s](../%s) from his profile or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname);
+            }
+        }
+        else {
+            $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname);
+        }
+
+        $this->elementStart('div', 'guide');
+        $this->raw(common_markup_to_html($message));
+        $this->elementEnd('div');
+    }
+
     function showContent()
     {
         $notice = $this->user->noticesWithFriends(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
@@ -101,6 +104,10 @@ class AllAction extends Action
 
         $cnt = $nl->show();
 
+        if (0 == $cnt) {
+            $this->showEmptyListMessage();
+        }
+
         $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
                           $this->page, 'all', array('nickname' => $this->user->nickname));
     }
index 20a354674f4185cee36c7d18e7198691e914c442..09ab1216a64cdfa256a1e4a6b3dda9ace53bbac3 100644 (file)
@@ -104,9 +104,9 @@ class FavoritedAction extends Action
     {
         parent::prepare($args);
         $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
-        
+
         common_set_returnto($this->selfUrl());
-        
+
         return true;
     }
 
@@ -143,14 +143,11 @@ class FavoritedAction extends Action
         $this->elementStart('div', 'instructions');
         $this->raw($output);
         $this->elementEnd('div');
+    }
 
-        $favorite = new Fave;
-
-        if ($favorite->count()) {
-            return;
-        }
-
-        $message = _('Favorite notices appear on this page but noone has favorited one yet.') . ' ';
+    function showEmptyList()
+    {
+        $message = _('Favorite notices appear on this page but no one has favorited one yet.') . ' ';
 
         if (common_logged_in()) {
             $message .= _('Be the first to add a notice to your favorites by clicking the fave button next to any notice you like.');
@@ -159,7 +156,7 @@ class FavoritedAction extends Action
             $message .= _('Why not [register an account](%%action.register%%) and be the first to add a notice to your favorites!');
         }
 
-        $this->elementStart('div', 'blankfiller');
+        $this->elementStart('div', 'guide');
         $this->raw(common_markup_to_html($message));
         $this->elementEnd('div');
     }
@@ -217,6 +214,10 @@ class FavoritedAction extends Action
 
         $cnt = $nl->show();
 
+        if ($cnt == 0) {
+            $this->showEmptyList();
+        }
+
         $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
                           $this->page, 'favorited');
     }
index 109a53ce112704a0ff6f92795fb703e1e1c53b06..06b4a77550cf33bd672207ace03e135e31754c93 100644 (file)
@@ -72,12 +72,23 @@ class GroupsearchAction extends SearchAction
             $terms = preg_split('/[\s,]+/', $q);
             $results = new GroupSearchResults($user_group, $terms, $this);
             $results->show();
+            $user_group->free();
+            $this->pagination($page > 1, $cnt > GROUPS_PER_PAGE,
+                          $page, 'groupsearch', array('q' => $q));
         } else {
-            $this->element('p', 'error', _('No results'));
+            $this->element('p', 'error', _('No results.'));
+            $this->searchSuggestions($q);
+            if (common_logged_in()) {
+                $message = _('If you can\'t find the group you\'re looking for, you can [create it](%%action.newgroup%%) yourself.');
+            }
+            else {
+                $message = _('Why not [register an account](%%action.register%%) and [create the group](%%action.newgroup%%) yourself!');
+            }
+            $this->elementStart('div', 'guide');
+            $this->raw(common_markup_to_html($message));
+            $this->elementEnd('div');
+            $user_group->free();
         }
-        $user_group->free();
-        $this->pagination($page > 1, $cnt > GROUPS_PER_PAGE,
-                          $page, 'groupsearch', array('q' => $q));
     }
 }
 
@@ -98,10 +109,5 @@ class GroupSearchResults extends GroupList
     {
         return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text));
     }
-
-    function isReadOnly()
-    {
-        return true;
-    }
 }
 
index 9058cf53c302d83ab6af1fc66c9602b8b032f2e6..095d0a454a157ff96ddcc3137dea89ed89557233 100644 (file)
@@ -114,22 +114,27 @@ class NoticesearchAction extends SearchAction
             $cnt = $notice->find();
         }
         if ($cnt === 0) {
-            $this->element('p', 'error', _('No results'));
+            $this->element('p', 'error', _('No results.'));
+
+            $this->searchSuggestions($q);
+            if (common_logged_in()) {
+                $message = sprintf(_('Be the first to [post on this topic](%%%%action.newnotice%%%%?status_textarea=%s)!'), urlencode($q));
+            }
+            else {
+                $message = sprintf(_('Why not [register an account](%%%%action.register%%%%) and be the first to  [post on this topic](%%%%action.newnotice%%%%?status_textarea=%s)!'), urlencode($q));
+            }
+
+            $this->elementStart('div', 'guide');
+            $this->raw(common_markup_to_html($message));
+            $this->elementEnd('div');
             return;
         }
         $terms = preg_split('/[\s,]+/', $q);
         $nl = new SearchNoticeList($notice, $this, $terms);
-
         $cnt = $nl->show();
-
         $this->pagination($page > 1, $cnt > NOTICES_PER_PAGE,
                           $page, 'noticesearch', array('q' => $q));
     }
-
-    function isReadOnly()
-    {
-        return true;
-    }
 }
 
 class SearchNoticeList extends NoticeList {
index 9e515ade1a9e7b6d21bab32f18e3e9cf44bc5677..65d970dd159a7fea183763d9fd8488fdde4cb2ab 100644 (file)
@@ -60,14 +60,8 @@ class PeoplesearchAction extends SearchAction
 
     function showResults($q, $page)
     {
-
         $profile = new Profile();
-
-        // lcase it for comparison
-        // $q = strtolower($q);
-
         $search_engine = $profile->getSearchEngine('identica_people');
-
         $search_engine->set_sort_mode('chron');
         // Ask for an extra to see if there's more.
         $search_engine->limit((($page-1)*PROFILES_PER_PAGE), PROFILES_PER_PAGE + 1);
@@ -81,14 +75,15 @@ class PeoplesearchAction extends SearchAction
             $terms = preg_split('/[\s,]+/', $q);
             $results = new PeopleSearchResults($profile, $terms, $this);
             $results->show();
+            $profile->free();
+            $this->pagination($page > 1, $cnt > PROFILES_PER_PAGE,
+                          $page, 'peoplesearch', array('q' => $q));
+
         } else {
-            $this->element('p', 'error', _('No results'));
+            $this->element('p', 'error', _('No results.'));
+            $this->searchSuggestions($q);
+            $profile->free();
         }
-
-        $profile->free();
-
-        $this->pagination($page > 1, $cnt > PROFILES_PER_PAGE,
-                          $page, 'peoplesearch', array('q' => $q));
     }
 }
 
index 5a2720a9ad919cdbb8442e3832c1c470908d5be2..5a380de9a8bb984ddf03b579febb03e348c6c147 100644 (file)
@@ -166,20 +166,9 @@ class PublicAction extends Action
         $nav->show();
     }
 
-    function showPageNotice()
+    function showEmptyList()
     {
-        $notice = new Notice;
-
-        if (!$notice) {
-            $this->serverError(_('Could not retrieve public stream.'));
-            return;
-        }
-
-        if ($notice->count()) {
-            return;
-        }
-
-        $message = _('This is the public timeline for %%site.name%% but noone has posted anything yet.') . ' ';
+        $message = _('This is the public timeline for %%site.name%% but no one has posted anything yet.') . ' ';
 
         if (common_logged_in()) {
             $message .= _('Be the first to post!');
@@ -188,7 +177,7 @@ class PublicAction extends Action
             $message .= _('Why not [register an account](%%action.register%%) and be the first to post!');
         }
 
-        $this->elementStart('div', 'blankfiller');
+        $this->elementStart('div', 'guide');
         $this->raw(common_markup_to_html($message));
         $this->elementEnd('div');
     }
@@ -216,6 +205,10 @@ class PublicAction extends Action
 
         $cnt = $nl->show();
 
+        if ($cnt == 0) {
+            $this->showEmptyList();
+        }
+
         $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
                           $this->page, 'public');
     }
index 5bc7e0cbffcf7a56a544e8cb8607aec864b53ef5..855cfed9b53437ea8a4ddd33f280e334b74cf15f 100644 (file)
@@ -62,13 +62,11 @@ class PublictagcloudAction extends Action
         $this->element('p', 'instructions',
                        sprintf(_('These are most popular recent tags on %s '),
                                common_config('site', 'name')));
+    }
 
-        $tags = new Notice_tag;
-        if ($tags->count()) {
-            return;
-        }
-
-        $message = _('Noone has posted a notice with a [hashtag](%%doc.tags%%) yet.') . ' ';
+    function showEmptyList()
+    {
+        $message = _('No one has posted a notice with a [hashtag](%%doc.tags%%) yet.') . ' ';
 
         if (common_logged_in()) {
             $message .= _('Be the first to post one!');
@@ -77,7 +75,7 @@ class PublictagcloudAction extends Action
             $message .= _('Why not [register an account](%%action.register%%) and be the first to post one!');
         }
 
-        $this->elementStart('div', 'blankfiller');
+        $this->elementStart('div', 'guide');
         $this->raw(common_markup_to_html($message));
         $this->elementEnd('div');
     }
@@ -144,6 +142,8 @@ class PublictagcloudAction extends Action
             $this->elementEnd('dd');
             $this->elementEnd('dl');
             $this->elementEnd('div');
+        } else {
+            $this->showEmptyList();
         }
     }
 
index 4ab9b14ed26be98187ee0949c0a2a4b3fac352c6..2769cb4227c05ea2796167a6e61a07913de4a4a0 100644 (file)
@@ -166,12 +166,36 @@ class RepliesAction extends Action
         $nl = new NoticeList($notice, $this);
 
         $cnt = $nl->show();
+        if (0 === $cnt) {
+            $this->showEmptyListMessage();
+        }
 
         $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
                           $this->page, 'replies',
                           array('nickname' => $this->user->nickname));
     }
 
+    function showEmptyListMessage()
+    {
+        $message = sprintf(_('This is the timeline showing replies to %s but %s hasn\'t received a notice to his attention yet.'), $this->user->nickname, $this->user->nickname) . ' ';
+
+        if (common_logged_in()) {
+            $current_user = common_current_user();
+            if ($this->user->id === $current_user->id) {
+                $message .= _('You can engage other users in a conversation, subscribe to more people or [join groups](%%action.groups%%).');
+            } else {
+                $message .= sprintf(_('You can try to [nudge %s](../%s) or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname);
+            }
+        }
+        else {
+            $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname);
+        }
+
+        $this->elementStart('div', 'guide');
+        $this->raw(common_markup_to_html($message));
+        $this->elementEnd('div');
+    }
+
     function isReadOnly()
     {
         return true;
index d1c9283f0f47bff1a6811039a891760898de3f87..4d434950548e14d00a0f0d738f225e90d62fb605 100644 (file)
@@ -162,6 +162,25 @@ class ShowfavoritesAction extends Action
         $nav->show();
     }
 
+    function showEmptyListMessage()
+    {
+        if (common_logged_in()) {
+            $current_user = common_current_user();
+            if ($this->user->id === $current_user->id) {
+                $message = _('You haven\'t chosen any favorite notices yet. Click the fave button on notices you like to bookmark them for later or shed a spotlight on them.');
+            } else {
+                $message = sprintf(_('%s hasn\'t added any notices to his favorites yet. Post something interesting they would add to their favorites :)'), $this->user->nickname);
+            }
+        }
+        else {
+            $message = sprintf(_('%s hasn\'t added any notices to his favorites yet. Why not [register an account](%%%%action.register%%%%) and then post something interesting they would add to thier favorites :)'), $this->user->nickname);
+        }
+
+        $this->elementStart('div', 'guide');
+        $this->raw(common_markup_to_html($message));
+        $this->elementEnd('div');
+    }
+
     /**
      * Show the content
      *
@@ -183,9 +202,17 @@ class ShowfavoritesAction extends Action
         $nl = new NoticeList($notice, $this);
 
         $cnt = $nl->show();
+        if (0 == $cnt) {
+            $this->showEmptyListMessage();
+        }
 
         $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
                           $this->page, 'showfavorites',
                           array('nickname' => $this->user->nickname));
     }
+
+    function showPageNotice() {
+        $this->element('p', 'instructions', _('This is a way to share what you like.'));
+    }
 }
+
index f5886f3d33b12b08417f8313c13239f58a3dd082..ce237dae225560dcdbdea879473851bc768b9f23 100644 (file)
@@ -54,12 +54,8 @@ require_once INSTALLDIR.'/lib/feedlist.php';
  * @link     http://laconi.ca/
  */
 
-class ShowstreamAction extends Action
+class ShowstreamAction extends ProfileAction
 {
-    var $user = null;
-    var $page = null;
-    var $profile = null;
-
     function isReadOnly()
     {
         return true;
@@ -82,45 +78,6 @@ class ShowstreamAction extends Action
         }
     }
 
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        $nickname_arg = $this->arg('nickname');
-        $nickname = common_canonical_nickname($nickname_arg);
-
-        // Permanent redirect on non-canonical nickname
-
-        if ($nickname_arg != $nickname) {
-            $args = array('nickname' => $nickname);
-            if ($this->arg('page') && $this->arg('page') != 1) {
-                $args['page'] = $this->arg['page'];
-            }
-            common_redirect(common_local_url('showstream', $args), 301);
-            return false;
-        }
-
-        $this->user = User::staticGet('nickname', $nickname);
-
-        if (!$this->user) {
-            $this->clientError(_('No such user.'), 404);
-            return false;
-        }
-
-        $this->profile = $this->user->getProfile();
-
-        if (!$this->profile) {
-            $this->serverError(_('User has no profile.'));
-            return false;
-        }
-
-        $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
-
-        common_set_returnto($this->selfUrl());
-
-        return true;
-    }
-
     function handle($args)
     {
 
@@ -372,165 +329,39 @@ class ShowstreamAction extends Action
                        _('Subscribe'));
     }
 
-    function showNotices()
-    {
-        $notice = $this->user->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
-
-        $pnl = new ProfileNoticeList($notice, $this);
-        $cnt = $pnl->show();
-
-        $this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page,
-                          'showstream', array('nickname' => $this->user->nickname));
-    }
-
-    function showSections()
-    {
-        $this->showSubscriptions();
-        $this->showSubscribers();
-        $this->showGroups();
-        $this->showStatistics();
-        $cloud = new PersonalTagCloudSection($this, $this->user);
-        $cloud->show();
-    }
-
-    function showSubscriptions()
-    {
-        $profile = $this->user->getSubscriptions(0, PROFILES_PER_MINILIST + 1);
-
-        $this->elementStart('div', array('id' => 'entity_subscriptions',
-                                         'class' => 'section'));
-
-        $this->element('h2', null, _('Subscriptions'));
-
-        if ($profile) {
-            $pml = new ProfileMiniList($profile, $this->user, $this);
-            $cnt = $pml->show();
-            if ($cnt == 0) {
-                $this->element('p', null, _('(None)'));
-            }
-        }
-
-        if ($cnt > PROFILES_PER_MINILIST) {
-            $this->elementStart('p');
-            $this->element('a', array('href' => common_local_url('subscriptions',
-                                                                 array('nickname' => $this->profile->nickname)),
-                                      'class' => 'more'),
-                           _('All subscriptions'));
-            $this->elementEnd('p');
-        }
-
-        $this->elementEnd('div');
-    }
-
-    function showSubscribers()
+    function showEmptyListMessage()
     {
-        $profile = $this->user->getSubscribers(0, PROFILES_PER_MINILIST + 1);
-
-        $this->elementStart('div', array('id' => 'entity_subscribers',
-                                         'class' => 'section'));
+        $message = sprintf(_('This is the timeline for %s but %s hasn\'t posted anything yet.'), $this->user->nickname, $this->user->nickname) . ' ';
 
-        $this->element('h2', null, _('Subscribers'));
-
-        if ($profile) {
-            $pml = new ProfileMiniList($profile, $this->user, $this);
-            $cnt = $pml->show();
-            if ($cnt == 0) {
-                $this->element('p', null, _('(None)'));
+        if (common_logged_in()) {
+            $current_user = common_current_user();
+            if ($this->user->id === $current_user->id) {
+                $message .= _('Seen anything interesting recently? You haven\'t posted any notices yet, now would be a good time to start :)');
+            } else {
+                $message .= sprintf(_('You can try to nudge %s or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%s).'), $this->user->nickname, '@' . $this->user->nickname);
             }
         }
-
-        if ($cnt > PROFILES_PER_MINILIST) {
-            $this->elementStart('p');
-            $this->element('a', array('href' => common_local_url('subscribers',
-                                                                 array('nickname' => $this->profile->nickname)),
-                                      'class' => 'more'),
-                           _('All subscribers'));
-            $this->elementEnd('p');
+        else {
+            $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname);
         }
 
+        $this->elementStart('div', 'guide');
+        $this->raw(common_markup_to_html($message));
         $this->elementEnd('div');
     }
 
-    function showStatistics()
-    {
-        // XXX: WORM cache this
-        $subs = new Subscription();
-        $subs->subscriber = $this->profile->id;
-        $subs_count = (int) $subs->count() - 1;
-
-        $subbed = new Subscription();
-        $subbed->subscribed = $this->profile->id;
-        $subbed_count = (int) $subbed->count() - 1;
-
-        $notices = new Notice();
-        $notices->profile_id = $this->profile->id;
-        $notice_count = (int) $notices->count();
-
-        $this->elementStart('div', array('id' => 'entity_statistics',
-                                         'class' => 'section'));
-
-        $this->element('h2', null, _('Statistics'));
-
-        // Other stats...?
-        $this->elementStart('dl', 'entity_member-since');
-        $this->element('dt', null, _('Member since'));
-        $this->element('dd', null, date('j M Y',
-                                        strtotime($this->profile->created)));
-        $this->elementEnd('dl');
-
-        $this->elementStart('dl', 'entity_subscriptions');
-        $this->elementStart('dt');
-        $this->element('a', array('href' => common_local_url('subscriptions',
-                                                             array('nickname' => $this->profile->nickname))),
-                       _('Subscriptions'));
-        $this->elementEnd('dt');
-        $this->element('dd', null, (is_int($subs_count)) ? $subs_count : '0');
-        $this->elementEnd('dl');
-
-        $this->elementStart('dl', 'entity_subscribers');
-        $this->elementStart('dt');
-        $this->element('a', array('href' => common_local_url('subscribers',
-                                                             array('nickname' => $this->profile->nickname))),
-                       _('Subscribers'));
-        $this->elementEnd('dt');
-        $this->element('dd', 'subscribers', (is_int($subbed_count)) ? $subbed_count : '0');
-        $this->elementEnd('dl');
-
-        $this->elementStart('dl', 'entity_notices');
-        $this->element('dt', null, _('Notices'));
-        $this->element('dd', null, (is_int($notice_count)) ? $notice_count : '0');
-        $this->elementEnd('dl');
-
-        $this->elementEnd('div');
-    }
-
-    function showGroups()
+    function showNotices()
     {
-        $groups = $this->user->getGroups(0, GROUPS_PER_MINILIST + 1);
-
-        $this->elementStart('div', array('id' => 'entity_groups',
-                                         'class' => 'section'));
-
-        $this->element('h2', null, _('Groups'));
-
-        if ($groups) {
-            $gml = new GroupMiniList($groups, $this->user, $this);
-            $cnt = $gml->show();
-            if ($cnt == 0) {
-                $this->element('p', null, _('(None)'));
-            }
-        }
+        $notice = $this->user->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
 
-        if ($cnt > GROUPS_PER_MINILIST) {
-            $this->elementStart('p');
-            $this->element('a', array('href' => common_local_url('usergroups',
-                                                                 array('nickname' => $this->profile->nickname)),
-                                      'class' => 'more'),
-                           _('All groups'));
-            $this->elementEnd('p');
+        $pnl = new ProfileNoticeList($notice, $this);
+        $cnt = $pnl->show();
+        if (0 == $cnt) {
+            $this->showEmptyListMessage();
         }
 
-        $this->elementEnd('div');
+        $this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page,
+                          'showstream', array('nickname' => $this->user->nickname));
     }
 
     function showAnonymousMessage()
@@ -550,6 +381,12 @@ class ShowstreamAction extends Action
         $this->elementEnd('div');
     }
 
+    function showSections()
+    {
+        parent::showSections();
+        $cloud = new PersonalTagCloudSection($this, $this->user);
+        $cloud->show();
+    }
 }
 
 // We don't show the author for a profile, since we already know who it is!
index 22faafaef945293dbd0279b57f6ff87424028538..7ebb54d33d6b6b00204bf8df7806650afd10f9d1 100644 (file)
@@ -88,6 +88,9 @@ class SubscribersAction extends GalleryAction
         if ($subscribers) {
             $subscribers_list = new SubscribersList($subscribers, $this->user, $this);
             $cnt = $subscribers_list->show();
+            if (0 == $cnt) {
+                $this->showEmptyListMessage();
+            }
         }
 
         $subscribers->free();
@@ -96,6 +99,25 @@ class SubscribersAction extends GalleryAction
                           $this->page, 'subscribers',
                           array('nickname' => $this->user->nickname));
     }
+
+    function showEmptyListMessage()
+    {
+        if (common_logged_in()) {
+            $current_user = common_current_user();
+            if ($this->user->id === $current_user->id) {
+                $message = _('You have no subscribers. Try subscribing to people you know and they might return the favor');
+            } else {
+                $message = sprintf(_('%s has no subscribers. Want to be the first?'), $this->user->nickname);
+            }
+        }
+        else {
+            $message = sprintf(_('%s has no subscribers. Why not [register an account](%%%%action.register%%%%) and be the first?'), $this->user->nickname);
+        }
+
+        $this->elementStart('div', 'guide');
+        $this->raw(common_markup_to_html($message));
+        $this->elementEnd('div');
+    }
 }
 
 class SubscribersList extends ProfileList
index 3fbea2039d29ec3250acf1b20a2471d99b4288d1..b0c0a9b8dff39ecacaa7da246b4b79128bfb2670 100644 (file)
@@ -95,6 +95,9 @@ class SubscriptionsAction extends GalleryAction
         if ($subscriptions) {
             $subscriptions_list = new SubscriptionsList($subscriptions, $this->user, $this);
             $cnt = $subscriptions_list->show();
+            if (0 == $cnt) {
+                $this->showEmptyListMessage();
+            }
         }
 
         $subscriptions->free();
@@ -103,6 +106,25 @@ class SubscriptionsAction extends GalleryAction
                           $this->page, 'subscriptions',
                           array('nickname' => $this->user->nickname));
     }
+
+    function showEmptyListMessage()
+    {
+        if (common_logged_in()) {
+            $current_user = common_current_user();
+            if ($this->user->id === $current_user->id) {
+                $message = _('You\'re not listening to anyone\'s notices right now, try subscribing to people you know. Try [people search](%%action.peoplesearch%%), look for members in groups you\'re interested in and in our [featured users](%%action.featured%%). If you\'re a [Twitter user](%%action.twittersettings%%), you can automatically subscribe to people you already follow there.');
+            } else {
+                $message = sprintf(_('%s is not listening to anyone.'), $this->user->nickname);
+            }
+        }
+        else {
+            $message = sprintf(_('%s is not listening to anyone.'), $this->user->nickname);
+        }
+
+        $this->elementStart('div', 'guide');
+        $this->raw(common_markup_to_html($message));
+        $this->elementEnd('div');
+    }
 }
 
 class SubscriptionsList extends ProfileList
index 6a76e3a4c20809e9eb37537e1a1258fa77bab3a4..8723848c7b975a7ff700774eef7dab9ebeacd81f 100644 (file)
@@ -25,7 +25,7 @@ define('TIMESTAMP_THRESHOLD', 300);
 class UserauthorizationAction extends Action
 {
     var $error;
-    var $req;
+    var $params;
 
     function handle($args)
     {
@@ -35,8 +35,8 @@ class UserauthorizationAction extends Action
             # CSRF protection
             $token = $this->trimmed('token');
             if (!$token || $token != common_session_token()) {
-                $req = $this->getStoredRequest();
-                $this->showForm($req, _('There was a problem with your session token. '.
+                $params = $this->getStoredParams();
+                $this->showForm($params, _('There was a problem with your session token. '.
                                         'Try again, please.'));
                 return;
             }
@@ -50,18 +50,13 @@ class UserauthorizationAction extends Action
                 common_redirect(common_local_url('login'));
                 return;
             }
+
             try {
-                # this must be a new request
-                $req = $this->getNewRequest();
-                if (!$req) {
-                    $this->clientError(_('No request found!'));
-                }
-                # XXX: only validate new requests, since nonce is one-time use
-                $this->validateRequest($req);
-                $this->storeRequest($req);
-                $this->showForm($req);
+                $this->validateRequest();
+                $this->storeParams($_GET);
+                $this->showForm($_GET);
             } catch (OAuthException $e) {
-                $this->clearRequest();
+                $this->clearParams();
                 $this->clientError($e->getMessage());
                 return;
             }
@@ -69,9 +64,9 @@ class UserauthorizationAction extends Action
         }
     }
 
-    function showForm($req, $error=null)
+    function showForm($params, $error=null)
     {
-        $this->req = $req;
+        $this->params = $params;
         $this->error = $error;
         $this->showPage();
     }
@@ -91,16 +86,16 @@ class UserauthorizationAction extends Action
 
     function showContent()
     {
-        $req = $this->req;
+        $params = $this->params;
 
-        $nickname = $req->get_parameter('omb_listenee_nickname');
-        $profile = $req->get_parameter('omb_listenee_profile');
-        $license = $req->get_parameter('omb_listenee_license');
-        $fullname = $req->get_parameter('omb_listenee_fullname');
-        $homepage = $req->get_parameter('omb_listenee_homepage');
-        $bio = $req->get_parameter('omb_listenee_bio');
-        $location = $req->get_parameter('omb_listenee_location');
-        $avatar = $req->get_parameter('omb_listenee_avatar');
+        $nickname = $params['omb_listenee_nickname'];
+        $profile = $params['omb_listenee_profile'];
+        $license = $params['omb_listenee_license'];
+        $fullname = $params['omb_listenee_fullname'];
+        $homepage = $params['omb_listenee_homepage'];
+        $bio = $params['omb_listenee_bio'];
+        $location = $params['omb_listenee_location'];
+        $avatar = $params['omb_listenee_avatar'];
 
         $this->elementStart('div', 'profile');
         if ($avatar) {
@@ -147,56 +142,56 @@ class UserauthorizationAction extends Action
 
     function sendAuthorization()
     {
-        $req = $this->getStoredRequest();
+        $params = $this->getStoredParams();
 
-        if (!$req) {
+        if (!$params) {
             $this->clientError(_('No authorization request!'));
             return;
         }
 
-        $callback = $req->get_parameter('oauth_callback');
+        $callback = $params['oauth_callback'];
 
         if ($this->arg('accept')) {
-            if (!$this->authorizeToken($req)) {
+            if (!$this->authorizeToken($params)) {
                 $this->clientError(_('Error authorizing token'));
             }
-            if (!$this->saveRemoteProfile($req)) {
+            if (!$this->saveRemoteProfile($params)) {
                 $this->clientError(_('Error saving remote profile'));
             }
             if (!$callback) {
-                $this->showAcceptMessage($req->get_parameter('oauth_token'));
+                $this->showAcceptMessage($params['oauth_token']);
             } else {
-                $params = array();
-                $params['oauth_token'] = $req->get_parameter('oauth_token');
-                $params['omb_version'] = OMB_VERSION_01;
-                $user = User::staticGet('uri', $req->get_parameter('omb_listener'));
+                $newparams = array();
+                $newparams['oauth_token'] = $params['oauth_token'];
+                $newparams['omb_version'] = OMB_VERSION_01;
+                $user = User::staticGet('uri', $params['omb_listener']);
                 $profile = $user->getProfile();
                 if (!$profile) {
                     common_log_db_error($user, 'SELECT', __FILE__);
                     $this->serverError(_('User without matching profile'));
                     return;
                 }
-                $params['omb_listener_nickname'] = $user->nickname;
-                $params['omb_listener_profile'] = common_local_url('showstream',
+                $newparams['omb_listener_nickname'] = $user->nickname;
+                $newparams['omb_listener_profile'] = common_local_url('showstream',
                                                                    array('nickname' => $user->nickname));
                 if (!is_null($profile->fullname)) {
-                    $params['omb_listener_fullname'] = $profile->fullname;
+                    $newparams['omb_listener_fullname'] = $profile->fullname;
                 }
                 if (!is_null($profile->homepage)) {
-                    $params['omb_listener_homepage'] = $profile->homepage;
+                    $newparams['omb_listener_homepage'] = $profile->homepage;
                 }
                 if (!is_null($profile->bio)) {
-                    $params['omb_listener_bio'] = $profile->bio;
+                    $newparams['omb_listener_bio'] = $profile->bio;
                 }
                 if (!is_null($profile->location)) {
-                    $params['omb_listener_location'] = $profile->location;
+                    $newparams['omb_listener_location'] = $profile->location;
                 }
                 $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
                 if ($avatar) {
-                    $params['omb_listener_avatar'] = $avatar->url;
+                    $newparams['omb_listener_avatar'] = $avatar->url;
                 }
                 $parts = array();
-                foreach ($params as $k => $v) {
+                foreach ($newparams as $k => $v) {
                     $parts[] = $k . '=' . OAuthUtil::urlencode_rfc3986($v);
                 }
                 $query_string = implode('&', $parts);
@@ -214,12 +209,10 @@ class UserauthorizationAction extends Action
         }
     }
 
-    function authorizeToken(&$req)
+    function authorizeToken(&$params)
     {
-        $consumer_key = $req->get_parameter('oauth_consumer_key');
-        $token_field = $req->get_parameter('oauth_token');
+        $token_field = $params['oauth_token'];
         $rt = new Token();
-        $rt->consumer_key = $consumer_key;
         $rt->tok = $token_field;
         $rt->type = 0;
         $rt->state = 0;
@@ -235,21 +228,21 @@ class UserauthorizationAction extends Action
 
     # XXX: refactor with similar code in finishremotesubscribe.php
 
-    function saveRemoteProfile(&$req)
+    function saveRemoteProfile(&$params)
     {
         # FIXME: we should really do this when the consumer comes
         # back for an access token. If they never do, we've got stuff in a
         # weird state.
 
-        $nickname = $req->get_parameter('omb_listenee_nickname');
-        $fullname = $req->get_parameter('omb_listenee_fullname');
-        $profile_url = $req->get_parameter('omb_listenee_profile');
-        $homepage = $req->get_parameter('omb_listenee_homepage');
-        $bio = $req->get_parameter('omb_listenee_bio');
-        $location = $req->get_parameter('omb_listenee_location');
-        $avatar_url = $req->get_parameter('omb_listenee_avatar');
+        $nickname = $params['omb_listenee_nickname'];
+        $fullname = $params['omb_listenee_fullname'];
+        $profile_url = $params['omb_listenee_profile'];
+        $homepage = $params['omb_listenee_homepage'];
+        $bio = $params['omb_listenee_bio'];
+        $location = $params['omb_listenee_location'];
+        $avatar_url = $params['omb_listenee_avatar'];
 
-        $listenee = $req->get_parameter('omb_listenee');
+        $listenee = $params['omb_listenee'];
         $remote = Remote_profile::staticGet('uri', $listenee);
 
         if ($remote) {
@@ -309,14 +302,11 @@ class UserauthorizationAction extends Action
         }
 
         $user = common_current_user();
-        $datastore = omb_oauth_datastore();
-        $consumer = $this->getConsumer($datastore, $req);
-        $token = $this->getToken($datastore, $req, $consumer);
 
         $sub = new Subscription();
         $sub->subscriber = $user->id;
         $sub->subscribed = $remote->id;
-        $sub->token = $token->key; # NOTE: request token, not valid for use!
+        $sub->token = $params['oauth_token']; # NOTE: request token, not valid for use!
         $sub->created = DB_DataObject_Cast::dateTime(); # current time
 
         if (!$sub->insert()) {
@@ -360,65 +350,59 @@ class UserauthorizationAction extends Action
         common_show_footer();
     }
 
-    function storeRequest($req)
+    function storeParams($params)
     {
         common_ensure_session();
-        $_SESSION['userauthorizationrequest'] = $req;
+        $_SESSION['userauthorizationparams'] = $params;
     }
 
-    function clearRequest()
+    function clearParams()
     {
         common_ensure_session();
-        unset($_SESSION['userauthorizationrequest']);
+        unset($_SESSION['userauthorizationparams']);
     }
 
-    function getStoredRequest()
+    function getStoredParams()
     {
         common_ensure_session();
-        $req = $_SESSION['userauthorizationrequest'];
-        return $req;
-    }
-
-    function getNewRequest()
-    {
-        common_remove_magic_from_request();
-        $req = OAuthRequest::from_request();
-        return $req;
+        $params = $_SESSION['userauthorizationparams'];
+        return $params;
     }
 
     # Throws an OAuthException if anything goes wrong
 
-    function validateRequest(&$req)
+    function validateRequest()
     {
-        # OAuth stuff -- have to copy from OAuth.php since they're
-        # all private methods, and there's no user-authentication method
-        $this->checkVersion($req);
-        $datastore = omb_oauth_datastore();
-        $consumer = $this->getConsumer($datastore, $req);
-        $token = $this->getToken($datastore, $req, $consumer);
-        $this->checkTimestamp($req);
-        $this->checkNonce($datastore, $req, $consumer, $token);
-        $this->checkSignature($req, $consumer, $token);
-        $this->validateOmb($req);
+        /* Find token.
+           TODO: If no token is passed the user should get a prompt to enter it
+                 according to OAuth Core 1.0 */
+        $t = new Token();
+        $t->tok = $_GET['oauth_token'];
+        $t->type = 0;
+        if (!$t->find(true)) {
+            throw new OAuthException("Invalid request token: " . $_GET['oauth_token']);
+        }
+
+        $this->validateOmb();
         return true;
     }
 
-    function validateOmb(&$req)
+    function validateOmb()
     {
         foreach (array('omb_version', 'omb_listener', 'omb_listenee',
                        'omb_listenee_profile', 'omb_listenee_nickname',
                        'omb_listenee_license') as $param)
         {
-            if (is_null($req->get_parameter($param))) {
+            if (!isset($_GET[$param]) || is_null($_GET[$param])) {
                 throw new OAuthException("Required parameter '$param' not found");
             }
         }
         # Now, OMB stuff
-        $version = $req->get_parameter('omb_version');
+        $version = $_GET['omb_version'];
         if ($version != OMB_VERSION_01) {
             throw new OAuthException("OpenMicroBlogging version '$version' not supported");
         }
-        $listener =    $req->get_parameter('omb_listener');
+        $listener = $_GET['omb_listener'];
         $user = User::staticGet('uri', $listener);
         if (!$user) {
             throw new OAuthException("Listener URI '$listener' not found here");
@@ -427,7 +411,7 @@ class UserauthorizationAction extends Action
         if ($cur->id != $user->id) {
             throw new OAuthException("Can't add for another user!");
         }
-        $listenee = $req->get_parameter('omb_listenee');
+        $listenee = $_GET['omb_listenee'];
         if (!Validate::uri($listenee) &&
             !common_valid_tag($listenee)) {
             throw new OAuthException("Listenee URI '$listenee' not a recognizable URI");
@@ -450,13 +434,13 @@ class UserauthorizationAction extends Action
                 throw new OAuthException("Already subscribed to user!");
             }
         }
-        $nickname = $req->get_parameter('omb_listenee_nickname');
+        $nickname = $_GET['omb_listenee_nickname'];
         if (!Validate::string($nickname, array('min_length' => 1,
                                                'max_length' => 64,
                                                'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) {
             throw new OAuthException('Nickname must have only letters and numbers and no spaces.');
         }
-        $profile = $req->get_parameter('omb_listenee_profile');
+        $profile = $_GET['omb_listenee_profile'];
         if (!common_valid_http_url($profile)) {
             throw new OAuthException("Invalid profile URL '$profile'.");
         }
@@ -465,7 +449,7 @@ class UserauthorizationAction extends Action
             throw new OAuthException("Profile URL '$profile' is for a local user.");
         }
 
-        $license = $req->get_parameter('omb_listenee_license');
+        $license = $_GET['omb_listenee_license'];
         if (!common_valid_http_url($license)) {
             throw new OAuthException("Invalid license URL '$license'.");
         }
@@ -474,23 +458,23 @@ class UserauthorizationAction extends Action
             throw new OAuthException("Listenee stream license '$license' not compatible with site license '$site_license'.");
         }
         # optional stuff
-        $fullname = $req->get_parameter('omb_listenee_fullname');
+        $fullname = $_GET['omb_listenee_fullname'];
         if ($fullname && mb_strlen($fullname) > 255) {
             throw new OAuthException("Full name '$fullname' too long.");
         }
-        $homepage = $req->get_parameter('omb_listenee_homepage');
+        $homepage = $_GET['omb_listenee_homepage'];
         if ($homepage && (!common_valid_http_url($homepage) || mb_strlen($homepage) > 255)) {
             throw new OAuthException("Invalid homepage '$homepage'");
         }
-        $bio = $req->get_parameter('omb_listenee_bio');
+        $bio = $_GET['omb_listenee_bio'];
         if ($bio && mb_strlen($bio) > 140) {
             throw new OAuthException("Bio too long '$bio'");
         }
-        $location = $req->get_parameter('omb_listenee_location');
+        $location = $_GET['omb_listenee_location'];
         if ($location && mb_strlen($location) > 255) {
             throw new OAuthException("Location too long '$location'");
         }
-        $avatar = $req->get_parameter('omb_listenee_avatar');
+        $avatar = $_GET['omb_listenee_avatar'];
         if ($avatar) {
             if (!common_valid_http_url($avatar) || strlen($avatar) > 255) {
                 throw new OAuthException("Invalid avatar URL '$avatar'");
@@ -507,7 +491,7 @@ class UserauthorizationAction extends Action
                 throw new OAuthException("Wrong image type for '$avatar'");
             }
         }
-        $callback = $req->get_parameter('oauth_callback');
+        $callback = $_GET['oauth_callback'];
         if ($callback && !common_valid_http_url($callback)) {
             throw new OAuthException("Invalid callback URL '$callback'");
         }
@@ -515,92 +499,4 @@ class UserauthorizationAction extends Action
             throw new OAuthException("Callback URL '$callback' is for local site.");
         }
     }
-
-    # Snagged from OAuthServer
-
-    function checkVersion(&$req)
-    {
-        $version = $req->get_parameter("oauth_version");
-        if (!$version) {
-            $version = 1.0;
-        }
-        if ($version != 1.0) {
-            throw new OAuthException("OAuth version '$version' not supported");
-        }
-        return $version;
-    }
-
-    # Snagged from OAuthServer
-
-    function getConsumer($datastore, $req)
-    {
-        $consumer_key = @$req->get_parameter("oauth_consumer_key");
-        if (!$consumer_key) {
-            throw new OAuthException("Invalid consumer key");
-        }
-
-        $consumer = $datastore->lookup_consumer($consumer_key);
-        if (!$consumer) {
-            throw new OAuthException("Invalid consumer");
-        }
-        return $consumer;
-    }
-
-    # Mostly cadged from OAuthServer
-
-    function getToken($datastore, &$req, $consumer)
-    {/*{{{*/
-        $token_field = @$req->get_parameter('oauth_token');
-        $token = $datastore->lookup_token($consumer, 'request', $token_field);
-        if (!$token) {
-            throw new OAuthException("Invalid $token_type token: $token_field");
-        }
-        return $token;
-    }
-
-    function checkTimestamp(&$req)
-    {
-        $timestamp = @$req->get_parameter('oauth_timestamp');
-        $now = time();
-        if ($now - $timestamp > TIMESTAMP_THRESHOLD) {
-            throw new OAuthException("Expired timestamp, yours $timestamp, ours $now");
-        }
-    }
-
-    # NOTE: don't call twice on the same request; will fail!
-    function checkNonce(&$datastore, &$req, $consumer, $token)
-    {
-        $timestamp = @$req->get_parameter('oauth_timestamp');
-        $nonce = @$req->get_parameter('oauth_nonce');
-        $found = $datastore->lookup_nonce($consumer, $token, $nonce, $timestamp);
-        if ($found) {
-            throw new OAuthException("Nonce already used");
-        }
-        return true;
-    }
-
-    function checkSignature(&$req, $consumer, $token)
-    {
-        $signature_method = $this->getSignatureMethod($req);
-        $signature = $req->get_parameter('oauth_signature');
-        $valid_sig = $signature_method->check_signature($req,
-                                                        $consumer,
-                                                        $token,
-                                                        $signature);
-        if (!$valid_sig) {
-            throw new OAuthException("Invalid signature");
-        }
-    }
-
-    function getSignatureMethod(&$req)
-    {
-        $signature_method = @$req->get_parameter("oauth_signature_method");
-        if (!$signature_method) {
-            $signature_method = "PLAINTEXT";
-        }
-        if ($signature_method != 'HMAC-SHA1') {
-            throw new OAuthException("Signature method '$signature_method' not supported.");
-        }
-        return omb_hmac_sha1();
-    }
 }
index ded4ba76b1bc1bcd7371d33d91cd318008ba6a16..06b2334bf5b636801e99b32f87e7ecfc830eea2e 100644 (file)
@@ -139,10 +139,28 @@ class UsergroupsAction extends Action
         if ($groups) {
             $gl = new GroupList($groups, $this->user, $this);
             $cnt = $gl->show();
+            if (0 == $cnt) {
+                $this->showEmptyListMessage();
+            }
         }
 
         $this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE,
                           $this->page, 'usergroups',
                           array('nickname' => $this->user->nickname));
     }
+
+    function showEmptyListMessage()
+    {
+        $message = sprintf(_('%s is not a member of any group.'), $this->user->nickname) . ' ';
+
+        if (common_logged_in()) {
+            $current_user = common_current_user();
+            if ($this->user->id === $current_user->id) {
+                $message .= _('Try [searching for groups](%%action.groupsearch%%) and joining them.');
+            }
+        }
+        $this->elementStart('div', 'guide');
+        $this->raw(common_markup_to_html($message));
+        $this->elementEnd('div');
+    }
 }
index d62a54fe74a14ade40f6e11023030816683ce835..0a01552feced630bb1ba3d6e3e49c89633f41250 100644 (file)
@@ -163,6 +163,10 @@ $config['sphinx']['port'] = 3312;
 # require_once('plugins/GoogleAnalyticsPlugin.php');
 # $ga = new GoogleAnalyticsPlugin('your secret code');
 
+# Use Templating (template: /tpl/index.php)
+# require_once('plugins/TemplatePlugin.php');
+# $tpl = new TemplatePlugin();
+
 #Don't allow saying the same thing more than once per hour
 #$config['site']['dupelimit'] = 3600;
 #Don't enforce the dupe limit
index 5d48e66b62c987fea013d8a981aa4d442512f07b..52d555dbfe1cdc2f69c35b86fcf9382429d236d2 100644 (file)
@@ -21,6 +21,7 @@ VALUES
     ('mbpidgin','mbpidgin','http://code.google.com/p/microblog-purple/', now()),
     ('Mobidentica', 'Mobidentica', 'http://www.substanceofcode.com/software/mobidentica/', now()),
     ('moconica','Moconica','http://moconica.com/', now()),
+    ('peoplebrowsr', 'PeopleBrowsr', 'http://www.peoplebrowsr.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()),
diff --git a/doc-src/bookmarklet b/doc-src/bookmarklet
new file mode 100644 (file)
index 0000000..6cd2c08
--- /dev/null
@@ -0,0 +1,7 @@
+A bookmarklet is a small piece of javascript code used as a bookmark. This one will let you post to %%site.name%% simply by selecting some text on a page and pressing the bookmarklet.
+
+Drag-and-drop the following link to your bookmarks bar or right-click it and add it to your browser favorites to keep it handy.
+
+<MTMarkdownOptions output='raw'>
+<a href="javascript:var%20d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='http://%%site.server%%/%%site.path%%/index.php?action=newnotice',l=d.location,e=encodeURIComponent,g=f+'&status_textarea=%22'+((e(s))?e(s):e(document.title))+'%22 from '+l.href;function%20a(){if(!w.open(g,'t','toolbar=0,resizable=0,scrollbars=1,status=1,width=800,height=570')){l.href=g;}}a();void(0);">Post to %%site.name%%</a>
+</MTMarkdownOptions>
index a8cfccd2b368132486a0439828ca1996a72d44f0..02cf0d14b0c1247f6db38ee9b72239ee7a2a608d 100644 (file)
@@ -30,3 +30,5 @@ Here are some documents that you might find helpful in understanding
 * [OpenMicroBlogging](%%doc.openmublog%%) - subscribing to remote users
 * [Privacy](%%doc.privacy%%) - %%site.name%%'s privacy policy
 * [Source](%%doc.source%%) - How to get the Laconica source code
+* [Badge](%%doc.badge%%) - How to put a Laconica badge on your blog or homepage
+* [Bookmarklet](%%doc.bookmarklet%%) - Bookmarklet for posting Web pages
\ No newline at end of file
index 75531d34b51a308bec75e393445ddcd9fbac452f..f2027e0f17c68daef94a50f04ec074907ed4cf35 100644 (file)
@@ -97,9 +97,18 @@ class Action extends HTMLOutputter // lawsuit
             $this->startHTML();
             Event::handle('EndShowHTML', array($this));
         }
+        if (Event::handle('StartShowHead', array($this))) {
         $this->showHead();
+            Event::handle('EndShowHead', array($this));
+        }
+        if (Event::handle('StartShowBody', array($this))) {
         $this->showBody();
+            Event::handle('EndShowBody', array($this));
+        }
+        if (Event::handle('StartEndHTML', array($this))) {
         $this->endHTML();
+            Event::handle('EndEndHTML', array($this));
+        }
     }
 
     /**
@@ -402,13 +411,8 @@ class Action extends HTMLOutputter // lawsuit
             if ($user) {
                 $this->menuItem(common_local_url('all', array('nickname' => $user->nickname)),
                                 _('Home'), _('Personal profile and friends timeline'), false, 'nav_home');
-            }
-            $this->menuItem(common_local_url('peoplesearch'),
-                            _('Search'), _('Search for people or text'), false, 'nav_search');
-            if ($user) {
                 $this->menuItem(common_local_url('profilesettings'),
                                 _('Account'), _('Change your email, avatar, password, profile'), false, 'nav_account');
-
                 if (common_config('xmpp', 'enabled')) {
                     $this->menuItem(common_local_url('imsettings'),
                                     _('Connect'), _('Connect to IM, SMS, Twitter'), false, 'nav_connect');
@@ -416,20 +420,28 @@ class Action extends HTMLOutputter // lawsuit
                     $this->menuItem(common_local_url('smssettings'),
                                     _('Connect'), _('Connect to SMS, Twitter'), false, 'nav_connect');
                 }
+                $this->menuItem(common_local_url('invite'),
+                                 _('Invite'),
+                                 sprintf(_('Invite friends and colleagues to join you on %s'),
+                                 common_config('site', 'name')),
+                                 false, 'nav_invitecontact');
                 $this->menuItem(common_local_url('logout'),
                                 _('Logout'), _('Logout from the site'), false, 'nav_logout');
-            } else {
-                $this->menuItem(common_local_url('login'),
-                                _('Login'), _('Login to the site'), false, 'nav_login');
+            }
+            else {
                 if (!common_config('site', 'closed')) {
                     $this->menuItem(common_local_url('register'),
                                     _('Register'), _('Create an account'), false, 'nav_register');
                 }
                 $this->menuItem(common_local_url('openidlogin'),
                                 _('OpenID'), _('Login with OpenID'), false, 'nav_openid');
+                $this->menuItem(common_local_url('login'),
+                                _('Login'), _('Login to the site'), false, 'nav_login');
             }
             $this->menuItem(common_local_url('doc', array('title' => 'help')),
                             _('Help'), _('Help me!'), false, 'nav_help');
+            $this->menuItem(common_local_url('peoplesearch'),
+                            _('Search'), _('Search for people or text'), false, 'nav_search');
             Event::handle('EndPrimaryNav', array($this));
         }
         $this->elementEnd('ul');
@@ -606,7 +618,10 @@ class Action extends HTMLOutputter // lawsuit
     {
         $this->elementStart('div', array('id' => 'aside_primary',
                                          'class' => 'aside'));
+        if (Event::handle('StartShowExportData', array($this))) {
         $this->showExportData();
+            Event::handle('EndShowExportData', array($this));
+        }
         if (Event::handle('StartShowSections', array($this))) {
             $this->showSections();
             Event::handle('EndShowSections', array($this));
@@ -923,11 +938,15 @@ class Action extends HTMLOutputter // lawsuit
      *
      * @return string current URL
      */
+
     function selfUrl()
     {
         $action = $this->trimmed('action');
         $args   = $this->args;
         unset($args['action']);
+        if (array_key_exists('submit', $args)) {
+            unset($args['submit']);
+        }
         foreach (array_keys($_COOKIE) as $cookie) {
             unset($args[$cookie]);
         }
index c2037c3ad6b0e29c189e404a732c079a3689f542..b3882d207958b2dd211519aa9136c7210f641c9b 100644 (file)
@@ -19,7 +19,7 @@
 
 if (!defined('LACONICA')) { exit(1); }
 
-define('LACONICA_VERSION', '0.7.2.1');
+define('LACONICA_VERSION', '0.7.3');
 
 define('AVATAR_PROFILE_SIZE', 96);
 define('AVATAR_STREAM_SIZE', 48);
index 25a5e3fd59dd52386eb25e3bd9bd94386e912aec..8e21d7393b082950a3113b7758e6f1a601a36ad3 100644 (file)
@@ -50,7 +50,7 @@ class GalleryAction extends Action
             if ($this->arg('page') && $this->arg('page') != 1) {
                 $args['page'] = $this->arg['page'];
             }
-            common_redirect(common_local_url('subscriptions', $args), 301);
+            common_redirect(common_local_url($this->trimmed('action'), $args), 301);
             return false;
         }
 
@@ -71,6 +71,7 @@ class GalleryAction extends Action
         $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
 
         $this->tag = $this->trimmed('tag');
+        $this->q   = $this->trimmed('q');
 
         return true;
     }
@@ -87,7 +88,7 @@ class GalleryAction extends Action
                # Post from the tag dropdown; redirect to a GET
 
                if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-                   common_redirect($this->selfUrl(), 307);
+                   common_redirect($this->selfUrl(), 303);
             return;
                }
 
@@ -136,7 +137,7 @@ class GalleryAction extends Action
                                                'method' => 'post'));
             $this->dropdown('tag', _('Tag'), $content,
                             _('Choose a tag to narrow list'), false, $tag);
-            $this->submit('go', _('Go'));
+            $this->submit('submit', _('Go'));
             $this->elementEnd('form');
             $this->elementEnd('li');
             $this->elementEnd('ul');
index fe38d03403aa3090100731a4f79fa8b16782ccd4..ae2d237f1de65b9c523d3ca00a5a98fc374ee2f2 100644 (file)
@@ -33,7 +33,7 @@ if (!defined('LACONICA')) {
 
 require_once INSTALLDIR.'/lib/grouplist.php';
 
-define('GROUPS_PER_MINILIST', 80);
+define('GROUPS_PER_MINILIST', 27);
 
 /**
  * Widget to show a list of groups, good for sidebar
@@ -75,8 +75,9 @@ class GroupMiniList extends GroupList
                                        'href' => $this->group->homeUrl(),
                                        'rel' => 'contact group',
                                        'class' => 'url'));
-        $logo = ($this->group->stream_logo) ?
-          $this->group->stream_logo : User_group::defaultLogo(AVATAR_STREAM_SIZE);
+
+        $logo = ($this->group->mini_logo) ?
+          $this->group->mini_logo : User_group::defaultLogo(AVATAR_MINI_SIZE);
 
         $this->out->element('img', array('src' => $logo,
                                     'width' => AVATAR_MINI_SIZE,
index d77234549af7f27bc33ece11ddcfca4719ae1c40..01bbf5721a4450426f2b53a0d731979fa76c1b02 100644 (file)
@@ -137,6 +137,9 @@ class MailboxAction extends PersonalAction
             $message->free();
             unset($message);
         }
+        else {
+            $this->element('p', 'guide', _('You have no private messages. You can send private message to engage other users in conversation. People can send you messages for your eyes only.'));
+        }
     }
 
     function getMessages()
diff --git a/lib/profileaction.php b/lib/profileaction.php
new file mode 100644 (file)
index 0000000..c81924e
--- /dev/null
@@ -0,0 +1,242 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Common parent of Personal and Profile actions
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Personal
+ * @package   Laconica
+ * @author    Evan Prodromou <evan@controlyourself.ca>
+ * @author    Sarven Capadisli <csarven@controlyourself.ca>
+ * @copyright 2008-2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/lib/profileminilist.php';
+require_once INSTALLDIR.'/lib/groupminilist.php';
+
+/**
+ * Profile action common superclass
+ *
+ * Abstracts out common code from profile and personal tabs
+ *
+ * @category Personal
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://laconi.ca/
+ */
+
+class ProfileAction extends Action
+{
+    var $user = null;
+    var $page = null;
+    var $profile = null;
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $nickname_arg = $this->arg('nickname');
+        $nickname = common_canonical_nickname($nickname_arg);
+
+        // Permanent redirect on non-canonical nickname
+
+        if ($nickname_arg != $nickname) {
+            $args = array('nickname' => $nickname);
+            if ($this->arg('page') && $this->arg('page') != 1) {
+                $args['page'] = $this->arg['page'];
+            }
+            common_redirect(common_local_url($this->trimmed('action'), $args), 301);
+            return false;
+        }
+
+        $this->user = User::staticGet('nickname', $nickname);
+
+        if (!$this->user) {
+            $this->clientError(_('No such user.'), 404);
+            return false;
+        }
+
+        $this->profile = $this->user->getProfile();
+
+        if (!$this->profile) {
+            $this->serverError(_('User has no profile.'));
+            return false;
+        }
+
+        $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
+
+        common_set_returnto($this->selfUrl());
+
+        return true;
+    }
+
+    function showSections()
+    {
+        $this->showSubscriptions();
+        $this->showSubscribers();
+        $this->showGroups();
+        $this->showStatistics();
+    }
+
+    function showSubscriptions()
+    {
+        $profile = $this->user->getSubscriptions(0, PROFILES_PER_MINILIST + 1);
+
+        $this->elementStart('div', array('id' => 'entity_subscriptions',
+                                         'class' => 'section'));
+
+        $this->element('h2', null, _('Subscriptions'));
+
+        if ($profile) {
+            $pml = new ProfileMiniList($profile, $this->user, $this);
+            $cnt = $pml->show();
+            if ($cnt == 0) {
+                $this->element('p', null, _('(None)'));
+            }
+        }
+
+        if ($cnt > PROFILES_PER_MINILIST) {
+            $this->elementStart('p');
+            $this->element('a', array('href' => common_local_url('subscriptions',
+                                                                 array('nickname' => $this->profile->nickname)),
+                                      'class' => 'more'),
+                           _('All subscriptions'));
+            $this->elementEnd('p');
+        }
+
+        $this->elementEnd('div');
+    }
+
+    function showSubscribers()
+    {
+        $profile = $this->user->getSubscribers(0, PROFILES_PER_MINILIST + 1);
+
+        $this->elementStart('div', array('id' => 'entity_subscribers',
+                                         'class' => 'section'));
+
+        $this->element('h2', null, _('Subscribers'));
+
+        if ($profile) {
+            $pml = new ProfileMiniList($profile, $this->user, $this);
+            $cnt = $pml->show();
+            if ($cnt == 0) {
+                $this->element('p', null, _('(None)'));
+            }
+        }
+
+        if ($cnt > PROFILES_PER_MINILIST) {
+            $this->elementStart('p');
+            $this->element('a', array('href' => common_local_url('subscribers',
+                                                                 array('nickname' => $this->profile->nickname)),
+                                      'class' => 'more'),
+                           _('All subscribers'));
+            $this->elementEnd('p');
+        }
+
+        $this->elementEnd('div');
+    }
+
+    function showStatistics()
+    {
+        // XXX: WORM cache this
+        $subs = new Subscription();
+        $subs->subscriber = $this->profile->id;
+        $subs_count = (int) $subs->count() - 1;
+
+        $subbed = new Subscription();
+        $subbed->subscribed = $this->profile->id;
+        $subbed_count = (int) $subbed->count() - 1;
+
+        $notices = new Notice();
+        $notices->profile_id = $this->profile->id;
+        $notice_count = (int) $notices->count();
+
+        $this->elementStart('div', array('id' => 'entity_statistics',
+                                         'class' => 'section'));
+
+        $this->element('h2', null, _('Statistics'));
+
+        // Other stats...?
+        $this->elementStart('dl', 'entity_member-since');
+        $this->element('dt', null, _('Member since'));
+        $this->element('dd', null, date('j M Y',
+                                        strtotime($this->profile->created)));
+        $this->elementEnd('dl');
+
+        $this->elementStart('dl', 'entity_subscriptions');
+        $this->elementStart('dt');
+        $this->element('a', array('href' => common_local_url('subscriptions',
+                                                             array('nickname' => $this->profile->nickname))),
+                       _('Subscriptions'));
+        $this->elementEnd('dt');
+        $this->element('dd', null, (is_int($subs_count)) ? $subs_count : '0');
+        $this->elementEnd('dl');
+
+        $this->elementStart('dl', 'entity_subscribers');
+        $this->elementStart('dt');
+        $this->element('a', array('href' => common_local_url('subscribers',
+                                                             array('nickname' => $this->profile->nickname))),
+                       _('Subscribers'));
+        $this->elementEnd('dt');
+        $this->element('dd', 'subscribers', (is_int($subbed_count)) ? $subbed_count : '0');
+        $this->elementEnd('dl');
+
+        $this->elementStart('dl', 'entity_notices');
+        $this->element('dt', null, _('Notices'));
+        $this->element('dd', null, (is_int($notice_count)) ? $notice_count : '0');
+        $this->elementEnd('dl');
+
+        $this->elementEnd('div');
+    }
+
+    function showGroups()
+    {
+        $groups = $this->user->getGroups(0, GROUPS_PER_MINILIST + 1);
+
+        $this->elementStart('div', array('id' => 'entity_groups',
+                                         'class' => 'section'));
+
+        $this->element('h2', null, _('Groups'));
+
+        if ($groups) {
+            $gml = new GroupMiniList($groups, $this->user, $this);
+            $cnt = $gml->show();
+            if ($cnt == 0) {
+                $this->element('p', null, _('(None)'));
+            }
+        }
+
+        if ($cnt > GROUPS_PER_MINILIST) {
+            $this->elementStart('p');
+            $this->element('a', array('href' => common_local_url('usergroups',
+                                                                 array('nickname' => $this->profile->nickname)),
+                                      'class' => 'more'),
+                           _('All groups'));
+            $this->elementEnd('p');
+        }
+
+        $this->elementEnd('div');
+    }
+}
\ No newline at end of file
index 0d466bba81e7be3139c3b7e03cc99ac6545b8a27..57496d0e97d77ee236a74d03adead1ad5860bdd8 100644 (file)
@@ -33,7 +33,7 @@ if (!defined('LACONICA')) {
 
 require_once INSTALLDIR.'/lib/profilelist.php';
 
-define('PROFILES_PER_MINILIST', 80);
+define('PROFILES_PER_MINILIST', 27);
 
 /**
  * Widget to show a list of profiles, good for sidebar
index c762db16f049a7f84f3bee989c78d41148b8d2f2..e7ad4affdaab9eaac36e4fae6fe7c586443b9a1e 100644 (file)
@@ -133,5 +133,31 @@ class SearchAction extends Action
             $this->showResults($q, $page);
         }
     }
+
+    function searchSuggestions($q) {
+        $qe = urlencode($q);
+        $message = sprintf(_(<<<E_O_T
+* Make sure all words are spelled correctly.
+* Try different keywords.
+* Try more general keywords.
+* Try fewer keywords.
+
+You can also try your search on other engines:
+
+* [Twingly](http://www.twingly.com/search?q=%s&content=microblog&site=identi.ca)
+* [Tweet scan](http://www.tweetscan.com/indexi.php?s=%s)
+* [Google](http://www.google.com/search?q=site%%3A%%%%site.server%%%%+%s)
+* [Yahoo](http://search.yahoo.com/search?p=site%%3A%%%%site.server%%%%+%s)
+
+
+E_O_T
+), $qe, $qe, $qe, $qe);
+        $this->elementStart('dl', array('id' => 'help_search', 'class' => 'help'));
+        $this->element('dt', null, _('Search help'));
+        $this->elementStart('dd', 'instructions');
+        $this->raw(common_markup_to_html($message));
+        $this->elementEnd('dd');
+        $this->elementEnd('div');
+    }
 }
 
index db2092210bd6520e8a88255616b7cece305671e0..c1d0dc254d8c99dc7ff8bc6a730644ee44e53097 100644 (file)
@@ -19,7 +19,7 @@
 
 if (!defined('LACONICA')) { exit(1); }
 
-define("TWITTER_SERVICE", 1); // Twitter is foreign_service ID 1
+define('TWITTER_SERVICE', 1); // Twitter is foreign_service ID 1
 
 function get_twitter_data($uri, $screen_name, $password)
 {
@@ -45,6 +45,10 @@ function get_twitter_data($uri, $screen_name, $password)
     if ($errmsg) {
         common_debug("Twitter bridge - cURL error: $errmsg - trying to load: $uri with user $screen_name.",
             __FILE__);
+
+        if (defined('SCRIPT_DEBUG')) {
+            print "cURL error: $errmsg - trying to load: $uri with user $screen_name.\n";
+        }
     }
 
     curl_close($ch);
@@ -52,63 +56,141 @@ function get_twitter_data($uri, $screen_name, $password)
     return $data;
 }
 
-function twitter_user_info($screen_name, $password)
+function twitter_json_data($uri, $screen_name, $password)
 {
+    $json_data = get_twitter_data($uri, $screen_name, $password);
 
-    $uri = "http://twitter.com/users/show/$screen_name.json";
-    $data = get_twitter_data($uri, $screen_name, $password);
-
-    if (!$data) {
+    if (!$json_data) {
         return false;
     }
 
-    $twit_user = json_decode($data);
+    $data = json_decode($json_data);
 
-    if (!$twit_user) {
+    if (!$data) {
         return false;
     }
 
-    return $twit_user;
+    return $data;
 }
 
-function update_twitter_user($fuser, $twitter_id, $screen_name)
+function twitter_user_info($screen_name, $password)
 {
+    $uri = "http://twitter.com/users/show/$screen_name.json";
+    return twitter_json_data($uri, $screen_name, $password);
+}
 
-    $original = clone($fuser);
-    $fuser->nickname = $screen_name;
-    $fuser->uri = 'http://twitter.com/' . $screen_name;
-    $result = $fuser->updateKeys($original);
+function twitter_friends_ids($screen_name, $password)
+{
+    $uri = "http://twitter.com/friends/ids/$screen_name.json";
+    return twitter_json_data($uri, $screen_name, $password);
+}
+
+function update_twitter_user($twitter_id, $screen_name)
+{
+    $uri = 'http://twitter.com/' . $screen_name;
+
+    $fuser = new Foreign_user();
+
+    $fuser->query('BEGIN');
+
+    // Dropping down to SQL because regular db_object udpate stuff doesn't seem
+    // to work so good with tables that have multiple column primary keys
+
+    // Any time we update the uri for a forein user we have to make sure there
+    // are no dupe entries first -- unique constraint on the uri column
+
+    $qry = 'UPDATE foreign_user set uri = \'\' WHERE uri = ';
+    $qry .= '\'' . $uri . '\'' . ' AND service = ' . TWITTER_SERVICE;
+
+    $result = $fuser->query($qry);
+
+    if ($result) {
+        common_debug("Removed uri ($uri) from another foreign_user who was squatting on it.");
+        if (defined('SCRIPT_DEBUG')) {
+            print("Removed uri ($uri) from another Twitter user who was squatting on it.\n");
+        }
+    }
+
+    // Update the user
+    $qry = 'UPDATE foreign_user SET nickname = ';
+    $qry .= '\'' . $screen_name . '\'' . ', uri = \'' . $uri . '\' ';
+    $qry .= 'WHERE id = ' . $twitter_id . ' AND service = ' . TWITTER_SERVICE;
+
+    $result = $fuser->query($qry);
 
     if (!$result) {
+        common_log(LOG_WARNING,
+            "Couldn't update foreign_user data for Twitter user: $screen_name");
         common_log_db_error($fuser, 'UPDATE', __FILE__);
+        if (defined('SCRIPT_DEBUG')) {
+            print "UPDATE failed: for Twitter user:  $twitter_id - $screen_name. - ";
+            print common_log_objstring($fuser) . "\n";
+            $error = &PEAR::getStaticProperty('DB_DataObject','lastError');
+            print "DB_DataObject Error: " . $error->getMessage() . "\n";
+        }
         return false;
     }
 
+    $fuser->query('COMMIT');
+
+    $fuser->free();
+    unset($fuser);
+
     return true;
 }
 
 function add_twitter_user($twitter_id, $screen_name)
 {
 
+    $new_uri = 'http://twitter.com/' . $screen_name;
+
+    // Clear out any bad old foreign_users with the new user's legit URL
+    // This can happen when users move around or fakester accounts get
+    // repoed, and things like that.
+    $luser = new Foreign_user();
+    $luser->uri = $new_uri;
+    $luser->service = TWITTER_SERVICE;
+    $result = $luser->delete();
+
+    if ($result) {
+        common_log(LOG_WARNING,
+            "Twitter bridge - removed invalid Twitter user squatting on uri: $new_uri");
+        if (defined('SCRIPT_DEBUG')) {
+            print "Removed invalid Twitter user squatting on uri: $new_uri\n";
+        }
+    }
+
+    $luser->free();
+    unset($luser);
+
     // Otherwise, create a new Twitter user
-    $fuser = DB_DataObject::factory('foreign_user');
+    $fuser = new Foreign_user();
 
     $fuser->nickname = $screen_name;
     $fuser->uri = 'http://twitter.com/' . $screen_name;
     $fuser->id = $twitter_id;
-    $fuser->service = TWITTER_SERVICE; // Twitter
+    $fuser->service = TWITTER_SERVICE;
     $fuser->created = common_sql_now();
     $result = $fuser->insert();
 
     if (!$result) {
-        common_debug("Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name.");
+        common_log(LOG_WARNING,
+            "Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name.");
         common_log_db_error($fuser, 'INSERT', __FILE__);
-        return false;
+        if (defined('SCRIPT_DEBUG')) {
+            print "INSERT failed: could not add new Twitter user: $twitter_id - $screen_name. - ";
+            print common_log_objstring($fuser) . "\n";
+            $error = &PEAR::getStaticProperty('DB_DataObject','lastError');
+            print "DB_DataObject Error: " . $error->getMessage() . "\n";
+        }
+    } else {
+        common_debug("Twitter bridge - Added new Twitter user: $screen_name ($twitter_id).");
+        if (defined('SCRIPT_DEBUG')) {
+            print "Added new Twitter user: $screen_name ($twitter_id).\n";
+        }
     }
 
-    common_debug("Twitter bridge - Added new Twitter user: $screen_name ($twitter_id).");
-
-    return true;
+    return $result;
 }
 
 // Creates or Updates a Twitter user
@@ -117,53 +199,87 @@ function save_twitter_user($twitter_id, $screen_name)
 
     // Check to see whether the Twitter user is already in the system,
     // and update its screen name and uri if so.
-    $fuser = Foreign_user::getForeignUser($twitter_id, 1);
+    $fuser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE);
 
     if ($fuser) {
 
+        $result = true;
+
         // Only update if Twitter screen name has changed
         if ($fuser->nickname != $screen_name) {
+            $result = update_twitter_user($twitter_id, $screen_name);
 
             common_debug('Twitter bridge - Updated nickname (and URI) for Twitter user ' .
                 "$fuser->id to $screen_name, was $fuser->nickname");
 
-            return update_twitter_user($fuser, $twitter_id, $screen_name);
+            if (defined('SCRIPT_DEBUG')) {
+                print 'Updated nickname (and URI) for Twitter user ' .
+                    "$fuser->id to $screen_name, was $fuser->nickname\n";
+            }
         }
 
+        return $result;
+
     } else {
         return add_twitter_user($twitter_id, $screen_name);
     }
 
+    $fuser->free();
+    unset($fuser);
+
     return true;
 }
 
 function retreive_twitter_friends($twitter_id, $screen_name, $password)
 {
+    $friends = array();
 
     $uri = "http://twitter.com/statuses/friends/$twitter_id.json?page=";
-    $twitter_user = twitter_user_info($screen_name, $password);
+    $friends_ids = twitter_friends_ids($screen_name, $password);
+
+    if (!$friends_ids) {
+        return $friends;
+    }
+
+    if (defined('SCRIPT_DEBUG')) {
+        print "Twitter 'social graph' ids method says $screen_name has " .
+            count($friends_ids) . " friends.\n";
+    }
 
     // Calculate how many pages to get...
-    $pages = ceil($twitter_user->friends_count / 100);
+    $pages = ceil(count($friends_ids) / 100);
 
     if ($pages == 0) {
-        common_debug("Twitter bridge - Twitter user $screen_name has no friends! Lame.");
+        common_log(LOG_WARNING,
+            "Twitter bridge - $screen_name seems to have no friends.");
+        if (defined('SCRIPT_DEBUG')) {
+            print "$screen_name seems to have no friends.\n";
+        }
     }
 
-    $friends = array();
-
     for ($i = 1; $i <= $pages; $i++) {
 
         $data = get_twitter_data($uri . $i, $screen_name, $password);
 
         if (!$data) {
-            return null;
+            common_log(LOG_WARNING,
+                "Twitter bridge - Couldn't retrieve page $i of $screen_name's friends.");
+            if (defined('SCRIPT_DEBUG')) {
+                print "Couldn't retrieve page $i of $screen_name's friends.\n";
+            }
+            continue;
         }
 
         $more_friends = json_decode($data);
 
         if (!$more_friends) {
-            return null;
+
+            common_log(LOG_WARNING,
+                "Twitter bridge - No data for page $i of $screen_name's friends.");
+            if (defined('SCRIPT_DEBUG')) {
+                print "No data for page $i of $screen_name's friends.\n";
+            }
+            continue;
         }
 
          $friends = array_merge($friends, $more_friends);
@@ -177,19 +293,27 @@ function save_twitter_friends($user, $twitter_id, $screen_name, $password)
 
     $friends = retreive_twitter_friends($twitter_id, $screen_name, $password);
 
-    if (is_null($friends)) {
-        common_debug("Twitter bridge - Couldn't get friends data from Twitter.");
+    if (empty($friends)) {
+        common_debug("Twitter bridge - Couldn't get friends data from Twitter for $screen_name.");
+        if (defined('SCRIPT_DEBUG')) {
+            print "Couldn't get friends data from Twitter for $screen_name.\n";
+        }
         return false;
     }
 
     foreach ($friends as $friend) {
 
         $friend_name = $friend->screen_name;
-        $friend_id = $friend->id;
+        $friend_id = (int) $friend->id;
 
         // Update or create the Foreign_user record
         if (!save_twitter_user($friend_id, $friend_name)) {
-            return false;
+            common_log(LOG_WARNING,
+                "Twitter bridge - couldn't save $screen_name's friend, $friend_name.");
+            if (defined('SCRIPT_DEBUG')) {
+                print "Couldn't save $screen_name's friend, $friend_name.\n";
+            }
+            continue;
         }
 
         // Check to see if there's a related local user
@@ -199,8 +323,20 @@ function save_twitter_friends($user, $twitter_id, $screen_name, $password)
 
             // Get associated user and subscribe her
             $friend_user = User::staticGet('id', $flink->user_id);
-            subs_subscribe_to($user, $friend_user);
-            common_debug("Twitter bridge - subscribed $friend_user->nickname to $user->nickname.");
+            if (!empty($friend_user)) {
+                $result = subs_subscribe_to($user, $friend_user);
+
+                if ($result === true) {
+                    common_debug("Twitter bridge - subscribed $friend_user->nickname to $user->nickname.");
+                    if (defined('SCRIPT_DEBUG')) {
+                        print("Subscribed $friend_user->nickname to $user->nickname.\n");
+                    }
+                } else {
+                    if (defined('SCRIPT_DEBUG')) {
+                        print "$result ($friend_user->nickname to $user->nickname)\n";
+                    }
+                }
+            }
         }
     }
 
@@ -295,4 +431,3 @@ function broadcast_twitter($notice)
 
     return $success;
 }
-
diff --git a/plugins/TemplatePlugin.php b/plugins/TemplatePlugin.php
new file mode 100644 (file)
index 0000000..03daf62
--- /dev/null
@@ -0,0 +1,344 @@
+<?php
+/**
+ * Plugin to render old skool templates
+ *
+ * Captures rendered parts from the output buffer, passes them through a template file: tpl/index.html
+ * Adds an API method at index.php/template/update which lets you overwrite the template file
+ * Requires username/password and a single POST parameter called "template"
+ * The method is disabled unless the user is #1, the first user of the system
+ *
+ * @category  Plugin
+ * @package   Laconica
+ * @author    Brian Hendrickson <brian@megapump.com>
+ * @copyright 2009 Megapump, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://megapump.com/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+define('TEMPLATEPLUGIN_VERSION', '0.1');
+
+class TemplatePlugin extends Plugin {
+  
+  var $blocks = array();
+  
+  function __construct() {
+    parent::__construct();
+  }
+  
+  // capture the RouterInitialized event
+  // and connect a new API method
+  // for updating the template
+  function onRouterInitialized( &$m ) {
+    $m->connect( 'template/update', array(
+      'action'      => 'template',
+    ));
+  }
+  
+  
+  // <%styles%>
+  // <%scripts%>
+  // <%search%>
+  // <%feeds%>
+  // <%description%>
+  // <%head%>
+  function onStartShowHead( &$act ) {
+    $this->clear_xmlWriter($act);
+    $act->extraHead();
+    $this->blocks['head'] = $act->xw->flush();
+    $act->showStylesheets();
+    $this->blocks['styles'] = $act->xw->flush();    
+    $act->showScripts();
+    $this->blocks['scripts'] = $act->xw->flush();    
+    $act->showFeeds();
+    $this->blocks['feeds'] = $act->xw->flush();    
+    $act->showOpenSearch();
+    $this->blocks['search'] = $act->xw->flush();    
+    $act->showDescription();
+    $this->blocks['description'] = $act->xw->flush();
+    return false;
+  }
+  
+  // <%bodytext%>
+  function onStartShowContentBlock( &$act ) {
+    $this->clear_xmlWriter($act);
+    return true;
+  }
+  function onEndShowContentBlock( &$act ) {
+    $this->blocks['bodytext'] = $act->xw->flush();
+  }
+  
+  // <%localnav%>
+  function onStartShowLocalNavBlock( &$act ) {
+    $this->clear_xmlWriter($act);
+    return true;
+  }
+  function onEndShowLocalNavBlock( &$act ) {
+    $this->blocks['localnav'] = $act->xw->flush();
+  }
+  
+  // <%export%>
+  function onStartShowExportData( &$act ) {
+    $this->clear_xmlWriter($act);
+    return true;
+  }
+  function onEndShowExportData( &$act ) {
+    $this->blocks['export'] = $act->xw->flush();
+  }
+  
+  // <%subscriptions%>
+  // <%subscribers%>
+  // <%groups%>
+  // <%statistics%>
+  // <%cloud%>
+  // <%groupmembers%>
+  // <%groupstatistics%>
+  // <%groupcloud%>
+  // <%popular%>
+  // <%groupsbyposts%>
+  // <%featuredusers%>
+  // <%groupsbymembers%>
+  function onStartShowSections( &$act ) {
+    global $action;
+    $this->clear_xmlWriter($act);
+    switch ($action) {
+      case "showstream":
+        $act->showSubscriptions();
+        $this->blocks['subscriptions'] = $act->xw->flush();
+        $act->showSubscribers();
+        $this->blocks['subscribers'] = $act->xw->flush();
+        $act->showGroups();
+        $this->blocks['groups'] = $act->xw->flush();
+        $act->showStatistics();
+        $this->blocks['statistics'] = $act->xw->flush();
+        $cloud = new PersonalTagCloudSection($act, $act->user);
+        $cloud->show();
+        $this->blocks['cloud'] = $act->xw->flush();
+        break;
+      case "showgroup":
+        $act->showMembers();
+        $this->blocks['groupmembers'] = $act->xw->flush();
+        $act->showStatistics();
+        $this->blocks['groupstatistics'] = $act->xw->flush();
+        $cloud = new GroupTagCloudSection($act, $act->group);
+        $cloud->show();
+        $this->blocks['groupcloud'] = $act->xw->flush();
+        break;
+      case "public":
+        $pop = new PopularNoticeSection($act);
+        $pop->show();
+        $this->blocks['popular'] = $act->xw->flush();
+        $gbp = new GroupsByPostsSection($act);
+        $gbp->show();
+        $this->blocks['groupsbyposts'] = $act->xw->flush();
+        $feat = new FeaturedUsersSection($act);
+        $feat->show();
+        $this->blocks['featuredusers'] = $act->xw->flush();
+        break;
+      case "groups":
+        $gbp = new GroupsByPostsSection($act);
+        $gbp->show();
+        $this->blocks['groupsbyposts'] = $act->xw->flush();
+        $gbm = new GroupsByMembersSection($act);
+        $gbm->show();
+        $this->blocks['groupsbymembers'] = $act->xw->flush();
+        break;
+    }
+    return false;
+  }
+  
+  // <%logo%>
+  // <%nav%>
+  // <%notice%>
+  // <%noticeform%>
+  function onStartShowHeader( &$act ) {
+    $this->clear_xmlWriter($act);
+    $act->showLogo();
+    $this->blocks['logo'] = $act->xw->flush();
+    $act->showPrimaryNav();
+    $this->blocks['nav'] = $act->xw->flush();
+    $act->showSiteNotice();
+    $this->blocks['notice'] = $act->xw->flush();
+    if (common_logged_in()) {
+        $act->showNoticeForm();
+    } else {
+        $act->showAnonymousMessage();
+    }
+    $this->blocks['noticeform'] = $act->xw->flush();
+    return false;
+  }
+  
+  // <%secondarynav%>
+  // <%licenses%>
+  function onStartShowFooter( &$act ) {
+    $this->clear_xmlWriter($act);
+    $act->showSecondaryNav();
+    $this->blocks['secondarynav'] = $act->xw->flush();
+    $act->showLicenses();
+    $this->blocks['licenses'] = $act->xw->flush();
+    return false;
+  }
+  
+  // capture the EndHTML event
+  // and include the template
+  function onEndEndHTML($act) {
+    
+    global $action, $tags;
+    
+    // set the action and title values
+    $vars = array(
+      'action'=>$action,
+      'title'=>$act->title(). " - ". common_config('site', 'name')
+    );
+    
+    // use the PHP template
+    // unless laconica config:
+    //   $config['template']['mode'] = 'html';
+    if (!(common_config('template', 'mode') == 'html')) {
+      $tpl_file = 'tpl/index.php';
+      $tags = array_merge($vars,$this->blocks);
+      include $tpl_file;
+      return;
+    }
+    
+    $tpl_file = 'tpl/index.html';
+    
+    // read the static template
+    $output = file_get_contents( $tpl_file );
+    
+    $tags = array();
+    
+    // get a list of the <%tags%> in the template
+    $pattern='/<%([a-z]+)%>/';
+    
+    if ( 1 <= preg_match_all( $pattern, $output, $found ))
+      $tags[] = $found;
+    
+    // for each found tag, set its value from the rendered blocks
+    foreach( $tags[0][1] as $pos=>$tag ) {
+      if (isset($this->blocks[$tag]))
+        $vars[$tag] = $this->blocks[$tag];
+        
+      // didn't find a block for the tag
+      elseif (!isset($vars[$tag]))
+        $vars[$tag] = '';
+    }
+    
+    // replace the tags in the template
+    foreach( $vars as $key=>$val )
+      $output = str_replace( '<%'.$key.'%>', $val, $output );
+    
+    echo $output;
+    
+    return true;
+    
+  }
+  
+  // catching the StartShowHTML event to halt the rendering
+  function onStartShowHTML( &$act ) {
+    $this->clear_xmlWriter($act);
+    return true;
+  }
+  
+  // clear the xmlWriter
+  function clear_xmlWriter( &$act ) {
+    $act->xw->openMemory();
+    $act->xw->setIndent(true);
+  }
+  
+}
+
+/**
+ * Action for updating the template remotely
+ *
+ * "template/update" -- a POST method that requires a single
+ * parameter "template", containing the new template code
+ *
+ * @category Plugin
+ * @package  Laconica
+ * @author   Brian Hendrickson <brian@megapump.com>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://megapump.com/
+ *
+ */
+class TemplateAction extends Action
+{
+
+  function prepare($args) {
+    parent::prepare($args);
+    return true;
+  }
+  
+  function handle($args) {
+    
+    parent::handle($args);
+    
+    if (!isset($_SERVER['PHP_AUTH_USER'])) {
+      
+      // not authenticated, show login form
+      header('WWW-Authenticate: Basic realm="Laconica API"');
+      
+      // cancelled the browser login form
+      $this->clientError(_('Authentication error!'), $code = 401);
+      
+    } else {
+      
+      $nick = $_SERVER['PHP_AUTH_USER'];
+      $pass = $_SERVER['PHP_AUTH_PW'];
+      
+      // check username and password
+      $user = common_check_user($nick,$pass);
+      
+      if ($user) {
+        
+        // verify that user is admin
+        if (!($user->id == 1))
+          $this->clientError(_('only User #1 can update the template'), $code = 401);
+        
+        // open the old template
+        $tpl_file = 'tpl/index.html';
+        $fp = fopen( $tpl_file, 'w+' );
+        
+        // overwrite with the new template
+        fwrite($fp, $this->arg('template'));
+        fclose($fp);
+        
+        header('HTTP/1.1 200 OK');
+        header('Content-type: text/plain');
+        print "Template Updated!";
+        
+      } else {
+        
+        // bad username and password
+        $this->clientError(_('Authentication error!'), $code = 401);
+        
+      }
+      
+    }
+  }
+}
+
+/**
+ * Function for retrieving a laconica display section
+ *
+ * requires one parameter, the name of the section
+ * section names are listed in the comments of the TemplatePlugin class
+ *
+ * @category Plugin
+ * @package  Laconica
+ * @author   Brian Hendrickson <brian@megapump.com>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://megapump.com/
+ *
+ */
+
+function section($tagname) {
+  global $tags;
+  if (isset($tags[$tagname]))
+    return $tags[$tagname];
+}
+
index 5f0ed5fa9298b1738aa16056f10451e165404a25..331e10671bb7cc7de0919875526696c196a0b28d 100644 (file)
@@ -1,11 +1,14 @@
+# This version needs to match the tarball and unpacked directory name.
+%define LACVER 0.7.3
+
 BuildRequires: php-pear
 BuildRequires: httpd-devel
 
 Name:           laconica
-Version:        0.7.2
+Version:        %{LACVER}
 Release:        1%{?dist}
 License:        GAGPL v3 or later
-Source:         laconica-0.7.2.tar.gz
+Source:         laconica-%{version}.tar.gz
 Group:          Applications/Internet
 Summary:        Laconica, the Open Source microblogging platform
 BuildArch:      noarch
@@ -49,6 +52,8 @@ cp -a * %{buildroot}%{wwwpath}
 mkdir -p %{buildroot}%{_datadir}/laconica
 cp -a db %{buildroot}%{_datadir}/laconica/db
 
+mkdir -p %{buildroot}%{_datadir}/laconica/avatar
+
 mkdir -p %{buildroot}%{_sysconfdir}/httpd/conf.d
 cat > %{buildroot}%{_sysconfdir}/httpd/conf.d/laconica.conf <<"EOF"
 Alias /laconica/ "/var/www/laconica/"
@@ -74,6 +79,12 @@ rm -rf %buildroot
 %config(noreplace) %{_sysconfdir}/httpd/conf.d/laconica.conf
 
 %changelog
+* Wed Apr 03 2009 Zach Copley <zach@controlyourself.ca> - 0.7.3
+- Changed version number to 0.7.3.
+
+* Fri Mar 13 2009 Ken Sedgwick <ksedgwic@bonsai.com> - 0.7.2.1-1
+- Factored laconica version to the first line of the file.
+
 * Wed Mar 03 2009 Zach Copley <zach@controlyourself.ca> - 0.7.2
 - Changed version number to 0.7.2.
 
index 0ce34c2ae44f943303dcdcfbeaa76244fa1787e5..794301f0f02f8b4af9f6fae0294f1a2bc6ec4c2b 100755 (executable)
@@ -18,7 +18,7 @@
  * along with this program.     If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
+// 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();
@@ -27,11 +27,16 @@ if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 define('LACONICA', true);
 
+// Uncomment this to get useful console output
+//define('SCRIPT_DEBUG', true);
+
 require_once(INSTALLDIR . '/lib/common.php');
 
 $flink = new Foreign_link();
 $flink->service = 1; // Twitter
-$flink->find();
+$cnt = $flink->find();
+
+print "Updating Twitter friends subscriptions for $cnt users.\n";
 
 while ($flink->fetch()) {
 
@@ -39,20 +44,30 @@ while ($flink->fetch()) {
 
         $user = User::staticGet($flink->user_id);
 
-        print "Updating Twitter friends for user $user->nickname ($user->id)\n";
+        if (empty($user)) {
+            common_log(LOG_WARNING, "Unmatched user for ID " . $flink->user_id);
+            print "Unmatched user for ID $flink->user_id\n";
+            continue;
+        }
+
+        print "Updating Twitter friends for $user->nickname (Laconica ID: $user->id)... ";
 
         $fuser = $flink->getForeignUser();
 
-        $result = save_twitter_friends($user, $fuser->id, $fuser->nickname, $flink->credentials);
+        if (empty($fuser)) {
+            common_log(LOG_WARNING, "Unmatched user for ID " . $flink->user_id);
+            print "Unmatched user for ID $flink->user_id\n";
+            continue;
+        }
 
-        if ($result == false) {
-            print "Problems updating Twitter friends! Check the log.\n";
-            exit(1);
+        $result = save_twitter_friends($user, $fuser->id,
+                       $fuser->nickname, $flink->credentials);
+        if (defined('SCRIPT_DEBUG')) {
+            print "\nDONE\n";
+        } else {
+            print "DONE\n";
         }
     }
-
 }
 
 exit(0);
-
-
index 4c9b522549268d7d1a1af783ea6d029d6e0dd015..8ea2befb4c2bbe45f0e543269f04381d5139598a 100644 (file)
@@ -83,12 +83,13 @@ left:0;
 border:0;
 }
 
-#page_notice .error,
-#page_notice .success {
+.error,
+.success {
 padding:4px 7px;
 border-radius:4px;
 -moz-border-radius:4px;
 -webkit-border-radius:4px;
+margin-bottom:18px;
 }
 form label.submit {
 display:none;
@@ -384,6 +385,7 @@ margin-bottom:1em;
 
 #content {
 width:64.009%;
+min-height:259px;
 padding:1.795%;
 float:left;
 border-radius:7px;
@@ -403,8 +405,9 @@ float:left;
 
 #aside_primary {
 width:27.917%;
+min-height:259px;
 float:left;
-margin-left:0.395%;
+margin-left:0.385%;
 padding:1.795%;
 border-radius:7px;
 -moz-border-radius:7px;
@@ -469,7 +472,7 @@ bottom:0;
 right:0;
 }
 #form_notice label[for=to] {
-margin-top:11px;
+margin-top:7px;
 }
 #form_notice select[id=to] {
 margin-bottom:7px;
@@ -1143,4 +1146,16 @@ clear:both;
 margin-bottom:0;
 }
 
-
+.instructions ul {
+list-style-position:inside;
+}
+.instructions p,
+.instructions ul {
+margin-bottom:18px;
+}
+.help dt {
+display:none;
+}
+.guide {
+clear:both;
+}
\ No newline at end of file
index bf46fc6bfd503cc0a6bd5f2dd9900f8516981292..42e29374f1c571c2bd6deb6e421aea271b7955f5 100644 (file)
@@ -37,7 +37,6 @@ background:none;
 
 input.submit,
 #form_notice.warning #notice_text-count,
-#nav_register a,
 .form_settings .form_note,
 .entity_remote_subscribe {
 background-color:#A9BF4F;
@@ -48,7 +47,6 @@ input:focus, textarea:focus, select:focus,
 border-color:#A9BF4F;
 }
 input.submit,
-#nav_register a,
 .entity_remote_subscribe {
 color:#fff;
 }
@@ -97,13 +95,6 @@ cursor:wait;
 text-indent:-9999px;
 }
 
-
-#nav_register a {
-text-decoration:none;
-font-weight:bold;
-padding:2px 4px;
-}
-
 #content,
 #site_nav_local_views a,
 #aside_primary {
@@ -122,10 +113,10 @@ background-color:rgba(255, 255, 255, 0.7);
 }
 
 
-#page_notice .error {
+.error {
 background-color:#F7E8E8;
 }
-#page_notice .success {
+.success {
 background-color:#EFF3DC;
 }
 
index 9e7c10ac5a296dc8fab3e82e5bec5ae038dceac7..8edb005a68ae7b74b79c9619eae7c36e5a7dfd25 100644 (file)
@@ -37,7 +37,6 @@ background:none;
 
 input.submit,
 #form_notice.warning #notice_text-count,
-#nav_register a,
 .form_settings .form_note,
 .entity_remote_subscribe {
 background-color:#9BB43E;
@@ -48,7 +47,6 @@ input:focus, textarea:focus, select:focus,
 border-color:#9BB43E;
 }
 input.submit,
-#nav_register a,
 .entity_remote_subscribe {
 color:#fff;
 }
@@ -97,12 +95,6 @@ cursor:wait;
 text-indent:-9999px;
 }
 
-#nav_register a {
-text-decoration:none;
-font-weight:bold;
-padding:2px 4px;
-}
-
 #content,
 #site_nav_local_views a,
 #aside_primary {
@@ -121,10 +113,10 @@ background-color:rgba(255, 255, 255, 0.7);
 }
 
 
-#page_notice .error {
+.error {
 background-color:#F7E8E8;
 }
-#page_notice .success {
+.success {
 background-color:#EFF3DC;
 }
 
diff --git a/tpl/index.php b/tpl/index.php
new file mode 100644 (file)
index 0000000..5f1ed84
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html
+PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+       <head>
+               <title><?php echo section('title'); ?></title>
+               <?php echo section('styles'); ?>
+               <?php echo section('scripts'); ?>
+               <?php echo section('search'); ?>
+               <?php echo section('feeds'); ?>
+               <?php echo section('description'); ?>
+               <?php echo section('head'); ?>
+               </head>
+       <body id="<?php echo section('action'); ?>">
+               <div id="wrap">
+                       <div id="header">
+                               <?php echo section('logo'); ?>
+                               <?php echo section('nav'); ?>
+                               <?php echo section('notice'); ?>
+                               <?php echo section('noticeform'); ?>
+                       </div>
+                       <div id="core">
+                               <?php echo section('localnav'); ?>
+                               <?php echo section('bodytext'); ?>
+                               <div id="aside_primary" class="aside">
+                                       <?php echo section('export'); ?>
+                                       <?php echo section('subscriptions'); ?>
+                                       <?php echo section('subscribers'); ?>
+                                       <?php echo section('groups'); ?>
+                                       <?php echo section('statistics'); ?>
+                                       <?php echo section('cloud'); ?>
+                                       <?php echo section('groupmembers'); ?>
+                                       <?php echo section('groupstatistics'); ?>
+                                       <?php echo section('groupcloud'); ?>
+                                       <?php echo section('popular'); ?>
+                                       <?php echo section('groupsbyposts'); ?>
+                                       <?php echo section('featuredusers'); ?>
+                                       <?php echo section('groupsbymembers'); ?>
+                                       </div>
+                               </div>
+                       <div id="footer">
+                               <?php echo section('secondarynav'); ?>
+                               <?php echo section('licenses'); ?>
+                       </div>
+                       </div>
+               </body>
+       </html>
\ No newline at end of file