Merge remote-tracking branch 'upstream/master' into social-master
authorRoland Haeder <roland@mxchange.org>
Sun, 3 Jan 2016 17:55:37 +0000 (18:55 +0100)
committerRoland Haeder <roland@mxchange.org>
Sun, 3 Jan 2016 17:55:37 +0000 (18:55 +0100)
Signed-off-by: Roland Haeder <roland@mxchange.org>
53 files changed:
.gitignore
CONFIGURE
actions/newnotice.php
actions/noticesearch.php
actions/passwordsettings.php
actions/recoverpassword.php
classes/Config.php
classes/Confirm_address.php
classes/File.php
classes/File_redirection.php
classes/File_thumbnail.php
classes/Managed_DataObject.php
classes/Notice.php
classes/Profile.php
classes/Profile_prefs.php
classes/Queue_item.php
classes/User.php
lib/activityhandlerplugin.php
lib/attachmentlistitem.php
lib/default.php
lib/dofollowlistitem.php
lib/gnusocial.php
lib/installer.php
lib/location.php
lib/mail.php
lib/mediafile.php
lib/noticelistitem.php
lib/serverexception.php
lib/spawningdaemon.php
lib/util.php
local/.gitignore [deleted file]
nginx.conf.sample
plugins/ActivityModeration/ActivityModerationPlugin.php
plugins/ActivityModeration/classes/Deleted_notice.php
plugins/Autocomplete/actions/autocomplete.php
plugins/Event/EventPlugin.php
plugins/Event/actions/cancelrsvp.php
plugins/Event/classes/Happening.php
plugins/Event/classes/RSVP.php
plugins/Event/lib/eventsnoticestream.php
plugins/OStatus/actions/groupsalmon.php
plugins/OStatus/actions/usersalmon.php
plugins/OStatus/classes/Ostatus_profile.php
plugins/OStatus/lib/huboutqueuehandler.php
plugins/Oembed/OembedPlugin.php
plugins/Oembed/classes/File_oembed.php
plugins/QnA/actions/qnanewanswer.php
plugins/TwitterBridge/lib/twitterimport.php
plugins/Xmpp/XmppPlugin.php
scripts/fixup_hashtags.php
scripts/nukefile.php
scripts/setpassword.php
scripts/upgrade.php

index fd12b8fa6e736b678034475db2fcef451b3fc6f9..00383151a6401dfbfde841ba02bceb70845f04fd 100644 (file)
@@ -1,5 +1,4 @@
 avatar/*
-background/*
 files/*
 file/*
 local/*
index 120db976df82862ae1ee327ca0fa8e638f2f4d4e..d0b6a2448439208c84b18f18b6264272d93eeb3a 100644 (file)
--- a/CONFIGURE
+++ b/CONFIGURE
@@ -632,20 +632,6 @@ notify third-party servers of updates.
 notify: an array of URLs for ping endpoints. Default is the empty
     array (no notification).
 
-design
-------
-
-Default design (colors and background) for the site. Actual appearance
-depends on the theme.  Null values mean to use the theme defaults.
-
-backgroundcolor: Hex color of the site background.
-contentcolor: Hex color of the content area background.
-sidebarcolor: Hex color of the sidebar background.
-textcolor: Hex color of all non-link text.
-linkcolor: Hex color of all links.
-backgroundimage: Image to use for the background.
-disposition: Flags for whether or not to tile the background image.
-
 notice
 ------
 
index ccc3f94fd4105ce86d76140937e55aa76d6eb048..17499312fbcbd45ea1157cba0e6a87010d468d5a 100644 (file)
@@ -96,7 +96,7 @@ class NewnoticeAction extends FormAction
         assert($this->scoped instanceof Profile); // XXX: maybe an error instead...
         $user = $this->scoped->getUser();
         $content = $this->trimmed('status_textarea');
-        $options = array();
+        $options = array('source' => 'web');
         Event::handle('StartSaveNewNoticeWeb', array($this, $user, &$content, &$options));
 
         if (empty($content)) {
@@ -117,31 +117,30 @@ class NewnoticeAction extends FormAction
             return;
         }
 
-        $content_shortened = $user->shortenLinks($content);
-        if (Notice::contentTooLong($content_shortened)) {
-            // TRANS: Client error displayed when the parameter "status" is missing.
-            // TRANS: %d is the maximum number of character for a notice.
-            $this->clientError(sprintf(_m('That\'s too long. Maximum notice size is %d character.',
-                                          'That\'s too long. Maximum notice size is %d characters.',
-                                          Notice::maxContent()),
-                                       Notice::maxContent()));
+        if ($this->int('inreplyto')) {
+            // Throws exception if the inreplyto Notice is given but not found.
+            $parent = Notice::getByID($this->int('inreplyto'));
+        } else {
+            $parent = null;
         }
 
-        $replyto = $this->int('inreplyto');
-        if ($replyto) {
-            $options['reply_to'] = $replyto;
-        }
+        $act = new Activity();
+        $act->verb = ActivityVerb::POST;
+        $act->time = time();
+        $act->actor = $this->scoped->asActivityObject();
+
+        $content = $this->scoped->shortenLinks($content);
 
         $upload = null;
         try {
             // throws exception on failure
             $upload = MediaFile::fromUpload('attach', $this->scoped);
-            if (Event::handle('StartSaveNewNoticeAppendAttachment', array($this, $upload, &$content_shortened, &$options))) {
-                $content_shortened .= ' ' . $upload->shortUrl();
+            if (Event::handle('StartSaveNewNoticeAppendAttachment', array($this, $upload, &$content, &$options))) {
+                $content .= ' ' . $upload->shortUrl();
             }
-            Event::handle('EndSaveNewNoticeAppendAttachment', array($this, $upload, &$content_shortened, &$options));
+            Event::handle('EndSaveNewNoticeAppendAttachment', array($this, $upload, &$content, &$options));
 
-            if (Notice::contentTooLong($content_shortened)) {
+            if (Notice::contentTooLong($content)) {
                 $upload->delete();
                 // TRANS: Client error displayed exceeding the maximum notice length.
                 // TRANS: %d is the maximum length for a notice.
@@ -150,10 +149,25 @@ class NewnoticeAction extends FormAction
                                               Notice::maxContent()),
                                            Notice::maxContent()));
             }
+
+            $act->enclosures[] = $upload->getEnclosure();
         } catch (NoUploadedMediaException $e) {
             // simply no attached media to the new notice
         }
 
+        $actobj = new ActivityObject();
+        $actobj->type = ActivityObject::NOTE;
+        $actobj->content = common_render_content($content, $this->scoped, $parent);
+
+        $act->objects[] = $actobj;
+
+
+        $act->context = new ActivityContext();
+
+        if ($parent instanceof Notice) {
+            $act->context->replyToID = $parent->getUri();
+            $act->context->replyToUrl = $parent->getUrl(true);  // maybe we don't have to send true here to force a URL?
+        }
 
         if ($this->scoped->shareLocation()) {
             // use browser data if checked; otherwise profile data
@@ -171,19 +185,20 @@ class NewnoticeAction extends FormAction
                                                       $this->scoped);
             }
 
-            $options = array_merge($options, $locOptions);
+            $act->context->location = Location::fromOptions($locOptions);
         }
 
         $author_id = $this->scoped->id;
-        $text      = $content_shortened;
+        $text      = $content;
 
         // Does the heavy-lifting for getting "To:" information
 
         ToSelector::fillOptions($this, $options);
 
+        // FIXME: Make sure NoticeTitle plugin gets a change to add the title to our activityobject!
         if (Event::handle('StartNoticeSaveWeb', array($this, &$author_id, &$text, &$options))) {
 
-            $this->stored = Notice::saveNew($this->scoped->id, $content_shortened, 'web', $options);
+            $this->stored = Notice::saveActivity($act, $this->scoped, $options);
 
             if ($upload instanceof MediaFile) {
                 $upload->attachToNotice($this->stored);
@@ -192,7 +207,7 @@ class NewnoticeAction extends FormAction
             Event::handle('EndNoticeSaveWeb', array($this, $this->stored));
         }
 
-        Event::handle('EndSaveNewNoticeWeb', array($this, $user, &$content_shortened, &$options));
+        Event::handle('EndSaveNewNoticeWeb', array($this, $user, &$content, &$options));
 
         if (!GNUsocial::isAjax()) {
             $url = common_local_url('shownotice', array('notice' => $this->stored->id));
index f1d6d1551640b9d771c780a22f4d7f080d648b5f..bcd2eb5065009597d1f079e5b89f109fed6d3a3b 100644 (file)
@@ -203,14 +203,7 @@ class SearchNoticeListItem extends NoticeListItem {
     {
         // FIXME: URL, image, video, audio
         $this->out->elementStart('p', array('class' => 'e-content'));
-        if ($this->notice->rendered) {
-            $this->out->raw($this->highlight($this->notice->rendered, $this->terms));
-        } else {
-            // XXX: may be some uncooked notices in the DB,
-            // we cook them right now. This should probably disappear in future
-            // versions (>> 0.4.x)
-            $this->out->raw($this->highlight(common_render_content($this->notice->content, $this->notice), $this->terms));
-        }
+        $this->out->raw($this->highlight($this->notice->getRendered(), $this->terms));
         $this->out->elementEnd('p');
 
     }
index cfdb6c78176e221fcef24a1cd3e7d04feb48584b..da752cbb9174a2de439b7865cb926be7cc16834f 100644 (file)
@@ -144,22 +144,8 @@ class PasswordsettingsAction extends SettingsAction
 
         if (Event::handle('StartChangePassword', array($this->scoped, $oldpassword, $newpassword))) {
             //no handler changed the password, so change the password internally
-            $user = $this->scoped->getUser();
-            $original = clone($user);
+            $user->setPassword($newpassword);
 
-            $user->password = common_munge_password($newpassword, $this->scoped);
-
-            $val = $user->validate();
-            if ($val !== true) {
-                // TRANS: Form validation error on page where to change password.
-                throw new ServerException(_('Error saving user; invalid.'));
-            }
-
-            if (!$user->update($original)) {
-                // TRANS: Server error displayed on page where to change password when password change
-                // TRANS: could not be made because of a server error.
-                throw new ServerException(_('Cannot save new password.'));
-            }
             Event::handle('EndChangePassword', array($this->scoped));
         }
 
index 51ee43234867a6443dd71be251f8e61ed578c83f..14ed582db122f391aaaac0016a5c7e0c36c21498 100644 (file)
@@ -322,16 +322,7 @@ class RecoverpasswordAction extends Action
         }
 
         // OK, we're ready to go
-
-        $original = clone($user);
-
-        $user->password = common_munge_password($newpassword, $user->getProfile());
-
-        if (!$user->update($original)) {
-            common_log_db_error($user, 'UPDATE', __FILE__);
-            // TRANS: Reset password form validation error message.
-            $this->serverError(_('Cannot save new password.'));
-        }
+        $user->setPassword($newpassword);
 
         $this->clearTempUser();
 
index 2e8492849c4b9f68abb1586aad52f9406e9c81f2..c457da1a5a1f74515f0b15537e49b59024c107e2 100644 (file)
  * along with this program.     If not, see <http://www.gnu.org/licenses/>.
  */
 
-if (!defined('STATUSNET')) {
-    exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
 
 /**
  * Table Definition for config
  */
 
-require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
-
 class Config extends Managed_DataObject
 {
     ###START_AUTOCODE
@@ -35,7 +31,7 @@ class Config extends Managed_DataObject
     public $__table = 'config';                          // table name
     public $section;                         // varchar(32)  primary_key not_null
     public $setting;                         // varchar(32)  primary_key not_null
-    public $value;                           // varchar(191)   not 255 because utf8mb4 takes more space
+    public $value;                           // text
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
@@ -46,7 +42,7 @@ class Config extends Managed_DataObject
             'fields' => array(
                 'section' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'default' => '', 'description' => 'configuration section'),
                 'setting' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'default' => '', 'description' => 'configuration setting'),
-                'value' => array('type' => 'varchar', 'length' => 191, 'description' => 'configuration value'),
+                'value' => array('type' => 'text', 'description' => 'configuration value'),
             ),
             'primary key' => array('section', 'setting'),
         );
index 2cb5b65131af2194c80616c1f80c00bdac1c8eff..af9dc9157ed127e9d4009a4fa97732232e99ed3d 100644 (file)
@@ -2,13 +2,9 @@
 /**
  * Table Definition for confirm_address
  */
-require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
 
 class Confirm_address extends Managed_DataObject 
 {
-    ###START_AUTOCODE
-    /* the code below is auto generated do not remove the above tag */
-
     public $__table = 'confirm_address';                 // table name
     public $code;                            // varchar(32)  primary_key not_null
     public $user_id;                         // int(4)   not_null
@@ -19,9 +15,6 @@ class Confirm_address extends Managed_DataObject
     public $sent;                            // datetime()  
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
-    /* the code above is auto generated do not remove the tag below */
-    ###END_AUTOCODE
-
     public static function schemaDef()
     {
         return array(
@@ -29,7 +22,7 @@ class Confirm_address extends Managed_DataObject
                 'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'good random code'),
                 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user who requested confirmation'),
                 'address' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'address (email, xmpp, SMS, etc.)'),
-                'address_extra' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'carrier ID, for SMS'),
+                'address_extra' => array('type' => 'varchar', 'length' => 191, 'description' => 'carrier ID, for SMS'),
                 'address_type' => array('type' => 'varchar', 'length' => 8, 'not null' => true, 'description' => 'address type ("email", "xmpp", "sms")'),
                 'claimed' => array('type' => 'datetime', 'description' => 'date this was claimed for queueing'),
                 'sent' => array('type' => 'datetime', 'description' => 'date this was sent for queueing'),
index fadbda1f9bb008ccd90c39373a508ff47173a38b..24c4e6a232509d00459d1200e522639c11951757 100644 (file)
@@ -31,10 +31,10 @@ class File extends Managed_DataObject
     public $filehash;                        // varchar(64)     indexed
     public $mimetype;                        // varchar(50)
     public $size;                            // int(4)
-    public $title;                           // varchar(191)   not 255 because utf8mb4 takes more space
+    public $title;                           // text()
     public $date;                            // int(4)
     public $protected;                       // int(4)
-    public $filename;                        // varchar(191)   not 255 because utf8mb4 takes more space
+    public $filename;                        // text()
     public $width;                           // int(4)
     public $height;                          // int(4)
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
@@ -52,10 +52,10 @@ class File extends Managed_DataObject
                 'filehash' => array('type' => 'varchar', 'length' => 64, 'not null' => false, 'description' => 'sha256 of the file contents, only for locally stored files of course'),
                 'mimetype' => array('type' => 'varchar', 'length' => 50, 'description' => 'mime type of resource'),
                 'size' => array('type' => 'int', 'description' => 'size of resource when available'),
-                'title' => array('type' => 'varchar', 'length' => 191, 'description' => 'title of resource when available'),
+                'title' => array('type' => 'text', 'description' => 'title of resource when available'),
                 'date' => array('type' => 'int', 'description' => 'date of resource according to http query'),
                 'protected' => array('type' => 'int', 'description' => 'true when URL is private (needs login)'),
-                'filename' => array('type' => 'varchar', 'length' => 191, 'description' => 'if a local file, name of the file'),
+                'filename' => array('type' => 'text', 'description' => 'if a local file, name of the file'),
                 'width' => array('type' => 'int', 'description' => 'width in pixels, if it can be described as such and data is available'),
                 'height' => array('type' => 'int', 'description' => 'height in pixels, if it can be described as such and data is available'),
 
@@ -424,7 +424,11 @@ class File extends Managed_DataObject
             if (self::hashurl($url) !== $this->urlhash) {
                 // For indexing purposes, in case we do a lookup on the 'url' field.
                 // also we're fixing possible changes from http to https, or paths
-                $this->updateUrl($url);
+                try {
+                       $this->updateUrl($url);
+                } catch (ServerException $e) {
+                       //
+                }      
             }
             return $url;
         }
@@ -656,4 +660,4 @@ class File extends Managed_DataObject
         echo "DONE.\n";
         echo "Resuming core schema upgrade...";
     }
-}
+}
\ No newline at end of file
index 1305f488f102cb478397992fadc1dfdf64eec157..bc405311fef63365ff2132cc04bf271f4ef3895e 100644 (file)
@@ -262,8 +262,9 @@ class File_redirection extends Managed_DataObject
         if (!empty($short_url) && $short_url != $long_url) {
             $short_url = (string)$short_url;
             // store it
-            $file = File::getKV('url', $long_url);
-            if (!$file instanceof File) {
+            try {
+                $file = File::getByUrl($long_url);
+            } catch (NoResultException $e) {
                 // Check if the target URL is itself a redirect...
                 $redir = File_redirection::where($long_url);
                 $file = $redir->getFile();
@@ -271,12 +272,14 @@ class File_redirection extends Managed_DataObject
                     $file->saveFile();
                 }
             }
-            $file_redir = File_redirection::getKV('url', $short_url);
-            if (!$file_redir instanceof File_redirection) {
-                $file_redir = new File_redirection;
+            // Now we definitely have a File object in $file
+            try {
+                $file_redir = File_redirection::getByUrl($short_url);
+            } catch (NoResultException $e) {
+                $file_redir = new File_redirection();
                 $file_redir->urlhash = File::hashurl($short_url);
                 $file_redir->url = $short_url;
-                $file_redir->file_id = $file->id;
+                $file_redir->file_id = $file->getID();
                 $file_redir->insert();
             }
             return $short_url;
index 274b815c4566435d1ccc87d9acd614581424d305..ef79eadc38efa892fad9d0e25a9832001bf69a97 100644 (file)
@@ -156,6 +156,26 @@ class File_thumbnail extends Managed_DataObject
         return $this->url;
     }
 
+    public function getHeight()
+    {
+        return $this->height;
+    }
+
+    public function getWidth()
+    {
+        return $this->width;
+    }
+
+    public function getHtmlAttrs(array $orig=array(), $overwrite=true)
+    {
+        $attrs = [
+                'height' => $this->getHeight(),
+                'width'  => $this->getWidth(),
+                'src'    => $this->getUrl(),
+            ];
+        return $overwrite ? array_merge($orig, $attrs) : array_merge($attrs, $orig);
+    }
+
     public function delete($useWhere=false)
     {
         if (!empty($this->filename) && file_exists(File_thumbnail::path($this->filename))) {
index 5f8445d81e641b7f595a415f185100c48bbb4580..fb18e594f99bdbb93603ca80513c865974f9d4ea 100644 (file)
@@ -371,7 +371,7 @@ abstract class Managed_DataObject extends Memcached_DataObject
             if (!array_key_exists($col, $vals)) {
                 continue;
             } elseif (is_null($vals[$col])) {
-                throw new ServerException("NULL values not allowed in getByPK for column '{$col}'");
+                throw new ServerException("NULL values not allowed in getByKeys for column '{$col}'");
             }
             $object->$col = $vals[$col];
         }
index d6e44124ba21b7ed37b50ef5aa7f10d79c085e99..757205761a3e48ec43eca2eb7a3683ecdb43662b 100644 (file)
@@ -623,7 +623,9 @@ class Notice extends Managed_DataObject
         if (!empty($rendered)) {
             $notice->rendered = $rendered;
         } else {
-            $notice->rendered = common_render_content($final, $notice);
+            $notice->rendered = common_render_content($final,
+                                                      $notice->getProfile(),
+                                                      $notice->hasParent() ? $notice->getParent() : null);
         }
 
         if (empty($verb)) {
@@ -684,33 +686,33 @@ class Notice extends Managed_DataObject
             }
         }
 
-        // Clear the cache for subscribed users, so they'll update at next request
-        // XXX: someone clever could prepend instead of clearing the cache
-
-        // Save per-notice metadata...
-
-        if (isset($replies)) {
-            $notice->saveKnownReplies($replies);
-        } else {
-            $notice->saveReplies();
-        }
+        // Only save 'attention' and metadata stuff (URLs, tags...) stuff if
+        // the activityverb is a POST (since stuff like repeat, favorite etc.
+        // reasonably handle notifications themselves.
+        if (ActivityUtils::compareVerbs($notice->verb, array(ActivityVerb::POST))) {
+            if (isset($replies)) {
+                $notice->saveKnownReplies($replies);
+            } else {
+                $notice->saveReplies();
+            }
 
-        if (isset($tags)) {
-            $notice->saveKnownTags($tags);
-        } else {
-            $notice->saveTags();
-        }
+            if (isset($tags)) {
+                $notice->saveKnownTags($tags);
+            } else {
+                $notice->saveTags();
+            }
 
-        // Note: groups may save tags, so must be run after tags are saved
-        // to avoid errors on duplicates.
-        // Note: groups should always be set.
+            // Note: groups may save tags, so must be run after tags are saved
+            // to avoid errors on duplicates.
+            // Note: groups should always be set.
 
-        $notice->saveKnownGroups($groups);
+            $notice->saveKnownGroups($groups);
 
-        if (isset($urls)) {
-            $notice->saveKnownUrls($urls);
-        } else {
-            $notice->saveUrls();
+            if (isset($urls)) {
+                $notice->saveKnownUrls($urls);
+            } else {
+                $notice->saveUrls();
+            }
         }
 
         if ($distribute) {
@@ -738,6 +740,7 @@ class Notice extends Managed_DataObject
         }
 
         // Get ActivityObject properties
+        $actobj = null;
         if (!empty($act->id)) {
             // implied object
             $options['uri'] = $act->id;
@@ -782,7 +785,9 @@ class Notice extends Managed_DataObject
             $stored->uri = $uri;
             if ($stored->find()) {
                 common_debug('cannot create duplicate Notice URI: '.$stored->uri);
-                throw new Exception('Notice URI already exists');
+                // I _assume_ saving a Notice with a colliding URI means we're really trying to
+                // save the same notice again...
+                throw new AlreadyFulfilledException('Notice URI already exists');
             }
         }
 
@@ -813,12 +818,26 @@ class Notice extends Managed_DataObject
         $stored->url = $url;
         $stored->verb = $act->verb;
 
-        // Use the local user's shortening preferences, if applicable.
-        $stored->rendered = $actor->isLocal()
-                                ? $actor->shortenLinks($act->content)
-                                : common_purify($act->content);
+        // Notice content. We trust local users to provide HTML we like, but of course not remote users.
+        // FIXME: What about local users importing feeds? Mirror functions must filter out bad HTML first...
+        $content = $act->content ?: $act->summary;
+        if (is_null($content) && !is_null($actobj)) {
+            $content = $actobj->content ?: $actobj->summary;
+        }
+        $stored->rendered = $actor->isLocal() ? $content : common_purify($content);
         $stored->content = common_strip_html($stored->rendered);
 
+        // Reject notice if it is too long (without the HTML)
+        // FIXME: Reject if too short (empty) too? But we have to pass the 
+        if ($actor->isLocal() && Notice::contentTooLong($stored->content)) {
+            // TRANS: Client error displayed when the parameter "status" is missing.
+            // TRANS: %d is the maximum number of character for a notice.
+            throw new ClientException(sprintf(_m('That\'s too long. Maximum notice size is %d character.',
+                                                 'That\'s too long. Maximum notice size is %d characters.',
+                                                 Notice::maxContent()),
+                                              Notice::maxContent()));
+        }
+
         // Maybe a missing act-time should be fatal if the actor is not local?
         if (!empty($act->time)) {
             $stored->created = common_sql_date($act->time);
@@ -965,28 +984,33 @@ class Notice extends Managed_DataObject
         // Used primarily for OStatus (and if we don't federate, all attentions would be local anyway)
         Event::handle('GetLocalAttentions', array($actor, $act->context->attention, &$mentions, &$group_ids));
 
-        if (!empty($mentions)) {
-            $stored->saveKnownReplies($mentions);
-        } else {
-            $stored->saveReplies();
-        }
+        // Only save 'attention' and metadata stuff (URLs, tags...) stuff if
+        // the activityverb is a POST (since stuff like repeat, favorite etc.
+        // reasonably handle notifications themselves.
+        if (ActivityUtils::compareVerbs($stored->verb, array(ActivityVerb::POST))) {
+            if (!empty($mentions)) {
+                $stored->saveKnownReplies($mentions);
+            } else {
+                $stored->saveReplies();
+            }
 
-        if (!empty($tags)) {
-            $stored->saveKnownTags($tags);
-        } else {
-            $stored->saveTags();
-        }
+            if (!empty($tags)) {
+                $stored->saveKnownTags($tags);
+            } else {
+                $stored->saveTags();
+            }
 
-        // Note: groups may save tags, so must be run after tags are saved
-        // to avoid errors on duplicates.
-        // Note: groups should always be set.
+            // Note: groups may save tags, so must be run after tags are saved
+            // to avoid errors on duplicates.
+            // Note: groups should always be set.
 
-        $stored->saveKnownGroups($group_ids);
+            $stored->saveKnownGroups($group_ids);
 
-        if (!empty($urls)) {
-            $stored->saveKnownUrls($urls);
-        } else {
-            $stored->saveUrls();
+            if (!empty($urls)) {
+                $stored->saveKnownUrls($urls);
+            } else {
+                $stored->saveUrls();
+            }
         }
 
         if ($distribute) {
@@ -1036,8 +1060,10 @@ class Notice extends Managed_DataObject
             $this->blowStream('networkpublic');
         }
 
-        self::blow('notice:list-ids:conversation:%s', $this->conversation);
-        self::blow('conversation:notice_count:%d', $this->conversation);
+        if ($this->conversation) {
+            self::blow('notice:list-ids:conversation:%s', $this->conversation);
+            self::blow('conversation:notice_count:%d', $this->conversation);
+        }
 
         if ($this->isRepeat()) {
             // XXX: we should probably only use one of these
@@ -1583,7 +1609,7 @@ class Notice extends Managed_DataObject
             return;
         }
 
-        $sender = Profile::getKV($this->profile_id);
+        $sender = $this->getProfile();
 
         foreach (array_unique($uris) as $uri) {
             try {
@@ -1598,11 +1624,9 @@ class Notice extends Managed_DataObject
                 continue;
             }
 
-            $this->saveReply($profile->id);
-            self::blow('reply:stream:%d', $profile->id);
+            $this->saveReply($profile->getID());
+            self::blow('reply:stream:%d', $profile->getID());
         }
-
-        return;
     }
 
     /**
@@ -1617,12 +1641,6 @@ class Notice extends Managed_DataObject
 
     function saveReplies()
     {
-        // Don't save reply data for repeats
-
-        if ($this->isRepeat()) {
-            return array();
-        }
-
         $sender = $this->getProfile();
 
         $replied = array();
@@ -1631,19 +1649,21 @@ class Notice extends Managed_DataObject
         try {
             $parent = $this->getParent();
             $parentauthor = $parent->getProfile();
-            $this->saveReply($parentauthor->id);
-            $replied[$parentauthor->id] = 1;
-            self::blow('reply:stream:%d', $parentauthor->id);
+            $this->saveReply($parentauthor->getID());
+            $replied[$parentauthor->getID()] = 1;
+            self::blow('reply:stream:%d', $parentauthor->getID());
         } catch (NoParentNoticeException $e) {
             // Not a reply, since it has no parent!
+            $parent = null;
         } catch (NoResultException $e) {
             // Parent notice was probably deleted
+            $parent = null;
         }
 
         // @todo ideally this parser information would only
         // be calculated once.
 
-        $mentions = common_find_mentions($this->content, $this);
+        $mentions = common_find_mentions($this->content, $sender, $parent);
 
         // store replied only for first @ (what user/notice what the reply directed,
         // we assume first @ is it)
@@ -1732,7 +1752,6 @@ class Notice extends Managed_DataObject
     function sendReplyNotifications()
     {
         // Don't send reply notifications for repeats
-
         if ($this->isRepeat()) {
             return array();
         }
@@ -1742,9 +1761,11 @@ class Notice extends Managed_DataObject
             require_once INSTALLDIR.'/lib/mail.php';
 
             foreach ($recipientIds as $recipientId) {
-                $user = User::getKV('id', $recipientId);
-                if ($user instanceof User) {
+                try {
+                    $user = User::getByID($recipientId);
                     mail_notify_attn($user, $this);
+                } catch (NoResultException $e) {
+                    // No such user
                 }
             }
             Event::handle('EndNotifyMentioned', array($this, $recipientIds));
@@ -2049,6 +2070,7 @@ class Notice extends Managed_DataObject
         if (Event::handle('StartActivityObjectFromNotice', array($this, &$object))) {
             $object->type    = $this->object_type ?: ActivityObject::NOTE;
             $object->id      = $this->getUri();
+            //FIXME: = $object->title ?: sprintf(... because we might get a title from StartActivityObjectFromNotice
             $object->title   = sprintf('New %1$s by %2$s', ActivityObject::canonicalType($object->type), $this->getProfile()->getNickname());
             $object->content = $this->rendered;
             $object->link    = $this->getUrl();
@@ -2807,24 +2829,33 @@ class Notice extends Managed_DataObject
         return false;
     }
 
+    public function hasParent()
+    {
+        try {
+            $this->getParent();
+        } catch (NoParentNoticeException $e) {
+            return false;
+        }
+        return true;
+    }
+
     public function getParent()
     {
-       $reply_to_id = null;
+        $reply_to_id = null;
 
         if (empty($this->reply_to)) {
             throw new NoParentNoticeException($this);
         }
 
-       // The reply_to ID in the table Notice could exist with a number
-       // however, the replied to notice might not exist in the database.
-       // Thus we need to catch the exception and throw the NoParentNoticeException else 
-       // the timeline will not display correctly.
-       try {
-               $reply_to_id = self::getByID($this->reply_to);
-       } catch(Exception $e){
-               throw new NoParentNoticeException($this);
-       }
-
+        // The reply_to ID in the table Notice could exist with a number
+        // however, the replied to notice might not exist in the database.
+        // Thus we need to catch the exception and throw the NoParentNoticeException else
+        // the timeline will not display correctly.
+        try {
+            $reply_to_id = self::getByID($this->reply_to);
+        } catch(Exception $e){
+            throw new NoParentNoticeException($this);
+        }
 
         return $reply_to_id;
     }
index 78ef18484c7e0d9f7c9f2559cb419783f39fd1ad..a45511fe54b60a1a02d87258cb61656e2f9d1010 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+if (!defined('GNUSOCIAL')) { exit(1); }
 
 /**
  * Table Definition for profile
  */
 class Profile extends Managed_DataObject
 {
-    ###START_AUTOCODE
-    /* the code below is auto generated do not remove the above tag */
-
     public $__table = 'profile';                         // table name
     public $id;                              // int(4)  primary_key not_null
     public $nickname;                        // varchar(64)  multiple_key not_null
-    public $fullname;                        // varchar(191)  multiple_key   not 255 because utf8mb4 takes more space
-    public $profileurl;                      // varchar(191)                 not 255 because utf8mb4 takes more space
-    public $homepage;                        // varchar(191)  multiple_key   not 255 because utf8mb4 takes more space
+    public $fullname;                        // text()
+    public $profileurl;                      // text()
+    public $homepage;                        // text()
     public $bio;                             // text()  multiple_key
-    public $location;                        // varchar(191)  multiple_key   not 255 because utf8mb4 takes more space
+    public $location;                        // text()
     public $lat;                             // decimal(10,7)
     public $lon;                             // decimal(10,7)
     public $location_id;                     // int(4)
@@ -49,11 +46,11 @@ class Profile extends Managed_DataObject
             'fields' => array(
                 'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
                 'nickname' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'nickname or username', 'collate' => 'utf8mb4_general_ci'),
-                'fullname' => array('type' => 'varchar', 'length' => 191, 'description' => 'display name', 'collate' => 'utf8mb4_general_ci'),
-                'profileurl' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL, cached so we dont regenerate'),
-                'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'identifying URL', 'collate' => 'utf8mb4_general_ci'),
+                'fullname' => array('type' => 'text', 'description' => 'display name', 'collate' => 'utf8mb4_general_ci'),
+                'profileurl' => array('type' => 'text', 'description' => 'URL, cached so we dont regenerate'),
+                'homepage' => array('type' => 'text', 'description' => 'identifying URL', 'collate' => 'utf8mb4_general_ci'),
                 'bio' => array('type' => 'text', 'description' => 'descriptive biography', 'collate' => 'utf8mb4_general_ci'),
-                'location' => array('type' => 'varchar', 'length' => 191, 'description' => 'physical location', 'collate' => 'utf8mb4_general_ci'),
+                'location' => array('type' => 'text', 'description' => 'physical location', 'collate' => 'utf8mb4_general_ci'),
                 'lat' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'),
                 'lon' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'),
                 'location_id' => array('type' => 'int', 'description' => 'location id if possible'),
@@ -76,9 +73,6 @@ class Profile extends Managed_DataObject
 
         return $def;
     }
-       
-    /* the code above is auto generated do not remove the tag below */
-    ###END_AUTOCODE
 
     public static function getByEmail($email)
     {
@@ -1439,6 +1433,11 @@ class Profile extends Managed_DataObject
             $user = User::getKV('id', $this->id);
             if ($user instanceof User) {
                 $uri = $user->getUri();
+            } else {
+                $group = User_group::getKV('profile_id', $this->id);
+                if ($group instanceof User_group) {
+                    $uri = $group->getUri();
+                }
             }
 
             Event::handle('EndGetProfileUri', array($this, &$uri));
index 72a707cae86813149afad1297b1afd0c8a1e2bcb..bc7c400b890a2c8d3775b359256d0fd88c61a4e3 100644 (file)
@@ -85,17 +85,17 @@ class Profile_prefs extends Managed_DataObject
     static function getAll(Profile $profile)
     {
         try {
-            $prefs = self::listFind('profile_id', $profile->getID());
+            $prefs = self::listFind('profile_id', array($profile->getID()));
         } catch (NoResultException $e) {
             return array();
         }
 
         $list = array();
-        while ($entry = $prefs->fetch()) {
-            if (!isset($list[$entry->namespace])) {
-                $list[$entry->namespace] = array();
+        while ($prefs->fetch()) {
+            if (!isset($list[$prefs->namespace])) {
+                $list[$prefs->namespace] = array();
             }
-            $list[$entry->namespace][$entry->topic] = $entry->data;
+            $list[$prefs->namespace][$prefs->topic] = $prefs->data;
         }
         return $list;
     }
index d41c53e0e068dac5a264030d70eaacd5f388d747..8a2cc87da355b4896bf42e2a6a6463d6b9e8d688 100644 (file)
@@ -68,16 +68,15 @@ class Queue_item extends Managed_DataObject
             // XXX: potential race condition
             // can we force it to only update if claimed is still null
             // (or old)?
-            common_log(LOG_INFO, 'claiming queue item id = ' . $qi->getID() .
-                ' for transport ' . $qi->transport);
+            common_log(LOG_INFO, 'claiming queue item id = ' . $qi->getID() . ' for transport ' . $qi->transport);
             $orig = clone($qi);
             $qi->claimed = common_sql_now();
             $result = $qi->update($orig);
             if ($result) {
-                common_log(LOG_INFO, 'claim succeeded.');
+                common_log(LOG_DEBUG, 'claim succeeded.');
                 return $qi;
             } else {
-                common_log(LOG_INFO, 'claim failed.');
+                common_log(LOG_ERR, 'claim of queue item id= ' . $qi->getID() . ' for transport ' . $qi->transport . ' failed.');
             }
         }
         $qi = null;
index e33c83e89cf0f74f3c07957a4caec50e94f84624..c8b334d489ab76c553f7a27e6601c1cb63c397d3 100644 (file)
@@ -1012,6 +1012,24 @@ class User extends Managed_DataObject
         return !empty($this->password);
     }
 
+    public function setPassword($password)
+    {
+        $orig = clone($this);
+        $this->password = common_munge_password($password, $this->getProfile());
+
+        if ($this->validate() !== true) {
+            // TRANS: Form validation error on page where to change password.
+            throw new ServerException(_('Error saving user; invalid.'));
+        }
+
+        if (!$this->update($orig)) {
+            common_log_db_error($this, 'UPDATE', __FILE__);
+            // TRANS: Server error displayed on page where to change password when password change
+            // TRANS: could not be made because of a server error.
+            throw new ServerException(_('Cannot save new password.'));
+        }
+    }
+
     public function delPref($namespace, $topic)
     {
         return $this->getProfile()->delPref($namespace, $topic);
index 8b2a458454711187e059d7dc7c39fea4dbf8763d..d22b4d2f4425ebfff5b215494bbba2b1982d7485 100644 (file)
@@ -233,6 +233,9 @@ abstract class ActivityHandlerPlugin extends Plugin
     protected function notifyMentioned(Notice $stored, array &$mentioned_ids)
     {
         // pass through silently by default
+
+        // If we want to stop any other plugin from notifying based on this activity, return false instead.
+        return true;
     }
 
     /**
@@ -305,10 +308,7 @@ abstract class ActivityHandlerPlugin extends Plugin
             return true;
         }
 
-        $this->notifyMentioned($stored, $mentioned_ids);
-
-        // If it was _our_ notice, only we should do anything with the mentions.
-        return false;
+        return $this->notifyMentioned($stored, $mentioned_ids);
     }
 
     /**
@@ -585,7 +585,8 @@ abstract class ActivityHandlerPlugin extends Plugin
         try {
             $this->showNoticeListItem($nli);
         } catch (Exception $e) {
-            $nli->out->element('p', 'error', 'Error showing notice: '.htmlspecialchars($e->getMessage()));
+            common_log(LOG_ERR, 'Error showing notice: ' . $e->getMessage());
+            $nli->out->element('p', 'error', sprintf(_('Error showing notice: %s'), $e->getMessage()));
         }
 
         Event::handle('EndShowNoticeItem', array($nli));
index 8d63afb7dceaf84ce5413a60967895d174ce2c2c..93dc4ca95e8840c3d9f4cb96d718400a9023a2fe 100644 (file)
@@ -120,7 +120,7 @@ class AttachmentListItem extends Widget
                     try {
                         // Tell getThumbnail that we can show an animated image if it has one (4th arg, "force_still")
                         $thumb = $this->attachment->getThumbnail(null, null, false, false);
-                        $this->out->element('img', array('class'=>'u-photo', 'src' => $thumb->getUrl(), 'alt' => ''));
+                        $this->out->element('img', $thumb->getHtmlAttrs(['class'=>'u-photo', 'alt' => '']));
                     } catch (UseFileAsThumbnailException $e) {
                         $this->out->element('img', array('class'=>'u-photo', 'src' => $e->file->getUrl(), 'alt' => $e->file->title));
                     } catch (UnsupportedMediaException $e) {
index b153027233099169d71de98b1c4465a9522c2955..65293a8c934342d0de76023da1b7a8993745506e 100644 (file)
@@ -140,11 +140,6 @@ $default =
               'path' => $_path . '/avatar/',
               'ssl' => null,
               'maxsize' => 300),
-        'background' =>
-        array('server' => null,
-              'dir' => INSTALLDIR . '/background/',
-              'path' => $_path . '/background/',
-              'ssl' => null),
         'public' =>
         array('localonly' => false,
               'blacklist' => array(),
index f3e2420677a8cc24f5b18892847aa42fa2e41b06..f65f7b4cbbe25b3e633261d00292548d39803069 100644 (file)
@@ -68,11 +68,7 @@ class DoFollowListItem extends NoticeListItem
         // FIXME: URL, image, video, audio
         $this->out->elementStart('article', array('class' => 'e-content'));
 
-        if (!empty($this->notice->rendered)) {
-            $html = $this->notice->rendered;
-        } else {
-            $html = common_render_content($this->notice->content, $this->notice);
-        }
+        $html = $this->notice->getRendered();
 
         if (common_config('nofollow', 'external') == 'sometimes') {
             // remove the nofollow part
index 08ca6f37c4be3b869f72629d03787b06b0c33753..ed87782c472e1f3a8dde0b6f5592dda487904fe7 100644 (file)
@@ -383,15 +383,11 @@ class GNUsocial
         if (isset($conffile)) {
             $config_files = array($conffile);
         } else {
-            $config_files = array('/etc/statusnet/statusnet.php',
-                                  '/etc/statusnet/laconica.php',
-                                  '/etc/laconica/laconica.php',
-                                  '/etc/statusnet/'.$_server.'.php',
-                                  '/etc/laconica/'.$_server.'.php');
+            $config_files = array('/etc/gnusocial/config.php',
+                                  '/etc/gnusocial/config.d/'.$_server.'.php');
 
             if (strlen($_path) > 0) {
-                $config_files[] = '/etc/statusnet/'.$_server.'_'.$_path.'.php';
-                $config_files[] = '/etc/laconica/'.$_server.'_'.$_path.'.php';
+                $config_files[] = '/etc/gnusocial/config.d/'.$_server.'_'.$_path.'.php';
             }
 
             $config_files[] = INSTALLDIR.'/config.php';
index 03da41850b9698c99ff416687c7695309d740d43..36f83a3ead5e723342022b2647d5ce8827e80c7e 100644 (file)
@@ -133,10 +133,16 @@ abstract class Installer
         }
 
         // Check the subdirs used for file uploads
-        $fileSubdirs = array('avatar', 'background', 'file');
+        $fileSubdirs = array('avatar', 'file');
         foreach ($fileSubdirs as $fileSubdir) {
-            $fileFullPath = INSTALLDIR."/$fileSubdir/";
-            if (!is_writable($fileFullPath)) {
+            $fileFullPath = INSTALLDIR."/$fileSubdir";
+            if (!file_exists($fileFullPath)) {
+                $pass = $pass && mkdir($fileFullPath);
+            } elseif (!is_dir($fileFullPath)) {
+                $this->warning(sprintf('GNU social expected a directory but found something else on this path: %s', $fileFullPath),
+                               'Either make sure it goes to a directory or remove it and a directory will be created.');
+                $pass = false;
+            } elseif (!is_writable($fileFullPath)) {
                 $this->warning(sprintf('Cannot write to %s directory: <code>%s</code>', $fileSubdir, $fileFullPath),
                                sprintf('On your server, try this command: <code>chmod a+w %s</code>', $fileFullPath));
                 $pass = false;
index 191550d6d6ca53e213f4a13f3b6b8bd2fc763a65..54e0d07acf61575fff990cf2d9f56c8123fabcc6 100644 (file)
@@ -56,6 +56,23 @@ class Location
 
     var $names = array();
 
+    /**
+     * Constructor that makes a Location from Notice::locationOptions(...)
+     *
+     * @param array $options    an array for example provided by Notice::locationOptions(...)
+     *
+     * @return Location Location with the given options (lat, lon, id, name)
+     */
+    static function fromOptions(array $options) {
+        $location = new Location();
+        foreach (['lat', 'lon', 'location_id', 'location_ns'] as $opt) {
+            if (isset($options[$opt])) {
+                $location->$opt = $options[$opt];
+            }
+        }
+        return $location;
+    }
+
     /**
      * Constructor that makes a Location from a string name
      *
index 7ac743bfee8d58fa9dae64de2bf3a0af0cfae725..2076476f875c54c6171eb05777a4072024f89a88 100644 (file)
@@ -30,9 +30,7 @@
  * @link      http://status.net/
  */
 
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
 
 require_once 'Mail.php';
 
@@ -54,7 +52,7 @@ function mail_backend()
         $backend = $mail->factory(common_config('mail', 'backend'),
                                  common_config('mail', 'params') ?: array());
         if ($_PEAR->isError($backend)) {
-            common_server_error($backend->getMessage(), 500);
+            throw new ServerException($backend->getMessage());
         }
     }
     return $backend;
@@ -71,6 +69,8 @@ function mail_backend()
  */
 function mail_send($recipients, $headers, $body)
 {
+    global $_PEAR;
+
     try {
         // XXX: use Mail_Queue... maybe
         $backend = mail_backend();
@@ -81,6 +81,9 @@ function mail_send($recipients, $headers, $body)
 
         assert($backend); // throws an error if it's bad
         $sent = $backend->send($recipients, $headers, $body);
+        if ($_PEAR->isError($sent)) {
+            throw new ServerException($sent->getMessage());
+        }
         return true;
     } catch (PEAR_Exception $e) {
         common_log(
index 2b8f324df2f88d5e8671c95f2e6166d07b57a380..9fe5432ad503273c47d35160a481dc84b6c5d3e7 100644 (file)
@@ -74,6 +74,11 @@ class MediaFile
         return $this->short_fileurl;
     }
 
+    function getEnclosure()
+    {
+        return $this->getFile()->getEnclosure();
+    }
+
     function delete()
     {
         $filepath = File::path($this->filename);
index f6638dc615ae8d0208c276c4287796ce61b557f2..2d538c6e6a9aa6f249cef1878b901acc0993df61 100644 (file)
@@ -154,7 +154,11 @@ class NoticeListItem extends Widget
 
         if (!empty($this->notice->reply_to) || count($this->getProfileAddressees()) > 0) {
             $this->elementStart('div', array('class' => 'parents'));
-            if (!empty($this->notice->reply_to)) { $this->showParent(); }
+            try {
+                $this->showParent();
+            } catch (NoParentNoticeException $e) {
+                // no parent notice
+            }
             if ($this->addressees) { $this->showAddressees(); }
             $this->elementEnd('div');
         }
@@ -341,13 +345,8 @@ class NoticeListItem extends Widget
         if (Event::handle('StartShowNoticeContent', array($this->notice, $this->out, $this->out->getScoped()))) {
             if ($this->maxchars > 0 && mb_strlen($this->notice->content) > $this->maxchars) {
                 $this->out->text(mb_substr($this->notice->content, 0, $this->maxchars) . '[…]');
-            } elseif ($this->notice->rendered) {
-                $this->out->raw($this->notice->rendered);
             } else {
-                // XXX: may be some uncooked notices in the DB,
-                // we cook them right now. This should probably disappear in future
-                // versions (>> 0.4.x)
-                $this->out->raw(common_render_content($this->notice->content, $this->notice));
+                $this->out->raw($this->notice->getRendered());
             }
             Event::handle('EndShowNoticeContent', array($this->notice, $this->out, $this->out->getScoped()));
         }
index 0dfbd04ffdd800e32a56d3c2cdc6b3b0ce726bdb..764e11c0fc6c577620783fbb7751ccd2df48d20d 100644 (file)
@@ -45,7 +45,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
 
 class ServerException extends Exception
 {
-    public function __construct($message = null, $code = 400) {
+    public function __construct($message = null, $code = 500) {
         parent::__construct($message, $code);
     }
 
index ea09b6fb2f3016f67553cc683b254b537c9da1f6..732237403dfa49cd75450f81120e7378f9ad71cb 100644 (file)
@@ -77,7 +77,7 @@ abstract class SpawningDaemon extends Daemon
         for ($i = 1; $i <= $this->threads; $i++) {
             $pid = pcntl_fork();
             if ($pid < 0) {
-                $this->log(LOG_ERROR, "Couldn't fork for thread $i; aborting\n");
+                $this->log(LOG_ERR, "Couldn't fork for thread $i; aborting\n");
                 exit(1);
             } else if ($pid == 0) {
                 $this->initAndRunChild($i);
@@ -113,7 +113,7 @@ abstract class SpawningDaemon extends Daemon
 
                     $pid = pcntl_fork();
                     if ($pid < 0) {
-                        $this->log(LOG_ERROR, "Couldn't fork to respawn thread $i; aborting thread.\n");
+                        $this->log(LOG_ERR, "Couldn't fork to respawn thread $i; aborting thread.\n");
                     } else if ($pid == 0) {
                         $this->initAndRunChild($i);
                     } else {
@@ -141,7 +141,7 @@ abstract class SpawningDaemon extends Daemon
             $this->parentWriter = $sockets[0];
             $this->parentReader = $sockets[1];
         } else {
-            $this->log(LOG_ERROR, "Couldn't create inter-process sockets");
+            $this->log(LOG_ERR, "Couldn't create inter-process sockets");
             exit(1);
         }
     }
index 5060561279fb84f3dcf9c704082fa201a7997397..62289ebe329d4d88bda89b1bdc07cd232d845ceb 100644 (file)
@@ -606,14 +606,15 @@ function common_remove_unicode_formatting($text)
 /**
  * Partial notice markup rendering step: build links to !group references.
  *
- * @param string $text partially rendered HTML
- * @param Notice $notice in whose context we're working
+ * @param string    $text partially rendered HTML
+ * @param Profile   $author the Profile that is composing the current notice
+ * @param Notice    $parent the Notice this is sent in reply to, if any
  * @return string partially rendered HTML
  */
-function common_render_content($text, Notice $notice)
+function common_render_content($text, Profile $author, Notice $parent=null)
 {
     $text = common_render_text($text);
-    $text = common_linkify_mentions($text, $notice);
+    $text = common_linkify_mentions($text, $author, $parent);
     return $text;
 }
 
@@ -623,13 +624,14 @@ function common_render_content($text, Notice $notice)
  *
  * Should generally not be called except from common_render_content().
  *
- * @param string $text partially-rendered HTML
- * @param Notice $notice in-progress or complete Notice object for context
+ * @param string    $text   partially-rendered HTML
+ * @param Profile   $author the Profile that is composing the current notice
+ * @param Notice    $parent the Notice this is sent in reply to, if any
  * @return string partially-rendered HTML
  */
-function common_linkify_mentions($text, Notice $notice)
+function common_linkify_mentions($text, Profile $author, Notice $parent=null)
 {
-    $mentions = common_find_mentions($text, $notice);
+    $mentions = common_find_mentions($text, $author, $parent);
 
     // We need to go through in reverse order by position,
     // so our positions stay valid despite our fudging with the
@@ -687,33 +689,25 @@ function common_linkify_mention(array $mention)
  * Note the return data format is internal, to be used for building links and
  * such. Should not be used directly; rather, call common_linkify_mentions().
  *
- * @param string $text
- * @param Notice $notice notice in whose context we're building links
+ * @param string    $text
+ * @param Profile   $sender the Profile that is sending the current text
+ * @param Notice    $parent the Notice this text is in reply to, if any
  *
  * @return array
  *
  * @access private
  */
-function common_find_mentions($text, Notice $notice)
+function common_find_mentions($text, Profile $sender, Notice $parent=null)
 {
-    // The getProfile call throws NoProfileException on failure
-    $sender = $notice->getProfile();
-
     $mentions = array();
 
     if (Event::handle('StartFindMentions', array($sender, $text, &$mentions))) {
         // Get the context of the original notice, if any
-        $origAuthor   = null;
-        $origNotice   = null;
         $origMentions = array();
 
-        // Is it a reply?
-
-        try {
-            $origNotice = $notice->getParent();
-            $origAuthor = $origNotice->getProfile();
-
-            $ids = $origNotice->getReplies();
+        // Does it have a parent notice for context?
+        if ($parent instanceof Notice) {
+            $ids = $parent->getReplies();   // replied-to _profile ids_
 
             foreach ($ids as $id) {
                 try {
@@ -723,10 +717,6 @@ function common_find_mentions($text, Notice $notice)
                     // continue foreach
                 }
             }
-        } catch (NoParentNoticeException $e) {
-            // It wasn't a reply to anything, so we can't harvest nickname-relations.
-        } catch (NoResultException $e) {
-            // The parent notice was deleted.
         }
 
         $matches = common_find_mentions_raw($text);
@@ -743,22 +733,23 @@ function common_find_mentions($text, Notice $notice)
             // Start with conversation context, then go to
             // sender context.
 
-            if ($origAuthor instanceof Profile && $origAuthor->nickname == $nickname) {
-                $mentioned = $origAuthor;
+            if ($parent instanceof Notice && $parent->getProfile()->getNickname() === $nickname) {
+                $mentioned = $parent->getProfile();
             } else if (!empty($origMentions) &&
                        array_key_exists($nickname, $origMentions)) {
                 $mentioned = $origMentions[$nickname];
             } else {
+                // sets to null if no match
                 $mentioned = common_relative_profile($sender, $nickname);
             }
 
             if ($mentioned instanceof Profile) {
                 $user = User::getKV('id', $mentioned->id);
 
-                if ($user instanceof User) {
-                    $url = common_local_url('userbyid', array('id' => $user->id));
-                } else {
-                    $url = $mentioned->profileurl;
+                try {
+                    $url = $mentioned->getUrl();
+                } catch (InvalidUrlException $e) {
+                    $url = common_local_url('userbyid', array('id' => $mentioned->getID()));
                 }
 
                 $mention = array('mentioned' => array($mentioned),
@@ -766,12 +757,9 @@ function common_find_mentions($text, Notice $notice)
                                  'text' => $match[0],
                                  'position' => $match[1],
                                  'length' => mb_strlen($match[0]),
+                                 'title' => $mentioned->getFullname(),
                                  'url' => $url);
 
-                if (!empty($mentioned->fullname)) {
-                    $mention['title'] = $mentioned->fullname;
-                }
-
                 $mentions[] = $mention;
             }
         }
@@ -782,7 +770,7 @@ function common_find_mentions($text, Notice $notice)
                        $text, $hmatches, PREG_OFFSET_CAPTURE);
         foreach ($hmatches[1] as $hmatch) {
             $tag = common_canonical_tag($hmatch[0]);
-            $plist = Profile_list::getByTaggerAndTag($sender->id, $tag);
+            $plist = Profile_list::getByTaggerAndTag($sender->getID(), $tag);
             if (!$plist instanceof Profile_list || $plist->private) {
                 continue;
             }
diff --git a/local/.gitignore b/local/.gitignore
deleted file mode 100644 (file)
index e69de29..0000000
index d05c676bc1f2a63861bf75fd0af4dd43c40cc45f..baeecb7757a2e1f236789686528f866851018fd9 100644 (file)
@@ -1,20 +1,28 @@
 server {
-  # Ports
   listen 80;
-  # Uncomment the following line
-  # to enable HTTPS
-  #listen 443 ssl;
+  listen [::]:80;
+
+  # FIXME: change domain name here (and also make sure you do the same in the next 'server' section)
+  server_name social.example.org;
+
+  # redirect all traffic to HTTPS
+  rewrite ^ https://$server_name$request_uri? permanent;
+}
+
+server {
+  # Use HTTPS. Seriously. Set it up with a cert (any cert) before you run the install.
+  listen 443 ssl;
 
   # Server name
-  # Change "example.org" to your domain name
-  server_name example.org;
+  # Change "social.example.org" to your site's domain name
+  server_name social.example.org;
 
   # SSL
   # Uncomment and change the paths to setup
   # your SSL key/cert. See https://cipherli.st/
   # for more information
-  #ssl_certificate       /path/to/ssl.cert;
-  #ssl_certificate_key   /path/to/ssl.key;
+  ssl_certificate       ssl/certs/social.example.org.crt;
+  ssl_certificate_key   ssl/private/social.example.org.key;
 
   # Logs
   # Uncomment and change the paths to setup
@@ -32,12 +40,14 @@ server {
 
   # PHP
   location ~ \.php {
-    fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
+    include snippets/fastcgi-php.conf;
+
+    # This should be the same value as in your (optional) /etc/php5/fpm/pool.d/$server.conf
+    fastcgi_pass unix:/var/run/php5-fpm.sock;
+
     # Remove the "fastcgi_pass" line above and uncomment
     # the one below to use TCP sockets instead of Unix sockets
     #fastcgi_pass 127.0.0.1:9000;
-    fastcgi_index index.php;
-    include fastcgi.conf;
   }
 
   # Location
@@ -49,5 +59,10 @@ server {
   location @gnusocial {
     rewrite ^(.*)$ /index.php?p=$1 last;
   }
+
+  # Restrict access that is unnecessary anyway
+  location ~ /\.(ht|git) {
+    deny all;
+  }
 }
 
index 4ddcf27c25e34e641bbadfd82e1d533c9b4f0cfd..c7a24ba991beaceba3fae7641a59de107f8d62c4 100644 (file)
@@ -146,26 +146,22 @@ class ActivityModerationPlugin extends ActivityVerbHandlerPlugin
         }
         //$stored->content = $stored->content ?: _('Notice deleted.');
         //$stored->rendered = $stored->rendered ?: $stored->rendered;
-        common_debug('DELETENOTICE: Replacement notice has been prepared: '.var_export($stored, true));
 
         // Let's see if this has been deleted already.
-        $deleted = Deleted_notice::getKV('uri', $stored->getUri());
-        if ($deleted instanceof Deleted_notice) {
+        try {
+            $deleted = Deleted_notice::getByKeys( ['uri' => $stored->getUri()] );
             return $deleted;
-        }
-
-        $deleted = new Deleted_notice();
+        } catch (NoResultException $e) {
+            $deleted = new Deleted_notice();
 
-        $deleted->id            = $target->getID();
-        $deleted->profile_id    = $actor->getID();
-        $deleted->uri           = $stored->getUri();
-        $deleted->act_uri       = $stored->getUri();
-        $deleted->act_created   = $stored->created;
-        $deleted->created       = common_sql_now();
+            $deleted->id            = $target->getID();
+            $deleted->profile_id    = $actor->getID();
+            $deleted->uri           = $stored->getUri();
+            $deleted->act_created   = $stored->created;
+            $deleted->created       = common_sql_now();
 
-        $result = $deleted->insert();
-        if ($result === false) {
-            throw new ServerException('Could not insert Deleted_notice entry into database!');
+            // throws exception on error
+            $result = $deleted->insert();
         }
 
         // Now we delete the original notice, leaving the id and uri free.
index 9a0a65c6b21acdebbcd7a102e4d42cca3ada7ffc..76674d9bec63d31dc60dd1f77c9ba10477eae9f0 100644 (file)
@@ -29,7 +29,6 @@ class Deleted_notice extends Managed_DataObject
     public $id;                              // int(4)  primary_key not_null
     public $profile_id;                      // int(4)   not_null
     public $uri;                             // varchar(191)  unique_key   not 255 because utf8mb4 takes more space
-    public $act_uri;                         // varchar(191)  unique_key   not 255 because utf8mb4 takes more space
     public $act_created;                     // datetime()   not_null
     public $created;                         // datetime()   not_null
 
@@ -40,14 +39,12 @@ class Deleted_notice extends Managed_DataObject
                 'id' => array('type' => 'int', 'not null' => true, 'description' => 'notice ID'),
                 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile that deleted the notice'),
                 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI of the deleted notice'),
-                'act_uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI of the delete activity, may exist in notice table'),
                 'act_created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was created'),
                 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was deleted'),
             ),
             'primary key' => array('id'),
             'unique keys' => array(
                 'deleted_notice_uri_key' => array('uri'),
-                'deleted_notice_act_uri_key' => array('act_uri'),
             ),
             'indexes' => array(
                 'deleted_notice_profile_id_idx' => array('profile_id'),
@@ -99,7 +96,7 @@ class Deleted_notice extends Managed_DataObject
     static public function fromStored(Notice $stored)
     {
         $class = get_called_class();
-        return self::getByPK(array('uri' => $stored->getUri()));
+        return self::getByKeys( ['uri' => $stored->getUri()] );
     }
 
     // The one who deleted the notice, not the notice's author
@@ -176,44 +173,47 @@ class Deleted_notice extends Managed_DataObject
         $schema = Schema::get();
         $schemadef = $schema->getTableDef($table);
 
-        // 2015-10-03 We change the meaning of the 'uri' field and move its 
-        // content to the 'act_uri' for the deleted activity. act_created is
-        // added too.
-        if (isset($schemadef['fields']['act_uri'])) {
-            // We already have the act_uri field, so no need to migrate to it.
+        // 2015-12-31 If we have the act_uri field we want to remove it
+        // since there's no difference in delete verbs and the original URI
+        // but the act_created field stays.
+        if (!isset($schemadef['fields']['act_uri']) && isset($schemadef['fields']['act_created'])) {
+            // We don't have an act_uri field, and we also have act_created, so no need to migrate.
             return;
+        } elseif (isset($schemadef['fields']['act_uri']) && !isset($schemadef['fields']['act_created'])) {
+            throw new ServerException('Something is wrong with your database, you have the act_uri field but NOT act_created in deleted_notice!');
         }
-        echo "\nFound old $table table, upgrading it to contain 'act_uri' and 'act_created' field...";
 
-        $schemadef['fields']['act_uri'] = array('type' => 'varchar', 'not null' => true, 'length' => 191, 'description' => 'URI of the deleted notice');
-        $schemadef['fields']['act_created'] = array('type' => 'datetime', 'not null' => true, 'description' => 'datetime the notice record was created');
-        unset($schemadef['unique keys']);
-        $schema->ensureTable($table, $schemadef);
+        if (!isset($schemadef['fields']['act_created'])) {
+            // this is a "normal" upgrade from StatusNet for example
+            echo "\nFound old $table table, upgrading it to add 'act_created' field...";
 
-        $deleted = new Deleted_notice();
-        $result = $deleted->find();
-        if ($result === false) {
-            print "\nFound no deleted_notice entries, continuing...";
-            return true;
-        }
-        print "\nFound $result deleted_notice entries, aligning with new database layout: ";
-        while($deleted->fetch()) {
-            $orig = clone($deleted);
-            $deleted->act_uri = $deleted->uri;
-            // this is a fake URI just to have something to put there to avoid NULL. crc32 of uri is to avoid collisions
-            $deleted->uri = TagURI::mint(strtolower(get_called_class()).':%d:%s:%s:%s:crc32=%x',
-                                $deleted->profile_id,
-                                ActivityUtils::resolveUri(self::getObjectType(), true),
-                                'unknown',
-                                common_date_iso8601($deleted->created),
-                                crc32($deleted->act_uri)
-                            );
-            $deleted->act_created = $deleted->created;  // we don't actually know when the notice was created
-            $deleted->updateWithKeys($orig, 'id');
-            print ".";
+            $schemadef['fields']['act_created'] = array('type' => 'datetime', 'not null' => true, 'description' => 'datetime the notice record was created');
+            $schema->ensureTable($table, $schemadef);
+
+            $deleted = new Deleted_notice();
+            // we don't actually know when the notice was created for the old ones
+            $deleted->query('UPDATE deleted_notice SET act_created=created;');
+        } else {
+            // 2015-10-03 For a while we had act_uri and act_created fields which
+            // apparently wasn't necessary.
+            echo "\nFound old $table table, upgrading it to remove 'act_uri' field...";
+
+            // we stored what should be in 'uri' in the 'act_uri' field for some night-coding reason.
+            $deleted = new Deleted_notice();
+            $deleted->query('UPDATE deleted_notice SET uri=act_uri;');
         }
         print "DONE.\n";
         print "Resuming core schema upgrade...";
     }
 
+    function insert()
+    {
+        $result = parent::insert();
+
+        if ($result === false) {
+            common_log_db_error($this, 'INSERT', __FILE__);
+            // TRANS: Server exception thrown when a stored object entry cannot be saved.
+            throw new ServerException('Could not save Deleted_notice');
+        }
+    }
 }
index 37ee06e7450746bcf95dbe7f89a726453dc82e80..c3e0433a891e1410acc98e1a9fc073fa2d5248ce 100644 (file)
@@ -29,9 +29,7 @@
  * @link      http://status.net/
  */
 
-if (!defined('GNUSOCIAL') && !defined('STATUSNET')) {
-    exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
 
 /**
  * List users for autocompletion
@@ -142,7 +140,7 @@ class AutocompleteAction extends Action
         foreach($this->profiles as $profile){
             $avatarUrl = $profile->avatarUrl(AVATAR_MINI_SIZE);
             $acct = $profile->getAcctUri();
-            $identifier = split(':', $profile->getAcctUri(), 2)[1];
+            $identifier = explode(':', $profile->getAcctUri(), 2)[1];
             $results[] = array(
                 'value' => '@'.$identifier,
                 'nickname' => $profile->getNickname(),
@@ -153,6 +151,7 @@ class AutocompleteAction extends Action
             );
         }
         foreach($this->groups as $group){
+            $profile = $group->getProfile();
             // sigh.... encapsulate this upstream!
             if ($group->mini_logo) {
                 $avatarUrl = $group->mini_logo;
@@ -160,7 +159,7 @@ class AutocompleteAction extends Action
                 $avatarUrl = User_group::defaultLogo(AVATAR_MINI_SIZE);
             }
             $acct = $profile->getAcctUri();
-            $identifier = split(':', $profile->getAcctUri(), 2)[1];
+            $identifier = explode(':', $profile->getAcctUri(), 2)[1];
             $results[] = array(
                 'value' => '!'.$group->getNickname(),
                 'nickname' => $group->getNickname(),
index d1b70a5059d9ec0f75fb725a1d7e213da4d88b59..a1d0761a4a03ce9042c23eab590b811b765e17da 100644 (file)
@@ -67,6 +67,12 @@ class EventPlugin extends MicroAppPlugin
         return true;
     }
 
+    public function onBeforePluginCheckSchema()
+    {
+        RSVP::beforeSchemaUpdate();
+        return true;
+    }
+
     /**
      * Map URLs to actions
      *
index 31a26837511a135540901dcc69f69a39809f93ad..f7e922dffb6bccaba36d91304ec221e0cccc1e53 100644 (file)
@@ -89,7 +89,7 @@ class CancelrsvpAction extends Action
             throw new ClientException(_m('No such RSVP.'));
         }
 
-        $this->event = Happening::getKV('id', $this->rsvp->event_id);
+        $this->event = Happening::getKV('uri', $this->rsvp->event_uri);
 
         if (empty($this->event)) {
             // TRANS: Client exception thrown when referring to a non-existing event.
index 9962815a67ad14cacac70c129cdd6773f029c91a..fd8c3c8a2aaf6ff841b6461ce48b4295bd30d924 100644 (file)
@@ -27,9 +27,7 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-if (!defined('STATUSNET')) {
-    exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
 
 /**
  * Data class for happenings
@@ -172,14 +170,14 @@ class Happening extends Managed_DataObject
                                $options);
 
         if (!array_key_exists('uri', $options)) {
-            $options['uri'] = $ev->uri;
+            $options['uri'] = $ev->getUri();
         }
 
         if (!empty($url)) {
             $options['urls'] = array($url);
         }
 
-        $saved = Notice::saveNew($profile->id,
+        $saved = Notice::saveNew($profile->getID(),
                                  $content,
                                  array_key_exists('source', $options) ?
                                  $options['source'] : 'web',
@@ -202,14 +200,19 @@ class Happening extends Managed_DataObject
         return $this->url;
     }
 
+    public function getUri()
+    {
+        return $this->uri;
+    }
+
     function getNotice()
     {
-        return Notice::getKV('uri', $this->uri);
+        return Notice::getKV('uri', $this->getUri());
     }
 
     static function fromNotice(Notice $notice)
     {
-        return Happening::getKV('uri', $notice->uri);
+        return Happening::getKV('uri', $notice->getUri());
     }
 
     function getRSVPs()
@@ -219,7 +222,7 @@ class Happening extends Managed_DataObject
 
     function getRSVP($profile)
     {
-        return RSVP::pkeyGet(array('profile_id' => $profile->id,
-                                   'event_id' => $this->id));
+        return RSVP::pkeyGet(array('profile_id' => $profile->getID(),
+                                   'event_uri' => $this->getUri()));
     }
 }
index 6a6f7f3811354773907fed9840d6531142d62065..c4ffc3ee340bb74ba09be17eac457161f10167e7 100644 (file)
@@ -27,9 +27,7 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-if (!defined('STATUSNET')) {
-    exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
 
 /**
  * Data class for event RSVPs
@@ -52,24 +50,10 @@ class RSVP extends Managed_DataObject
     public $id;                // varchar(36) UUID
     public $uri;               // varchar(191)   not 255 because utf8mb4 takes more space
     public $profile_id;        // int
-    public $event_id;          // varchar(36) UUID
+    public $event_uri;         // varchar(191)   not 255 because utf8mb4 takes more space
     public $response;            // tinyint
     public $created;           // datetime
 
-    /**
-     * Add the compound profile_id/event_id index to our cache keys
-     * since the DB_DataObject stuff doesn't understand compound keys
-     * except for the primary.
-     *
-     * @return array
-     */
-    function _allCacheKeys() {
-        $keys = parent::_allCacheKeys();
-        $keys[] = self::multicacheKey('RSVP', array('profile_id' => $this->profile_id,
-                                                    'event_id' => $this->event_id));
-        return $keys;
-    }
-
     /**
      * The One True Thingy that must be defined and declared.
      */
@@ -86,10 +70,10 @@ class RSVP extends Managed_DataObject
                                'length' => 191,
                                'not null' => true),
                 'profile_id' => array('type' => 'int'),
-                'event_id' => array('type' => 'char',
-                              'length' => 36,
+                'event_uri' => array('type' => 'varchar',
+                              'length' => 191,
                               'not null' => true,
-                              'description' => 'UUID'),
+                              'description' => 'Event URI'),
                 'response' => array('type' => 'char',
                                   'length' => '1',
                                   'description' => 'Y, N, or ? for three-state yes, no, maybe'),
@@ -99,14 +83,47 @@ class RSVP extends Managed_DataObject
             'primary key' => array('id'),
             'unique keys' => array(
                 'rsvp_uri_key' => array('uri'),
-                'rsvp_profile_event_key' => array('profile_id', 'event_id'),
+                'rsvp_profile_event_key' => array('profile_id', 'event_uri'),
             ),
-            'foreign keys' => array('rsvp_event_id_key' => array('event', array('event_id' => 'id')),
+            'foreign keys' => array('rsvp_event_uri_key' => array('happening', array('event_uri' => 'uri')),
                                     'rsvp_profile_id__key' => array('profile', array('profile_id' => 'id'))),
             'indexes' => array('rsvp_created_idx' => array('created')),
         );
     }
 
+    static public function beforeSchemaUpdate()
+    {
+        $table = strtolower(get_called_class());
+        $schema = Schema::get();
+        $schemadef = $schema->getTableDef($table);
+
+        // 2015-12-31 RSVPs refer to Happening by event_uri now, not event_id. Let's migrate!
+        if (isset($schemadef['fields']['event_uri'])) {
+            // We seem to have already migrated, good!
+            return;
+        }
+
+        // this is a "normal" upgrade from StatusNet for example
+        echo "\nFound old $table table, upgrading it to add 'event_uri' field...";
+
+        $schemadef['fields']['event_uri'] = array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'Event URI');
+        $schema->ensureTable($table, $schemadef);
+
+        $rsvp = new RSVP();
+        $rsvp->find();
+        while ($rsvp->fetch()) {
+            $event = Happening::getKV('id', $rsvp->event_id);
+            if (!$event instanceof Happening) {
+                continue;
+            }
+            $orig = clone($rsvp);
+            $rsvp->event_uri = $event->uri;
+            $rsvp->updateWithKeys($orig);
+        }
+        print "DONE.\n";
+        print "Resuming core schema upgrade...";
+    }
+
     function saveNew(Profile $profile, $event, $verb, array $options = array())
     {
         if (array_key_exists('uri', $options)) {
@@ -117,19 +134,21 @@ class RSVP extends Managed_DataObject
             }
         }
 
-        $other = RSVP::pkeyGet(array('profile_id' => $profile->id,
-                                     'event_id' => $event->id));
-
-        if (!empty($other)) {
+        try {
+            $other = RSVP::getByKeys( [ 'profile_id' => $profile->getID(),
+                                        'event_uri' => $event->getUri(),
+                                      ] );
             // TRANS: Client exception thrown when trying to save an already existing RSVP ("please respond").
-            throw new ClientException(_m('RSVP already exists.'));
+            throw new AlreadyFulfilledException(_m('RSVP already exists.'));
+        } catch (NoResultException $e) {
+            // No previous RSVP, so go ahead and add.
         }
 
         $rsvp = new RSVP();
 
         $rsvp->id          = UUID::gen();
-        $rsvp->profile_id  = $profile->id;
-        $rsvp->event_id    = $event->id;
+        $rsvp->profile_id  = $profile->getID();
+        $rsvp->event_uri    = $event->getUri();
         $rsvp->response      = self::codeFor($verb);
 
         if (array_key_exists('created', $options)) {
@@ -147,7 +166,7 @@ class RSVP extends Managed_DataObject
 
         $rsvp->insert();
 
-        self::blow('rsvp:for-event:%s', $event->id);
+        self::blow('rsvp:for-event:%s', $event->getUri());
 
         // XXX: come up with something sexier
 
@@ -165,10 +184,10 @@ class RSVP extends Managed_DataObject
         $eventNotice = $event->getNotice();
 
         if (!empty($eventNotice)) {
-            $options['reply_to'] = $eventNotice->id;
+            $options['reply_to'] = $eventNotice->getID();
         }
 
-        $saved = Notice::saveNew($profile->id,
+        $saved = Notice::saveNew($profile->getID(),
                                  $content,
                                  array_key_exists('source', $options) ?
                                  $options['source'] : 'web',
@@ -236,7 +255,7 @@ class RSVP extends Managed_DataObject
 
     static function forEvent(Happening $event)
     {
-        $keypart = sprintf('rsvp:for-event:%s', $event->id);
+        $keypart = sprintf('rsvp:for-event:%s', $event->getUri());
 
         $idstr = self::cacheGet($keypart);
 
@@ -250,7 +269,7 @@ class RSVP extends Managed_DataObject
             $rsvp->selectAdd();
             $rsvp->selectAdd('id');
 
-            $rsvp->event_id = $event->id;
+            $rsvp->event_uri = $event->getUri();
 
             if ($rsvp->find()) {
                 while ($rsvp->fetch()) {
@@ -288,30 +307,26 @@ class RSVP extends Managed_DataObject
 
     function getEvent()
     {
-        $event = Happening::getKV('id', $this->event_id);
+        $event = Happening::getKV('uri', $this->event_uri);
         if (empty($event)) {
             // TRANS: Exception thrown when requesting a non-existing event.
             // TRANS: %s is the ID of the non-existing event.
-            throw new Exception(sprintf(_m('No event with ID %s.'),$this->event_id));
+            throw new Exception(sprintf(_m('No event with URI %s.'),$this->event_uri));
         }
         return $event;
     }
 
     function asHTML()
     {
-        $event = Happening::getKV('id', $this->event_id);
-
         return self::toHTML($this->getProfile(),
-                            $event,
+                            $this->getEvent(),
                             $this->response);
     }
 
     function asString()
     {
-        $event = Happening::getKV('id', $this->event_id);
-
         return self::toString($this->getProfile(),
-                              $event,
+                              $this->getEvent(),
                               $this->response);
     }
 
index 1e8c4fe6923c5bd333887ff1a7b6a3caba159be4..c0d91060be36bbcfd77707c7d1216168cf34ae97 100644 (file)
@@ -2,14 +2,6 @@
 
 class RawEventsNoticeStream extends NoticeStream
 {
-    protected $target;
-    protected $own;
-
-    function __construct(Profile $target)
-    {
-        $this->target   = $target;
-    }
-
     function getNoticeIds($offset, $limit, $since_id, $max_id)
     {
         $notice = new Notice();
@@ -17,7 +9,6 @@ class RawEventsNoticeStream extends NoticeStream
 
         $qry =  'SELECT notice.* FROM notice ';
         $qry .= 'INNER JOIN happening ON happening.uri = notice.uri ';
-        $qry .= 'WHERE happening.profile_id = ' . $this->target->getID() . ' ';
         $qry .= 'AND notice.is_local != ' . Notice::GATEWAY . ' ';
 
         if ($since_id != 0) {
@@ -28,8 +19,8 @@ class RawEventsNoticeStream extends NoticeStream
             $qry .= 'AND notice.id <= ' . $max_id . ' ';
         }
 
-        // NOTE: we sort by bookmark time, not by notice time!
-        $qry .= 'ORDER BY created DESC ';
+        // NOTE: we sort by event time, not by notice time!
+        $qry .= 'ORDER BY happening.created DESC ';
         if (!is_null($offset)) {
             $qry .= "LIMIT $limit OFFSET $offset";
         }
@@ -48,16 +39,48 @@ class RawEventsNoticeStream extends NoticeStream
 
 class EventsNoticeStream extends ScopingNoticeStream
 {
-    function __construct(Profile $target, Profile $scoped=null)
+    // possible values of RSVP in our database
+    protected $rsvp = ['Y', 'N', '?'];
+    protected $target = null;
+
+    function __construct(Profile $target, Profile $scoped=null, array $rsvp=array())
     {
-        $stream = new RawEventsNoticeStream($target);
+        $stream = new RawEventsNoticeStream();
 
         if ($target->sameAs($scoped)) {
-            $key = 'bookmark:ids_by_user_own:'.$target->getID();
+            $key = 'happening:ids_for_user_own:'.$target->getID();
         } else {
-            $key = 'bookmark:ids_by_user:'.$target->getID();
+            $key = 'happening:ids_for_user:'.$target->getID();
         }
 
+        // Match RSVP against our possible values, given in the class variable
+        // and if no RSVPs are given is empty, assume we want all events, even
+        // without RSVPs from this profile.
+        $this->rsvp = array_intersect($this->rsvp, $rsvp);
+        $this->target = $target;
+
         parent::__construct(new CachingNoticeStream($stream, $key), $scoped);
     }
+
+    function filter($notice)
+    {
+        if (!parent::filter($notice)) {
+            // if not in our scope, return false
+            return false;
+        }
+
+        if (empty($this->rsvp)) {
+            // Don't filter on RSVP (for only events with RSVP if no responses
+            // are given (give ['Y', 'N', '?'] for only RSVP'd events!).
+            return true;
+        }
+
+        $rsvp = new RSVP();
+        $rsvp->profile_id = $this->target->getID();
+        $rsvp->event_uri  = $notice->getUri();
+        $rsvp->whereAddIn('response', $this->rsvp, $rsvp->columnType('response'));
+
+        // filter out if no RSVP match was found
+        return $rsvp->N > 0;
+    }
 }
index a255a3d022999d8d6bc67108a46bcee362eeaca5..921221498fbc2f0337aa6ab66e860847b5183c37 100644 (file)
@@ -88,7 +88,11 @@ class GroupsalmonAction extends SalmonAction
             }
         }
 
-        $this->saveNotice();
+        try {
+            $this->saveNotice();
+        } catch (AlreadyFulfilledException $e) {
+            return;
+        }
     }
 
     /**
index 7fce6c808c5118fba49450ce95049fd4973cd2c1..5d2a92d99b3d93da1d821fc8bfc9931d190f9122 100644 (file)
@@ -95,13 +95,11 @@ class UsersalmonAction extends SalmonAction
             throw new ClientException(_m('Not to anyone in reply to anything.'));
         }
 
-        $existing = Notice::getKV('uri', $this->activity->objects[0]->id);
-        if ($existing instanceof Notice) {
-            common_log(LOG_ERR, "Not saving notice with duplicate URI '".$existing->getUri()."' (seems it already exists).");
+        try {
+            $this->saveNotice();
+        } catch (AlreadyFulfilledException $e) {
             return;
         }
-
-        $this->saveNotice();
     }
 
     /**
index 2467e28d552dbbabe8a3297b417094ee232da1c0..ca1708282e9c392cff1a5b452033ffb4ff348de7 100644 (file)
@@ -445,10 +445,7 @@ class Ostatus_profile extends Managed_DataObject
             return;
         }
 
-        for ($i = 0; $i < $entries->length; $i++) {
-            $entry = $entries->item($i);
-            $this->processEntry($entry, $feed, $source);
-        }
+        $this->processEntries($entries, $feed, $source);
     }
 
     public function processRssFeed(DOMElement $rss, $source)
@@ -466,9 +463,18 @@ class Ostatus_profile extends Managed_DataObject
 
         $items = $channel->getElementsByTagName('item');
 
-        for ($i = 0; $i < $items->length; $i++) {
-            $item = $items->item($i);
-            $this->processEntry($item, $channel, $source);
+        $this->processEntries($items, $channel, $source);
+    }
+
+    public function processEntries(DOMNodeList $entries, DOMElement $feed, $source)
+    {
+        for ($i = 0; $i < $entries->length; $i++) {
+            $entry = $entries->item($i);
+            try {
+                $this->processEntry($entry, $feed, $source);
+            } catch (AlreadyFulfilledException $e) {
+                common_debug('We already had this entry: '.$e->getMessage());
+            }
         }
     }
 
index 67a9d9e36b8aee0d167d79efe5c8e9864c219fb8..9cab847cc29646b3f8a2469d44dd7d17fc01728f 100644 (file)
@@ -49,7 +49,7 @@ class HubOutQueueHandler extends QueueHandler
             $msg = "Failed PuSH to $sub->callback for $sub->topic: " .
                    $e->getMessage();
             if ($retries > 0) {
-                common_log(LOG_ERR, "$msg; scheduling for $retries more tries");
+                common_log(LOG_INFO, "$msg; scheduling for $retries more tries");
 
                 // @fixme when we have infrastructure to schedule a retry
                 // after a delay, use it.
index c51e0bfad1626e983e0a5bf8f1581b37856628bf..f9bd2af896c31818109506c40c7a158074ab89dd 100644 (file)
@@ -77,6 +77,9 @@ class OembedPlugin extends Plugin
                 'title'=>'oEmbed'),null);
             break;
         case 'shownotice':
+            if (!$action->notice->isLocal()) {
+                break;
+            }
             try {
                 $action->element('link',array('rel'=>'alternate',
                     'type'=>'application/json+oembed',
index 7de0bcca859299a14c5acecec23633e6acac759e..02f48f623e62a8600bdbd59e88524b2579b6887b 100644 (file)
@@ -49,15 +49,15 @@ class File_oembed extends Managed_DataObject
                 'version' => array('type' => 'varchar', 'length' => 20, 'description' => 'oEmbed spec. version'),
                 'type' => array('type' => 'varchar', 'length' => 20, 'description' => 'oEmbed type: photo, video, link, rich'),
                 'mimetype' => array('type' => 'varchar', 'length' => 50, 'description' => 'mime type of resource'),
-                'provider' => array('type' => 'varchar', 'length' => 50, 'description' => 'name of this oEmbed provider'),
-                'provider_url' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL of this oEmbed provider'),
+                'provider' => array('type' => 'text', 'description' => 'name of this oEmbed provider'),
+                'provider_url' => array('type' => 'text', 'description' => 'URL of this oEmbed provider'),
                 'width' => array('type' => 'int', 'description' => 'width of oEmbed resource when available'),
                 'height' => array('type' => 'int', 'description' => 'height of oEmbed resource when available'),
                 'html' => array('type' => 'text', 'description' => 'html representation of this oEmbed resource when applicable'),
-                'title' => array('type' => 'varchar', 'length' => 191, 'description' => 'title of oEmbed resource when available'),
-                'author_name' => array('type' => 'varchar', 'length' => 50, 'description' => 'author name for this oEmbed resource'),
-                'author_url' => array('type' => 'varchar', 'length' => 191, 'description' => 'author URL for this oEmbed resource'),
-                'url' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL for this oEmbed resource when applicable (photo, link)'),
+                'title' => array('type' => 'text', 'description' => 'title of oEmbed resource when available'),
+                'author_name' => array('type' => 'text', 'description' => 'author name for this oEmbed resource'),
+                'author_url' => array('type' => 'text', 'description' => 'author URL for this oEmbed resource'),
+                'url' => array('type' => 'text', 'description' => 'URL for this oEmbed resource when applicable (photo, link)'),
                 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
             ),
             'primary key' => array('file_id'),
index 561ea2e01639587117edda5f790a89b2ad61ae04..5543cba06c623dbff942744d3401abcff7b8e6b2 100644 (file)
@@ -327,14 +327,7 @@ class NoticeAnswerListItem extends NoticeListItem
     function showContent()
     {
         $this->out->elementStart('p', array('class' => 'e-content answer-content'));
-        if ($this->notice->rendered) {
-            $this->out->raw($this->notice->rendered);
-        } else {
-            // XXX: may be some uncooked notices in the DB,
-            // we cook them right now. This should probably disappear in future
-            // versions (>> 0.4.x)
-            $this->out->raw(common_render_content($this->notice->content, $this->notice));
-        }
+        $this->out->raw($this->notice->getRendered());
 
         if (!empty($this->answer)) {
             $form = new QnashowanswerForm($this->out, $this->answer);
index f99120a929255f2276f65492148889b91ac53fa2..a7cac7fd0d0bdd5d951a51b94f2714bf808079f1 100644 (file)
@@ -566,8 +566,10 @@ class TwitterImport
                 $reply->modified   = $notice->created;
                 common_log(LOG_INFO, __METHOD__ . ": saving reply: notice {$notice->id} to profile {$user->id}");
                 $id = $reply->insert();
+            } catch (NoSuchUserException $e) {
+                common_log(LOG_WARNING, 'No local user found for Foreign_link with id: '.$mention->id);
             } catch (NoResultException $e) {
-                common_log(LOG_WARNING, 'No local user found for Foreign_link with local User id: '.$flink->user_id);
+                common_log(LOG_WARNING, 'No foreign link or profile found for Foreign_link with id: '.$mention->id);
             }
         }
     }
index 290d7f3e3f5da1ae581642e3bd26c4d0fd2c5489..7112b8b63eaeb8013bf24be41ff05047ab8b1b33 100644 (file)
@@ -355,12 +355,8 @@ class XmppPlugin extends ImPlugin
             // Parent notice was probably deleted.
             $xs->text(": ");
         }
-        if (!empty($notice->rendered)) {
-            $notice->rendered = str_replace("\t", "", $notice->rendered);
-            $xs->raw($notice->rendered);
-        } else {
-            $xs->raw(common_render_content($notice->content, $notice));
-        }
+        // FIXME: Why do we replace \t with ''? is it just to make it pretty? shouldn't whitespace be handled well...?
+        $xs->raw(str_replace("\t", "", $notice->getRendered()));
         $xs->text(" ");
         $xs->element('a', array(
             'href'=>common_local_url('conversation',
index ac6522f3d91d157775954bb030207057125a0b21..b5cdb764982a223f89b8db5ccd9e4f48b53f338e 100755 (executable)
@@ -39,7 +39,9 @@ while ($notice->fetch()) {
     common_log(LOG_INFO, 'Getting tags for notice #' . $notice->id);
     $notice->saveTags();
     $original = clone($notice);
-    $notice->rendered = common_render_content($notice->content, $notice);
+    $notice->rendered = common_render_content($notice->content,
+                                              $notice->getProfile(),
+                                              $notice->hasParent() ? $notice->getParent() : null);
     $result = $notice->update($original);
     if (!$result) {
         common_log_db_error($notice, 'UPDATE', __FILE__);
index 460a4d8d0fc94b0acd24e8f3887819dab9d78e59..5e510d118fe74ab59bc93783b37e2d4a53e45a92 100755 (executable)
@@ -54,7 +54,7 @@ if (!have_option('y', 'yes')) {
     } catch (Exception $e) {
         $filename = '(remote file or no filename)';
     }
-    print "About to PERMANENTLY delete file ($filename) ({$file->id}). Are you sure? [y/N] ";
+    print "About to PERMANENTLY delete file ($filename) with id=={$file->id}) AND its related notices. Are you sure? [y/N] ";
     $response = fgets(STDIN);
     if (strtolower(trim($response)) != 'y') {
         print "Aborting.\n";
index 470143ee6ce6324cd0733c5cc788f7b819d75f83..b8feb207957ba3171714178c61e3975affa8bb30 100755 (executable)
@@ -41,21 +41,12 @@ if (mb_strlen($password) < 6) {
     exit(1);
 }
 
-$user = User::getKV('nickname', $nickname);
-
-if (!$user) {
-    print "No such user '$nickname'.\n";
+try {
+    $user = User::getByNickname($nickname);
+    $user->setPassword($password);
+} catch (NoSuchUserException $e) {
+    print $e->getMessage();
     exit(1);
 }
 
-$original = clone($user);
-
-$user->password = common_munge_password($password, $user->getProfile());
-
-if (!$user->update($original)) {
-    print "Error updating user '$nickname'.\n";
-    exit(1);
-} else {
-    print "Password for user '$nickname' updated.\n";
-    exit(0);
-}
+print "Password for user '$nickname' updated.\n";
index e17e512cb22588f93719f44277002f05623d7b20..c6f3e28606a7fbd2c5ef03b229d6fb0f583beff1 100755 (executable)
@@ -105,7 +105,9 @@ function fixupNoticeRendered()
 
     while ($notice->fetch()) {
         $original = clone($notice);
-        $notice->rendered = common_render_content($notice->content, $notice);
+        $notice->rendered = common_render_content($notice->content,
+                                                  $notice->getProfile(),
+                                                  $notice->hasParent() ? $notice->getParent() : null);
         $notice->update($original);
     }
 
@@ -359,7 +361,7 @@ function initGroupMemberURI()
                 $mem->query(sprintf('update group_member set uri = "%s" '.
                                     'where profile_id = %d ' . 
                                     'and group_id = %d ',
-                                    Group_member::newURI($mem->profile_id, $mem->group_id, $mem->created),
+                                    Group_member::newUri(Profile::getByID($mem->profile_id), User_group::getByID($mem->group_id), $mem->created),
                                     $mem->profile_id,
                                     $mem->group_id));
             } catch (Exception $e) {