]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch 'translation-snapshot-october-2015' into 'nightly'
authormmn <mmn@hethane.se>
Sat, 10 Oct 2015 20:36:04 +0000 (20:36 +0000)
committermmn <mmn@hethane.se>
Sat, 10 Oct 2015 20:36:04 +0000 (20:36 +0000)
Snapshot of the Transifex translation project - October 2015

It's been 7 months since the last localization update, and the files in the repository are out of sync with the current state of the project. Our Transifex team has since grown to 63 translators, many of them are active members of the GNU social community. I don't know how we will resolve this situation, but it is clear that we have to act. If you plan to redesign the plugin system in the future to support external repository for localizations, that would work as well. But now, please let us do an update. It would be a serious disgrace to the community to throw all of their hard work away.

See merge request !32

23 files changed:
actions/apimediaupload.php
actions/apitimelineuser.php
actions/usergroups.php
classes/Group_member.php
classes/Managed_DataObject.php
classes/Notice.php
classes/Oauth_application.php
classes/Profile.php
classes/Subscription.php
lib/activity.php
lib/activityhandlerplugin.php
lib/apiauthaction.php
lib/default.php
lib/search_engines.php
plugins/ActivityModeration/ActivityModerationPlugin.php
plugins/ActivityModeration/classes/Deleted_notice.php
plugins/Bookmark/BookmarkPlugin.php
plugins/Bookmark/classes/Bookmark.php
plugins/Diaspora/DiasporaPlugin.php
plugins/Event/EventPlugin.php
plugins/Event/actions/newevent.php
plugins/Event/classes/Happening.php
plugins/Favorite/classes/Fave.php

index 8e59fec59c963c728b4c96456bcbd5def2247a86..fd1ff565c74422a80bff4d97084644f7578d7d4b 100644 (file)
@@ -30,7 +30,7 @@ if (!defined('GNUSOCIAL')) { exit(1); }
 
 /**
  * Upload an image via the API.  Returns a shortened URL for the image
- * to the user.
+ * to the user. Apparently modelled after a former Twitpic API.
  *
  * @category API
  * @package  StatusNet
@@ -91,9 +91,14 @@ class ApiMediaUploadAction extends ApiAuthAction
     function showResponse(MediaFile $upload)
     {
         $this->initDocument();
-        $this->elementStart('rsp', array('stat' => 'ok'));
+        $this->elementStart('rsp', array('stat' => 'ok', 'xmlns:atom'=>Activity::ATOM));
         $this->element('mediaid', null, $upload->fileRecord->id);
         $this->element('mediaurl', null, $upload->shortUrl());
+
+        $enclosure = $upload->fileRecord->getEnclosure();
+        $this->element('atom:link', array('rel'  => 'enclosure',
+                                          'href' => $enclosure->url,
+                                          'type' => $enclosure->mimetype));
         $this->elementEnd('rsp');
         $this->endDocument();
     }
@@ -103,7 +108,7 @@ class ApiMediaUploadAction extends ApiAuthAction
      *
      * @param String $msg an error message
      */
-    function clientError($msg)
+    function clientError($msg, $code=400, $format=null)
     {
         $this->initDocument();
         $this->elementStart('rsp', array('stat' => 'fail'));
@@ -114,5 +119,6 @@ class ApiMediaUploadAction extends ApiAuthAction
         $this->element('err', $errAttr, null);
         $this->elementEnd('rsp');
         $this->endDocument();
+        exit;
     }
 }
index e8c58e6e8b85d405d596b407484e3699d2d66b5c..5debe3ed8ca0d6550e6161bd53d21e21f24f7e77 100644 (file)
@@ -34,9 +34,7 @@
  * @link      http://status.net/
  */
 
-if (!defined('STATUSNET')) {
-    exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
 
 /**
  * Returns the most recent notices (default 20) posted by the authenticating
@@ -115,11 +113,11 @@ class ApiTimelineUserAction extends ApiBareAuthAction
     {
         // We'll use the shared params from the Atom stub
         // for other feed types.
-        $atom = new AtomUserNoticeFeed($this->target->getUser(), $this->auth_user);
+        $atom = new AtomUserNoticeFeed($this->target->getUser(), $this->scoped);
 
         $link = common_local_url(
                                  'showstream',
-                                 array('nickname' => $this->target->nickname)
+                                 array('nickname' => $this->target->getNickname())
                                  );
 
         $self = $this->getSelfUri();
@@ -127,7 +125,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
         // FriendFeed's SUP protocol
         // Also added RSS and Atom feeds
 
-        $suplink = common_local_url('sup', null, null, $this->target->id);
+        $suplink = common_local_url('sup', null, null, $this->target->getID());
         header('X-SUP-ID: ' . $suplink);
 
 
@@ -135,7 +133,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
         $nextUrl = !empty($this->next_id)
                     ? common_local_url('ApiTimelineUser',
                                     array('format' => $this->format,
-                                          'id' => $this->target->id),
+                                          'id' => $this->target->getID()),
                                     array('max_id' => $this->next_id))
                     : null;
 
@@ -147,11 +145,11 @@ class ApiTimelineUserAction extends ApiBareAuthAction
 
         $prevUrl = common_local_url('ApiTimelineUser',
                                     array('format' => $this->format,
-                                          'id' => $this->target->id),
+                                          'id' => $this->target->getID()),
                                     $prevExtra);
         $firstUrl = common_local_url('ApiTimelineUser',
                                     array('format' => $this->format,
-                                          'id' => $this->target->id));
+                                          'id' => $this->target->getID()));
 
         switch($this->format) {
         case 'xml':
@@ -206,7 +204,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
             break;
         case 'as':
             header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
-            $doc = new ActivityStreamJSONDocument($this->auth_user);
+            $doc = new ActivityStreamJSONDocument($this->scoped);
             $doc->setTitle($atom->title);
             $doc->addLink($link, 'alternate', 'text/html');
             $doc->addItemsFromNotices($this->notices);
@@ -307,9 +305,9 @@ class ApiTimelineUserAction extends ApiBareAuthAction
             return '"' . implode(
                                  ':',
                                  array($this->arg('action'),
-                                       common_user_cache_hash($this->auth_user),
+                                       common_user_cache_hash($this->scoped),
                                        common_language(),
-                                       $this->target->id,
+                                       $this->target->getID(),
                                        strtotime($this->notices[0]->created),
                                        strtotime($this->notices[$last]->created))
                                  )
@@ -321,10 +319,10 @@ class ApiTimelineUserAction extends ApiBareAuthAction
 
     function handlePost()
     {
-        if (empty($this->auth_user) ||
-            $this->auth_user->id != $this->target->id) {
+        if (!$this->scoped instanceof Profile ||
+                !$this->target->sameAs($this->scoped)) {
             // TRANS: Client error displayed trying to add a notice to another user's timeline.
-            $this->clientError(_('Only the user can add to their own timeline.'));
+            $this->clientError(_('Only the user can add to their own timeline.'), 403);
         }
 
         // Only handle posts for Atom
@@ -356,156 +354,28 @@ class ApiTimelineUserAction extends ApiBareAuthAction
 
         $activity = new Activity($dom->documentElement);
 
-        $saved = null;
-
-        if (Event::handle('StartAtomPubNewActivity', array(&$activity, $this->target->getUser(), &$saved))) {
-            if ($activity->verb != ActivityVerb::POST) {
-                // TRANS: Client error displayed when not using the POST verb. Do not translate POST.
-                $this->clientError(_('Can only handle POST activities.'));
-            }
-
-            $note = $activity->objects[0];
-
-            if (!in_array($note->type, array(ActivityObject::NOTE,
-                                             ActivityObject::BLOGENTRY,
-                                             ActivityObject::STATUS))) {
-                // TRANS: Client error displayed when using an unsupported activity object type.
-                // TRANS: %s is the unsupported activity object type.
-                $this->clientError(sprintf(_('Cannot handle activity object type "%s".'),
-                                             $note->type));
-            }
+        common_debug('AtomPub: Ignoring right now, but this POST was made to collection: '.$activity->id);
 
-            $saved = $this->postNote($activity);
+        // Reset activity data so we can handle it in the same functions as with OStatus
+        // because we don't let clients set their own UUIDs... Not sure what AtomPub thinks
+        // about that though.
+        $activity->id = null;
+        $activity->actor = null;    // not used anyway, we use $this->target
+        $activity->objects[0]->id = null;
 
-            Event::handle('EndAtomPubNewActivity', array($activity, $this->target->getUser(), $saved));
+        $stored = null;
+        if (Event::handle('StartAtomPubNewActivity', array($activity, $this->target, &$stored))) {
+            // TRANS: Client error displayed when not using the POST verb. Do not translate POST.
+            throw new ClientException(_('Could not handle this Atom Activity.'));
         }
-
-        if (!empty($saved)) {
-            header('HTTP/1.1 201 Created');
-            header("Location: " . common_local_url('ApiStatusesShow', array('id' => $saved->id,
-                                                                            'format' => 'atom')));
-            $this->showSingleAtomStatus($saved);
-        }
-    }
-
-    function postNote($activity)
-    {
-        $note = $activity->objects[0];
-
-        // Use summary as fallback for content
-
-        if (!empty($note->content)) {
-            $sourceContent = $note->content;
-        } else if (!empty($note->summary)) {
-            $sourceContent = $note->summary;
-        } else if (!empty($note->title)) {
-            $sourceContent = $note->title;
-        } else {
-            // @fixme fetch from $sourceUrl?
-            // TRANS: Client error displayed when posting a notice without content through the API.
-            // TRANS: %d is the notice ID (number).
-            $this->clientError(sprintf(_('No content for notice %d.'), $note->id));
+        if (!$stored instanceof Notice) {
+            throw new ServerException('Server did not create a Notice object from handled AtomPub activity.');
         }
+        Event::handle('EndAtomPubNewActivity', array($activity, $this->target, $stored));
 
-        // Get (safe!) HTML and text versions of the content
-
-        $rendered = common_purify($sourceContent);
-        $content = common_strip_html($rendered);
-
-        $shortened = $this->auth_user->shortenLinks($content);
-
-        $options = array('is_local' => Notice::LOCAL_PUBLIC,
-                         'rendered' => $rendered,
-                         'replies' => array(),
-                         'groups' => array(),
-                         'tags' => array(),
-                         'urls' => array());
-
-        // accept remote URI (not necessarily a good idea)
-
-        common_debug("Note ID is {$note->id}");
-
-        if (!empty($note->id)) {
-            $notice = Notice::getKV('uri', trim($note->id));
-
-            if (!empty($notice)) {
-                // TRANS: Client error displayed when using another format than AtomPub.
-                // TRANS: %s is the notice URI.
-                $this->clientError(sprintf(_('Notice with URI "%s" already exists.'), $note->id));
-            }
-            common_log(LOG_NOTICE, "Saving client-supplied notice URI '$note->id'");
-            $options['uri'] = $note->id;
-        }
-
-        // accept remote create time (also maybe not such a good idea)
-
-        if (!empty($activity->time)) {
-            common_log(LOG_NOTICE, "Saving client-supplied create time {$activity->time}");
-            $options['created'] = common_sql_date($activity->time);
-        }
-
-        // Check for optional attributes...
-
-        if ($activity->context instanceof ActivityContext) {
-
-            foreach ($activity->context->attention as $uri=>$type) {
-                try {
-                    $profile = Profile::fromUri($uri);
-                    if ($profile->isGroup()) {
-                        $options['groups'][] = $profile->id;
-                    } else {
-                        $options['replies'][] = $uri;
-                    }
-                } catch (UnknownUriException $e) {
-                    common_log(LOG_WARNING, sprintf('AtomPub post with unknown attention URI %s', $uri));
-                }
-            }
-
-            // Maintain direct reply associations
-            // @fixme what about conversation ID?
-
-            if (!empty($activity->context->replyToID)) {
-                $orig = Notice::getKV('uri',
-                                          $activity->context->replyToID);
-                if (!empty($orig)) {
-                    $options['reply_to'] = $orig->id;
-                }
-            }
-
-            $location = $activity->context->location;
-
-            if ($location) {
-                $options['lat'] = $location->lat;
-                $options['lon'] = $location->lon;
-                if ($location->location_id) {
-                    $options['location_ns'] = $location->location_ns;
-                    $options['location_id'] = $location->location_id;
-                }
-            }
-        }
-
-        // Atom categories <-> hashtags
-
-        foreach ($activity->categories as $cat) {
-            if ($cat->term) {
-                $term = common_canonical_tag($cat->term);
-                if ($term) {
-                    $options['tags'][] = $term;
-                }
-            }
-        }
-
-        // Atom enclosures -> attachment URLs
-        foreach ($activity->enclosures as $href) {
-            // @fixme save these locally or....?
-            $options['urls'][] = $href;
-        }
-
-        $saved = Notice::saveNew($this->target->id,
-                                 $content,
-                                 'atompub', // TODO: deal with this
-                                 $options);
-
-        return $saved;
+        header('HTTP/1.1 201 Created');
+        header("Location: " . common_local_url('ApiStatusesShow', array('id' => $stored->getID(),
+                                                                        'format' => 'atom')));
+        $this->showSingleAtomStatus($stored);
     }
 }
index d4756dffb5914a995eaa0feac300b48a3ab86eb3..ce5dc20178102b03dc39ee74b35aadca6963b5c5 100644 (file)
@@ -62,21 +62,44 @@ class UsergroupsAction extends GalleryAction
         }
     }
 
-    function showContent()
+    function showPageNotice()
     {
-        $this->elementStart('p', array('id' => 'new_group'));
-        $this->element('a', array('href' => common_local_url('newgroup'),
-                                  'class' => 'more'),
-                       // TRANS: Link text on group page to create a new group.
-                       _('Create a new group'));
-        $this->elementEnd('p');
+        if ($this->scoped instanceof Profile && $this->scoped->sameAs($this->getTarget())) {
+            $this->element('p', 'instructions',
+                           // TRANS: Page notice for page with an overview of all subscribed groups
+                           // TRANS: of the logged in user's own profile.
+                           _('These are the groups whose notices '.
+                             'you listen to.'));
+        } else {
+            $this->element('p', 'instructions',
+                           // TRANS: Page notice for page with an overview of all groups a user other
+                           // TRANS: than the logged in user. %s is the user nickname.
+                           sprintf(_('These are the groups whose '.
+                                     'notices %s listens to.'),
+                                   $this->target->getNickname()));
+        }
+    }
 
-        $this->elementStart('p', array('id' => 'group_search'));
-        $this->element('a', array('href' => common_local_url('groupsearch'),
-                                  'class' => 'more'),
-                       // TRANS: Link text on group page to search for groups.
-                       _('Search for more groups'));
-        $this->elementEnd('p');
+    function showContent()
+    {
+        if ($this->scoped instanceof Profile && $this->scoped->sameAs($this->getTarget())) {
+         $notice =
+          // TRANS: Page notice of user's groups page.
+          // TRANS: %%%%action.groupsearch%%%% and %%%%action.newgroup%%%% are URLs. Do not change them.
+          // TRANS: This message contains Markdown links in the form [link text](link).
+          sprintf(_('Groups let you find and talk with ' .
+                   'people of similar interests. ' .
+                   'You can [search for groups](%%%%action.groups%%%%) in your instance or ' .
+                    '[create a new group](%%%%action.newgroup%%%%). ' .
+                   'You can also follow groups ' .
+                   'from other GNU social instances: click on the remote button below ' .
+                   'and copy the group\'s link. ' .
+                   'You can find a list of GNU social groups [here](http://skilledtests.com/wiki/List_of_federated_GNU_social_groups)' .
+                   ''));
+         $this->elementStart('div', 'instructions');
+         $this->raw(common_markup_to_html($notice));
+         $this->elementEnd('div');
+       }
 
         if (Event::handle('StartShowUserGroupsContent', array($this))) {
             $offset = ($this->page-1) * GROUPS_PER_PAGE;
@@ -87,11 +110,13 @@ class UsergroupsAction extends GalleryAction
             if ($groups instanceof User_group) {
                 $gl = new GroupList($groups, $this->getTarget(), $this);
                 $cnt = $gl->show();
-                $this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE,
-                              $this->page, 'usergroups',
-                              array('nickname' => $this->getTarget()->getNickname()));
-            } else {
-                $this->showEmptyListMessage();
+               if (0 == $cnt) {
+                 $this->showEmptyListMessage();
+               } else {
+                 $this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE,
+                                   $this->page, 'usergroups',
+                                   array('nickname' => $this->getTarget()->getNickname()));
+               }
             }
 
             Event::handle('EndShowUserGroupsContent', array($this));
@@ -103,15 +128,15 @@ class UsergroupsAction extends GalleryAction
         // TRANS: Text on group page for a user that is not a member of any group.
         // TRANS: %s is a user nickname.
         $message = sprintf(_('%s is not a member of any group.'), $this->getTarget()->getNickname()) . ' ';
-
         if (common_logged_in()) {
-            $current_user = common_current_user();
             if ($this->scoped->sameAs($this->getTarget())) {
                 // TRANS: Text on group page for a user that is not a member of any group. This message contains
                 // TRANS: a Markdown link in the form [link text](link) and a variable that should not be changed.
-                $message .= _('Try [searching for groups](%%action.groupsearch%%) and joining them.');
+               $message = _('You are not member of any group yet. After you join a group ' .
+                            'you can send messages to its members using the ' .
+                            'syntax "!groupname".');
             }
-        }
+       }
         $this->elementStart('div', 'guide');
         $this->raw(common_markup_to_html($message));
         $this->elementEnd('div');
index 392440222b066d6c153c2a9704acb88f4ade03a1..3abb7681e8e5517d67a00693b90ce85f8fe45203 100644 (file)
@@ -65,7 +65,9 @@ class Group_member extends Managed_DataObject
         $member->group_id   = $group_id;
         $member->profile_id = $profile_id;
         $member->created    = common_sql_now();
-        $member->uri        = self::newURI($profile_id, $group_id, $member->created);
+        $member->uri        = self::newUri(Profile::getByID($profile_id),
+                                           User_group::getByID($group_id),
+                                           $member->created);
 
         $result = $member->insert();
 
@@ -166,7 +168,7 @@ class Group_member extends Managed_DataObject
 
         $act = new Activity();
 
-        $act->id = $this->getURI();
+        $act->id = $this->getUri();
 
         $act->actor     = $member->asActivityObject();
         $act->verb      = ActivityVerb::JOIN;
@@ -201,20 +203,8 @@ class Group_member extends Managed_DataObject
         mail_notify_group_join($this->getGroup(), $this->getMember());
     }
 
-    function getURI()
+    function getUri()
     {
-        if (!empty($this->uri)) {
-            return $this->uri;
-        } else {
-            return self::newURI($this->profile_id, $this->group_id, $this->created);
-        }
-    }
-
-    static function newURI($profile_id, $group_id, $created)
-    {
-        return TagURI::mint('join:%d:%d:%s',
-                            $profile_id,
-                            $group_id,
-                            common_date_iso8601($created));
+        return $this->uri ?: self::newUri($this->getMember(), $this->getGroup()->getProfile(), $this->created);
     }
 }
index ec514029939547997f5e098925e79816868d0034..8d9bca11b0309fe4cfed7bc1a4d73efb6f81daca 100644 (file)
@@ -334,7 +334,7 @@ abstract class Managed_DataObject extends Memcached_DataObject
         $object = new $classname();
         foreach ($pkey as $col) {
             if (!array_key_exists($col, $vals)) {
-                throw new ServerException("Missing primary key column '{$col}'");
+                throw new ServerException("Missing primary key column '{$col}' for ".get_called_class()." among provided keys: ".implode(',', array_keys($vals)));
             } elseif (is_null($vals[$col])) {
                 throw new ServerException("NULL values not allowed in getByPK for column '{$col}'");
             }
@@ -453,4 +453,16 @@ abstract class Managed_DataObject extends Memcached_DataObject
     {
         // NOOP
     }
+
+    static function newUri(Profile $actor, Managed_DataObject $object, $created=null)
+    {
+        if (is_null($created)) {
+            $created = common_sql_now();
+        }
+        return TagURI::mint(strtolower(get_called_class()).':%d:%s:%d:%s',
+                                        $actor->getID(),
+                                        ActivityUtils::resolveUri($object->getObjectType(), true),
+                                        $object->getID(),
+                                        common_date_iso8601($created));
+    }
 }
index 1d9823c79cb429ee160080d2fda172908f0fb6d3..5c7d9f10262701ace66b02237ed1fffb4bbcf3c9 100644 (file)
@@ -280,16 +280,8 @@ class Notice extends Managed_DataObject
         }
     }
 
-    public function get_object_type($canonical=false) {
-        return $canonical
-                ? ActivityObject::canonicalType($this->object_type)
-                : $this->object_type;
-    }
-
-    // activity plugins tend to use this function instead, but it's the same
-    public function getObjectType()
-    {
-        return $this->get_object_type();
+    public function getObjectType($canonical=false) {
+        return ActivityUtils::resolveUri($this->object_type, $canonical);
     }
 
     public static function getByUri($uri)
@@ -777,8 +769,9 @@ class Notice extends Managed_DataObject
         }
         extract($options, EXTR_SKIP);
 
+        // dupe check
         $stored = new Notice();
-        if (!empty($uri)) {
+        if (!empty($uri) && !ActivityUtils::compareVerbs($act->verb, array(ActivityVerb::DELETE))) {
             $stored->uri = $uri;
             if ($stored->find()) {
                 common_debug('cannot create duplicate Notice URI: '.$stored->uri);
@@ -891,11 +884,24 @@ class Notice extends Managed_DataObject
             $urls[] = $href;
         }
 
+        if (ActivityUtils::compareVerbs($stored->verb, array(ActivityVerb::POST))) {
+            if (empty($act->objects[0]->type)) {
+                // Default type for the post verb is 'note', but we know it's
+                // a 'comment' if it is in reply to something.
+                $stored->object_type = empty($stored->reply_to) ? ActivityObject::NOTE : ActivityObject::COMMENT;
+            } else {
+                //TODO: Is it safe to always return a relative URI? The
+                // JSON version of ActivityStreams always use it, so we
+                // should definitely be able to handle it...
+                $stored->object_type = ActivityUtils::resolveUri($act->objects[0]->type, true);
+            }
+        }
+
         if (Event::handle('StartNoticeSave', array(&$stored))) {
             // XXX: some of these functions write to the DB
 
             try {
-                $stored->insert();    // throws exception on error
+                $result = $stored->insert();    // throws exception on error
 
                 if ($notloc instanceof Notice_location) {
                     $notloc->notice_id = $stored->getID();
@@ -907,7 +913,7 @@ class Notice extends Managed_DataObject
                 $object = null;
                 Event::handle('StoreActivityObject', array($act, $stored, $options, &$object));
                 if (empty($object)) {
-                    throw new ServerException('Unsuccessful call to StoreActivityObject '.$stored->uri . ': '.$act->asString());
+                    throw new ServerException('Unsuccessful call to StoreActivityObject '.$stored->getUri() . ': '.$act->asString());
                 }
 
                 // If it's not part of a conversation, it's the beginning
@@ -2405,7 +2411,7 @@ class Notice extends Managed_DataObject
             $this->uri = sprintf('%s%s=%d:%s=%s',
                                 TagURI::mint(),
                                 'noticeId', $this->id,
-                                'objectType', $this->get_object_type(true));
+                                'objectType', $this->getObjectType(true));
             $changed = true;
         }
 
index d5d38d0d662be231d5a5b9c2467039cd51b8a773..5d12484cf59df43678e41116c5d89ed5d6a81be3 100644 (file)
@@ -166,7 +166,7 @@ class Oauth_application extends Managed_DataObject
                 'consumer_key' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'application consumer key'),
                 'name' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'name of the application'),
                 'description' => array('type' => 'varchar', 'length' => 191, 'description' => 'description of the application'),
-                'icon' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'application icon'),
+                'icon' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'default' => '/theme/base/default-avatar-stream.png', 'description' => 'application icon'),
                 'source_url' => array('type' => 'varchar', 'length' => 191, 'description' => 'application homepage - used for source link'),
                 'organization' => array('type' => 'varchar', 'length' => 191, 'description' => 'name of the organization running the application'),
                 'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'homepage for the organization'),
index 25a41bee72cb9b5b768be616035af9d78e5bada1..49addfe47fe887d72d84f73c57fde62c25ed1098 100644 (file)
@@ -144,7 +144,7 @@ class Profile extends Managed_DataObject
     public function hasPassword()
     {
         try {
-            return !empty($this->getUser()->hasPassword());
+            return $this->getUser()->hasPassword();
         } catch (NoSuchUserException $e) {
             return false;
         }
index 314a597f55e681e08a55e1adc26a25327384c523..f77472ae7050447d203c0767944d68fd123786f5 100644 (file)
@@ -159,8 +159,8 @@ class Subscription extends Managed_DataObject
         $sub->jabber     = 1;
         $sub->sms        = 1;
         $sub->created    = common_sql_now();
-        $sub->uri        = self::newURI($sub->subscriber,
-                                        $sub->subscribed,
+        $sub->uri        = self::newUri($subscriber,
+                                        $other,
                                         $sub->created);
 
         $result = $sub->insert();
@@ -267,18 +267,20 @@ class Subscription extends Managed_DataObject
         return $sub;
     }
 
-    function asActivity()
+    public function getSubscriber()
     {
-        $subscriber = Profile::getKV('id', $this->subscriber);
-        $subscribed = Profile::getKV('id', $this->subscribed);
+        return Profile::getByID($this->subscriber);
+    }
 
-        if (!$subscriber instanceof Profile) {
-            throw new NoProfileException($this->subscriber);
-        }
+    public function getSubscribed()
+    {
+        return Profile::getByID($this->subscribed);
+    }
 
-        if (!$subscribed instanceof Profile) {
-            throw new NoProfileException($this->subscribed);
-        }
+    function asActivity()
+    {
+        $subscriber = $this->getSubscriber();
+        $subscribed = $this->getSubscribed();
 
         $act = new Activity();
 
@@ -286,7 +288,7 @@ class Subscription extends Managed_DataObject
 
         // XXX: rationalize this with the URL
 
-        $act->id   = $this->getURI();
+        $act->id   = $this->getUri();
 
         $act->time    = strtotime($this->created);
         // TRANS: Activity title when subscribing to another person.
@@ -431,20 +433,8 @@ class Subscription extends Managed_DataObject
         return parent::update($dataObject);
     }
 
-    function getURI()
-    {
-        if (!empty($this->uri)) {
-            return $this->uri;
-        } else {
-            return self::newURI($this->subscriber, $this->subscribed, $this->created);
-        }
-    }
-
-    static function newURI($subscriber_id, $subscribed_id, $created)
+    public function getUri()
     {
-        return TagURI::mint('follow:%d:%d:%s',
-                            $subscriber_id,
-                            $subscribed_id,
-                            common_date_iso8601($created));
+        return $this->uri ?: self::newUri($this->getSubscriber(), $this->getSubscribed(), $this->created);
     }
 }
index 4db3b9ff4e801c2bab10772ebb800ddadf578a0c..3128088ca21b45772ff844e60ce98cc14378888d 100644 (file)
@@ -579,8 +579,8 @@ class Activity
 
             if (!empty($this->link)) {
                 $xs->element('link', array('rel' => 'alternate',
-                                           'type' => 'text/html'),
-                             $this->link);
+                                           'type' => 'text/html',
+                                           'href' => $this->link));
             }
 
         }
index c403e55a936f1fc9cf665594879647fbbe0a9907..2544179c5e2e96a6a196f75de45325fa737674f0 100644 (file)
@@ -281,10 +281,14 @@ abstract class ActivityHandlerPlugin extends Plugin
      *
      * @return boolean hook value
      */
-    function onNoticeDeleteRelated(Notice $notice)
+    public function onNoticeDeleteRelated(Notice $notice)
     {
         if ($this->isMyNotice($notice)) {
-            $this->deleteRelated($notice);
+            try {
+                $this->deleteRelated($notice);
+            } catch (AlreadyFulfilledException $e) {
+                // Nothing to see here, it's obviously already gone...
+            }
         }
 
         // Always continue this event in our activity handling plugins.
@@ -436,14 +440,13 @@ abstract class ActivityHandlerPlugin extends Plugin
     /**
      * Handle object posted via AtomPub
      *
-     * @param Activity &$activity Activity that was posted
+     * @param Activity  $activity Activity that was posted
      * @param Profile   $scoped   Profile of user posting
      * @param Notice   &$notice   Resulting notice
      *
      * @return boolean hook value
      */
-    // FIXME: Make sure we can really do strong Notice typing with a $notice===null without having =null here
-    public function onStartAtomPubNewActivity(Activity &$activity, Profile $scoped, Notice &$notice)
+    public function onStartAtomPubNewActivity(Activity $activity, Profile $scoped, Notice &$notice=null)
     {
         if (!$this->isMyActivity($activity)) {
             return true;
@@ -453,8 +456,6 @@ abstract class ActivityHandlerPlugin extends Plugin
 
         $notice = $this->saveNoticeFromActivity($activity, $scoped, $options);
 
-        Event::handle('EndAtomPubNewActivity', array($activity, $scoped, $notice));
-
         return false;
     }
 
index 2f2e50a4d747b852f1fcbfb18eed1e384cd9b69b..0e81082c35ea79a322a3dc26d92f6848074036cc 100644 (file)
@@ -295,7 +295,7 @@ class ApiAuthAction extends ApiAction
             // TRANS: Client error thrown when authentication fails because a user clicked "Cancel".
             $this->clientError(_('Could not authenticate you.'), 401);
 
-        } elseif ($required) {
+        } else {
             // $this->auth_user_nickname - i.e. PHP_AUTH_USER - will have a value since it was not empty
 
             $user = common_check_user($this->auth_user_nickname,
@@ -314,10 +314,10 @@ class ApiAuthAction extends ApiAction
                 $this->auth_user = null;
             }
 
-            // By default, basic auth users have rw access
-            $this->access = self::READ_WRITE;
-
-            if (!$this->auth_user instanceof User) {
+            if ($required && $this->auth_user instanceof User) {
+                // By default, basic auth users have rw access
+                $this->access = self::READ_WRITE;
+            } elseif ($required) {
                 $msg = sprintf(
                     "basic auth nickname = %s",
                     $this->auth_user_nickname
@@ -328,10 +328,10 @@ class ApiAuthAction extends ApiAction
                 header('WWW-Authenticate: Basic realm="' . $realm . '"');
                 // TRANS: Client error thrown when authentication fails.
                 $this->clientError(_('Could not authenticate you.'), 401);
+            } else {
+                // all get rw access for actions that don't require auth
+                $this->access = self::READ_WRITE;
             }
-        } else {
-            // all get rw access for actions that don't require auth
-            $this->access = self::READ_WRITE;
         }
     }
 
index 554d3ae63ceff4f518dcfc25b3abaf2e723ac084..490553f80b21ea1163cd4e995f8a284b18d985a6 100644 (file)
@@ -312,7 +312,6 @@ $default =
                             'Favorite' => array(),
                             'Share' => array(),
                             'LRDD' => array(),
-                            'StrictTransportSecurity' => array(),
                         ),
               'default' => array(
                             'Activity' => array(),
index 7f1684a3e756b0a2cd7ea3b4ba79970bb4486915..6cb2ebcfd53929fa977fd64a7b866cef8be8d8c5 100644 (file)
@@ -54,7 +54,7 @@ class SearchEngine
                     'nickname_desc sort mode can only be use when searching profile.'
                 );
             } else {
-                return $this->target->orderBy('nickname DESC');
+                return $this->target->orderBy(sprintf('%1$s.nickname DESC', $this->table));
             }
             break;
         case 'nickname_asc':
@@ -63,7 +63,7 @@ class SearchEngine
                     'nickname_desc sort mode can only be use when searching profile.'
                 );
             } else {
-                return $this->target->orderBy('nickname ASC');
+                return $this->target->orderBy(sprintf('%1$s.nickname ASC', $this->table));
             }
             break;
         default:
@@ -112,11 +112,13 @@ class MySQLLikeSearch extends SearchEngine
     function query($q)
     {
         if ('profile' === $this->table) {
-            $qry = sprintf('(nickname LIKE "%%%1$s%%" OR '.
-                           ' fullname LIKE "%%%1$s%%" OR '.
-                           ' location LIKE "%%%1$s%%" OR '.
-                           ' bio      LIKE "%%%1$s%%" OR '.
-                           ' homepage LIKE "%%%1$s%%")', $this->target->escape($q, true));
+            $qry = sprintf('(%2$s.nickname LIKE "%%%1$s%%" OR '.
+                            ' %2$s.fullname LIKE "%%%1$s%%" OR '.
+                            ' %2$s.location LIKE "%%%1$s%%" OR '.
+                            ' %2$s.bio      LIKE "%%%1$s%%" OR '.
+                            ' %2$s.homepage LIKE "%%%1$s%%")',
+                           $this->target->escape($q, true),
+                           $this->table);
         } else if ('notice' === $this->table) {
             $qry = sprintf('content LIKE "%%%1$s%%"', $this->target->escape($q, true));
         } else {
index 34735ebf2c67d8acdb6cdc1d297c7399f524fd97..4ddcf27c25e34e641bbadfd82e1d533c9b4f0cfd 100644 (file)
@@ -83,22 +83,84 @@ class ActivityModerationPlugin extends ActivityVerbHandlerPlugin
      */
     protected function saveObjectFromActivity(Activity $act, Notice $stored, array $options=array())
     {
+        // Everything is done in the StartNoticeSave event
+        return true;
+    }
+
+    // FIXME: Put this in lib/activityhandlerplugin.php when we're ready
+    //          with the other microapps/activityhandlers as well.
+    //          Also it should be StartNoticeAsActivity (with a prepped Activity, including ->context etc.)
+    public function onEndNoticeAsActivity(Notice $stored, Activity $act, Profile $scoped=null)
+    {
+        if (!$this->isMyNotice($stored)) {
+            return true;
+        }
+
+        common_debug('Extending activity '.$stored->id.' with '.get_called_class());
+        $this->extendActivity($stored, $act, $scoped);
+        return false;
+    }
+
+    /**
+     * This is run before ->insert, so our task in this function is just to
+     * delete if it is the delete verb.
+     */ 
+    public function onStartNoticeSave(Notice $stored)
+    {
+        // DELETE is a bit special, we have to remove the existing entry and then
+        // add a new one with the same URI in order to trigger the distribution.
+        // (that's why we don't use $this->isMyNotice(...))
+        if (!ActivityUtils::compareVerbs($stored->verb, array(ActivityVerb::DELETE))) {
+            return true;
+        }
+
+        try {
+            $target = Notice::getByUri($stored->uri);
+        } catch (NoResultException $e) {
+            throw new AlreadyFulfilledException('Notice URI not found, so we have nothing to delete.');
+        }
+
+        $actor = $stored->getProfile();
+        $owner = $target->getProfile();
+
+        if ($owner->hasRole(Profile_role::DELETED)) {
+            // Don't bother with replacing notices if its author is being deleted.
+            // The later "StoreActivityObject" will pick this up and execute
+            // the deletion then.
+            // (the "delete verb notice" is too new to ever pass through Notice::saveNew
+            // which otherwise wouldn't execute the StoreActivityObject event)
+            return true;
+        }
+
+        // Since the user deleting may not be the same as the notice's owner,
+        // double-check this and also set the "re-stored" notice profile_id.
+        if (!$actor->sameAs($owner) && !$actor->hasRight(Right::DELETEOTHERSNOTICE)) {
+            throw new AuthorizationException(_('You are not allowed to delete another user\'s notice.'));
+        }
+
+        // We copy the identifying fields and replace the sensitive ones.
+        //$stored->id = $target->id;    // We can't copy this since DB_DataObject won't inject it anyway
+        $props = array('uri', 'profile_id', 'conversation', 'reply_to', 'created', 'repeat_of', 'object_type', 'is_local', 'scope');
+        foreach($props as $prop) {
+            $stored->$prop = $target->$prop;
+        }
+        //$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', $act->id);
+        $deleted = Deleted_notice::getKV('uri', $stored->getUri());
         if ($deleted instanceof Deleted_notice) {
             return $deleted;
         }
 
-        $target = Notice::getByUri($act->objects[0]->id);
-        common_debug('DELETING notice: ' . $act->objects[0]->id . ' on behalf of profile id==' . $target->getProfile()->getID());
-
         $deleted = new Deleted_notice();
 
         $deleted->id            = $target->getID();
-        $deleted->profile_id    = $target->getProfile()->getID();
-        $deleted->uri           = $act->id;
-        $deleted->act_uri       = $target->getUri();
-        $deleted->act_created   = $target->created;
+        $deleted->profile_id    = $actor->getID();
+        $deleted->uri           = $stored->getUri();
+        $deleted->act_uri       = $stored->getUri();
+        $deleted->act_created   = $stored->created;
         $deleted->created       = common_sql_now();
 
         $result = $deleted->insert();
@@ -106,23 +168,10 @@ class ActivityModerationPlugin extends ActivityVerbHandlerPlugin
             throw new ServerException('Could not insert Deleted_notice entry into database!');
         }
 
+        // Now we delete the original notice, leaving the id and uri free.
         $target->delete();
 
-        return $deleted;
-    }
-
-    // FIXME: Put this in lib/activityhandlerplugin.php when we're ready
-    //          with the other microapps/activityhandlers as well.
-    //          Also it should be StartNoticeAsActivity (with a prepped Activity, including ->context etc.)
-    public function onEndNoticeAsActivity(Notice $stored, Activity $act, Profile $scoped=null)
-    {
-        if (!$this->isMyNotice($stored)) {
-            return true;
-        }
-
-        common_debug('Extending activity '.$stored->id.' with '.get_called_class());
-        $this->extendActivity($stored, $act, $scoped);
-        return false;
+        return true;
     }
 
     public function extendActivity(Notice $stored, Activity $act, Profile $scoped=null)
index ff1b56e2738fbb8a2acb7f53e98a3bfb6b2ec647..9a0a65c6b21acdebbcd7a102e4d42cca3ada7ffc 100644 (file)
@@ -30,15 +30,15 @@ class Deleted_notice extends Managed_DataObject
     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
-    public $deleted;                         // datetime()   not_null
 
     public static function schemaDef()
     {
         return array(
             'fields' => array(
                 'id' => array('type' => 'int', 'not null' => true, 'description' => 'notice ID'),
-                'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'author of the notice'),
+                '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'),
@@ -67,10 +67,9 @@ class Deleted_notice extends Managed_DataObject
         }
 
         $act = new Activity();
-        $act->type = ActivityObject::ACTIVITY;
         $act->verb = ActivityVerb::DELETE;
         $act->time = time();
-        $act->id   = self::newUri($actor, $notice);
+        $act->id   = $notice->getUri();
 
         $act->content = sprintf(_m('<a href="%1$s">%2$s</a> deleted notice <a href="%3$s">{{%4$s}}</a>.'),
                             htmlspecialchars($actor->getUrl()),
@@ -82,6 +81,7 @@ class Deleted_notice extends Managed_DataObject
         $act->actor = $actor->asActivityObject();
         $act->target = new ActivityObject();    // We don't save the notice object, as it's supposed to be removed!
         $act->target->id = $notice->getUri();
+        $act->target->type = $notice->getObjectType();
         $act->objects = array(clone($act->target));
 
         $url = $notice->getUrl();
@@ -99,19 +99,16 @@ class Deleted_notice extends Managed_DataObject
     static public function fromStored(Notice $stored)
     {
         $class = get_called_class();
-        $object = new $class;
-        $object->uri = $stored->getUri();   // Lookup by delete activity's URI! (that's what is _stored_ in our db!)
-        if (!$object->find(true)) {
-            throw new NoResultException($object);
-        }
-        return $object;
+        return self::getByPK(array('uri' => $stored->getUri()));
     }
 
+    // The one who deleted the notice, not the notice's author
     public function getActor()
     {
         return Profile::getByID($this->profile_id);
     }
 
+    // As above: The one who deleted the notice, not the notice's author
     public function getActorObject()
     {
         return $this->getActor()->asActivityObject();
@@ -126,23 +123,13 @@ class Deleted_notice extends Managed_DataObject
 
     public function getStored()
     {
-        $uri = $this->getTargetUri();
+        $uri = $this->getUri();
         if (!isset($this->_stored[$uri])) {
-            $stored = new Notice();
-            $stored->uri = $uri;
-            if (!$stored->find(true)) {
-                throw new NoResultException($stored);
-            }
-            $this->_stored[$uri] = $stored;
+            $this->_stored[$uri] = Notice::getByPK(array('uri' => $uri));
         }
         return $this->_stored[$uri];
     }
 
-    public function getTargetUri()
-    {
-        return $this->act_uri;
-    }
-
     public function getUri()
     {
         return $this->uri;
@@ -155,7 +142,8 @@ class Deleted_notice extends Managed_DataObject
         $actobj->type = ActivityObject::ACTIVITY;
         $actobj->actor = $this->getActorObject();
         $actobj->target = new ActivityObject();
-        $actobj->target->id = $this->getTargetUri();
+        $actobj->target->id = $this->getUri();
+        // FIXME: actobj->target->type? as in extendActivity, and actobj->target->actor maybe?
         $actobj->objects = array(clone($actobj->target));
         $actobj->verb = ActivityVerb::DELETE;
         $actobj->title = ActivityUtils::verbToTitle($actobj->verb);
@@ -164,7 +152,7 @@ class Deleted_notice extends Managed_DataObject
         $actobj->content = sprintf(_m('<a href="%1$s">%2$s</a> deleted notice {{%3$s}}.'),
                             htmlspecialchars($actor->getUrl()),
                             htmlspecialchars($actor->getBestName()),
-                            htmlspecialchars($this->getTargetUri())
+                            htmlspecialchars($this->getUri())
                            );
 
         return $actobj;
@@ -172,29 +160,16 @@ class Deleted_notice extends Managed_DataObject
 
     static public function extendActivity(Notice $stored, Activity $act, Profile $scoped=null)
     {
-        // the original notice is deleted, but we have stored some important data
-        $object = self::fromStored($stored);
-
+        // the original notice id and type is still stored in the Notice table
+        // so we use that information to describe the delete activity
         $act->target = new ActivityObject();
-        $act->target->id = $object->getTargetUri();
+        $act->target->id = $stored->getUri();
+        $act->target->type = $stored->getObjectType();
         $act->objects = array(clone($act->target));
 
-        $act->context->replyToID = $object->getTargetUri();
         $act->title = ActivityUtils::verbToTitle($act->verb);
     }
 
-    static function newUri(Profile $actor, Managed_DataObject $object, $created=null)
-    {
-        if (is_null($created)) {
-            $created = common_sql_now();
-        }
-        return TagURI::mint(strtolower(get_called_class()).':%d:%s:%d:%s',
-                                        $actor->getID(),
-                                        ActivityUtils::resolveUri($object->getObjectType(), true),
-                                        $object->getID(),
-                                        common_date_iso8601($created));
-    }
-
     static public function beforeSchemaUpdate()
     {
         $table = strtolower(get_called_class());
index 547efa2370104187891241cfbd7ba90d7ad689c8..e0f30700ae8c99504ba92029a120fbaa55a2f701 100644 (file)
@@ -280,14 +280,12 @@ class BookmarkPlugin extends MicroAppPlugin
      */
     function deleteRelated(Notice $notice)
     {
-       if ($this->isMyNotice($notice)) {
-               
-               $nb = Bookmark::getByNotice($notice);
-
-               if (!empty($nb)) {
-               $nb->delete();
-               }
-       }
+        try {
+            $nb = Bookmark::fromStored($notice);
+        } catch (NoResultException $e) {
+            throw new AlreadyFulfilledException('Bookmark already gone when deleting: '.$e->getMessage());
+        }
+        $nb->delete();
        
         return true;
     }
@@ -388,12 +386,12 @@ class BookmarkPlugin extends MicroAppPlugin
                    "Formatting notice {$notice->uri} as a bookmark.");
 
         $object = new ActivityObject();
-        $nb = Bookmark::getByNotice($notice);
+        $nb = Bookmark::fromStored($notice);
 
         $object->id      = $notice->uri;
         $object->type    = ActivityObject::BOOKMARK;
-        $object->title   = $nb->title;
-        $object->summary = $nb->description;
+        $object->title   = $nb->getTitle();
+        $object->summary = $nb->getDescription();
         $object->link    = $notice->getUrl();
 
         // Attributes of the URL
@@ -481,14 +479,10 @@ class BookmarkPlugin extends MicroAppPlugin
     {
         assert($obj->type == ActivityObject::BOOKMARK);
 
-        $bm = Bookmark::getKV('uri', $obj->id);
-
-        if (empty($bm)) {
-            throw new ServerException("Unknown bookmark: " . $obj->id);
-        }
+        $bm = Bookmark::getByPK(array('uri' => $obj->id));
 
-        $out['displayName'] = $bm->title;
-        $out['targetUrl']   = $bm->url;
+        $out['displayName'] = $bm->getTitle();
+        $out['targetUrl']   = $bm->getUrl();
 
         return true;
     }
@@ -501,24 +495,32 @@ class BookmarkPlugin extends MicroAppPlugin
         $nli->out->elementEnd('div');
     }
 
-    protected function showNoticeContent(Notice $stored, HTMLOutputter $out, Profile $scoped=null)
+    public function getDescription()
+    {
+        return $this->description;
+    }
+
+    public function getTitle()
     {
-        $nb = Bookmark::getByNotice($stored);
-
-        if (empty($nb)) {
-            common_log(LOG_ERR, "No bookmark for notice {$stored->id}");
-            parent::showContent();
-            return;
-        } else if (empty($nb->url)) {
-            common_log(LOG_ERR, "No url for bookmark {$nb->id} for notice {$stored->id}");
-            parent::showContent();
-            return;
+        return $this->title;
+    }
+
+    public function getUrl()
+    {
+        if (empty($this->url)) {
+            throw new InvalidUrlException($this->url);
         }
+        return $this->url;
+    }
+
+    protected function showNoticeContent(Notice $stored, HTMLOutputter $out, Profile $scoped=null)
+    {
+        $nb = Bookmark::fromStored($stored);
 
         $profile = $stored->getProfile();
 
         // Whether to nofollow
-        $attrs = array('href' => $nb->url, 'class' => 'bookmark-title');
+        $attrs = array('href' => $nb->getUrl(), 'class' => 'bookmark-title');
 
         $nf = common_config('nofollow', 'external');
 
index ade6b4fa7bb6fe4018410388206061dba635d8a5..cdbdf526cae733953ccef6e7e835787c8ea262bf 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); }
 
 /**
  * For storing the fact that a notice is a bookmark
@@ -71,12 +69,13 @@ class Bookmark extends Managed_DataObject
                 'description' => array('type' => 'text'),
                 'created' => array('type' => 'datetime', 'not null' => true),
             ),
-            'primary key' => array('id'),
+            'primary key' => array('uri'),
             'unique keys' => array(
-                'bookmark_uri_key' => array('uri'),
+                'bookmark_id_key' => array('id'),
             ),
             'foreign keys' => array(
-                'bookmark_profile_id_fkey' => array('profile', array('profile_id' => 'id'))
+                'bookmark_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
+                'bookmark_uri_fkey' => array('notice', array('uri' => 'uri')),
             ),
             'indexes' => array('bookmark_created_idx' => array('created'),
                             'bookmark_url_idx' => array('url'),
@@ -88,13 +87,14 @@ class Bookmark extends Managed_DataObject
     /**
      * Get a bookmark based on a notice
      *
-     * @param Notice $notice Notice to check for
+     * @param   Notice              $stored Notice activity which represents the Bookmark
      *
-     * @return Bookmark found bookmark or null
+     * @return  Bookmark            The found bookmark object.
+     * @throws  NoResultException   When you don't find it after all.
      */
-    static function getByNotice($notice)
+    static public function fromStored(Notice $stored)
     {
-        return self::getKV('uri', $notice->uri);
+        return self::getByPK(array('uri' => $stored->getUri()));
     }
 
     /**
@@ -276,9 +276,7 @@ class Bookmark extends Managed_DataObject
                                      'object_type' => ActivityObject::BOOKMARK),
                                $options);
 
-        if (!array_key_exists('uri', $options)) {
-            $options['uri'] = $nb->uri;
-        }
+        $options['uri'] = $nb->uri;
 
         try {
             $saved = Notice::saveNew($profile->id,
index 75dba7384a487a87e7849d163f02807bc0b9947b..376ad098b20dbfda085730646765f84e2b7d7aa6 100644 (file)
@@ -104,10 +104,14 @@ class DiasporaPlugin extends Plugin
 
         /**
          * https://wiki.diasporafoundation.org/Federation_protocol_overview
+         * http://www.rubydoc.info/github/Raven24/diaspora-federation/master/DiasporaFederation/Salmon/EncryptedSlap
          *
          * Constructing the encryption header
          */
 
+        // For some reason diaspora wants the salmon slap in a <diaspora> header.
+        $xs->elementStart('diaspora', array('xmlns'=>'https://joindiaspora.com/protocol'));
+
         /**
          * Choose an AES key and initialization vector, suitable for the
          * aes-256-cbc cipher. I shall refer to this as the “inner key”
@@ -201,6 +205,9 @@ class DiasporaPlugin extends Plugin
          * 2. Base64-encode the encrypted payload message.
          */
         $payload = $inner_key->encrypt($magic_env->getData());
+        //FIXME: This means we don't actually put an <atom:entry> in the payload,
+        // since Diaspora has its own update method! Silly me. Read up on:
+        // https://wiki.diasporafoundation.org/Federation_Message_Semantics
         $magic_env->signMessage(base64_encode($payload), 'application/xml');
 
 
@@ -214,6 +221,8 @@ class DiasporaPlugin extends Plugin
         $xs->element('me:sig', null, $magic_env->getSignature());
         $xs->elementEnd('me:env');
 
+        $xs->elementEnd('entry');
+
         return false;
     }
 
index 9fa6e17fc3f2c0014cf98afb7aeb4a66b2be8b53..4e9d153b1616c3e7ddfafe8d8cdfb5b84c419b4b 100644 (file)
@@ -46,6 +46,9 @@ if (!defined('STATUSNET')) {
  */
 class EventPlugin extends MicroAppPlugin
 {
+
+    var $oldSaveNew = true;
+
     /**
      * Set up our tables (event and rsvp)
      *
@@ -142,18 +145,44 @@ class EventPlugin extends MicroAppPlugin
             throw new Exception(_m('Wrong type for object.'));
         }
 
+        $dtstart = $happeningObj->element->getElementsByTagName('dtstart');
+        if($dtstart->length == 0) {
+            // TRANS: Exception thrown when has no start date
+            throw new Exception(_m('No start date for event.'));
+        }
+
+        $dtend = $happeningObj->element->getElementsByTagName('dtend');
+        if($dtend->length == 0) {
+            // TRANS: Exception thrown when has no end date
+            throw new Exception(_m('No end date for event.'));
+        }
+
+        // location is optional
+        $location = null;
+        $location_object = $happeningObj->element->getElementsByTagName('location');
+        if($location_object->length > 0) {
+            $location = $location_object->item(0)->nodeValue;
+        }
+
+        // url is optional
+        $url = null;
+        $url_object = $happeningObj->element->getElementsByTagName('url');
+        if($url_object->length > 0) {
+            $url = $url_object->item(0)->nodeValue;
+        }
+
         $notice = null;
 
         switch ($activity->verb) {
         case ActivityVerb::POST:
                // FIXME: get startTime, endTime, location and URL
             $notice = Happening::saveNew($actor,
-                                         $start_time,
-                                         $end_time,
+                                         $dtstart->item(0)->nodeValue,
+                                         $dtend->item(0)->nodeValue,
                                          $happeningObj->title,
-                                         null,
+                                         $location,
                                          $happeningObj->summary,
-                                         null,
+                                         $url,
                                          $options);
             break;
         case RSVP::POSITIVE:
@@ -228,9 +257,9 @@ class EventPlugin extends MicroAppPlugin
                               array('xmlns' => 'urn:ietf:params:xml:ns:xcal'),
                               common_date_iso8601($happening->end_time));
 
-               // FIXME: add location
-               // FIXME: add URL
-               
+        $obj->extra[] = array('location', false, $happening->location);
+        $obj->extra[] = array('url', false, $happening->url);
+
         // XXX: probably need other stuff here
 
         return $obj;
index 9483db7b4e55ab5360410560bcbfef389ff407b6..57cd1bb37e1558aaa3fd55a261aa592b69582224 100644 (file)
@@ -220,8 +220,8 @@ class NeweventAction extends Action
             $profile = $this->user->getProfile();
 
             $saved = Happening::saveNew($profile,
-                                        $this->startTime,
-                                        $this->endTime,
+                                        common_sql_date($this->startTime),
+                                        common_sql_date($this->endTime),
                                         $this->title,
                                         $this->location,
                                         $this->description,
index 733235655c28c9faca4f72fe5d6ce138e70f2bd4..8c713bacbd99a848e906cd5898ca303438d27cd6 100644 (file)
@@ -116,8 +116,8 @@ class Happening extends Managed_DataObject
 
         $ev->id          = UUID::gen();
         $ev->profile_id  = $profile->id;
-        $ev->start_time  = common_sql_date($start_time);
-        $ev->end_time    = common_sql_date($end_time);
+        $ev->start_time  = $start_time;
+        $ev->end_time    = $end_time;
         $ev->title       = $title;
         $ev->location    = $location;
         $ev->description = $description;
index 1e011decf8d74bca5723d45031911eaf12e2044a..ad7cc67df86ebccd10b9ac7c3a1a37430368cd77 100644 (file)
@@ -440,16 +440,4 @@ class Fave extends Managed_DataObject
         // We (should've in this case) created it ourselves, so we tag it ourselves
         return self::newUri($this->getActor(), $this->getTarget(), $this->created);
     }
-
-    static function newUri(Profile $actor, Managed_DataObject $target, $created=null)
-    {
-        if (is_null($created)) {
-            $created = common_sql_now();
-        }
-        return TagURI::mint(strtolower(get_called_class()).':%d:%s:%d:%s',
-                                        $actor->id,
-                                        ActivityUtils::resolveUri(self::getObjectType(), true),
-                                        $target->id,
-                                        common_date_iso8601($created));
-    }
 }