X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=classes%2FNotice.php;h=7a77d9859af3653d16f0e4f75e7f418e5c9720f3;hb=6255e1dca39310ecf229446ab1a30c1140106e73;hp=1afc6d09772d583df574ec4395f0053e97bc16d5;hpb=95d415257a43d5d4338ce22089463c1820b6bf8b;p=quix0rs-gnu-social.git diff --git a/classes/Notice.php b/classes/Notice.php index 1afc6d0977..7a77d9859a 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -110,6 +110,7 @@ class Notice extends Managed_DataObject 'notice_profile_id_idx' => array('profile_id', 'created', 'id'), 'notice_repeat_of_created_id_idx' => array('repeat_of', 'created', 'id'), 'notice_conversation_created_id_idx' => array('conversation', 'created', 'id'), + 'notice_verb_idx' => array('verb'), 'notice_replyto_idx' => array('reply_to') ) ); @@ -259,6 +260,15 @@ class Notice extends Managed_DataObject public function getRendered() { + if (is_null($this->rendered) || $this->rendered === '') { + // update to include rendered content on-the-fly, so we don't have to have a fix-up script in upgrade.php + common_debug('Rendering notice '.$this->getID().' as it had no rendered HTML content.'); + $orig = clone($this); + $this->rendered = common_render_content($this->getContent(), + $this->getProfile(), + $this->hasParent() ? $this->getParent() : null); + $this->update($orig); + } return $this->rendered; } @@ -272,15 +282,17 @@ class Notice extends Managed_DataObject // The risk is we start having empty urls and non-http uris... // and we can't really handle any other protocol right now. switch (true) { + case $this->isLocal(): + return common_local_url('shownotice', array('notice' => $this->getID()), null, null, false); case common_valid_http_url($this->url): // should we allow non-http/https URLs? return $this->url; - case !$this->isLocal() && common_valid_http_url($this->uri): // Sometimes we only have the URI for remote posts. + case common_valid_http_url($this->uri): // Sometimes we only have the URI for remote posts. return $this->uri; - case $this->isLocal() || $fallback: + case $fallback: // let's generate a valid link to our locally available notice on demand - return common_local_url('shownotice', array('notice' => $this->id), null, null, false); + return common_local_url('shownotice', array('notice' => $this->getID()), null, null, false); default: - common_debug('No URL available for notice: id='.$this->id); + common_debug('No URL available for notice: id='.$this->getID()); throw new InvalidUrlException($this->url); } } @@ -592,21 +604,25 @@ class Notice extends Managed_DataObject $conv = Conversation::getKV('uri', $options['conversation']); if ($conv instanceof Conversation) { common_debug('Conversation stitched together from (probably) a reply to unknown remote user. Activity creation time ('.$notice->created.') should maybe be compared to conversation creation time ('.$conv->created.').'); - $notice->conversation = $conv->id; } else { - // Conversation URI was not found, so we must create it. But we can't create it - // until we have a Notice ID because of the database layout... - // $options['conversation'] will be used later after the $notice->insert(); - common_debug('Conversation URI not found, so we have to create it after inserting this Notice: '.$options['conversation']); + // 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 options to Notice::saveNew: '.$options['conversation']); + // The insert in Conversation::create throws exception on failure + $conv = Conversation::create($options['conversation'], $notice->created); } - } else { - // If we're not using the attached conversation URI let's remove it - // so we don't mistake ourselves later, when creating our own Conversation. - // This implies that the notice knows which conversation it belongs to. - $options['conversation'] = null; + $notice->conversation = $conv->getID(); + unset($conv); } } + // If it's not part of a conversation, it's the beginning of a new conversation. + if (empty($notice->conversation)) { + $conv = Conversation::create(); + $notice->conversation = $conv->getID(); + unset($conv); + } + + $notloc = new Notice_location(); if (!empty($lat) && !empty($lon)) { $notloc->lat = $lat; @@ -662,17 +678,6 @@ class Notice extends Managed_DataObject $notloc->notice_id = $notice->getID(); $notloc->insert(); // store the notice location if it had any information } - - // If it's not part of a conversation, it's - // the beginning of a new conversation. - if (empty($notice->conversation)) { - $orig = clone($notice); - // $act->context->conversation will be null if it was not provided - - $conv = Conversation::create($notice, $options['conversation']); - $notice->conversation = $conv->id; - $notice->update($orig); - } } catch (Exception $e) { // Let's test if we managed initial insert, which would imply // failing on some update-part (check 'insert()'). Delete if @@ -823,19 +828,9 @@ class Notice extends Managed_DataObject $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); - // Reject notice if it is too long (without the HTML) - // FIXME: Reject if too short (empty) too? But we have to pass the - if ($actor->isLocal() && Notice::contentTooLong($stored->content)) { - // TRANS: Client error displayed when the parameter "status" is missing. - // TRANS: %d is the maximum number of character for a notice. - throw new ClientException(sprintf(_m('That\'s too long. Maximum notice size is %d character.', - 'That\'s too long. Maximum notice size is %d characters.', - Notice::maxContent()), - Notice::maxContent())); - } - // Maybe a missing act-time should be fatal if the actor is not local? if (!empty($act->time)) { $stored->created = common_sql_date($act->time); @@ -883,16 +878,24 @@ class Notice extends Managed_DataObject $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.').'); - $stored->conversation = $conv->getID(); } else { - // Conversation URI was not found, so we must create it. But we can't create it - // until we have a Notice ID because of the database layout... - // $options['conversation'] will be used later after the $stored->insert(); - common_debug('Conversation URI from activity context not found, so we have to create it after inserting this Notice: '.$act->context->conversation); + // 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) { @@ -950,15 +953,7 @@ class Notice extends Managed_DataObject throw new ServerException('Unsuccessful call to StoreActivityObject '.$stored->getUri() . ': '.$act->asString()); } - // If it's not part of a conversation, it's the beginning - // of a new one (or belongs to a previously unknown URI). - if (empty($stored->conversation)) { - // $act->context->conversation will be null if it was not provided - common_debug('Creating a new conversation for stored notice ID='.$stored->getID().' with URI: '.$act->context->conversation); - $conv = Conversation::create($stored, $act->context->conversation); - $stored->conversation = $conv->getID(); - } - + // If something changed in the Notice during StoreActivityObject $stored->update($orig); } catch (Exception $e) { if (empty($stored->id)) { @@ -974,23 +969,12 @@ class Notice extends Managed_DataObject throw new ServerException('StartNoticeSave did not give back a Notice'); } - // Save per-notice metadata... - $mentions = array(); - $group_ids = array(); - - // This event lets plugins filter out non-local recipients (attentions we don't care about) - // Used primarily for OStatus (and if we don't federate, all attentions would be local anyway) - Event::handle('GetLocalAttentions', array($actor, $act->context->attention, &$mentions, &$group_ids)); - // Only save 'attention' and metadata stuff (URLs, tags...) stuff if // the activityverb is a POST (since stuff like repeat, favorite etc. // reasonably handle notifications themselves. if (ActivityUtils::compareVerbs($stored->verb, array(ActivityVerb::POST))) { - if (!empty($mentions)) { - $stored->saveKnownReplies($mentions); - } else { - $stored->saveReplies(); - } + + $stored->saveAttentions($act->context->attention); if (!empty($tags)) { $stored->saveKnownTags($tags); @@ -1002,8 +986,6 @@ class Notice extends Managed_DataObject // to avoid errors on duplicates. // Note: groups should always be set. - $stored->saveKnownGroups($group_ids); - if (!empty($urls)) { $stored->saveKnownUrls($urls); } else { @@ -1589,6 +1571,43 @@ class Notice extends Managed_DataObject return true; } + function saveAttentions(array $uris) + { + foreach ($uris as $uri=>$type) { + try { + $target = Profile::fromUri($uri); + } catch (UnknownUriException $e) { + common_log(LOG_WARNING, "Unable to determine profile for URI '$uri'"); + continue; + } + + $this->saveAttention($target); + } + } + + function saveAttention(Profile $target, $reason=null) + { + if ($target->isGroup()) { + // FIXME: Make sure we check that users are in the groups they send to! + } else { + if ($target->hasBlocked($this->getProfile())) { + common_log(LOG_INFO, "Not saving reply to profile {$target->id} ($uri) from sender {$sender->id} because of a block."); + return false; + } + } + + try { + $att = Attention::saveNew($this, $target, $reason); + } catch (AlreadyFulfilledException $e) { + common_debug('Could not save Attention: '.$e->getMessage()); + } catch (Exception $e) { + common_log(LOG_ERR, 'Could not save Attention: '.$e->getMessage()); + } + + self::blow('reply:stream:%d', $target->getID()); + return true; + } + /** * Save reply records indicating that this notice needs to be * delivered to the local users with the given URIs. @@ -1663,9 +1682,6 @@ class Notice extends Managed_DataObject $mentions = common_find_mentions($this->content, $sender, $parent); - // store replied only for first @ (what user/notice what the reply directed, - // we assume first @ is it) - foreach ($mentions as $mention) { foreach ($mention['mentioned'] as $mentioned) { @@ -1676,9 +1692,7 @@ class Notice extends Managed_DataObject } // Don't save replies from blocked profile to local user - - $mentioned_user = User::getKV('id', $mentioned->id); - if ($mentioned_user instanceof User && $mentioned_user->hasBlocked($sender)) { + if ($mentioned->hasBlocked($sender)) { continue; } @@ -1706,6 +1720,23 @@ class Notice extends Managed_DataObject return $reply; } + protected $_attentionids = array(); + + /** + * Pull the complete list of known activity context attentions for this notice. + * + * @return array of integer profile ids (also group profiles) + */ + function getAttentionProfileIDs() + { + if (!isset($this->_attentionids[$this->getID()])) { + $atts = Attention::multiGet('notice_id', array($this->getID())); + // (array)null means empty array + $this->_attentionids[$this->getID()] = (array)$atts->fetchAll('profile_id'); + } + return $this->_attentionids[$this->getID()]; + } + protected $_replies = array(); /** @@ -1732,11 +1763,11 @@ class Notice extends Managed_DataObject * * @return array of Profiles */ - function getReplyProfiles() + function getAttentionProfiles() { - $ids = $this->getReplies(); + $ids = array_unique(array_merge($this->getReplies(), $this->getGroupProfileIDs(), $this->getAttentionProfileIDs())); - $profiles = Profile::multiGet('id', $ids); + $profiles = Profile::multiGet('id', (array)$ids); return $profiles->fetchAll(); } @@ -1770,6 +1801,23 @@ class Notice extends Managed_DataObject } } + /** + * Pull list of Profile IDs of groups this notice addresses. + * + * @return array of Group _profile_ IDs + */ + + function getGroupProfileIDs() + { + $ids = array(); + + foreach ($this->getGroups() as $group) { + $ids[] = $group->profile_id; + } + + return $ids; + } + /** * Pull list of groups this notice needs to be delivered to, * as previously recorded by saveKnownGroups(). @@ -1836,7 +1884,7 @@ class Notice extends Managed_DataObject // The notice is probably a share or similar, which don't // have a representational URL of their own. } - $act->content = common_xml_safe_str($this->rendered); + $act->content = common_xml_safe_str($this->getRendered()); $profile = $this->getProfile(); @@ -2070,7 +2118,7 @@ class Notice extends Managed_DataObject $object->id = $this->getUri(); //FIXME: = $object->title ?: sprintf(... because we might get a title from StartActivityObjectFromNotice $object->title = sprintf('New %1$s by %2$s', ActivityObject::canonicalType($object->type), $this->getProfile()->getNickname()); - $object->content = $this->rendered; + $object->content = $this->getRendered(); $object->link = $this->getUrl(); $object->extra[] = array('status_net', array('notice_id' => $this->id)); @@ -2391,8 +2439,11 @@ class Notice extends Managed_DataObject // If there's a failure, we want to _force_ // distribution at this point. try { + $json = json_encode((object)array('id' => $this->getID(), + 'type' => 'Notice', + )); $qm = QueueManager::get(); - $qm->enqueue($this, 'distrib'); + $qm->enqueue($json, 'distrib'); } catch (Exception $e) { // If the exception isn't transient, this // may throw more exceptions as DQH does