Example #1
2
 private function _userReward($res, $activityId, $accessToken, $accessSecret)
 {
     $uid = AppbymeUserAccess::getUserIdByAccess($accessToken, $accessSecret);
     if (!$uid) {
         return $this->makeErrorInfo($res, 'mobcent_user_error');
     }
     $exchangeInfo = AppbymeActivityInviteUser::getExchangeInfo($uid);
     $config = ActivityUtils::getInviteConfig($activityId);
     $res['body']['exchangeMin'] = (int) $config['exchange_min'];
     $res['body']['exchangeStatus'] = (int) $exchangeInfo['exchange_status'];
     $res['body']['virtualName'] = (string) $config['virtual_name'];
     $res['body']['exchangeRatio'] = (int) $config['exchange_ratio'];
     $res['body']['rewardSum'] = (int) $exchangeInfo['reward_sum'];
     $res['body']['availableReward'] = (int) $exchangeInfo['available_reward'];
     return $res;
 }
Example #2
0
 function __construct($element = null)
 {
     if (empty($element)) {
         return;
     }
     $replyToEl = ActivityUtils::child($element, self::INREPLYTO, self::THR);
     if (!empty($replyToEl)) {
         $this->replyToID = $replyToEl->getAttribute(self::REF);
         $this->replyToUrl = $replyToEl->getAttribute(self::HREF);
     }
     $this->location = $this->getLocation($element);
     $this->conversation = ActivityUtils::getLink($element, self::CONVERSATION);
     // Multiple attention links allowed
     $links = $element->getElementsByTagNameNS(ActivityUtils::ATOM, ActivityUtils::LINK);
     $attention = array();
     for ($i = 0; $i < $links->length; $i++) {
         $link = $links->item($i);
         $linkRel = $link->getAttribute(ActivityUtils::REL);
         // XXX: Deprecate this in favour of "mentioned" from Salmon spec
         // http://salmon-protocol.googlecode.com/svn/trunk/draft-panzer-salmon-00.html#SALR
         if ($linkRel == self::ATTENTION) {
             $attention[] = $link->getAttribute(self::HREF);
         } elseif ($linkRel == self::MENTIONED) {
             $attention[] = $link->getAttribute(self::HREF);
         }
     }
     $this->attention = array_unique($attention);
 }
Example #3
0
 private function _inviteCheck($res, $code, $device, $accessToken, $accessSecret, $activityId)
 {
     global $_G;
     // 获取邀请注册活动的配置
     $config = ActivityUtils::getInviteConfig($activityId);
     // 验证是否能进行参数活动
     $checkInvite = ActivityUtils::checkInvite($config, $_G['uid'], $device);
     if ($checkInvite['rs'] == 0) {
         return $this->makeErrorInfo($res, $checkInvite['errcode']);
     }
     $isSelf = AppbymeActivityInviteUser::getCheckByUidCode($_G['uid'], $code);
     if ($isSelf) {
         // 输入的是自己的验证码
         return $this->makeErrorInfo($res, 'mobcent_check_code_self');
     }
     $checkCode = AppbymeActivityInviteUser::checkCode($code);
     if (!$checkCode) {
         // 兑换码验证失败
         return $this->makeErrorInfo($res, 'mobcent_check_code_self');
     }
     if ($checkCode) {
         AppbymeActivityInviteUser::checkCodeSuccess($activityId, $code, $_G['uid']);
     }
     return $res;
 }
 function __construct($element = null)
 {
     if (empty($element)) {
         return;
     }
     $replyToEl = ActivityUtils::child($element, self::INREPLYTO, self::THR);
     if (!empty($replyToEl)) {
         $this->replyToID = $replyToEl->getAttribute(self::REF);
         $this->replyToUrl = $replyToEl->getAttribute(self::HREF);
     }
     $this->location = $this->getLocation($element);
     $convs = $element->getElementsByTagNameNS(self::OSTATUS, self::CONVERSATION);
     foreach ($convs as $conv) {
         $this->conversation = $conv->textContent;
     }
     if (empty($this->conversation)) {
         // fallback to the atom:link rel="ostatus:conversation" element
         $this->conversation = ActivityUtils::getLink($element, self::CONVERSATION);
     }
     // Multiple attention links allowed
     $links = $element->getElementsByTagNameNS(ActivityUtils::ATOM, ActivityUtils::LINK);
     for ($i = 0; $i < $links->length; $i++) {
         $link = $links->item($i);
         $linkRel = $link->getAttribute(ActivityUtils::REL);
         $linkHref = $link->getAttribute(self::HREF);
         if ($linkRel == self::MENTIONED && $linkHref !== '') {
             $this->attention[$linkHref] = $link->getAttribute(ActivityContext::OBJECTTYPE);
         }
     }
 }
Example #5
0
 private function _getAddress($element)
 {
     $addressEl = ActivityUtils::child($element, PoCoAddress::ADDRESS, PoCo::NS);
     if (!empty($addressEl)) {
         $formatted = ActivityUtils::childContent($addressEl, PoCoAddress::FORMATTED, self::NS);
         if (!empty($formatted)) {
             $address = new PoCoAddress();
             $address->formatted = $formatted;
             return $address;
         }
     }
     return null;
 }
Example #6
0
 static function noticeCount($id)
 {
     $keypart = sprintf('conversation:notice_count:%d', $id);
     $cnt = self::cacheGet($keypart);
     if ($cnt !== false) {
         return $cnt;
     }
     $notice = new Notice();
     $notice->conversation = $id;
     $notice->whereAddIn('verb', array(ActivityVerb::POST, ActivityUtils::resolveUri(ActivityVerb::POST, true)), $notice->columnType('verb'));
     $cnt = $notice->count();
     self::cacheSet($keypart, $cnt);
     return $cnt;
 }
 protected function saveObjectFromActivity(Activity $act, Notice $stored, array $options = array())
 {
     assert($this->isMyActivity($act));
     $stored->object_type = ActivityUtils::resolveUri($act->objects[0]->type);
     if (common_valid_http_url($act->objects[0]->link)) {
         $stored->url = $act->objects[0]->link;
     }
     // We don't have to do just about anything for a new, remote notice since the fields
     // are handled in the main Notice::saveActivity function. Such as content, attachments,
     // parent/conversation etc.
     // By returning true here instead of something that evaluates
     // to false, we show that we have processed everything properly.
     return true;
 }
 function saveNoticeFromActivity(Activity $activity, Profile $actor, array $options = array())
 {
     if (count($activity->objects) != 1) {
         throw new Exception('Too many activity objects.');
     }
     $videoObj = $activity->objects[0];
     if ($videoObj->type != Video::OBJECT_TYPE) {
         throw new Exception('Wrong type for object.');
     }
     // For now we read straight from the xml tree, no other way to get this information.
     // When there's a better API for this, we should change to it.
     $uri = ActivityUtils::getLink($activity->entry, 'enclosure');
     $options['object_type'] = Video::OBJECT_TYPE;
     Video::saveNew($actor, $uri, $options);
 }
 public static function checkCodeSuccess($activityId, $code, $uid)
 {
     $config = ActivityUtils::getInviteConfig($activityId);
     $sql1 = 'UPDATE %t SET';
     $sql1 .= ' invite_count=invite_count+1,';
     $sql1 .= 'reward_sum=reward_sum+' . $config['invite_reward'] . ',';
     $sql1 .= 'available_reward=available_reward+' . $config['invite_reward'];
     $sql1 .= ' WHERE exchange_num=%s';
     $sql2 = 'UPDATE %t SET';
     $sql2 .= ' joining=1';
     // $sql2 .= 'reward_sum=reward_sum+'.$config['invite_reward'].',';
     // $sql2 .= 'available_reward=available_reward+'.$config['invite_reward'];
     $sql2 .= ' WHERE uid=%d';
     DbUtils::getDzDbUtils(true)->query($sql1, array('appbyme_activity_invite_user', $code));
     DbUtils::getDzDbUtils(true)->query($sql2, array('appbyme_activity_invite_user', $uid));
 }
Example #10
0
 function __construct($element)
 {
     $replyToEl = ActivityUtils::child($element, self::INREPLYTO, self::THR);
     if (!empty($replyToEl)) {
         $this->replyToID = $replyToEl->getAttribute(self::REF);
         $this->replyToUrl = $replyToEl->getAttribute(self::HREF);
     }
     $this->location = $this->getLocation($element);
     $this->conversation = ActivityUtils::getLink($element, self::CONVERSATION);
     // Multiple attention links allowed
     $links = $element->getElementsByTagNameNS(ActivityUtils::ATOM, ActivityUtils::LINK);
     for ($i = 0; $i < $links->length; $i++) {
         $link = $links->item($i);
         $linkRel = $link->getAttribute(ActivityUtils::REL);
         if ($linkRel == self::ATTENTION) {
             $this->attention[] = $link->getAttribute(self::HREF);
         }
     }
 }
Example #11
0
 function handle($data)
 {
     list($user, $xml, $trusted) = $data;
     try {
         $doc = DOMDocument::loadXML($xml);
         $feed = $doc->documentElement;
         if ($feed->namespaceURI != Activity::ATOM || $feed->localName != 'feed') {
             // TRANS: Client exception thrown when an imported feed is not an Atom feed.
             throw new ClientException(_("Not an Atom feed."));
         }
         $author = ActivityUtils::getFeedAuthor($feed);
         if (empty($author)) {
             // TRANS: Client exception thrown when an imported feed does not have an author.
             throw new ClientException(_("No author in the feed."));
         }
         if (empty($user)) {
             if ($trusted) {
                 $user = $this->userFromAuthor($author);
             } else {
                 // TRANS: Client exception thrown when an imported feed does not have an author that
                 // TRANS: can be associated with a user.
                 throw new ClientException(_("Cannot import without a user."));
             }
         }
         $activities = $this->getActivities($feed);
         $qm = QueueManager::get();
         foreach ($activities as $activity) {
             $qm->enqueue(array($user, $author, $activity, $trusted), 'actimp');
         }
     } catch (ClientException $ce) {
         common_log(LOG_WARNING, $ce->getMessage());
         return true;
     } catch (ServerException $se) {
         common_log(LOG_ERR, $ce->getMessage());
         return false;
     } catch (Exception $e) {
         common_log(LOG_ERR, $ce->getMessage());
         return false;
     }
 }
Example #12
0
function importActivityStream($user, $doc)
{
    $feed = $doc->documentElement;
    $subjectEl = ActivityUtils::child($feed, Activity::SUBJECT, Activity::SPEC);
    if (!empty($subjectEl)) {
        $subject = new ActivityObject($subjectEl);
        printfv(_("Backup file for user %s (%s)") . "\n", $subject->id, Ostatus_profile::getActivityObjectNickname($subject));
    } else {
        throw new Exception("Feed doesn't have an <activity:subject> element.");
    }
    if (is_null($user)) {
        printfv(_("No user specified; using backup user.") . "\n");
        $user = userFromSubject($subject);
    }
    $entries = $feed->getElementsByTagNameNS(Activity::ATOM, 'entry');
    printfv(_("%d entries in backup.") . "\n", $entries->length);
    for ($i = $entries->length - 1; $i >= 0; $i--) {
        try {
            $entry = $entries->item($i);
            $activity = new Activity($entry, $feed);
            switch ($activity->verb) {
                case ActivityVerb::FOLLOW:
                    subscribeProfile($user, $subject, $activity);
                    break;
                case ActivityVerb::JOIN:
                    joinGroup($user, $activity);
                    break;
                case ActivityVerb::POST:
                    postNote($user, $activity);
                    break;
                default:
                    throw new Exception("Unknown verb: {$activity->verb}");
            }
        } catch (Exception $e) {
            print $e->getMessage() . "\n";
            continue;
        }
    }
}
Example #13
0
 private function _inviteExchange($res, $mobile, $type, $activityId)
 {
     global $_G;
     $config = ActivityUtils::getInviteConfig($activityId);
     // 是否结束
     if (time() > $config['stop_time']) {
         return $this->makeErrorInfo($res, 'mobcent_activity_end');
     }
     // 兑换金额是否超过最低兑换值
     $exchangeInfo = AppbymeActivityInviteUser::getExchangeInfo($_G['uid']);
     if ($exchangeInfo['available_reward'] < $config['exchange_min']) {
         return $this->makeErrorInfo($res, 'mobcent_exchange_min');
     }
     if (!in_array($type, array('mobile', 'forum'))) {
         return $this->makeErrorInfo($res, 'mobcent_exchange_type_error');
     }
     $exchange = array('exchange_type' => $type, 'mobile' => $mobile, 'exchange_status' => 1);
     $excInfo = AppbymeActivityInviteUser::inviteExchange($_G['uid'], $exchange);
     if (!$excInfo) {
         return $this->makeErrorInfo($res, 'mobcent_exchange_error');
     }
     return $res;
 }
 protected function doActionPost(ManagedAction $action, $verb, Notice $target, Profile $scoped)
 {
     switch (true) {
         case ActivityUtils::compareVerbs($verb, array(ActivityVerb::FAVORITE, ActivityVerb::LIKE)):
             Fave::addNew($scoped, $target);
             break;
         case ActivityUtils::compareVerbs($verb, array(ActivityVerb::UNFAVORITE, ActivityVerb::UNLIKE)):
             Fave::removeEntry($scoped, $target);
             break;
         default:
             throw new ServerException('ActivityVerb POST not handled by plugin that was supposed to do it.');
     }
     return false;
 }
Example #15
0
 /**
  * Insert notifications for replies, mentions and repeats
  *
  * @return boolean hook flag
  */
 function onStartNoticeDistribute($notice)
 {
     assert($notice->id > 0);
     // since we removed tests below
     // repeats
     if ($notice->isRepeat()) {
         $repeated_notice = Notice::getKV('id', $notice->repeat_of);
         if ($repeated_notice instanceof Notice) {
             $this->insertNotification($repeated_notice->profile_id, $notice->profile_id, 'repeat', $repeated_notice->id);
             // mark reply/mention-notifications as read if we're repeating to a notice we're notified about
             self::markNotificationAsSeen($repeated_notice->id, $notice->profile_id, 'mention');
             self::markNotificationAsSeen($repeated_notice->id, $notice->profile_id, 'reply');
             // (no other notifications repeats)
             return true;
         }
     }
     // don't add notifications for activity/non-post-verb notices
     if ($notice->source == 'activity' || !ActivityUtils::compareVerbs($notice->verb, array(ActivityVerb::POST))) {
         return true;
     }
     // mark reply/mention-notifications as read if we're replying to a notice we're notified about
     if ($notice->reply_to) {
         self::markNotificationAsSeen($notice->reply_to, $notice->profile_id, 'mention');
         self::markNotificationAsSeen($notice->reply_to, $notice->profile_id, 'reply');
     }
     // replies and mentions
     $reply_notification_to = false;
     // check for reply to insert in notifications
     if ($notice->reply_to) {
         try {
             $replyauthor = $notice->getParent()->getProfile();
             $reply_notification_to = $replyauthor->id;
             $this->insertNotification($replyauthor->id, $notice->profile_id, 'reply', $notice->id);
             //} catch (NoParentNoticeException $e) {	// TODO: catch this when everyone runs latest GNU social!
             // This is not a reply to something (has no parent)
         } catch (NoResultException $e) {
             // Parent author's profile not found! Complain louder?
             common_log(LOG_ERR, "Parent notice's author not found: " . $e->getMessage());
         }
     }
     // check for mentions to insert in notifications
     $mentions = $notice->getReplies();
     $sender = Profile::getKV($notice->profile_id);
     $all_mentioned_user_ids = array();
     foreach ($mentions as $mentioned) {
         // no duplicate mentions
         if (in_array($mentioned, $all_mentioned_user_ids)) {
             continue;
         }
         $all_mentioned_user_ids[] = $mentioned;
         // only notify if mentioned user is not already notified for reply
         if ($reply_notification_to != $mentioned) {
             $this->insertNotification($mentioned, $notice->profile_id, 'mention', $notice->id);
         }
     }
     return true;
 }
Example #16
0
 public function getAtomLink($rel, $type = null)
 {
     return ActivityUtils::getLink($this->root, $rel, $type);
 }
Example #17
0
 static function saveActivity(Activity $act, Profile $actor, array $options = array())
 {
     // First check if we're going to let this Activity through from the specific actor
     if (!$actor->hasRight(Right::NEWNOTICE)) {
         common_log(LOG_WARNING, "Attempted post from user disallowed to post: " . $actor->getNickname());
         // TRANS: Client exception thrown when a user tries to post while being banned.
         throw new ClientException(_m('You are banned from posting notices on this site.'), 403);
     }
     if (common_config('throttle', 'enabled') && !self::checkEditThrottle($actor->id)) {
         common_log(LOG_WARNING, 'Excessive posting by profile #' . $actor->id . '; throttled.');
         // TRANS: Client exception thrown when a user tries to post too many notices in a given time frame.
         throw new ClientException(_m('Too many notices too fast; take a breather ' . 'and post again in a few minutes.'));
     }
     // Get ActivityObject properties
     $actobj = null;
     if (!empty($act->id)) {
         // implied object
         $options['uri'] = $act->id;
         $options['url'] = $act->link;
     } else {
         $actobj = count($act->objects) == 1 ? $act->objects[0] : null;
         if (!is_null($actobj) && !empty($actobj->id)) {
             $options['uri'] = $actobj->id;
             if ($actobj->link) {
                 $options['url'] = $actobj->link;
             } elseif (preg_match('!^https?://!', $actobj->id)) {
                 $options['url'] = $actobj->id;
             }
         }
     }
     $defaults = array('groups' => array(), 'is_local' => $actor->isLocal() ? self::LOCAL_PUBLIC : self::REMOTE, 'mentions' => array(), 'reply_to' => null, 'repeat_of' => null, 'scope' => null, 'source' => 'unknown', 'tags' => array(), 'uri' => null, 'url' => null, 'urls' => array(), 'distribute' => true);
     // options will have default values when nothing has been supplied
     $options = array_merge($defaults, $options);
     foreach (array_keys($defaults) as $key) {
         // Only convert the keynames we specify ourselves from 'defaults' array into variables
         ${$key} = $options[$key];
     }
     extract($options, EXTR_SKIP);
     // dupe check
     $stored = new Notice();
     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);
             // 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');
         }
     }
     $autosource = common_config('public', 'autosource');
     // Sandboxed are non-false, but not 1, either
     if (!$actor->hasRight(Right::PUBLICNOTICE) || $source && $autosource && in_array($source, $autosource)) {
         // FIXME: ...what about remote nonpublic? Hmmm. That is, if we sandbox remote profiles...
         $stored->is_local = Notice::LOCAL_NONPUBLIC;
     } else {
         $stored->is_local = intval($is_local);
     }
     if (!$stored->isLocal()) {
         // Only do these checks for non-local notices. Local notices will generate these values later.
         if (!common_valid_http_url($url)) {
             common_debug('Bad notice URL: [' . $url . '], URI: [' . $uri . ']. Cannot link back to original! This is normal for shared notices etc.');
         }
         if (empty($uri)) {
             throw new ServerException('No URI for remote notice. Cannot accept that.');
         }
     }
     $stored->profile_id = $actor->id;
     $stored->source = $source;
     $stored->uri = $uri;
     $stored->url = $url;
     $stored->verb = $act->verb;
     // 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);
     // yeah, just don't use getRendered() here since it's not inserted yet ;)
     $stored->content = common_strip_html($stored->rendered);
     // 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);
     } else {
         $stored->created = common_sql_now();
     }
     $reply = null;
     if ($act->context instanceof ActivityContext && !empty($act->context->replyToID)) {
         $reply = self::getKV('uri', $act->context->replyToID);
     }
     if (!$reply instanceof Notice && $act->target instanceof ActivityObject) {
         $reply = self::getKV('uri', $act->target->id);
     }
     if ($reply instanceof Notice) {
         if (!$reply->inScope($actor)) {
             // TRANS: Client error displayed when trying to reply to a notice a the target has no access to.
             // TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
             throw new ClientException(sprintf(_m('%1$s has no right to reply to notice %2$d.'), $actor->getNickname(), $reply->id), 403);
         }
         $stored->reply_to = $reply->id;
         $stored->conversation = $reply->conversation;
         // If the original is private to a group, and notice has no group specified,
         // make it to the same group(s)
         if (empty($groups) && $reply->scope & Notice::GROUP_SCOPE) {
             $replyGroups = $reply->getGroups();
             foreach ($replyGroups as $group) {
                 if ($actor->isMember($group)) {
                     $groups[] = $group->id;
                 }
             }
         }
         if (is_null($scope)) {
             $scope = $reply->scope;
         }
     } else {
         // If we don't know the reply, we might know the conversation!
         // This will happen if a known remote user replies to an
         // unknown remote user - within a known conversation.
         if (empty($stored->conversation) and !empty($act->context->conversation)) {
             $conv = Conversation::getKV('uri', $act->context->conversation);
             if ($conv instanceof Conversation) {
                 common_debug('Conversation stitched together from (probably) a reply activity to unknown remote user. Activity creation time (' . $stored->created . ') should maybe be compared to conversation creation time (' . $conv->created . ').');
             } else {
                 // Conversation entry with specified URI was not found, so we must create it.
                 common_debug('Conversation URI not found, so we will create it with the URI given in the context of the activity: ' . $act->context->conversation);
                 // The insert in Conversation::create throws exception on failure
                 $conv = Conversation::create($act->context->conversation, $stored->created);
             }
             $stored->conversation = $conv->getID();
             unset($conv);
         }
     }
     // If it's not part of a conversation, it's the beginning of a new conversation.
     if (empty($stored->conversation)) {
         $conv = Conversation::create();
         $stored->conversation = $conv->getID();
         unset($conv);
     }
     $notloc = null;
     if ($act->context instanceof ActivityContext) {
         if ($act->context->location instanceof Location) {
             $notloc = Notice_location::fromLocation($act->context->location);
         }
     } else {
         $act->context = new ActivityContext();
     }
     $stored->scope = self::figureOutScope($actor, $groups, $scope);
     foreach ($act->categories as $cat) {
         if ($cat->term) {
             $term = common_canonical_tag($cat->term);
             if (!empty($term)) {
                 $tags[] = $term;
             }
         }
     }
     foreach ($act->enclosures as $href) {
         // @todo FIXME: Save these locally or....?
         $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 {
             $result = $stored->insert();
             // throws exception on error
             if ($notloc instanceof Notice_location) {
                 $notloc->notice_id = $stored->getID();
                 $notloc->insert();
             }
             $orig = clone $stored;
             // for updating later in this try clause
             $object = null;
             Event::handle('StoreActivityObject', array($act, $stored, $options, &$object));
             if (empty($object)) {
                 throw new ServerException('Unsuccessful call to StoreActivityObject ' . $stored->getUri() . ': ' . $act->asString());
             }
             // If something changed in the Notice during StoreActivityObject
             $stored->update($orig);
         } catch (Exception $e) {
             if (empty($stored->id)) {
                 common_debug('Failed to save stored object entry in database (' . $e->getMessage() . ')');
             } else {
                 common_debug('Failed to store activity object in database (' . $e->getMessage() . '), deleting notice id ' . $stored->id);
                 $stored->delete();
             }
             throw $e;
         }
     }
     if (!$stored instanceof Notice) {
         throw new ServerException('StartNoticeSave did not give back a Notice');
     }
     // 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($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.
         $stored->saveAttentions($act->context->attention);
         if (!empty($urls)) {
             $stored->saveKnownUrls($urls);
         } else {
             $stored->saveUrls();
         }
     }
     if ($distribute) {
         // Prepare inbox delivery, may be queued to background.
         $stored->distribute();
     }
     return $stored;
 }
 /**
  * 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};
     }
     // Let's see if this has been deleted already.
     try {
         $deleted = Deleted_notice::getByKeys(['uri' => $stored->getUri()]);
         return $deleted;
     } catch (NoResultException $e) {
         $deleted = new Deleted_notice();
         $deleted->id = $target->getID();
         $deleted->profile_id = $actor->getID();
         $deleted->uri = $stored->getUri();
         $deleted->act_created = $stored->created;
         $deleted->created = common_sql_now();
         // throws exception on error
         $result = $deleted->insert();
     }
     // Now we delete the original notice, leaving the id and uri free.
     $target->delete();
     return true;
 }
Example #19
0
 /**
  * Save a bookmark from an activity
  *
  * @param Activity $activity Activity to save
  * @param Profile  $actor    Profile to use as author
  * @param array    $options  Options to pass to bookmark-saving code
  *
  * @return Notice resulting notice
  */
 function saveNoticeFromActivity(Activity $activity, Profile $actor, array $options = array())
 {
     $bookmark = $activity->objects[0];
     $relLinkEls = ActivityUtils::getLinks($bookmark->element, 'related');
     if (count($relLinkEls) < 1) {
         // TRANS: Client exception thrown when a bookmark is formatted incorrectly.
         throw new ClientException(_m('Expected exactly 1 link ' . 'rel=related in a Bookmark.'));
     }
     if (count($relLinkEls) > 1) {
         common_log(LOG_WARNING, "Got too many link rel=related in a Bookmark.");
     }
     $linkEl = $relLinkEls[0];
     $url = $linkEl->getAttribute('href');
     $tags = array();
     foreach ($activity->categories as $category) {
         $tags[] = common_canonical_tag($category->term);
     }
     if (!empty($activity->time)) {
         $options['created'] = common_sql_date($activity->time);
     }
     // Fill in location if available
     $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;
         }
     }
     $options['groups'] = array();
     $options['replies'] = array();
     // TODO: context->attention
     foreach ($activity->context->attention as $attnUrl => $type) {
         try {
             $other = Profile::fromUri($attnUrl);
             if ($other->isGroup()) {
                 $options['groups'][] = $other->id;
             } else {
                 $options['replies'][] = $attnUrl;
             }
         } catch (UnknownUriException $e) {
             // We simply don't know this URI, despite lookup attempts.
         }
     }
     // 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;
         }
     }
     return Bookmark::saveNew($actor, $bookmark->title, $url, $tags, $bookmark->summary, $options);
 }
 static function canonicalType($type)
 {
     return ActivityUtils::resolveUri($type, true);
 }
Example #21
0
 static function textConstruct($el)
 {
     $src = $el->getAttribute(self::SRC);
     if (!empty($src)) {
         // TRANS: Client exception thrown when there is no source attribute.
         throw new ClientException(_("Can't handle remote content yet."));
     }
     $type = $el->getAttribute(self::TYPE);
     // slavishly following http://atompub.org/rfc4287.html#rfc.section.4.1.3.3
     if (empty($type) || $type == 'text') {
         // We have plaintext saved as the XML text content.
         // Since we want HTML, we need to escape any special chars.
         return htmlspecialchars($el->textContent);
     } else {
         if ($type == 'html') {
             // We have HTML saved as the XML text content.
             // No additional processing required once we've got it.
             $text = $el->textContent;
             return $text;
         } else {
             if ($type == 'xhtml') {
                 // Per spec, the <content type="xhtml"> contains a single
                 // HTML <div> with XHTML namespace on it as a child node.
                 // We need to pull all of that <div>'s child nodes and
                 // serialize them back to an (X)HTML source fragment.
                 $divEl = ActivityUtils::child($el, 'div', 'http://www.w3.org/1999/xhtml');
                 if (empty($divEl)) {
                     return null;
                 }
                 $doc = $divEl->ownerDocument;
                 $text = '';
                 $children = $divEl->childNodes;
                 for ($i = 0; $i < $children->length; $i++) {
                     $child = $children->item($i);
                     $text .= $doc->saveXML($child);
                 }
                 return trim($text);
             } else {
                 if (in_array($type, array('text/xml', 'application/xml')) || preg_match('#(+|/)xml$#', $type)) {
                     // TRANS: Client exception thrown when there embedded XML content is found that cannot be processed yet.
                     throw new ClientException(_("Can't handle embedded XML content yet."));
                 } else {
                     if (strncasecmp($type, 'text/', 5)) {
                         return $el->textContent;
                     } else {
                         // TRANS: Client exception thrown when base64 encoded content is found that cannot be processed yet.
                         throw new ClientException(_("Can't handle embedded Base64 content yet."));
                     }
                 }
             }
         }
     }
 }
 private function _getSource($element)
 {
     $sourceEl = ActivityUtils::child($element, 'source');
     if (empty($sourceEl)) {
         return null;
     } else {
         $href = ActivityUtils::getLink($sourceEl, 'self');
         if (!empty($href)) {
             return $href;
         } else {
             return ActivityUtils::childContent($sourceEl, 'id');
         }
     }
 }
Example #23
0
 private function _child($element, $tag, $namespace = self::SPEC)
 {
     return ActivityUtils::child($element, $tag, $namespace);
 }
 public function testBookmarkRelated()
 {
     global $_example11;
     $dom = new DOMDocument();
     $dom->loadXML($_example11);
     $feed = $dom->documentElement;
     $entry = $dom->getElementsByTagName('entry')->item(0);
     $expected = 'http://blog.teambox.com/open-source-companies';
     $links = ActivityUtils::getLinks($entry, 'related');
     $this->assertFalse(empty($links));
     $this->assertTrue(is_array($links));
     $this->assertEquals(count($links), 1);
     $url = $links[0]->getAttribute('href');
     $this->assertEquals($url, $expected);
 }
Example #25
0
 /**
  * Add stuff to notices in API responses
  *
  * @param Action $action action being executed
  *
  * @return boolean hook return
  */
 function onNoticeSimpleStatusArray($notice, &$twitter_status, $scoped)
 {
     // groups
     $notice_groups = $notice->getGroups();
     $group_addressees = false;
     foreach ($notice_groups as $g) {
         $group_addressees[] = array('nickname' => $g->nickname, 'url' => $g->mainpage);
     }
     $twitter_status['statusnet_in_groups'] = $group_addressees;
     // for older verions of gnu social: include the repeat-id, which we need when unrepeating later
     if (array_key_exists('repeated', $twitter_status) && $twitter_status['repeated'] === true) {
         $repeated = Notice::pkeyGet(array('profile_id' => $scoped->id, 'repeat_of' => $notice->id, 'verb' => 'http://activitystrea.ms/schema/1.0/share'));
         $twitter_status['repeated_id'] = $repeated->id;
     }
     // more metadata about attachments
     // get all attachments first, and put all the extra meta data in an array
     $attachments = $notice->attachments();
     $attachment_url_to_id = array();
     if (!empty($attachments)) {
         foreach ($attachments as $attachment) {
             if (is_object($attachment)) {
                 try {
                     $enclosure_o = $attachment->getEnclosure();
                     // add id to all attachments
                     $attachment_url_to_id[$enclosure_o->url]['id'] = $attachment->id;
                     // add data about thumbnails
                     $thumb = $attachment->getThumbnail();
                     $large_thumb = $attachment->getThumbnail(1000, 3000, false);
                     if (method_exists('File_thumbnail', 'url')) {
                         $thumb_url = File_thumbnail::url($thumb->filename);
                         $large_thumb_url = File_thumbnail::url($large_thumb->filename);
                     } else {
                         $thumb_url = $thumb->getUrl();
                         $large_thumb_url = $large_thumb->getUrl();
                     }
                     $attachment_url_to_id[$enclosure_o->url]['thumb_url'] = $thumb_url;
                     $attachment_url_to_id[$enclosure_o->url]['large_thumb_url'] = $large_thumb_url;
                     $attachment_url_to_id[$enclosure_o->url]['width'] = $attachment->width;
                     $attachment_url_to_id[$enclosure_o->url]['height'] = $attachment->height;
                     // animated gif?
                     if ($attachment->mimetype == 'image/gif') {
                         $image = ImageFile::fromFileObject($attachment);
                         if ($image->animated == 1) {
                             $attachment_url_to_id[$enclosure_o->url]['animated'] = true;
                         } else {
                             $attachment_url_to_id[$enclosure_o->url]['animated'] = false;
                         }
                     }
                     // this applies to older versions of gnu social, i think
                 } catch (ServerException $e) {
                     $thumb = File_thumbnail::getKV('file_id', $attachment->id);
                     if ($thumb instanceof File_thumbnail) {
                         $thumb_url = $thumb->getUrl();
                         $attachment_url_to_id[$enclosure_o->url]['id'] = $attachment->id;
                         $attachment_url_to_id[$enclosure_o->url]['thumb_url'] = $thumb_url;
                         $attachment_url_to_id[$enclosure_o->url]['large_thumb_url'] = $thumb_url;
                         $attachment_url_to_id[$enclosure_o->url]['width'] = $attachment->width;
                         $attachment_url_to_id[$enclosure_o->url]['height'] = $attachment->height;
                         // animated gif?
                         if ($attachment->mimetype == 'image/gif') {
                             $image = ImageFile::fromFileObject($attachment);
                             if ($image->animated == 1) {
                                 $attachment_url_to_id[$enclosure_o->url]['animated'] = true;
                             } else {
                                 $attachment_url_to_id[$enclosure_o->url]['animated'] = false;
                             }
                         }
                     }
                 }
             }
         }
     }
     // add the extra meta data to $twitter_status
     if (!empty($twitter_status['attachments'])) {
         foreach ($twitter_status['attachments'] as &$attachment) {
             if (!empty($attachment_url_to_id[$attachment['url']])) {
                 $attachment = array_merge($attachment, $attachment_url_to_id[$attachment['url']]);
             }
         }
     }
     // reply-to profile url
     try {
         $reply = $notice->getParent();
         $twitter_status['in_reply_to_profileurl'] = $reply->getProfile()->getUrl();
     } catch (ServerException $e) {
         $twitter_status['in_reply_to_profileurl'] = null;
     }
     // fave number
     $faves = Fave::byNotice($notice);
     $favenum = count($faves);
     $twitter_status['fave_num'] = $favenum;
     // repeat number
     $repeats = $notice->repeatStream();
     $repeatnum = 0;
     while ($repeats->fetch()) {
         if ($repeats->verb == ActivityVerb::SHARE) {
             // i.e. not deleted repeats
             $repeatnum++;
         }
     }
     $twitter_status['repeat_num'] = $repeatnum;
     // some more metadata about notice
     if ($notice->is_local == '1') {
         $twitter_status['is_local'] = true;
     } else {
         $twitter_status['is_local'] = false;
         if ($notice->object_type != 'activity') {
             try {
                 $twitter_status['external_url'] = $notice->getUrl(true);
             } catch (InvalidUrlException $e) {
                 common_debug('Qvitter: No URL available for external notice: id=' . $notice->id);
             }
         }
     }
     if ($notice->source == 'activity' || $notice->object_type == 'activity' || $notice->object_type == 'http://activitystrea.ms/schema/1.0/activity' || $notice->verb == 'delete') {
         $twitter_status['is_activity'] = true;
     } else {
         $twitter_status['is_activity'] = false;
     }
     if (ActivityUtils::compareTypes($notice->verb, array('qvitter-delete-notice', 'delete'))) {
         $twitter_status['qvitter_delete_notice'] = true;
     }
     return true;
 }
 public function testConversationLink()
 {
     $orig = $this->_fakeNotice($this->targetUser1);
     $text = "@" . $this->targetUser1->nickname . " reply text " . common_good_rand(4);
     $reply = Notice::saveNew($this->author1->id, $text, 'test', array('uri' => null, 'reply_to' => $orig->id));
     $conv = Conversation::staticGet('id', $reply->conversation);
     $entry = $reply->asAtomEntry();
     $element = $this->_entryToElement($entry, true);
     $this->assertEquals($conv->uri, ActivityUtils::getLink($element, 'ostatus:conversation'));
 }
Example #27
0
 /**
  * Get the identifier URI for the remote entity described
  * by this ActivityObject. This URI is *not* guaranteed to be
  * a resolvable HTTP/HTTPS URL.
  *
  * @param ActivityObject $object
  * @return string
  * @throws ServerException if feed info invalid
  */
 protected static function getActivityObjectProfileURI(ActivityObject $object)
 {
     if ($object->id) {
         if (ActivityUtils::validateUri($object->id)) {
             return $object->id;
         }
     }
     // If the id is missing or invalid (we've seen feeds mistakenly listing
     // things like local usernames in that field) then we'll use the profile
     // page link, if valid.
     if ($object->link && common_valid_http_url($object->link)) {
         return $object->link;
     }
     // TRANS: Server exception.
     throw new ServerException(_m('No author ID URI found.'));
 }
Example #28
0
 protected function saveObjectFromActivity(Activity $act, Notice $stored, array $options = array())
 {
     assert($this->isMyActivity($act));
     // The below algorithm is mainly copied from the previous Ostatus_profile->processShare()
     if (count($act->objects) !== 1) {
         // TRANS: Client exception thrown when trying to share multiple activities at once.
         throw new ClientException(_m('Can only handle share activities with exactly one object.'));
     }
     $shared = $act->objects[0];
     if (!$shared instanceof Activity) {
         // TRANS: Client exception thrown when trying to share a non-activity object.
         throw new ClientException(_m('Can only handle shared activities.'));
     }
     $sharedUri = $shared->id;
     if (!empty($shared->objects[0]->id)) {
         // Because StatusNet since commit 8cc4660 sets $shared->id to a TagURI which
         // f***s up federation, because the URI is no longer recognised by the origin.
         // So we set it to the object ID if it exists, otherwise we trust $shared->id
         $sharedUri = $shared->objects[0]->id;
     }
     if (empty($sharedUri)) {
         throw new ClientException(_m('Shared activity does not have an id'));
     }
     try {
         // First check if we have the shared activity. This has to be done first, because
         // we can't use these functions to "ensureActivityObjectProfile" of a local user,
         // who might be the creator of the shared activity in question.
         $sharedNotice = Notice::getByUri($sharedUri);
     } catch (NoResultException $e) {
         // If no locally stored notice is found, process it!
         // TODO: Remember to check Deleted_notice!
         // TODO: If a post is shared that we can't retrieve - what to do?
         $other = Ostatus_profile::ensureActivityObjectProfile($shared->actor);
         $sharedNotice = $other->processActivity($shared, 'push');
         // FIXME: push/salmon/what?
         if (!$sharedNotice instanceof Notice) {
             // And if we apparently can't get the shared notice, we'll abort the whole thing.
             // TRANS: Client exception thrown when saving an activity share fails.
             // TRANS: %s is a share ID.
             throw new ClientException(sprintf(_m('Failed to save activity %s.'), $sharedUri));
         }
     } catch (FeedSubException $e) {
         // Remote feed could not be found or verified, should we
         // transform this into an "RT @user Blah, blah, blah..."?
         common_log(LOG_INFO, __METHOD__ . ' got a ' . get_class($e) . ': ' . $e->getMessage());
         return false;
     }
     // Setting this here because when the algorithm gets back to
     // Notice::saveActivity it will update the Notice object.
     $stored->repeat_of = $sharedNotice->getID();
     $stored->conversation = $sharedNotice->conversation;
     $stored->object_type = ActivityUtils::resolveUri(ActivityObject::ACTIVITY, true);
     // We don't have to save a repeat in a separate table, we can
     // find repeats by just looking at the notice.repeat_of field.
     // By returning true here instead of something that evaluates
     // to false, we show that we have processed everything properly.
     return true;
 }
Example #29
0
 /**
  * Action of the form
  *
  * @return string URL of the action
  */
 function action()
 {
     return common_local_url('activityverb', array('id' => $this->notice->getID(), 'verb' => ActivityUtils::resolveUri(ActivityVerb::UNFAVORITE, true)));
 }
 /**
  * Handle a posted object from Salmon
  *
  * @param Activity $activity activity to handle
  * @param mixed    $target   user or group targeted
  *
  * @return boolean hook value
  */
 function onStartHandleSalmonTarget(Activity $activity, $target)
 {
     if (!$this->isMyActivity($activity)) {
         return true;
     }
     $this->log(LOG_INFO, "Checking {$activity->id} as a valid Salmon slap.");
     if ($target instanceof User_group || $target->isGroup()) {
         $uri = $target->getUri();
         if (!array_key_exists($uri, $activity->context->attention)) {
             // @todo FIXME: please document (i18n).
             // TRANS: Client exception thrown when ...
             throw new ClientException(_('Object not posted to this group.'));
         }
     } elseif ($target instanceof Profile && $target->isLocal()) {
         $original = null;
         // FIXME: Shouldn't favorites show up with a 'target' activityobject?
         if (!ActivityUtils::compareTypes($activity->verb, array(ActivityVerb::POST)) && isset($activity->objects[0])) {
             // If this is not a post, it's a verb targeted at something (such as a Favorite attached to a note)
             if (!empty($activity->objects[0]->id)) {
                 $activity->context->replyToID = $activity->objects[0]->id;
             }
         }
         if (!empty($activity->context->replyToID)) {
             $original = Notice::getKV('uri', $activity->context->replyToID);
         }
         if ((!$original instanceof Notice || $original->profile_id != $target->id) && !array_key_exists($target->getUri(), $activity->context->attention)) {
             // @todo FIXME: Please document (i18n).
             // TRANS: Client exception when ...
             throw new ClientException(_('Object not posted to this user.'));
         }
     } else {
         // TRANS: Server exception thrown when a micro app plugin uses a target that cannot be handled.
         throw new ServerException(_('Do not know how to handle this kind of target.'));
     }
     $oactor = Ostatus_profile::ensureActivityObjectProfile($activity->actor);
     $actor = $oactor->localProfile();
     // FIXME: will this work in all cases? I made it work for Favorite...
     if (ActivityUtils::compareTypes($activity->verb, array(ActivityVerb::POST))) {
         $object = $activity->objects[0];
     } else {
         $object = $activity;
     }
     $options = array('uri' => $object->id, 'url' => $object->link, 'is_local' => Notice::REMOTE, 'source' => 'ostatus');
     if (!isset($this->oldSaveNew)) {
         $notice = Notice::saveActivity($activity, $actor, $options);
     } else {
         $notice = $this->saveNoticeFromActivity($activity, $actor, $options);
     }
     return false;
 }