X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=classes%2FNotice.php;h=b5dd1fd79b73603025676a0698e2098d9c418afa;hb=0bdbb32f3bbca58d94f3bb928b4e393c0eff781c;hp=f9d80c1289f1463c651e4f08fed3b998088a0973;hpb=fe6498e7c875f5b386a9c7d2cc1fba5677daae09;p=quix0rs-gnu-social.git diff --git a/classes/Notice.php b/classes/Notice.php index f9d80c1289..b5dd1fd79b 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -65,10 +65,6 @@ class Notice extends Managed_DataObject public $is_local; // int(4) public $source; // varchar(32) public $conversation; // int(4) - public $lat; // decimal(10,7) - public $lon; // decimal(10,7) - public $location_id; // int(4) - public $location_ns; // int(4) public $repeat_of; // int(4) public $verb; // varchar(191) not 255 because utf8mb4 takes more space public $object_type; // varchar(191) not 255 because utf8mb4 takes more space @@ -93,10 +89,6 @@ class Notice extends Managed_DataObject 'is_local' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'notice was generated by a user'), 'source' => array('type' => 'varchar', 'length' => 32, 'description' => 'source of comment, like "web", "im", or "clientname"'), 'conversation' => array('type' => 'int', 'description' => 'id of root notice in this conversation'), - 'lat' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'), - 'lon' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'), - 'location_id' => array('type' => 'int', 'description' => 'location id if possible'), - 'location_ns' => array('type' => 'int', 'description' => 'namespace for location'), 'repeat_of' => array('type' => 'int', 'description' => 'notice this is a repeat of'), 'object_type' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams object type', 'default' => 'http://activitystrea.ms/schema/1.0/note'), 'verb' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams verb', 'default' => 'http://activitystrea.ms/schema/1.0/post'), @@ -166,45 +158,36 @@ class Notice extends Managed_DataObject $this->_profile[$this->profile_id] = $profile; } - function delete($useWhere=false) + public function deleteAs(Profile $actor, $delete_event=true) { - // For auditing purposes, save a record that the notice - // was deleted. - - // @fixme we have some cases where things get re-run and so the - // insert fails. - $deleted = Deleted_notice::getKV('id', $this->id); - - if (!$deleted instanceof Deleted_notice) { - $deleted = Deleted_notice::getKV('uri', $this->uri); - } - - if (!$deleted instanceof Deleted_notice) { - $deleted = new Deleted_notice(); - - $deleted->id = $this->id; - $deleted->profile_id = $this->profile_id; - $deleted->uri = $this->uri; - $deleted->created = $this->created; - $deleted->deleted = common_sql_now(); - - $deleted->insert(); + if (!$this->getProfile()->sameAs($actor) && !$actor->hasRight(Right::DELETEOTHERSNOTICE)) { + throw new AuthorizationException(_('You are not allowed to delete another user\'s notice.')); } if (Event::handle('NoticeDeleteRelated', array($this))) { - // Clear related records - $this->clearReplies(); + $this->clearLocation(); $this->clearRepeats(); $this->clearTags(); $this->clearGroupInboxes(); $this->clearFiles(); $this->clearAttentions(); - // NOTE: we don't clear queue items } + $result = null; + if (!$delete_event || Event::handle('DeleteNoticeAsProfile', array($this, $actor, &$result))) { + // If $delete_event is true, we run the event. If the Event then + // returns false it is assumed everything was handled properly + // and the notice was deleted. + $result = $this->delete(); + } + return $result; + } + + public function delete($useWhere=false) + { $result = parent::delete($useWhere); $this->blowOnDelete(); @@ -297,7 +280,7 @@ class Notice extends Managed_DataObject } } - public function get_object_type($canonical=false) { + public function getObjectType($canonical=false) { return $canonical ? ActivityObject::canonicalType($this->object_type) : $this->object_type; @@ -313,16 +296,6 @@ class Notice extends Managed_DataObject return $notice; } - public static function getById($id) - { - $notice = new Notice(); - $notice->id = $id; - if (!$notice->find(true)) { - throw new NoResultException($notice); - } - return $notice; - } - /** * Extract #hashtags from this notice's content and save them to the database. */ @@ -533,18 +506,13 @@ class Notice extends Managed_DataObject // Handle repeat case - if (isset($repeat_of)) { + if (!empty($options['repeat_of'])) { // Check for a private one - $repeat = Notice::getKV('id', $repeat_of); - - if (!($repeat instanceof Notice)) { - // TRANS: Client exception thrown in notice when trying to repeat a missing or deleted notice. - throw new ClientException(_('Cannot repeat; original notice is missing or deleted.')); - } + $repeat = Notice::getByID($options['repeat_of']); - if ($profile->id == $repeat->profile_id) { + if ($profile->sameAs($repeat->getProfile())) { // TRANS: Client error displayed when trying to repeat an own notice. throw new ClientException(_('You cannot repeat your own notice.')); } @@ -620,12 +588,13 @@ class Notice extends Managed_DataObject if (empty($notice->conversation) and !empty($options['conversation'])) { $conv = Conversation::getKV('uri', $options['conversation']); if ($conv instanceof Conversation) { - common_debug('Conversation stitched together from (probably) reply to unknown remote user. Activity creation time ('.$notice->created.') should maybe be compared to conversation creation time ('.$conv->created.').'); + 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... - $notice->tmp_conv_uri = $options['conversation']; + // $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']); } } else { // If we're not using the attached conversation URI let's remove it @@ -635,14 +604,15 @@ class Notice extends Managed_DataObject } } + $notloc = new Notice_location(); if (!empty($lat) && !empty($lon)) { - $notice->lat = $lat; - $notice->lon = $lon; + $notloc->lat = $lat; + $notloc->lon = $lon; } if (!empty($location_ns) && !empty($location_id)) { - $notice->location_id = $location_id; - $notice->location_ns = $location_ns; + $notloc->location_id = $location_id; + $notloc->location_ns = $location_ns; } if (!empty($rendered)) { @@ -681,12 +651,19 @@ class Notice extends Managed_DataObject // XXX: some of these functions write to the DB try { - $notice->insert(); // throws exception on failure + $notice->insert(); // throws exception on failure, if successful we have an ->id + + if (($notloc->lat && $notloc->lon) || ($notloc->location_id && $notloc->location_ns)) { + $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); @@ -821,6 +798,8 @@ class Notice extends Managed_DataObject if (!$actor->hasRight(Right::PUBLICNOTICE) || ($source && $autosource && in_array($source, $autosource))) { $stored->is_local = Notice::LOCAL_NONPUBLIC; + } else { + $stored->is_local = $is_local; } // Maybe a missing act-time should be fatal if the actor is not local? @@ -863,17 +842,28 @@ class Notice extends Managed_DataObject 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.').'); + $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); + } + } } + $notloc = null; if ($act->context instanceof ActivityContext) { - $location = $act->context->location; - if ($location) { - $stored->lat = $location->lat; - $stored->lon = $location->lon; - if ($location->location_id) { - $stored->location_ns = $location->location_ns; - $stored->location_id = $location->location_id; - } + if ($act->context->location instanceof Location) { + $notloc = Notice_location::fromLocation($act->context->location); } } else { $act->context = new ActivityContext(); @@ -900,6 +890,12 @@ class Notice extends Managed_DataObject try { $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; @@ -908,12 +904,13 @@ class Notice extends Managed_DataObject throw new ServerException('Unsuccessful call to StoreActivityObject '.$stored->uri . ': '.$act->asString()); } - // If it's not part of a conversation, it's - // the beginning of a new conversation. + // 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->id; + $stored->conversation = $conv->getID(); } $stored->update($orig); @@ -989,12 +986,14 @@ class Notice extends Managed_DataObject // Force the scope for private groups foreach ($groups as $group_id) { - $group = User_group::staticGet('id', $group_id); - if ($group instanceof User_group) { + try { + $group = User_group::getByID($group_id); if ($group->force_scope) { $scope |= Notice::GROUP_SCOPE; break; } + } catch (Exception $e) { + common_log(LOG_ERR, 'Notice figureOutScope threw exception: '.$e->getMessage()); } } @@ -1211,17 +1210,15 @@ class Notice extends Managed_DataObject $this->_attachments[$this->id] = $attachments; } - function publicStream($offset=0, $limit=20, $since_id=0, $max_id=0) + static function publicStream($offset=0, $limit=20, $since_id=null, $max_id=null) { $stream = new PublicNoticeStream(); return $stream->getNotices($offset, $limit, $since_id, $max_id); } - - function conversationStream($id, $offset=0, $limit=20, $since_id=0, $max_id=0) + static function conversationStream($id, $offset=0, $limit=20, $since_id=null, $max_id=null) { $stream = new ConversationNoticeStream($id); - return $stream->getNotices($offset, $limit, $since_id, $max_id); } @@ -1233,18 +1230,17 @@ class Notice extends Managed_DataObject */ function hasConversation() { - if (!empty($this->conversation)) { - $conversation = Notice::conversationStream( - $this->conversation, - 1, - 1 - ); - - if ($conversation->N > 0) { - return true; - } + if (empty($this->conversation)) { + // this notice is not part of a conversation apparently + // FIXME: all notices should have a conversation value, right? + return false; } - return false; + + $stream = new ConversationNoticeStream($this->conversation); + $notice = $stream->getNotices(/*offset*/ 1, /*limit*/ 1); + + // if our "offset 1, limit 1" query got a result, return true else false + return $notice->N > 0; } /** @@ -1279,8 +1275,7 @@ class Notice extends Managed_DataObject $root = new Notice; $root->conversation = $this->conversation; $root->orderBy('notice.created ASC'); - $root->find(); - $root->fetch(); + $root->find(true); // true means "fetch first result" $root->free(); return $root; } @@ -1309,6 +1304,10 @@ class Notice extends Managed_DataObject } } catch (NoParentNoticeException $e) { // Latest notice has no parent + } catch (NoResultException $e) { + // Notice was not found, so we can't go further up in the tree. + // FIXME: Maybe we should do this in a more stable way where deleted + // notices won't break conversation chains? } // No parent, or parent out of scope $root = $last; @@ -1615,6 +1614,8 @@ class Notice extends Managed_DataObject self::blow('reply:stream:%d', $parentauthor->id); } catch (NoParentNoticeException $e) { // Not a reply, since it has no parent! + } catch (NoResultException $e) { + // Parent notice was probably deleted } // @todo ideally this parser information would only @@ -1668,32 +1669,22 @@ class Notice extends Managed_DataObject protected $_replies = array(); /** - * Pull the complete list of @-reply targets for this notice. + * Pull the complete list of @-mentioned profile IDs for this notice. * * @return array of integer profile ids */ function getReplies() { - if (isset($this->_replies[$this->id])) { - return $this->_replies[$this->id]; - } - - $replyMap = Reply::listGet('notice_id', array($this->id)); - - $ids = array(); - - foreach ($replyMap[$this->id] as $reply) { - $ids[] = $reply->profile_id; + if (!isset($this->_replies[$this->getID()])) { + $mentions = Reply::multiGet('notice_id', array($this->getID())); + $this->_replies[$this->getID()] = $mentions->fetchAll('profile_id'); } - - $this->_replies[$this->id] = $ids; - - return $ids; + return $this->_replies[$this->getID()]; } function _setReplies($replies) { - $this->_replies[$this->id] = $replies; + $this->_replies[$this->getID()] = $replies; } /** @@ -1850,9 +1841,15 @@ class Notice extends Managed_DataObject $ctx->replyToUrl = $reply->getUrl(true); // true for fallback to local URL, less messy } catch (NoParentNoticeException $e) { // This is not a reply to something + } catch (NoResultException $e) { + // Parent notice was probably deleted } - $ctx->location = $this->getLocation(); + try { + $ctx->location = Notice_location::locFromStored($this); + } catch (ServerException $e) { + $ctx->location = null; + } $conv = null; @@ -2098,23 +2095,6 @@ class Notice extends Managed_DataObject return ($contentlimit > 0 && !empty($content) && (mb_strlen($content) > $contentlimit)); } - function getLocation() - { - $location = null; - - if (!empty($this->location_id) && !empty($this->location_ns)) { - $location = Location::fromId($this->location_id, $this->location_ns); - } - - if (is_null($location)) { // no ID, or Location::fromId() failed - if (!empty($this->lat) && !empty($this->lon)) { - $location = Location::fromLatLon($this->lat, $this->lon); - } - } - - return $location; - } - /** * Convenience function for posting a repeat of an existing message. * @@ -2201,7 +2181,7 @@ class Notice extends Managed_DataObject return $notice->fetchAll('id'); } - function locationOptions($lat, $lon, $location_id, $location_ns, $profile = null) + static function locationOptions($lat, $lon, $location_id, $location_ns, $profile = null) { $options = array(); @@ -2285,6 +2265,16 @@ class Notice extends Managed_DataObject $reply->free(); } + function clearLocation() + { + $loc = new Notice_location(); + $loc->notice_id = $this->id; + + if ($loc->find()) { + $loc->delete(); + } + } + function clearFiles() { $f2p = new File_to_post(); @@ -2409,7 +2399,7 @@ class Notice extends Managed_DataObject $this->uri = sprintf('%s%s=%d:%s=%s', TagURI::mint(), 'noticeId', $this->id, - 'objectType', $this->get_object_type(true)); + 'objectType', $this->getObjectType(true)); $changed = true; } @@ -2761,7 +2751,7 @@ class Notice extends Managed_DataObject if (empty($this->reply_to)) { throw new NoParentNoticeException($this); } - return self::getById($this->reply_to); + return self::getByID($this->reply_to); } /** @@ -2893,4 +2883,51 @@ class Notice extends Managed_DataObject $notice->_setReplies($ids); } } + + static public function beforeSchemaUpdate() + { + $table = strtolower(get_called_class()); + $schema = Schema::get(); + $schemadef = $schema->getTableDef($table); + + // 2015-09-04 We move Notice location data to Notice_location + // First we see if we have to do this at all + if (!isset($schemadef['fields']['lat']) + && !isset($schemadef['fields']['lon']) + && !isset($schemadef['fields']['location_id']) + && !isset($schemadef['fields']['location_ns'])) { + // We have already removed the location fields, so no need to migrate. + return; + } + // Then we make sure the Notice_location table is created! + $schema->ensureTable('notice_location', Notice_location::schemaDef()); + + // Then we continue on our road to migration! + echo "\nFound old $table table, moving location data to 'notice_location' table... (this will probably take a LONG time, but can be aborted and continued)"; + + $notice = new Notice(); + $notice->query(sprintf('SELECT id, lat, lon, location_id, location_ns FROM %1$s ' . + 'WHERE lat IS NOT NULL ' . + 'OR lon IS NOT NULL ' . + 'OR location_id IS NOT NULL ' . + 'OR location_ns IS NOT NULL', + $schema->quoteIdentifier($table))); + print "\nFound {$notice->N} notices with location data, inserting"; + while ($notice->fetch()) { + $notloc = Notice_location::getKV('notice_id', $notice->id); + if ($notloc instanceof Notice_location) { + print "-"; + continue; + } + $notloc = new Notice_location(); + $notloc->notice_id = $notice->id; + $notloc->lat= $notice->lat; + $notloc->lon= $notice->lon; + $notloc->location_id= $notice->location_id; + $notloc->location_ns= $notice->location_ns; + $notloc->insert(); + print "."; + } + print "\n"; + } }