X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;ds=sidebyside;f=classes%2FNotice.php;h=b70dd97dc36f7bf8b05521607e9addc899496f17;hb=8ea83aac48545e2cdf81b4787b14b86fe819aa19;hp=b387b627bf82bf75dc3bd201751081e172373ec9;hpb=ede6dcd4ac4cd6b39e2e4ebe8dcc4e4ec693742c;p=quix0rs-gnu-social.git diff --git a/classes/Notice.php b/classes/Notice.php index b387b627bf..b70dd97dc3 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -248,10 +248,10 @@ class Notice extends Managed_DataObject return common_local_url('shownotice', array('notice' => $this->id), null, null, false); } - public function getTitle() + public function getTitle($imply=true) { $title = null; - if (Event::handle('GetNoticeTitle', array($this, &$title))) { + if (Event::handle('GetNoticeTitle', array($this, &$title)) && $imply) { // TRANS: Title of a notice posted without a title value. // TRANS: %1$s is a user name, %2$s is the notice creation date/time. $title = sprintf(_('%1$s\'s status on %2$s'), @@ -320,6 +320,21 @@ class Notice extends Managed_DataObject } } + public function getSelfLink() + { + if ($this->isLocal()) { + return common_local_url('ApiStatusesShow', array('id' => $this->getID(), 'format' => 'atom')); + } + + $selfLink = $this->getPref('ostatus', 'self'); + + if (!common_valid_http_url($selfLink)) { + throw new InvalidUrlException($selfLink); + } + + return $selfLink; + } + public function getObjectType($canonical=false) { if (is_null($this->object_type) || $this->object_type==='') { throw new NoObjectTypeException($this); @@ -336,16 +351,6 @@ class Notice extends Managed_DataObject } } - public static function getByUri($uri) - { - $notice = new Notice(); - $notice->uri = $uri; - if (!$notice->find(true)) { - throw new NoResultException($notice); - } - return $notice; - } - /** * Extract #hashtags from this notice's content and save them to the database. */ @@ -452,6 +457,7 @@ class Notice extends Managed_DataObject static function saveNew($profile_id, $content, $source, array $options=null) { $defaults = array('uri' => null, 'url' => null, + 'self' => null, 'conversation' => null, // URI of conversation 'reply_to' => null, // This will override convo URI if the parent is known 'repeat_of' => null, // This will override convo URI if the repeated notice is known @@ -634,8 +640,13 @@ class Notice extends Managed_DataObject } else { // Conversation entry with specified URI was not found, so we must create it. common_debug('Conversation URI not found, so we will create it with the URI given in the options to Notice::saveNew: '.$options['conversation']); + $convctx = new ActivityContext(); + $convctx->conversation = $options['conversation']; + if (array_key_exists('conversation_url', $options)) { + $convctx->conversation_url = $options['conversation_url']; + } // The insert in Conversation::create throws exception on failure - $conv = Conversation::create($options['conversation'], $notice->created); + $conv = Conversation::create($convctx, $notice->created); } $notice->conversation = $conv->getID(); unset($conv); @@ -716,6 +727,10 @@ class Notice extends Managed_DataObject } } + if ($self && common_valid_http_url($self)) { + $notice->setPref('ostatus', 'self', $self); + } + // 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. @@ -775,6 +790,9 @@ class Notice extends Managed_DataObject // implied object $options['uri'] = $act->id; $options['url'] = $act->link; + if ($act->selfLink) { + $options['self'] = $act->selfLink; + } } else { $actobj = count($act->objects)===1 ? $act->objects[0] : null; if (!is_null($actobj) && !empty($actobj->id)) { @@ -785,6 +803,9 @@ class Notice extends Managed_DataObject $options['url'] = $actobj->id; } } + if ($actobj->selfLink) { + $options['self'] = $actobj->selfLink; + } } $defaults = array( @@ -794,6 +815,7 @@ class Notice extends Managed_DataObject 'reply_to' => null, 'repeat_of' => null, 'scope' => null, + 'self' => null, 'source' => 'unknown', 'tags' => array(), 'uri' => null, @@ -931,7 +953,7 @@ class Notice extends Managed_DataObject // 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); + $conv = Conversation::create($act->context, $stored->created); } $stored->conversation = $conv->getID(); unset($conv); @@ -955,10 +977,10 @@ class Notice extends Managed_DataObject $act->context = new ActivityContext(); } - if (array_key_exists('http://activityschema.org/collection/public', $act->context->attention)) { + if (array_key_exists(ActivityContext::ATTN_PUBLIC, $act->context->attention)) { $stored->scope = Notice::PUBLIC_SCOPE; // TODO: maybe we should actually keep this? if the saveAttentions thing wants to use it... - unset($act->context->attention['http://activityschema.org/collection/public']); + unset($act->context->attention[ActivityContext::ATTN_PUBLIC]); } else { $stored->scope = self::figureOutScope($actor, $groups, $scope); } @@ -1030,6 +1052,14 @@ class Notice extends Managed_DataObject throw new ServerException('Supposedly saved Notice has no ID.'); } + if ($self && common_valid_http_url($self)) { + $stored->setPref('ostatus', 'self', $self); + } + + if ($self && common_valid_http_url($self)) { + $stored->setPref('ostatus', 'self', $self); + } + // 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. @@ -1537,12 +1567,16 @@ class Notice extends Managed_DataObject function getProfileTags() { - $profile = $this->getProfile(); - $list = $profile->getOtherTags($profile); $ptags = array(); + try { + $profile = $this->getProfile(); + $list = $profile->getOtherTags($profile); - while($list->fetch()) { - $ptags[] = clone($list); + while($list->fetch()) { + $ptags[] = clone($list); + } + } catch (Exception $e) { + common_log(LOG_ERR, "Error during Notice->getProfileTags() for id=={$this->getID()}: {$e->getMessage()}"); } return $ptags; @@ -1584,12 +1618,12 @@ class Notice extends Managed_DataObject if (common_config('group', 'addtag')) { // we automatically add a tag for every group name, too - - $tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($group->nickname), - 'notice_id' => $this->id)); + common_debug('Adding hashtag matching group nickname: '._ve($group->getNickname())); + $tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($group->getNickname()), + 'notice_id' => $this->getID())); if (is_null($tag)) { - $this->saveTag($group->nickname); + $this->saveTag($group->getNickname()); } } @@ -1928,9 +1962,11 @@ class Notice extends Managed_DataObject /** * Convert a notice into an activity for export. * - * @param Profile $scoped The currently logged in/scoped profile + * @param Profile $scoped The currently logged in/scoped profile * * @return Activity activity object representing this Notice. + * @throws ClientException + * @throws ServerException */ function asActivity(Profile $scoped=null) @@ -2014,6 +2050,7 @@ class Notice extends Managed_DataObject $conv = Conversation::getKV('id', $this->conversation); if ($conv instanceof Conversation) { $ctx->conversation = $conv->uri; + $ctx->conversation_url = $conv->url; } } @@ -2076,9 +2113,12 @@ class Notice extends Managed_DataObject } } + try { + $act->selfLink = $this->getSelfLink(); + } catch (InvalidUrlException $e) { + $act->selfLink = null; + } if ($this->isLocal()) { - $act->selfLink = common_local_url('ApiStatusesShow', array('id' => $this->id, - 'format' => 'atom')); $act->editLink = $act->selfLink; } @@ -2129,11 +2169,7 @@ class Notice extends Managed_DataObject if (!empty($ns->url)) { $noticeInfoAttr['source_link'] = $ns->url; if (!empty($ns->name)) { - $noticeInfoAttr['source'] = '' - . htmlspecialchars($ns->name) - . ''; + $noticeInfoAttr['source'] = $ns->name; } } } @@ -2180,8 +2216,13 @@ class Notice extends Managed_DataObject $object->title = sprintf('New %1$s by %2$s', ActivityObject::canonicalType($object->type), $this->getProfile()->getNickname()); $object->content = $this->getRendered(); $object->link = $this->getUrl(); + try { + $object->selfLink = $this->getSelfLink(); + } catch (InvalidUrlException $e) { + $object->selfLink = null; + } - $object->extra[] = array('status_net', array('notice_id' => $this->id)); + $object->extra[] = array('statusnet:notice_id', null, $this->id); Event::handle('EndActivityObjectFromNotice', array($this, &$object)); } @@ -2633,6 +2674,13 @@ class Notice extends Managed_DataObject return !empty($this->repeat_of); } + public function isRepeated() + { + $n = new Notice(); + $n->repeat_of = $this->getID(); + return $n->find() && $n->N > 0; + } + /** * Get the list of hash tags saved with this notice. * @@ -2679,7 +2727,7 @@ class Notice extends Managed_DataObject public static function getAsTimestamp($id) { if (empty($id)) { - throw new EmptyIdException('Notice'); + throw new EmptyPkeyValueException('Notice', 'id'); } $timestamp = null; @@ -3089,42 +3137,136 @@ class Notice extends Managed_DataObject // 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; + if (isset($schemadef['fields']['lat']) + && isset($schemadef['fields']['lon']) + && isset($schemadef['fields']['location_id']) + && isset($schemadef['fields']['location_ns'])) { + // 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"; + } + + /** + * Make sure constraints are met before upgrading, if foreign keys + * are not already in use. + * 2016-03-31 + */ + if (!isset($schemadef['foreign keys'])) { + $newschemadef = self::schemaDef(); + printfnq("\nConstraint checking Notice table...\n"); + /** + * Improve typing and make sure no NULL values in any id-related columns are 0 + * 2016-03-31 + */ + foreach (['reply_to', 'repeat_of'] as $field) { + $notice = new Notice(); // reset the object + $notice->query(sprintf('UPDATE %1$s SET %2$s=NULL WHERE %2$s=0', $notice->escapedTableName(), $field)); + // Now we're sure that no Notice entries have repeat_of=0, only an id > 0 or NULL + unset($notice); + } + + /** + * This Will find foreign keys which do not fulfill the constraints and fix + * where appropriate, such as delete when "repeat_of" ID not found in notice.id + * or set to NULL for "reply_to" in the same case. + * 2016-03-31 + * + * XXX: How does this work if we would use multicolumn foreign keys? + */ + foreach (['reply_to' => 'reset', 'repeat_of' => 'delete', 'profile_id' => 'delete'] as $field=>$action) { + $notice = new Notice(); + + $fkeyname = $notice->tableName().'_'.$field.'_fkey'; + assert(isset($newschemadef['foreign keys'][$fkeyname])); + assert($newschemadef['foreign keys'][$fkeyname]); + + $foreign_key = $newschemadef['foreign keys'][$fkeyname]; + $fkeytable = $foreign_key[0]; + assert(isset($foreign_key[1][$field])); + $fkeycol = $foreign_key[1][$field]; + + printfnq("* {$fkeyname} ({$field} => {$fkeytable}.{$fkeycol})\n"); + + // NOTE: Above we set all repeat_of to NULL if they were 0, so this really gets them all. + $notice->whereAdd(sprintf('%1$s NOT IN (SELECT %2$s FROM %3$s)', $field, $fkeycol, $fkeytable)); + if ($notice->find()) { + printfnq("\tFound {$notice->N} notices with {$field} NOT IN notice.id, {$action}ing..."); + switch ($action) { + case 'delete': // since it's a directly dependant notice for an unknown ID we don't want it in our DB + while ($notice->fetch()) { + $notice->delete(); + } + break; + case 'reset': // just set it to NULL to be compatible with our constraints, if it was related to an unknown ID + $ids = []; + foreach ($notice->fetchAll('id') as $id) { + settype($id, 'int'); + $ids[] = $id; + } + unset($notice); + $notice = new Notice(); + $notice->query(sprintf('UPDATE %1$s SET %2$s=NULL WHERE id IN (%3$s)', + $notice->escapedTableName(), + $field, + implode(',', $ids))); + break; + default: + throw new ServerException('The programmer sucks, invalid action name when fixing table.'); + } + printfnq("DONE.\n"); + } + unset($notice); + } } - // 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)"; + public function delPref($namespace, $topic) { + return Notice_prefs::setData($this, $namespace, $topic, null); + } - $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 "."; + public function getPref($namespace, $topic, $default=null) { + // If you want an exception to be thrown, call Notice_prefs::getData directly + try { + return Notice_prefs::getData($this, $namespace, $topic, $default); + } catch (NoResultException $e) { + return null; } - print "\n"; + } + + // The same as getPref but will fall back to common_config value for the same namespace/topic + public function getConfigPref($namespace, $topic) + { + return Notice_prefs::getConfigData($this, $namespace, $topic); + } + + public function setPref($namespace, $topic, $data) { + return Notice_prefs::setData($this, $namespace, $topic, $data); } }