Пример #1
0
 function getUsers($y, $m, $d, $i)
 {
     $u = User::cacheGet("sitemap:user:{$y}:{$m}:{$d}:{$i}");
     if ($u === false) {
         $user = new User();
         $begindt = sprintf('%04d-%02d-%02d 00:00:00', $y, $m, $d);
         // XXX: estimates 1d == 24h, which screws up days
         // with leap seconds (1d == 24h + 1s). Thankfully they're
         // few and far between.
         $theend = strtotime($begindt) + 24 * 60 * 60;
         $enddt = common_sql_date($theend);
         $user->selectAdd();
         $user->selectAdd('nickname');
         $user->whereAdd("created >= '{$begindt}'");
         $user->whereAdd("created <  '{$enddt}'");
         $user->orderBy('created');
         $offset = ($i - 1) * SitemapPlugin::USERS_PER_MAP;
         $limit = SitemapPlugin::USERS_PER_MAP;
         $user->limit($offset, $limit);
         $user->find();
         while ($user->fetch()) {
             $u[] = $user->nickname;
         }
         $c = Cache::instance();
         if (!empty($c)) {
             $c->set(Cache::key("sitemap:user:{$y}:{$m}:{$d}:{$i}"), $u, Cache::COMPRESSED, time() > $theend ? time() + 90 * 24 * 60 * 60 : time() + 5 * 60);
         }
     }
     return $u;
 }
Пример #2
0
 function getNotices($y, $m, $d, $i)
 {
     $n = Notice::cacheGet("sitemap:notice:{$y}:{$m}:{$d}:{$i}");
     if ($n === false) {
         $notice = new Notice();
         $begindt = sprintf('%04d-%02d-%02d 00:00:00', $y, $m, $d);
         // XXX: estimates 1d == 24h, which screws up days
         // with leap seconds (1d == 24h + 1s). Thankfully they're
         // few and far between.
         $theend = strtotime($begindt) + 24 * 60 * 60;
         $enddt = common_sql_date($theend);
         $notice->selectAdd();
         $notice->selectAdd('id, created');
         $notice->whereAdd("created >= '{$begindt}'");
         $notice->whereAdd("created <  '{$enddt}'");
         $notice->whereAdd('is_local = ' . Notice::LOCAL_PUBLIC);
         $notice->orderBy('created');
         $offset = ($i - 1) * SitemapPlugin::NOTICES_PER_MAP;
         $limit = SitemapPlugin::NOTICES_PER_MAP;
         $notice->limit($offset, $limit);
         $notice->find();
         $n = array();
         while ($notice->fetch()) {
             $n[] = array($notice->id, $notice->created);
         }
         $c = Cache::instance();
         if (!empty($c)) {
             $c->set(Cache::key("sitemap:notice:{$y}:{$m}:{$d}:{$i}"), $n, Cache::COMPRESSED, time() > $theend ? time() + 90 * 24 * 60 * 60 : time() + 5 * 60);
         }
     }
     return $n;
 }
Пример #3
0
 /**
  * Save a favorite record.
  * @fixme post-author notification should be moved here
  *
  * @param Profile $actor  the local or remote Profile who favorites
  * @param Notice  $target the notice that is favorited
  * @return Fave record on success
  * @throws Exception on failure
  */
 static function addNew(Profile $actor, Notice $target)
 {
     if (self::existsForProfile($target, $actor)) {
         // TRANS: Client error displayed when trying to mark a notice as favorite that already is a favorite.
         throw new AlreadyFulfilledException(_('You have already favorited this!'));
     }
     $act = new Activity();
     $act->type = ActivityObject::ACTIVITY;
     $act->verb = ActivityVerb::FAVORITE;
     $act->time = time();
     $act->id = self::newUri($actor, $target, common_sql_date($act->time));
     $act->title = _("Favor");
     // TRANS: Message that is the "content" of a favorite (%1$s is the actor's nickname, %2$ is the favorited
     //        notice's nickname and %3$s is the content of the favorited notice.)
     $act->content = sprintf(_('%1$s favorited something by %2$s: %3$s'), $actor->getNickname(), $target->getProfile()->getNickname(), $target->rendered ?: $target->content);
     $act->actor = $actor->asActivityObject();
     $act->target = $target->asActivityObject();
     $act->objects = array(clone $act->target);
     $url = common_local_url('AtomPubShowFavorite', array('profile' => $actor->id, 'notice' => $target->id));
     $act->selfLink = $url;
     $act->editLink = $url;
     // saveActivity will in turn also call Fave::saveActivityObject which does
     // what this function used to do before this commit.
     $stored = Notice::saveActivity($act, $actor);
     return $stored;
 }
Пример #4
0
 function getNotices()
 {
     // @fixme there should be a common func for this
     if (common_config('db', 'type') == 'pgsql') {
         if (!empty($this->out->tag)) {
             $tag = pg_escape_string($this->out->tag);
         }
     } else {
         if (!empty($this->out->tag)) {
             $tag = mysql_escape_string($this->out->tag);
         }
     }
     $weightexpr = common_sql_weight('fave.modified', common_config('popular', 'dropoff'));
     $cutoff = sprintf("fave.modified > '%s'", common_sql_date(time() - common_config('popular', 'cutoff')));
     $qry = "SELECT notice.*, {$weightexpr} as weight ";
     if (isset($tag)) {
         $qry .= 'FROM notice_tag, notice JOIN fave ON notice.id = fave.notice_id ' . "WHERE {$cutoff} and notice.id = notice_tag.notice_id and '{$tag}' = notice_tag.tag";
     } else {
         $qry .= 'FROM notice JOIN fave ON notice.id = fave.notice_id ' . "WHERE {$cutoff}";
     }
     $qry .= ' GROUP BY notice.id,notice.profile_id,notice.content,notice.uri,' . 'notice.rendered,notice.url,notice.created,notice.modified,' . 'notice.reply_to,notice.is_local,notice.source,notice.conversation, ' . 'notice.lat,notice.lon,location_id,location_ns,notice.repeat_of' . ' ORDER BY weight DESC';
     $offset = 0;
     $limit = NOTICES_PER_SECTION + 1;
     $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
     $notice = Memcached_DataObject::cachedQuery('Notice', $qry, 1200);
     return $notice;
 }
Пример #5
0
 public function setupFeedSub(FeedSub $feedsub, $interval = 300)
 {
     $orig = clone $feedsub;
     $feedsub->sub_state = 'nohub';
     $feedsub->sub_start = common_sql_date(time());
     $feedsub->sub_end = '';
     $feedsub->last_update = common_sql_date(time() - $interval);
     // force polling as soon as we can
     $feedsub->update($orig);
 }
Пример #6
0
 function getUpdates($seconds)
 {
     $notice = new Notice();
     # XXX: cache this. Depends on how big this protocol becomes;
     # Re-doing this query every 15 seconds isn't the end of the world.
     $divider = common_sql_date(time() - $seconds);
     $notice->query('SELECT profile_id, max(id) AS max_id ' . 'FROM ( ' . 'SELECT profile_id, id FROM notice ' . (common_config('db', 'type') == 'pgsql' ? 'WHERE extract(epoch from created) > (extract(epoch from now()) - ' . $seconds . ') ' : 'WHERE created > "' . $divider . '" ') . ') AS latest ' . 'GROUP BY profile_id');
     $updates = array();
     while ($notice->fetch()) {
         $updates[] = array($notice->profile_id, $notice->max_id);
     }
     return $updates;
 }
Пример #7
0
 function lookup_nonce($consumer, $token, $nonce, $timestamp)
 {
     $n = new Nonce();
     $n->consumer_key = $consumer->key;
     $n->ts = common_sql_date($timestamp);
     $n->nonce = $nonce;
     if ($n->find(true)) {
         return true;
     } else {
         $n->created = DB_DataObject_Cast::dateTime();
         $n->insert();
         return false;
     }
 }
Пример #8
0
function cleanupChannels()
{
    $rc = new Realtime_channel();
    $rc->selectAdd();
    $rc->selectAdd('channel_key');
    $rc->whereAdd('modified < "' . common_sql_date(time() - Realtime_channel::TIMEOUT) . '"');
    if ($rc->find()) {
        $keys = $rc->fetchAll();
        foreach ($keys as $key) {
            $rc = Realtime_channel::staticGet('channel_key', $key);
            if (!empty($rc)) {
                printfv("Deleting realtime channel '{$key}'\n");
                $rc->delete();
            }
        }
    }
}
Пример #9
0
function importActivityStream($user, $doc)
{
    $feed = $doc->documentElement;
    $entries = $feed->getElementsByTagNameNS(Activity::ATOM, 'entry');
    for ($i = $entries->length - 1; $i >= 0; $i--) {
        $entry = $entries->item($i);
        $activity = new Activity($entry, $feed);
        $object = $activity->objects[0];
        if (!have_option('q', 'quiet')) {
            print $activity->content . "\n";
        }
        $html = getTweetHtml($object->link);
        $config = array('safe' => 1, 'deny_attribute' => 'class,rel,id,style,on*');
        $html = htmLawed($html, $config);
        $content = html_entity_decode(strip_tags($html), ENT_QUOTES, 'UTF-8');
        $notice = Notice::saveNew($user->id, $content, 'importtwitter', array('uri' => $object->id, 'url' => $object->link, 'rendered' => $html, 'created' => common_sql_date($activity->time), 'replies' => array(), 'groups' => array()));
    }
}
Пример #10
0
 /**
  * Validates a requested lease length, sets length plus
  * subscription start & end dates.
  *
  * Does not save to database -- use before insert() or update().
  *
  * @param int $length in seconds
  */
 function setLease($length)
 {
     assert(is_int($length));
     $min = 86400;
     $max = 86400 * 30;
     if ($length == 0) {
         // We want to garbage collect dead subscriptions!
         $length = $max;
     } elseif ($length < $min) {
         $length = $min;
     } else {
         if ($length > $max) {
             $length = $max;
         }
     }
     $this->lease = $length;
     $this->start_sub = common_sql_now();
     $this->end_sub = common_sql_date(time() + $length);
 }
Пример #11
0
 /**
  * Content area
  *
  * Shows the list of popular notices
  *
  * @return void
  */
 function showContent()
 {
     $groupId = intval($this->group->id);
     $weightexpr = common_sql_weight('fave.modified', common_config('popular', 'dropoff'));
     $cutoff = sprintf("fave.modified > '%s'", common_sql_date(time() - common_config('popular', 'cutoff')));
     $qry = 'SELECT notice.*, ' . $weightexpr . ' as weight ' . 'FROM notice ' . "JOIN group_inbox ON notice.id = group_inbox.notice_id " . 'JOIN fave ON notice.id = fave.notice_id ' . "WHERE {$cutoff} AND group_id = {$groupId} " . 'GROUP BY id,profile_id,uri,content,rendered,url,created,notice.modified,reply_to,is_local,source,notice.conversation ' . 'ORDER BY weight DESC';
     $offset = ($this->page - 1) * NOTICES_PER_PAGE;
     $limit = NOTICES_PER_PAGE + 1;
     if (common_config('db', 'type') == 'pgsql') {
         $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
     } else {
         $qry .= ' LIMIT ' . $offset . ', ' . $limit;
     }
     $notice = Memcached_DataObject::cachedQuery('Notice', $qry, 600);
     $nl = new NoticeList($notice, $this);
     $cnt = $nl->show();
     if ($cnt == 0) {
         //$this->showEmptyList();
     }
     $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->page, 'groupfavorited', array('nickname' => $this->group->nickname));
 }
 function getNoticeIds($offset, $limit, $since_id, $max_id)
 {
     $weightexpr = common_sql_weight('modified', common_config('popular', 'dropoff'));
     $cutoff = sprintf("modified > '%s'", common_sql_date(time() - common_config('popular', 'cutoff')));
     $fave = new Fave();
     $fave->selectAdd();
     $fave->selectAdd('notice_id');
     $fave->selectAdd("{$weightexpr} as weight");
     $fave->whereAdd($cutoff);
     $fave->orderBy('weight DESC');
     $fave->groupBy('notice_id');
     if (!is_null($offset)) {
         $fave->limit($offset, $limit);
     }
     // FIXME: $since_id, $max_id are ignored
     $ids = array();
     if ($fave->find()) {
         while ($fave->fetch()) {
             $ids[] = $fave->notice_id;
         }
     }
     return $ids;
 }
Пример #13
0
 public function onAppendUserActivityStreamObjects(UserActivityStream $uas, array &$objs)
 {
     $fave = new Fave();
     $fave->user_id = $uas->getUser()->id;
     if (!empty($uas->after)) {
         $fave->whereAdd("modified > '" . common_sql_date($uas->after) . "'");
     }
     if ($fave->find()) {
         while ($fave->fetch()) {
             $objs[] = clone $fave;
         }
     }
     return true;
 }
Пример #14
0
 /**
  * Save PuSH subscription confirmation.
  * Sets approximate lease start and end times and finalizes state.
  *
  * @param int $lease_seconds provided hub.lease_seconds parameter, if given
  */
 public function confirmSubscribe($lease_seconds)
 {
     $original = clone $this;
     $this->sub_state = 'active';
     $this->sub_start = common_sql_date(time());
     if ($lease_seconds > 0) {
         $this->sub_end = common_sql_date(time() + $lease_seconds);
     } else {
         $this->sub_end = null;
         // Backwards compatibility to StatusNet (PuSH <0.4 supported permanent subs)
     }
     $this->modified = common_sql_now();
     return $this->update($original);
 }
Пример #15
0
 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));
             }
         }
     }
     // 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', $options);
     return $saved;
 }
Пример #16
0
 static function gc($maxlifetime)
 {
     self::logdeb("garbage collection (maxlifetime = {$maxlifetime})");
     $epoch = common_sql_date(time() - $maxlifetime);
     $ids = array();
     $session = new Session();
     $session->whereAdd('modified < "' . $epoch . '"');
     $session->selectAdd();
     $session->selectAdd('id');
     $limit = common_config('sessions', 'gc_limit');
     if ($limit > 0) {
         // On large sites, too many sessions to expire
         // at once will just result in failure.
         $session->limit($limit);
     }
     $session->find();
     while ($session->fetch()) {
         $ids[] = $session->id;
     }
     $session->free();
     self::logdeb("Found " . count($ids) . " ids to delete.");
     foreach ($ids as $id) {
         self::logdeb("Destroying session '{$id}'.");
         self::destroy($id);
     }
 }
 /**
  * Import a single bookmark
  *
  * Takes a <dt>/<dd> pair. The <dt> has a single
  * <a> in it with some non-standard attributes.
  *
  * A <dt><dt><dd> sequence will appear as a <dt> with
  * anothe <dt> as a child. We handle this case recursively.
  *
  * @param User       $user User to import data as
  * @param DOMElement $dt   <dt> element
  * @param DOMElement $dd   <dd> element
  *
  * @return Notice imported notice
  */
 function importBookmark($user, $dt, $dd = null)
 {
     $as = $dt->getElementsByTagName('a');
     if ($as->length == 0) {
         // TRANS: Client exception thrown when a bookmark in an import file is incorrectly formatted.
         throw new ClientException(_m("No <A> tag in a <DT>."));
     }
     $a = $as->item(0);
     $private = $a->getAttribute('private');
     if ($private != 0) {
         // TRANS: Client exception thrown when a bookmark in an import file is private.
         throw new ClientException(_m('Skipping private bookmark.'));
     }
     if (!empty($dd)) {
         $description = $dd->nodeValue;
     } else {
         $description = null;
     }
     $addDate = $a->getAttribute('add_date');
     $data = array('profile_id' => $user->id, 'title' => $a->nodeValue, 'description' => $description, 'url' => $a->getAttribute('href'), 'tags' => $a->getAttribute('tags'), 'created' => common_sql_date(intval($addDate)));
     $qm = QueueManager::get();
     $qm->enqueue($data, 'dlcsbkmk');
 }
Пример #18
0
 static function saveNew($profile, $start_time, $end_time, $title, $location, $description, $url, $options = array())
 {
     if (array_key_exists('uri', $options)) {
         $other = Happening::getKV('uri', $options['uri']);
         if (!empty($other)) {
             // TRANS: Client exception thrown when trying to create an event that already exists.
             throw new ClientException(_m('Event already exists.'));
         }
     }
     $ev = new Happening();
     $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->title = $title;
     $ev->location = $location;
     $ev->description = $description;
     $ev->url = $url;
     if (array_key_exists('created', $options)) {
         $ev->created = $options['created'];
     } else {
         $ev->created = common_sql_now();
     }
     if (array_key_exists('uri', $options)) {
         $ev->uri = $options['uri'];
     } else {
         $ev->uri = common_local_url('showevent', array('id' => $ev->id));
     }
     $ev->insert();
     // XXX: does this get truncated?
     // TRANS: Event description. %1$s is a title, %2$s is start time, %3$s is end time,
     // TRANS: %4$s is location, %5$s is a description.
     $content = sprintf(_m('"%1$s" %2$s - %3$s (%4$s): %5$s'), $title, common_exact_date($ev->start_time), common_exact_date($ev->end_time), $location, $description);
     // TRANS: Rendered microformats2 tagged event description.
     // TRANS: %1$s is a title, %2$s is start time, %3$s is start time,
     // TRANS: %4$s is end time, %5$s is end time, %6$s is location, %7$s is description.
     // TRANS: Class names should not be translated.
     $rendered = sprintf(_m('<div class="h-event">' . '<p class="p-name p-summary">%1$s</p> ' . '<time class="dt-start" datetime="%2$s">%3$s</time> - ' . '<time class="dt-end" datetime="%4$s">%5$s</time> ' . '(<span class="p-location">%6$s</span>): ' . '<div class="p-description">%7$s</div> ' . '</div>'), htmlspecialchars($title), htmlspecialchars(common_date_iso8601($ev->start_time)), htmlspecialchars(common_exact_date($ev->start_time)), htmlspecialchars(common_date_iso8601($ev->end_time)), htmlspecialchars(common_exact_date($ev->end_time)), htmlspecialchars($location), htmlspecialchars($description));
     $options = array_merge(array('object_type' => Happening::OBJECT_TYPE), $options);
     if (!array_key_exists('uri', $options)) {
         $options['uri'] = $ev->uri;
     }
     if (!empty($url)) {
         $options['urls'] = array($url);
     }
     $saved = Notice::saveNew($profile->id, $content, array_key_exists('source', $options) ? $options['source'] : 'web', $options);
     return $saved;
 }
Пример #19
0
 /**
  * Process an incoming post activity from this remote feed.
  * @param Activity $activity
  * @param string $method 'push' or 'salmon'
  * @return mixed saved Notice or false
  * @todo FIXME: Break up this function, it's getting nasty long
  */
 public function processPost($activity, $method)
 {
     $notice = null;
     $oprofile = $this->checkAuthorship($activity);
     if (empty($oprofile)) {
         return null;
     }
     // It's not always an ActivityObject::NOTE, but... let's just say it is.
     $note = $activity->objects[0];
     // The id URI will be used as a unique identifier for for the notice,
     // protecting against duplicate saves. It isn't required to be a URL;
     // tag: URIs for instance are found in Google Buzz feeds.
     $sourceUri = $note->id;
     $dupe = Notice::staticGet('uri', $sourceUri);
     if ($dupe) {
         common_log(LOG_INFO, "OStatus: ignoring duplicate post: {$sourceUri}");
         return $dupe;
     }
     // We'll also want to save a web link to the original notice, if provided.
     $sourceUrl = null;
     if ($note->link) {
         $sourceUrl = $note->link;
     } else {
         if ($activity->link) {
             $sourceUrl = $activity->link;
         } else {
             if (preg_match('!^https?://!', $note->id)) {
                 $sourceUrl = $note->id;
             }
         }
     }
     // 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 {
                 // @todo FIXME: Fetch from $sourceUrl?
                 // TRANS: Client exception. %s is a source URI.
                 throw new ClientException(sprintf(_m('No content for notice %s.'), $sourceUri));
             }
         }
     }
     // Get (safe!) HTML and text versions of the content
     $rendered = $this->purify($sourceContent);
     $content = html_entity_decode(strip_tags($rendered), ENT_QUOTES, 'UTF-8');
     $shortened = common_shorten_links($content);
     // If it's too long, try using the summary, and make the
     // HTML an attachment.
     $attachment = null;
     if (Notice::contentTooLong($shortened)) {
         $attachment = $this->saveHTMLFile($note->title, $rendered);
         $summary = html_entity_decode(strip_tags($note->summary), ENT_QUOTES, 'UTF-8');
         if (empty($summary)) {
             $summary = $content;
         }
         $shortSummary = common_shorten_links($summary);
         if (Notice::contentTooLong($shortSummary)) {
             $url = common_shorten_url($sourceUrl);
             $shortSummary = substr($shortSummary, 0, Notice::maxContent() - (mb_strlen($url) + 2));
             $content = $shortSummary . ' ' . $url;
             // We mark up the attachment link specially for the HTML output
             // so we can fold-out the full version inline.
             // @todo FIXME i18n: This tooltip will be saved with the site's default language
             // TRANS: Shown when a notice is longer than supported and/or when attachments are present. At runtime
             // TRANS: this will usually be replaced with localised text from StatusNet core messages.
             $showMoreText = _m('Show more');
             $attachUrl = common_local_url('attachment', array('attachment' => $attachment->id));
             $rendered = common_render_text($shortSummary) . '<a href="' . htmlspecialchars($attachUrl) . '"' . ' class="attachment more"' . ' title="' . htmlspecialchars($showMoreText) . '">' . '&#8230;' . '</a>';
         }
     }
     $options = array('is_local' => Notice::REMOTE, 'url' => $sourceUrl, 'uri' => $sourceUri, 'rendered' => $rendered, 'replies' => array(), 'groups' => array(), 'peopletags' => array(), 'tags' => array(), 'urls' => array());
     // Check for optional attributes...
     if (!empty($activity->time)) {
         $options['created'] = common_sql_date($activity->time);
     }
     if ($activity->context) {
         // Any individual or group attn: targets?
         $replies = $activity->context->attention;
         $options['groups'] = $this->filterReplies($oprofile, $replies);
         $options['replies'] = $replies;
         // Maintain direct reply associations
         // @todo FIXME: What about conversation ID?
         if (!empty($activity->context->replyToID)) {
             $orig = Notice::staticGet('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;
             }
         }
     }
     if ($this->isPeopletag()) {
         $options['peopletags'][] = $this->localPeopletag();
     }
     // 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) {
         // @todo FIXME: Save these locally or....?
         $options['urls'][] = $href;
     }
     try {
         $saved = Notice::saveNew($oprofile->profile_id, $content, 'ostatus', $options);
         if ($saved) {
             Ostatus_source::saveNew($saved, $this, $method);
             if (!empty($attachment)) {
                 File_to_post::processNew($attachment->id, $saved->id);
             }
         }
     } catch (Exception $e) {
         common_log(LOG_ERR, "OStatus save of remote message {$sourceUri} failed: " . $e->getMessage());
         throw $e;
     }
     common_log(LOG_INFO, "OStatus saved remote message {$sourceUri} as notice id {$saved->id}");
     return $saved;
 }
Пример #20
0
 function getGroups()
 {
     $groups = array();
     $gm = new Group_member();
     $gm->profile_id = $this->user->id;
     if (!empty($this->after)) {
         $gm->whereAdd("created > '" . common_sql_date($this->after) . "'");
     }
     if ($gm->find()) {
         while ($gm->fetch()) {
             $groups[] = clone $gm;
         }
     }
     return $groups;
 }
Пример #21
0
 /**
  * Normalize timestamp format.
  * @param string $ts
  * @return string
  */
 private function timestamp($ts)
 {
     return common_sql_date(strtotime($ts));
 }
Пример #22
0
function initFaveURI()
{
    printfnq("Ensuring all faves have a URI...");
    $fave = new Fave();
    $fave->whereAdd('uri IS NULL');
    if ($fave->find()) {
        while ($fave->fetch()) {
            try {
                $fave->decache();
                $fave->query(sprintf('update fave ' . 'set uri = "%s", ' . '    modified = "%s" ' . 'where user_id = %d ' . 'and notice_id = %d', Fave::newURI($fave->user_id, $fave->notice_id, $fave->modified), common_sql_date(strtotime($fave->modified)), $fave->user_id, $fave->notice_id));
            } catch (Exception $e) {
                common_log(LOG_ERR, "Error updated fave URI: " . $e->getMessage());
            }
        }
    }
    printfnq("DONE.\n");
}
 /**
  * Process an incoming post activity from this remote feed.
  * @param Activity $activity
  * @param string $method 'push' or 'salmon'
  * @return mixed saved Notice or false
  * @fixme break up this function, it's getting nasty long
  */
 public function processPost($activity, $method)
 {
     if ($this->isGroup()) {
         // A group feed will contain posts from multiple authors.
         // @fixme validate these profiles in some way!
         $oprofile = self::ensureActorProfile($activity);
         if ($oprofile->isGroup()) {
             // Groups can't post notices in StatusNet.
             common_log(LOG_WARNING, "OStatus: skipping post with group listed as author: {$oprofile->uri} in feed from {$this->uri}");
             return false;
         }
     } else {
         $actor = $activity->actor;
         if (empty($actor)) {
             // OK here! assume the default
         } else {
             if ($actor->id == $this->uri || $actor->link == $this->uri) {
                 $this->updateFromActivityObject($actor);
             } else {
                 throw new Exception("Got an actor '{$actor->title}' ({$actor->id}) on single-user feed for {$this->uri}");
             }
         }
         $oprofile = $this;
     }
     // It's not always an ActivityObject::NOTE, but... let's just say it is.
     $note = $activity->objects[0];
     // The id URI will be used as a unique identifier for for the notice,
     // protecting against duplicate saves. It isn't required to be a URL;
     // tag: URIs for instance are found in Google Buzz feeds.
     $sourceUri = $note->id;
     $dupe = Notice::staticGet('uri', $sourceUri);
     if ($dupe) {
         common_log(LOG_INFO, "OStatus: ignoring duplicate post: {$sourceUri}");
         return false;
     }
     // We'll also want to save a web link to the original notice, if provided.
     $sourceUrl = null;
     if ($note->link) {
         $sourceUrl = $note->link;
     } else {
         if ($activity->link) {
             $sourceUrl = $activity->link;
         } else {
             if (preg_match('!^https?://!', $note->id)) {
                 $sourceUrl = $note->id;
             }
         }
     }
     // 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?
                 throw new ClientException("No content for notice {$sourceUri}");
             }
         }
     }
     // Get (safe!) HTML and text versions of the content
     $rendered = $this->purify($sourceContent);
     $content = html_entity_decode(strip_tags($rendered));
     $shortened = common_shorten_links($content);
     // If it's too long, try using the summary, and make the
     // HTML an attachment.
     $attachment = null;
     if (Notice::contentTooLong($shortened)) {
         $attachment = $this->saveHTMLFile($note->title, $rendered);
         $summary = html_entity_decode(strip_tags($note->summary));
         if (empty($summary)) {
             $summary = $content;
         }
         $shortSummary = common_shorten_links($summary);
         if (Notice::contentTooLong($shortSummary)) {
             $url = common_shorten_url($sourceUrl);
             $shortSummary = substr($shortSummary, 0, Notice::maxContent() - (mb_strlen($url) + 2));
             $content = $shortSummary . ' ' . $url;
             // We mark up the attachment link specially for the HTML output
             // so we can fold-out the full version inline.
             $attachUrl = common_local_url('attachment', array('attachment' => $attachment->id));
             $rendered = common_render_text($shortSummary) . '<a href="' . htmlspecialchars($attachUrl) . '"' . ' class="attachment more"' . ' title="' . htmlspecialchars(_m('Show more')) . '">' . '&#8230;' . '</a>';
         }
     }
     $options = array('is_local' => Notice::REMOTE_OMB, 'url' => $sourceUrl, 'uri' => $sourceUri, 'rendered' => $rendered, 'replies' => array(), 'groups' => array(), 'tags' => array(), 'urls' => array());
     // Check for optional attributes...
     if (!empty($activity->time)) {
         $options['created'] = common_sql_date($activity->time);
     }
     if ($activity->context) {
         // Any individual or group attn: targets?
         $replies = $activity->context->attention;
         $options['groups'] = $this->filterReplies($oprofile, $replies);
         $options['replies'] = $replies;
         // Maintain direct reply associations
         // @fixme what about conversation ID?
         if (!empty($activity->context->replyToID)) {
             $orig = Notice::staticGet('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;
     }
     try {
         $saved = Notice::saveNew($oprofile->profile_id, $content, 'ostatus', $options);
         if ($saved) {
             Ostatus_source::saveNew($saved, $this, $method);
             if (!empty($attachment)) {
                 File_to_post::processNew($attachment->id, $saved->id);
             }
         }
     } catch (Exception $e) {
         common_log(LOG_ERR, "OStatus save of remote message {$sourceUri} failed: " . $e->getMessage());
         throw $e;
     }
     common_log(LOG_INFO, "OStatus saved remote message {$sourceUri} as notice id {$saved->id}");
     return $saved;
 }
Пример #24
0
 /**
  * Save PuSH subscription confirmation.
  * Sets approximate lease start and end times and finalizes state.
  *
  * @param int $lease_seconds provided hub.lease_seconds parameter, if given
  */
 public function confirmSubscribe($lease_seconds = 0)
 {
     $original = clone $this;
     $this->sub_state = 'active';
     $this->sub_start = common_sql_date(time());
     if ($lease_seconds > 0) {
         $this->sub_end = common_sql_date(time() + $lease_seconds);
     } else {
         $this->sub_end = null;
     }
     $this->modified = common_sql_now();
     return $this->update($original);
 }
Пример #25
0
function common_sql_now()
{
    return common_sql_date(time());
}
Пример #26
0
 static function checkDupes($profile_id, $content)
 {
     $profile = Profile::getKV($profile_id);
     if (!$profile instanceof Profile) {
         return false;
     }
     $notice = $profile->getNotices(0, CachingNoticeStream::CACHE_WINDOW);
     if (!empty($notice)) {
         $last = 0;
         while ($notice->fetch()) {
             if (time() - strtotime($notice->created) >= common_config('site', 'dupelimit')) {
                 return true;
             } else {
                 if ($notice->content == $content) {
                     return false;
                 }
             }
         }
     }
     // If we get here, oldest item in cache window is not
     // old enough for dupe limit; do direct check against DB
     $notice = new Notice();
     $notice->profile_id = $profile_id;
     $notice->content = $content;
     $threshold = common_sql_date(time() - common_config('site', 'dupelimit'));
     $notice->whereAdd(sprintf("created > '%s'", $notice->escape($threshold)));
     $cnt = $notice->count();
     return $cnt == 0;
 }
Пример #27
0
 function postNote($user, $author, $activity)
 {
     $note = $activity->objects[0];
     $sourceUri = $note->id;
     $notice = Notice::getKV('uri', $sourceUri);
     if ($notice instanceof Notice) {
         common_log(LOG_INFO, "Notice {$sourceUri} already exists.");
         if ($this->trusted) {
             $profile = $notice->getProfile();
             $uri = $profile->getUri();
             if ($uri === $author->id) {
                 common_log(LOG_INFO, sprintf('Updating notice author from %s to %s', $author->id, $user->getUri()));
                 $orig = clone $notice;
                 $notice->profile_id = $user->id;
                 $notice->update($orig);
                 return;
             } else {
                 // TRANS: Client exception thrown when trying to import a notice by another user.
                 // TRANS: %1$s is the source URI of the notice, %2$s is the URI of the author.
                 throw new ClientException(sprintf(_('Already know about notice %1$s and ' . ' it has a different author %2$s.'), $sourceUri, $uri));
             }
         } else {
             // TRANS: Client exception thrown when trying to overwrite the author information for a non-trusted user during import.
             throw new ClientException(_('Not overwriting author info for non-trusted user.'));
         }
     }
     // 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 exception thrown when trying to import a notice without content.
                 // TRANS: %s is the notice URI.
                 throw new ClientException(sprintf(_('No content for notice %s.'), $sourceUri));
             }
         }
     }
     // Get (safe!) HTML and text versions of the content
     $rendered = common_purify($sourceContent);
     $content = common_strip_html($rendered);
     $shortened = $user->shortenLinks($content);
     $options = array('is_local' => Notice::LOCAL_PUBLIC, 'uri' => $sourceUri, 'rendered' => $rendered, 'replies' => array(), 'groups' => array(), 'tags' => array(), 'urls' => array(), 'distribute' => false);
     // Check for optional attributes...
     if (!empty($activity->time)) {
         $options['created'] = common_sql_date($activity->time);
     }
     if ($activity->context) {
         // Any individual or group attn: targets?
         list($options['groups'], $options['replies']) = $this->filterAttention($activity->context->attention);
         // Maintain direct reply associations
         // @fixme what about conversation ID?
         if (!empty($activity->context->replyToID)) {
             $orig = Notice::getKV('uri', $activity->context->replyToID);
             if ($orig instanceof Notice) {
                 $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;
     }
     common_log(LOG_INFO, "Saving notice {$options['uri']}");
     $saved = Notice::saveNew($user->id, $content, 'restore', $options);
     return $saved;
 }
Пример #28
0
 static function getAllChannels($action, $arg1, $arg2)
 {
     $channel = new Realtime_channel();
     $channel->action = $action;
     if (is_null($arg1)) {
         $channel->whereAdd('arg1 is null');
     } else {
         $channel->arg1 = $arg1;
     }
     if (is_null($arg2)) {
         $channel->whereAdd('arg2 is null');
     } else {
         $channel->arg2 = $arg2;
     }
     $channel->whereAdd('modified > "' . common_sql_date(time() - self::TIMEOUT) . '"');
     $channels = array();
     if ($channel->find()) {
         $channels = $channel->fetchAll();
     }
     return $channels;
 }
Пример #29
0
 static function gc($maxlifetime)
 {
     self::logdeb("garbage collection (maxlifetime = {$maxlifetime})");
     $epoch = common_sql_date(time() - $maxlifetime);
     $ids = array();
     $session = new Session();
     $session->whereAdd('modified < "' . $epoch . '"');
     $session->selectAdd();
     $session->selectAdd('id');
     $session->find();
     while ($session->fetch()) {
         $ids[] = $session->id;
     }
     $session->free();
     self::logdeb("Found " . count($ids) . " ids to delete.");
     foreach ($ids as $id) {
         self::logdeb("Destroying session '{$id}'.");
         self::destroy($id);
     }
 }
Пример #30
0
 function parseDate($fieldname, $datestr, $required = false)
 {
     if (empty($datestr)) {
         if ($required) {
             $msg = sprintf(_m('You must supply a date for "%s".'), $fieldname);
             throw new Exception($msg);
         }
     } else {
         $ts = strtotime($datestr);
         if ($ts === false) {
             throw new Exception(sprintf(_m('Invalid date entered for "%1$s": %2$s.'), $fieldname, $ts));
         }
         return common_sql_date($ts);
     }
     return null;
 }