]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.7.x' into 0.8.x
authorEvan Prodromou <evan@controlyourself.ca>
Thu, 12 Mar 2009 15:56:23 +0000 (11:56 -0400)
committerEvan Prodromou <evan@controlyourself.ca>
Thu, 12 Mar 2009 15:56:23 +0000 (11:56 -0400)
Conflicts:

classes/Notice.php
lib/action.php
lib/router.php
lib/twitter.php

1  2 
README
classes/Notice.php
classes/laconica.ini
db/laconica.sql
lib/action.php
lib/router.php
lib/twitter.php

diff --combined README
index d3c80df458287d3589f6870eeb50d56ae4b83d57,a7798a26a871f7892220f4685527ea63212d4c36..cdb3feba6fdb50568eb5bb40a3fc108dbe68fbb8
--- 1/README
--- 2/README
+++ b/README
@@@ -2,8 -2,8 +2,8 @@@
  README
  ------
  
- Laconica 0.7.1 ("West of the Fields")
6 February 2009
+ Laconica 0.7.2 ("Talk about the Passion")
11 March 2009
  
  This is the README file for Laconica, the Open Source microblogging
  platform. It includes installation instructions, descriptions of
@@@ -71,8 -71,47 +71,47 @@@ for additional terms
  New this version
  ================
  
- This is a minor bug-fix release since version 0.7.0, released Jan 29
- 2009. Notable changes 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
@@@ -201,10 -240,6 +240,10 @@@ and the URLs are listed here for your c
    version may render your Laconica site unable to send or receive XMPP
    messages.
  - Facebook library. Used for the Facebook application.
 +- PEAR Services_oEmbed. Used for some multimedia integration.
 +- PEAR HTTP_Request is an oEmbed dependency.
 +- PEAR Validat is an oEmbed dependency.e
 +- PEAR Net_URL is an oEmbed dependency.2
  
  A design goal of Laconica is that the basic Web functionality should
  work on even the most restrictive commercial hosting services.
@@@ -749,16 -784,19 +788,19 @@@ to the end first before trying them
     directory to your new directory.
  9. Copy htaccess.sample to .htaccess in the new directory. Change the
     RewriteBase to use the correct path.
- 10. Rebuild the database. Go to your Laconica directory and run the
-    rebuilddb.sh script like this:
-    ./scripts/rebuilddb.sh rootuser rootpassword database db/laconica.sql
-    Here, rootuser and rootpassword are the username and password for a
-    user who can drop and create databases as well as tables; typically
-    that's _not_ the user Laconica runs as.
- 11. Use mysql client to log into your database and make sure that the
-     notice, user, profile, subscription etc. tables are non-empty.
+ 10. Rebuild the database. For MySQL, go to your Laconica directory and
+     run the rebuilddb.sh script like this:
+     ./scripts/rebuilddb.sh rootuser rootpassword database db/laconica.sql
+     Here, rootuser and rootpassword are the username and password for a
+     user who can drop and create databases as well as tables; typically
+     that's _not_ the user Laconica runs as.
+     For PostgreSQL databases there is an equivalent, rebuilddb_psql.sh,
+     which operates slightly differently. Read the documentation in that
+     script before running it.
+ 11. Use mysql or psql client to log into your database and make sure that
+     the notice, user, profile, subscription etc. tables are non-empty.
  12. Turn back on the Web server, and check that things still work.
  13. Turn back on XMPP bots and email maildaemon. Note that the XMPP
      bots have changed since version 0.5; see above for details.
@@@ -883,6 -921,10 +925,10 @@@ notice: A plain string that will appea
        to put introductory information about your service, or info about
        upgrades and outages, or other community info. Any HTML will
          be escaped.
+ dupelimit: Time in which it's not OK for the same person to post the
+            same notice; default = 60 seconds.
+ logo: URL of an image file to use as the logo for the site. Overrides
+       the logo in the theme, if any.
  
  db
  --
@@@ -1225,6 -1267,9 +1271,9 @@@ if anyone's been overlooked in error
  * Ken Sheppardson (Trac server, man-about-town)
  * Tiago 'gouki' Faria (i18n managerx)
  * Sean Murphy
+ * Leslie Michael Orchard
+ * Eric Helgeson
+ * Ken Sedgwick
  
  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,
diff --combined classes/Notice.php
index 9b5194a5c8e63dd4a0b32fb2ec773fb7d7d92ae2,3087e39a78cf7be40bff84a453280799597625ad..adeed2796bff0ba1771f76021f6dbf6ef0b20e1e
@@@ -46,7 -46,6 +46,7 @@@ class Notice extends Memcached_DataObje
      public $reply_to;                        // int(4)
      public $is_local;                        // tinyint(1)
      public $source;                          // varchar(32)
 +    public $conversation;                    // int(4)
  
      /* Static get */
      function staticGet($k,$v=NULL) {
  
          $profile = Profile::staticGet($profile_id);
  
+         $final =  common_shorten_links($content);
          if (!$profile) {
              common_log(LOG_ERR, 'Problem saving notice. Unknown user.');
              return _('Problem saving notice. Unknown user.');
              return _('Too many notices too fast; take a breather and post again in a few minutes.');
          }
  
-         $banned = common_config('profile', 'banned');
+         if (common_config('site', 'dupelimit') > 0 && !Notice::checkDupes($profile_id, $final)) {
+             common_log(LOG_WARNING, 'Dupe posting by profile #' . $profile_id . '; throttled.');
+                       return _('Too many duplicate messages too quickly; take a breather and post again in a few minutes.');
+         }
+               $banned = common_config('profile', 'banned');
  
          if ( in_array($profile_id, $banned) || in_array($profile->nickname, $banned)) {
              common_log(LOG_WARNING, "Attempted post from banned user: $profile->nickname (user id = $profile_id).");
  
                $notice->query('BEGIN');
  
-         $notice->created = common_sql_now();
-         $notice->content = common_shorten_links($content);
-         $notice->rendered = common_render_content($notice->content, $notice);
-         $notice->source = $source;
-         $notice->uri = $uri;
+               $notice->reply_to = $reply_to;
+               $notice->created = common_sql_now();
+               $notice->content = $final;
+               $notice->rendered = common_render_content($final, $notice);
+               $notice->source = $source;
+               $notice->uri = $uri;
  
 +        if (!empty($reply_to)) {
 +            $reply_notice = Notice::staticGet('id', $reply_to);
 +            if (!empty($reply_notice)) {
 +                $notice->reply_to = $reply_to;
 +                $notice->conversation = $reply_notice->conversation;
 +            }
 +        }
 +
          if (Event::handle('StartNoticeSave', array(&$notice))) {
  
              $id = $notice->insert();
          return $notice;
      }
  
+     static function checkDupes($profile_id, $content) {
+         $profile = Profile::staticGet($profile_id);
+         if (!$profile) {
+             return false;
+         }
+         $notice = $profile->getNotices(0, NOTICE_CACHE_WINDOW);
+         if ($notice) {
+             $last = 0;
+             while ($notice->fetch()) {
+                 if (time() - strtotime($notice->created) >= common_config('site', 'dupelimit')) {
+                     return true;
+                 } else if ($notice->content == $content) {
+                     return false;
+                 }
+             }
+         }
+         # If we get here, oldest item in cache window is not
+         # old enough for dupe limit; do direct check against DB
+         $notice = new Notice();
+         $notice->profile_id = $profile_id;
+         $notice->content = $content;
+         if (common_config('db','type') == 'pgsql')
+             $notice->whereAdd('extract(epoch from now() - created) < ' . common_config('site', 'dupelimit'));
+         else
+             $notice->whereAdd('now() - created < ' . common_config('site', 'dupelimit'));
+         $cnt = $notice->count();
+         return ($cnt == 0);
+     }
      static function checkEditThrottle($profile_id) {
          $profile = Profile::staticGet($profile_id);
          if (!$profile) {
                  if ($recipient_notice) {
                      $orig = clone($this);
                      $this->reply_to = $recipient_notice->id;
 +                    $this->conversation = $recipient_notice->conversation;
                      $this->update($orig);
                  }
              }
              }
          }
  
 +        // If it's not a reply, make it the root of a new conversation
 +
 +        if (empty($this->conversation)) {
 +            $orig = clone($this);
 +            $this->conversation = $this->id;
 +            $this->update($orig);
 +        }
 +
          foreach (array_keys($replied) as $recipient) {
              $user = User::staticGet('id', $recipient);
              if ($user) {
diff --combined classes/laconica.ini
index aaa7035a445f4f4adc02f4b71d8f48eaa5249dbb,529454d99bf8ef4bae0406523bcdb55c541f428a..dd424bbdd376338908a206f2f9627eadeef5a84a
@@@ -145,7 -145,7 +145,7 @@@ id = 
  
  [nonce]
  consumer_key = 130
- tok = 130
+ tok = 2
  nonce = 130
  ts = 142
  created = 142
@@@ -153,8 -153,8 +153,8 @@@ modified = 38
  
  [nonce__keys]
  consumer_key = K
- tok = K
  nonce = K
+ ts = K
  
  [notice]
  id = 129
@@@ -168,7 -168,6 +168,7 @@@ modified = 38
  reply_to = 1
  is_local = 17
  source = 2
 +conversation = 1
  
  [notice__keys]
  id = N
diff --combined db/laconica.sql
index c7c1826d260f4c04b080a66bfcf409c6eb767224,098fa4fd1aa7f09b5cd109e6f0e33ff9c7c86c83..0d66716053caa5190028f7d46c005b64ff6275eb
@@@ -115,10 -115,8 +115,10 @@@ create table notice 
      reply_to integer comment 'notice replied to (usually a guess)' references notice (id),
      is_local tinyint default 0 comment 'notice was generated by a user',
      source varchar(32) comment 'source of comment, like "web", "im", or "clientname"',
 +    conversation integer comment 'id of root notice in this conversation' references notice (id),
  
      index notice_profile_id_idx (profile_id),
 +    index notice_conversation_idx (conversation),
      index notice_created_idx (created),
      FULLTEXT(content)
  ) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_bin;
@@@ -183,15 -181,14 +183,14 @@@ create table token 
  
  create table nonce (
      consumer_key varchar(255) not null comment 'unique identifier, root URL',
-     tok char(32) not null comment 'identifying value',
+     tok char(32) null comment 'buggy old value, ignored',
      nonce char(32) not null comment 'nonce',
      ts datetime not null comment 'timestamp sent',
  
      created datetime not null comment 'date this record was created',
      modified timestamp comment 'date this record was modified',
  
-     constraint primary key (consumer_key, tok, nonce),
-     constraint foreign key (consumer_key, tok) references token (consumer_key, tok)
+     constraint primary key (consumer_key, ts, nonce)
  ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
  
  /* One-to-many relationship of user to openid_url */
diff --combined lib/action.php
index ee23059115cba0629f74934420cbc90f0baf9b39,975c2bfcb8a23adb01c4ec7dbeb479119547479c..5020b92b0936a129901178015002e9c60179b3a1
@@@ -156,15 -156,10 +156,10 @@@ class Action extends HTMLOutputter // l
      {
          if (Event::handle('StartShowStyles', array($this))) {
              if (Event::handle('StartShowLaconicaStyles', array($this))) {
                  $this->element('link', array('rel' => 'stylesheet',
                                               'type' => 'text/css',
                                               'href' => theme_path('css/display.css', 'base') . '?version=' . LACONICA_VERSION,
                                               'media' => 'screen, projection, tv'));
-                 $this->element('link', array('rel' => 'stylesheet',
-                                              'type' => 'text/css',
-                                              'href' => theme_path('css/modal.css', 'base') . '?version=' . LACONICA_VERSION,
-                                              'media' => 'screen, projection, tv'));
                  $this->element('link', array('rel' => 'stylesheet',
                                               'type' => 'text/css',
                                               'href' => theme_path('css/display.css', null) . '?version=' . LACONICA_VERSION,
                  $this->element('script', array('type' => 'text/javascript',
                                                 'src' => common_path('js/jquery.form.js')),
                                 ' ');
-                 $this->element('script', array('type' => 'text/javascript',
-                                                'src' => common_path('js/jquery.simplemodal-1.2.2.pack.js')),
-                                ' ');
                  Event::handle('EndShowJQueryScripts', array($this));
              }
              if (Event::handle('StartShowLaconicaScripts', array($this))) {
                  // Frame-busting code to avoid clickjacking attacks.
                  $this->element('script', array('type' => 'text/javascript'),
                                 'if (window.top !== window.self) { window.top.location.href = window.self.location.href; }');
-                 $this->element('script', array('type' => 'text/javascript',
-                                                'src' => common_path('js/flowplayer-3.0.5.min.js')),
-                                ' ');
-                 $this->element('script', array('type' => 'text/javascript',
-                                                'src' => common_path('js/video.js')),
-                                ' ');
                  Event::handle('EndShowLaconicaScripts', array($this));
              }
              Event::handle('EndShowScripts', array($this));
          }
          if ($lm) {
              header('Last-Modified: ' . date(DATE_RFC1123, $lm));
 -            if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
 -                $ims = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
 +            if (array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER)) {
 +                $if_modified_since = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
 +                $ims = strtotime($if_modified_since);
                  if ($lm <= $ims) {
 -                    $if_none_match = $_SERVER['HTTP_IF_NONE_MATCH'];
 +                    $if_none_match = (array_key_exists('HTTP_IF_NONE_MATCH', $_SERVER)) ?
 +                      $_SERVER['HTTP_IF_NONE_MATCH'] : null;
                      if (!$if_none_match ||
                          !$etag ||
                          $this->_hasEtag($etag, $if_none_match)) {
diff --combined lib/router.php
index aab28672109476f575705ca3cba7c9925417e003,50d5a4ee13df0035c8840ca17e38adda2a874c38..d0b56e88b4fb2cddb7b5aa3be6320de85a60d304
@@@ -49,6 -49,9 +49,9 @@@ class Route
  {
      var $m = null;
      static $inst = null;
+     static $bare = array('requesttoken', 'accesstoken', 'userauthorization',
+                          'postnotice', 'updateprofile', 'finishremotesubscribe',
+                          'finishopenidlogin', 'finishaddopenid');
  
      static function get()
      {
          $main = array('login', 'logout', 'register', 'subscribe',
                        'unsubscribe', 'confirmaddress', 'recoverpassword',
                        'invite', 'favor', 'disfavor', 'sup',
-                       'block');
+                       'block', 'subedit');
  
          foreach ($main as $a) {
              $m->connect('main/'.$a, array('action' => $a));
          $m->connect('main/remote', array('action' => 'remotesubscribe'));
          $m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+'));
  
-         foreach (array('requesttoken', 'accesstoken', 'userauthorization',
-                     'postnotice', 'updateprofile', 'finishremotesubscribe') as $action) {
+         foreach (Router::$bare as $action) {
              $m->connect('index.php?action=' . $action, array('action' => $action));
          }
  
                      array('action' => 'deletenotice'),
                      array('notice' => '[0-9]+'));
  
 +        // conversation
 +
 +        $m->connect('conversation/:id',
 +                    array('action' => 'conversation'),
 +                    array('id' => '[0-9]+'));
 +
          $m->connect('message/new', array('action' => 'newmessage'));
          $m->connect('message/new?to=:to', array('action' => 'newmessage'), array('to' => '[A-Za-z0-9_-]'));
          $m->connect('message/:message',
          $m->connect('api/statuses/:method/:argument',
                      array('action' => 'api',
                            'apiaction' => 'statuses'),
-                     array('method' => '(user_timeline|friends_timeline|show|destroy|friends|followers)'));
+                     array('method' => '(user_timeline|friends_timeline|replies|show|destroy|friends|followers)'));
  
          // users
  
          }
  
          foreach (array('xml', 'json', 'rss', 'atom') as $e) {
-             $m->connect('api/direct_message/sent.'.$e,
+             $m->connect('api/direct_messages/sent.'.$e,
                          array('action' => 'api',
                          'apiaction' => 'direct_messages',
                          'method' => 'sent.'.$e));
          $m->connect('api/friendships/:method',
                      array('action' => 'api',
                            'apiaction' => 'friendships'),
-                     array('method' => 'exists(\.(xml|json|rss|atom))'));
+                     array('method' => 'exists(\.(xml|json))'));
  
          // Social graph
  
                      array('action' => 'api',
                            'apiaction' => 'laconica'));
  
          // search
          $m->connect('api/search.atom', array('action' => 'twitapisearchatom'));
          $m->connect('api/search.json', array('action' => 'twitapisearchjson'));
  
          // user stuff
  
-       foreach (array('subscriptions', 'subscribers',
+         foreach (array('subscriptions', 'subscribers',
                         'nudge', 'xrds', 'all', 'foaf',
                         'replies', 'inbox', 'outbox', 'microsummary') as $a) {
              $m->connect(':nickname/'.$a,
diff --combined lib/twitter.php
index 8a54afb9c40730c836a96a1cbaad5fc1578b912a,7abb40151686eb3779d689ba6bff9aa60f88ebed..db2092210bd6520e8a88255616b7cece305671e0
@@@ -210,7 -210,7 +210,7 @@@ function save_twitter_friends($user, $t
  function is_twitter_bound($notice, $flink) {
  
      // Check to see if notice should go to Twitter
 -    if ($flink->noticesync & FOREIGN_NOTICE_SEND) {
 +    if (!empty($flink) && ($flink->noticesync & FOREIGN_NOTICE_SEND)) {
  
          // If it's not a Twitter-style reply, or if the user WANTS to send replies.
          if (!preg_match('/^@[a-zA-Z0-9_]{1,15}\b/u', $notice->content) ||
                  return true;
          }
      }
 -    
 +
      return false;
  }
  
  function broadcast_twitter($notice)
  {
-     global $config;
      $success = true;
  
 -    $flink = Foreign_link::getByUserID($notice->profile_id, 
 +    $flink = Foreign_link::getByUserID($notice->profile_id,
          TWITTER_SERVICE);
 -            
 -    // XXX: Not sure WHERE to check whether a notice should go to 
 +
 +    // XXX: Not sure WHERE to check whether a notice should go to
      // Twitter. Should we even put in the queue if it shouldn't? --Zach
-     if (is_twitter_bound($notice, $flink)) {
+     if (!is_null($flink) && is_twitter_bound($notice, $flink)) {
  
          $fuser = $flink->getForeignUser();
          $twitter_user = $fuser->nickname;
          $options = array(
              CURLOPT_USERPWD        => "$twitter_user:$twitter_password",
              CURLOPT_POST           => true,
 -            CURLOPT_POSTFIELDS     => 
 +            CURLOPT_POSTFIELDS     =>
                  array(
                          'status' => $statustxt,
-                         'source' => $config['integration']['source']
+                         'source' => common_config('integration', 'source')
                       ),
              CURLOPT_RETURNTRANSFER => true,
              CURLOPT_FAILONERROR    => true,
              $success = false;
          }
      }
 -    
 +
      return $success;
  }