X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;ds=sidebyside;f=classes%2FNotice.php;h=d520f4728f60d98758041aae7fce74a923a497a0;hb=39f8d2c72830d7bc3c08f60d1df38d19c846a74b;hp=de5104fdbba3ff3018e42241d784d75b72aa001a;hpb=76946ea5a3636479d713f64471e86643936bc540;p=quix0rs-gnu-social.git
diff --git a/classes/Notice.php b/classes/Notice.php
index de5104fdbb..d520f4728f 100644
--- a/classes/Notice.php
+++ b/classes/Notice.php
@@ -72,6 +72,7 @@ class Notice extends Memcached_DataObject
public $location_id; // int(4)
public $location_ns; // int(4)
public $repeat_of; // int(4)
+ public $object_type; // varchar(255)
/* Static get */
function staticGet($k,$v=NULL)
@@ -109,6 +110,11 @@ class Notice extends Memcached_DataObject
// @fixme we have some cases where things get re-run and so the
// insert fails.
$deleted = Deleted_notice::staticGet('id', $this->id);
+
+ if (!$deleted) {
+ $deleted = Deleted_notice::staticGet('uri', $this->uri);
+ }
+
if (!$deleted) {
$deleted = new Deleted_notice();
@@ -130,6 +136,7 @@ class Notice extends Memcached_DataObject
$this->clearFaves();
$this->clearTags();
$this->clearGroupInboxes();
+ $this->clearFiles();
// NOTE: we don't clear inboxes
// NOTE: we don't clear queue items
@@ -147,7 +154,7 @@ class Notice extends Memcached_DataObject
function saveTags()
{
/* extract all #hastags */
- $count = preg_match_all('/(?:^|\s)#([\pL\pN_\-\.]{1,64})/', strtolower($this->content), $match);
+ $count = preg_match_all('/(?:^|\s)#([\pL\pN_\-\.]{1,64})/u', strtolower($this->content), $match);
if (!$count) {
return true;
}
@@ -234,6 +241,9 @@ class Notice extends Memcached_DataObject
* in place of extracting # tags from content
* array 'urls' list of attached/referred URLs to save with the
* notice in place of extracting links from content
+ * boolean 'distribute' whether to distribute the notice, default true
+ * string 'object_type' URL of the associated object type (default ActivityObject::NOTE)
+ *
* @fixme tag override
*
* @return Notice
@@ -243,7 +253,8 @@ class Notice extends Memcached_DataObject
$defaults = array('uri' => null,
'url' => null,
'reply_to' => null,
- 'repeat_of' => null);
+ 'repeat_of' => null,
+ 'distribute' => true);
if (!empty($options)) {
$options = $options + $defaults;
@@ -351,6 +362,12 @@ class Notice extends Memcached_DataObject
$notice->rendered = common_render_content($final, $notice);
}
+ if (empty($object_type)) {
+ $notice->object_type = (empty($notice->reply_to)) ? ActivityObject::NOTE : ActivityObject::COMMENT;
+ } else {
+ $notice->object_type = $object_type;
+ }
+
if (Event::handle('StartNoticeSave', array(&$notice))) {
// XXX: some of these functions write to the DB
@@ -426,8 +443,10 @@ class Notice extends Memcached_DataObject
$notice->saveUrls();
}
- // Prepare inbox delivery, may be queued to background.
- $notice->distribute();
+ if ($distribute) {
+ // Prepare inbox delivery, may be queued to background.
+ $notice->distribute();
+ }
return $notice;
}
@@ -435,7 +454,10 @@ class Notice extends Memcached_DataObject
function blowOnInsert($conversation = false)
{
self::blow('profile:notice_ids:%d', $this->profile_id);
- self::blow('public');
+
+ if ($this->isPublic()) {
+ self::blow('public');
+ }
// XXX: Before we were blowing the casche only if the notice id
// was not the root of the conversation. What to do now?
@@ -470,7 +492,10 @@ class Notice extends Memcached_DataObject
$this->blowOnInsert();
self::blow('profile:notice_ids:%d;last', $this->profile_id);
- self::blow('public;last');
+
+ if ($this->isPublic()) {
+ self::blow('public;last');
+ }
}
/** save all urls in the notice to the db
@@ -599,7 +624,7 @@ class Notice extends Memcached_DataObject
function getStreamByIds($ids)
{
- $cache = common_memcache();
+ $cache = Cache::instance();
if (!empty($cache)) {
$notices = array();
@@ -654,7 +679,7 @@ class Notice extends Memcached_DataObject
$notice->selectAdd(); // clears it
$notice->selectAdd('id');
- $notice->orderBy('id DESC');
+ $notice->orderBy('created DESC, id DESC');
if (!is_null($offset)) {
$notice->limit($offset, $limit);
@@ -668,13 +693,8 @@ class Notice extends Memcached_DataObject
$notice->whereAdd('is_local !='. Notice::GATEWAY);
}
- if ($since_id != 0) {
- $notice->whereAdd('id > ' . $since_id);
- }
-
- if ($max_id != 0) {
- $notice->whereAdd('id <= ' . $max_id);
- }
+ Notice::addWhereSinceId($notice, $since_id);
+ Notice::addWhereMaxId($notice, $max_id);
$ids = array();
@@ -709,19 +729,14 @@ class Notice extends Memcached_DataObject
$notice->conversation = $id;
- $notice->orderBy('id DESC');
+ $notice->orderBy('created DESC, id DESC');
if (!is_null($offset)) {
$notice->limit($offset, $limit);
}
- if ($since_id != 0) {
- $notice->whereAdd('id > ' . $since_id);
- }
-
- if ($max_id != 0) {
- $notice->whereAdd('id <= ' . $max_id);
- }
+ Notice::addWhereSinceId($notice, $since_id);
+ Notice::addWhereMaxId($notice, $max_id);
$ids = array();
@@ -775,7 +790,7 @@ class Notice extends Memcached_DataObject
$c = self::memcache();
if (!empty($c)) {
- $ni = $c->get(common_cache_key('notice:who_gets:'.$this->id));
+ $ni = $c->get(Cache::key('notice:who_gets:'.$this->id));
if ($ni !== false) {
return $ni;
}
@@ -818,16 +833,25 @@ class Notice extends Memcached_DataObject
// Exclude any deleted, non-local, or blocking recipients.
$profile = $this->getProfile();
+ $originalProfile = null;
+ if ($this->repeat_of) {
+ // Check blocks against the original notice's poster as well.
+ $original = Notice::staticGet('id', $this->repeat_of);
+ if ($original) {
+ $originalProfile = $original->getProfile();
+ }
+ }
foreach ($ni as $id => $source) {
$user = User::staticGet('id', $id);
- if (empty($user) || $user->hasBlocked($profile)) {
+ if (empty($user) || $user->hasBlocked($profile) ||
+ ($originalProfile && $user->hasBlocked($originalProfile))) {
unset($ni[$id]);
}
}
if (!empty($c)) {
// XXX: pack this data better
- $c->set(common_cache_key('notice:who_gets:'.$this->id), $ni);
+ $c->set(Cache::key('notice:who_gets:'.$this->id), $ni);
}
return $ni;
@@ -948,7 +972,7 @@ class Notice extends Memcached_DataObject
$groups = array();
/* extract all !group */
- $count = preg_match_all('/(?:^|\s)!([A-Za-z0-9]{1,64})/',
+ $count = preg_match_all('/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/',
strtolower($this->content),
$match);
if (!$count) {
@@ -1056,6 +1080,7 @@ class Notice extends Memcached_DataObject
$reply->notice_id = $this->id;
$reply->profile_id = $profile->id;
+ $reply->modified = $this->created;
common_log(LOG_INFO, __METHOD__ . ": saving reply: notice $this->id to profile $profile->id");
@@ -1116,6 +1141,7 @@ class Notice extends Memcached_DataObject
$reply->notice_id = $this->id;
$reply->profile_id = $mentioned->id;
+ $reply->modified = $this->created;
$id = $reply->insert();
@@ -1230,27 +1256,33 @@ class Notice extends Memcached_DataObject
* Convert a notice into an activity for export.
*
* @param User $cur Current user
- *
+ *
* @return Activity activity object representing this Notice.
*/
- function asActivity($cur = null, $source = false)
+ function asActivity($cur)
{
+ $act = self::cacheGet(Cache::codeKey('notice:as-activity:'.$this->id));
+
+ if (!empty($act)) {
+ return $act;
+ }
$act = new Activity();
-
+
if (Event::handle('StartNoticeAsActivity', array($this, &$act))) {
-
+
$profile = $this->getProfile();
-
- $act->actor = ActivityObject::fromProfile($profile);
- $act->verb = ActivityVerb::POST;
- $act->objects[] = ActivityObject::fromNotice($this);
+
+ $act->actor = ActivityObject::fromProfile($profile);
+ $act->actor->extra[] = $profile->profileInfo($cur);
+ $act->verb = ActivityVerb::POST;
+ $act->objects[] = ActivityObject::fromNotice($this);
// XXX: should this be handled by default processing for object entry?
$act->time = strtotime($this->created);
$act->link = $this->bestUrl();
-
+
$act->content = common_xml_safe_str($this->rendered);
$act->id = $this->uri;
$act->title = common_xml_safe_str($this->content);
@@ -1277,9 +1309,9 @@ class Notice extends Memcached_DataObject
$act->enclosures[] = $enclosure;
}
}
-
+
$ctx = new ActivityContext();
-
+
if (!empty($this->reply_to)) {
$reply = Notice::staticGet('id', $this->reply_to);
if (!empty($reply)) {
@@ -1287,31 +1319,31 @@ class Notice extends Memcached_DataObject
$ctx->replyToUrl = $reply->bestUrl();
}
}
-
+
$ctx->location = $this->getLocation();
-
+
$conv = null;
-
+
if (!empty($this->conversation)) {
$conv = Conversation::staticGet('id', $this->conversation);
if (!empty($conv)) {
$ctx->conversation = $conv->uri;
}
}
-
+
$reply_ids = $this->getReplies();
-
+
foreach ($reply_ids as $id) {
$profile = Profile::staticGet('id', $id);
if (!empty($profile)) {
$ctx->attention[] = $profile->getUri();
}
}
-
+
$groups = $this->getGroups();
-
+
foreach ($groups as $group) {
- $ctx->attention[] = $group->uri;
+ $ctx->attention[] = $group->getUri();
}
// XXX: deprecated; use ActivityVerb::SHARE instead
@@ -1323,71 +1355,40 @@ class Notice extends Memcached_DataObject
$ctx->forwardID = $repeat->uri;
$ctx->forwardUrl = $repeat->bestUrl();
}
-
- $act->context = $ctx;
-
- $noticeInfoAttr = array('local_id' => $this->id); // local notice ID (useful to clients for ordering)
- $ns = $this->getSource();
-
- if (!empty($ns)) {
- $noticeInfoAttr['source'] = $ns->code;
- if (!empty($ns->url)) {
- $noticeInfoAttr['source_link'] = $ns->url;
- if (!empty($ns->name)) {
- $noticeInfoAttr['source'] = ''
- . htmlspecialchars($ns->name)
- . '';
- }
- }
- }
+ $act->context = $ctx;
- if (!empty($cur)) {
- $noticeInfoAttr['favorite'] = ($cur->hasFave($this)) ? "true" : "false";
- $cp = $cur->getProfile();
- $noticeInfoAttr['repeated'] = ($cp->hasRepeated($this->id)) ? "true" : "false";
- }
+ // Source
- if (!empty($this->repeat_of)) {
- $noticeInfoAttr['repeat_of'] = $this->repeat_of;
- }
+ $atom_feed = $profile->getAtomFeed();
- $act->extra[] = array('statusnet:notice_info', $noticeInfoAttr, null);
+ if (!empty($atom_feed)) {
- if ($source) {
-
- $atom_feed = $profile->getAtomFeed();
+ $act->source = new ActivitySource();
- if (!empty($atom_feed)) {
+ // XXX: we should store the actual feed ID
- $act->source = new ActivitySource();
-
- // XXX: we should store the actual feed ID
+ $act->source->id = $atom_feed;
- $act->source->id = $atom_feed;
+ // XXX: we should store the actual feed title
- // XXX: we should store the actual feed title
+ $act->source->title = $profile->getBestName();
- $act->source->title = $profile->getBestName();
+ $act->source->links['alternate'] = $profile->profileurl;
+ $act->source->links['self'] = $atom_feed;
- $act->source->links['alternate'] = $profile->profileurl;
- $act->source->links['self'] = $atom_feed;
+ $act->source->icon = $profile->avatarUrl(AVATAR_PROFILE_SIZE);
- $act->source->icon = $profile->avatarUrl(AVATAR_PROFILE_SIZE);
-
- $notice = $profile->getCurrentNotice();
+ $notice = $profile->getCurrentNotice();
- if (!empty($notice)) {
- $act->source->updated = self::utcDate($notice->created);
- }
+ if (!empty($notice)) {
+ $act->source->updated = self::utcDate($notice->created);
+ }
- $user = User::staticGet('id', $profile->id);
+ $user = User::staticGet('id', $profile->id);
- if (!empty($user)) {
- $act->source->links['license'] = common_config('license', 'url');
- }
+ if (!empty($user)) {
+ $act->source->links['license'] = common_config('license', 'url');
}
}
@@ -1399,19 +1400,74 @@ class Notice extends Memcached_DataObject
Event::handle('EndNoticeAsActivity', array($this, &$act));
}
-
+
+ self::cacheSet(Cache::codeKey('notice:as-activity:'.$this->id), $act);
+
return $act;
}
// This has gotten way too long. Needs to be sliced up into functional bits
// or ideally exported to a utility class.
- function asAtomEntry($namespace=false, $source=false, $author=true, $cur=null)
+ function asAtomEntry($namespace=false,
+ $source=false,
+ $author=true,
+ $cur=null)
{
- $act = $this->asActivity($cur, $source);
- return $act->asString($namespace, $author);
+ $act = $this->asActivity($cur);
+ $act->extra[] = $this->noticeInfo($cur);
+ return $act->asString($namespace, $author, $source);
+ }
+
+ /**
+ * Extra notice info for atom entries
+ *
+ * Clients use some extra notice info in the atom stream.
+ * This gives it to them.
+ *
+ * @param User $cur Current user
+ *
+ * @return array representation of element
+ */
+
+ function noticeInfo($cur)
+ {
+ // local notice ID (useful to clients for ordering)
+
+ $noticeInfoAttr = array('local_id' => $this->id);
+
+ // notice source
+
+ $ns = $this->getSource();
+
+ if (!empty($ns)) {
+ $noticeInfoAttr['source'] = $ns->code;
+ if (!empty($ns->url)) {
+ $noticeInfoAttr['source_link'] = $ns->url;
+ if (!empty($ns->name)) {
+ $noticeInfoAttr['source'] = ''
+ . htmlspecialchars($ns->name)
+ . '';
+ }
+ }
+ }
+
+ // favorite and repeated
+
+ if (!empty($cur)) {
+ $noticeInfoAttr['favorite'] = ($cur->hasFave($this)) ? "true" : "false";
+ $cp = $cur->getProfile();
+ $noticeInfoAttr['repeated'] = ($cp->hasRepeated($this->id)) ? "true" : "false";
+ }
+
+ if (!empty($this->repeat_of)) {
+ $noticeInfoAttr['repeat_of'] = $this->repeat_of;
+ }
+
+ return array('statusnet:notice_info', $noticeInfoAttr, null);
}
-
/**
* Returns an XML string fragment with a reference to a notice as an
@@ -1443,7 +1499,7 @@ class Notice extends Memcached_DataObject
function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0)
{
- $cache = common_memcache();
+ $cache = Cache::instance();
if (empty($cache) ||
$since_id != 0 || $max_id != 0 ||
@@ -1453,7 +1509,7 @@ class Notice extends Memcached_DataObject
$max_id)));
}
- $idkey = common_cache_key($cachekey);
+ $idkey = Cache::key($cachekey);
$idstr = $cache->get($idkey);
@@ -1635,17 +1691,17 @@ class Notice extends Memcached_DataObject
function repeatStream($limit=100)
{
- $cache = common_memcache();
+ $cache = Cache::instance();
if (empty($cache)) {
$ids = $this->_repeatStreamDirect($limit);
} else {
- $idstr = $cache->get(common_cache_key('notice:repeats:'.$this->id));
+ $idstr = $cache->get(Cache::key('notice:repeats:'.$this->id));
if ($idstr !== false) {
$ids = explode(',', $idstr);
} else {
$ids = $this->_repeatStreamDirect(100);
- $cache->set(common_cache_key('notice:repeats:'.$this->id), implode(',', $ids));
+ $cache->set(Cache::key('notice:repeats:'.$this->id), implode(',', $ids));
}
if ($limit < 100) {
// We do a max of 100, so slice down to limit
@@ -1665,10 +1721,10 @@ class Notice extends Memcached_DataObject
$notice->repeat_of = $this->id;
- $notice->orderBy('created'); // NB: asc!
+ $notice->orderBy('created, id'); // NB: asc!
- if (!is_null($offset)) {
- $notice->limit($offset, $limit);
+ if (!is_null($limit)) {
+ $notice->limit(0, $limit);
}
$ids = array();
@@ -1755,6 +1811,21 @@ class Notice extends Memcached_DataObject
$reply->free();
}
+ function clearFiles()
+ {
+ $f2p = new File_to_post();
+
+ $f2p->post_id = $this->id;
+
+ if ($f2p->find()) {
+ while ($f2p->fetch()) {
+ $f2p->delete();
+ }
+ }
+ // FIXME: decide whether to delete File objects
+ // ...and related (actual) files
+ }
+
function clearRepeats()
{
$repeatNotice = new Notice();
@@ -1796,10 +1867,10 @@ class Notice extends Memcached_DataObject
if ($tag->find()) {
while ($tag->fetch()) {
- self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, common_keyize($tag->tag));
- self::blow('profile:notice_ids_tagged:%d:%s;last', $this->profile_id, common_keyize($tag->tag));
- self::blow('notice_tag:notice_ids:%s', common_keyize($tag->tag));
- self::blow('notice_tag:notice_ids:%s;last', common_keyize($tag->tag));
+ self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, Cache::keyize($tag->tag));
+ self::blow('profile:notice_ids_tagged:%d:%s;last', $this->profile_id, Cache::keyize($tag->tag));
+ self::blow('notice_tag:notice_ids:%s', Cache::keyize($tag->tag));
+ self::blow('notice_tag:notice_ids:%s;last', Cache::keyize($tag->tag));
$tag->delete();
}
}
@@ -1948,4 +2019,118 @@ class Notice extends Memcached_DataObject
$d = new DateTime($dateStr, new DateTimeZone('UTC'));
return $d->format(DATE_W3C);
}
+
+ /**
+ * Look up the creation timestamp for a given notice ID, even
+ * if it's been deleted.
+ *
+ * @param int $id
+ * @return mixed string recorded creation timestamp, or false if can't be found
+ */
+ public static function getAsTimestamp($id)
+ {
+ if (!$id) {
+ return false;
+ }
+
+ $notice = Notice::staticGet('id', $id);
+ if ($notice) {
+ return $notice->created;
+ }
+
+ $deleted = Deleted_notice::staticGet('id', $id);
+ if ($deleted) {
+ return $deleted->created;
+ }
+
+ return false;
+ }
+
+ /**
+ * Build an SQL 'where' fragment for timestamp-based sorting from a since_id
+ * parameter, matching notices posted after the given one (exclusive).
+ *
+ * If the referenced notice can't be found, will return false.
+ *
+ * @param int $id
+ * @param string $idField
+ * @param string $createdField
+ * @return mixed string or false if no match
+ */
+ public static function whereSinceId($id, $idField='id', $createdField='created')
+ {
+ $since = Notice::getAsTimestamp($id);
+ if ($since) {
+ return sprintf("($createdField = '%s' and $idField > %d) or ($createdField > '%s')", $since, $id, $since);
+ }
+ return false;
+ }
+
+ /**
+ * Build an SQL 'where' fragment for timestamp-based sorting from a since_id
+ * parameter, matching notices posted after the given one (exclusive), and
+ * if necessary add it to the data object's query.
+ *
+ * @param DB_DataObject $obj
+ * @param int $id
+ * @param string $idField
+ * @param string $createdField
+ * @return mixed string or false if no match
+ */
+ public static function addWhereSinceId(DB_DataObject $obj, $id, $idField='id', $createdField='created')
+ {
+ $since = self::whereSinceId($id, $idField, $createdField);
+ if ($since) {
+ $obj->whereAdd($since);
+ }
+ }
+
+ /**
+ * Build an SQL 'where' fragment for timestamp-based sorting from a max_id
+ * parameter, matching notices posted before the given one (inclusive).
+ *
+ * If the referenced notice can't be found, will return false.
+ *
+ * @param int $id
+ * @param string $idField
+ * @param string $createdField
+ * @return mixed string or false if no match
+ */
+ public static function whereMaxId($id, $idField='id', $createdField='created')
+ {
+ $max = Notice::getAsTimestamp($id);
+ if ($max) {
+ return sprintf("($createdField < '%s') or ($createdField = '%s' and $idField <= %d)", $max, $max, $id);
+ }
+ return false;
+ }
+
+ /**
+ * Build an SQL 'where' fragment for timestamp-based sorting from a max_id
+ * parameter, matching notices posted before the given one (inclusive), and
+ * if necessary add it to the data object's query.
+ *
+ * @param DB_DataObject $obj
+ * @param int $id
+ * @param string $idField
+ * @param string $createdField
+ * @return mixed string or false if no match
+ */
+ public static function addWhereMaxId(DB_DataObject $obj, $id, $idField='id', $createdField='created')
+ {
+ $max = self::whereMaxId($id, $idField, $createdField);
+ if ($max) {
+ $obj->whereAdd($max);
+ }
+ }
+
+ function isPublic()
+ {
+ if (common_config('public', 'localonly')) {
+ return ($this->is_local == Notice::LOCAL_PUBLIC);
+ } else {
+ return (($this->is_local != Notice::LOCAL_NONPUBLIC) &&
+ ($this->is_local != Notice::GATEWAY));
+ }
+ }
}