aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikael Nordfeldth <mmn@hethane.se>2015-10-03 02:02:37 +0200
committerMikael Nordfeldth <mmn@hethane.se>2015-10-03 02:02:37 +0200
commit88f7bb1ed5faded5563eeb1d75d5fb44126b0712 (patch)
treee9551f83f7e8369eb717a83f92b5451342eae27c
parentae73baf4ee524bfea97a3c43c61b5fea92df0f7f (diff)
downloadgnu-social-88f7bb1ed5faded5563eeb1d75d5fb44126b0712.tar
gnu-social-88f7bb1ed5faded5563eeb1d75d5fb44126b0712.zip
Some work on ActivityModeration with notice deletion
Let's now create an event called DeleteNotice and also make sure we handle the onNoticeDeleteRelated properly in ActivityModeration to avoid possible endless loops etc.
-rw-r--r--classes/Deleted_notice.php54
-rw-r--r--classes/Notice.php27
-rw-r--r--db/core.php1
-rw-r--r--lib/default.php1
-rw-r--r--plugins/ActivityModeration/ActivityModerationPlugin.php118
-rw-r--r--plugins/ActivityModeration/classes/Deleted_notice.php224
-rw-r--r--plugins/ActivityModeration/forms/deletenotice.php (renamed from lib/deletenoticeform.php)0
-rwxr-xr-xscripts/upgrade.php1
8 files changed, 346 insertions, 80 deletions
diff --git a/classes/Deleted_notice.php b/classes/Deleted_notice.php
deleted file mode 100644
index 23bbea1bab..0000000000
--- a/classes/Deleted_notice.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-/*
- * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, Control Yourself, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('GNUSOCIAL')) { exit(1); }
-
-/**
- * Table Definition for deleted_notice
- */
-
-class Deleted_notice extends Managed_DataObject
-{
- public $__table = 'deleted_notice'; // table name
- public $id; // int(4) primary_key not_null
- public $profile_id; // int(4) not_null
- public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
- public $created; // datetime() not_null
- public $deleted; // datetime() not_null
-
- public static function schemaDef()
- {
- return array(
- 'fields' => array(
- 'id' => array('type' => 'int', 'not null' => true, 'description' => 'identity of notice'),
- 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'author of the notice'),
- 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'),
- 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was created'),
- 'deleted' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was created'),
- ),
- 'primary key' => array('id'),
- 'unique keys' => array(
- 'deleted_notice_uri_key' => array('uri'),
- ),
- 'indexes' => array(
- 'deleted_notice_profile_id_idx' => array('profile_id'),
- ),
- );
- }
-}
diff --git a/classes/Notice.php b/classes/Notice.php
index 44d5adb1b7..737f186a10 100644
--- a/classes/Notice.php
+++ b/classes/Notice.php
@@ -163,34 +163,11 @@ class Notice extends Managed_DataObject
if ($this->getProfile()->sameAs($actor) || $actor->hasRight(Right::DELETEOTHERSNOTICE)) {
return $this->delete();
}
- throw new AuthorizationException('You are not allowed to delete other user\'s notices');
+ throw new AuthorizationException(_('You are not allowed to delete other user\'s notices'));
}
- function delete($useWhere=false)
+ public function delete($useWhere=false)
{
- // 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 (Event::handle('NoticeDeleteRelated', array($this))) {
// Clear related records
diff --git a/db/core.php b/db/core.php
index d779717fd4..f654d79d99 100644
--- a/db/core.php
+++ b/db/core.php
@@ -72,7 +72,6 @@ $classes = array('Schema_version',
'Group_block',
'Group_alias',
'Session',
- 'Deleted_notice',
'Config',
'Profile_role',
'Location_namespace',
diff --git a/lib/default.php b/lib/default.php
index 38b8bcb1af..554d3ae63c 100644
--- a/lib/default.php
+++ b/lib/default.php
@@ -306,6 +306,7 @@ $default =
array('core' => array(
'ActivityVerb' => array(),
'ActivityVerbPost' => array(),
+ 'ActivityModeration' => array(),
'AuthCrypt' => array(),
'Cronish' => array(),
'Favorite' => array(),
diff --git a/plugins/ActivityModeration/ActivityModerationPlugin.php b/plugins/ActivityModeration/ActivityModerationPlugin.php
new file mode 100644
index 0000000000..1494833992
--- /dev/null
+++ b/plugins/ActivityModeration/ActivityModerationPlugin.php
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * @package Activity
+ * @maintainer Mikael Nordfeldth <mmn@hethane.se>
+ */
+class ActivityModerationPlugin extends ActivityVerbHandlerPlugin
+{
+ public function tag()
+ {
+ return 'actmod';
+ }
+
+ public function types()
+ {
+ return array();
+ }
+
+ public function verbs()
+ {
+ return array(ActivityVerb::DELETE);
+ }
+
+ public function onBeforePluginCheckSchema()
+ {
+ Deleted_notice::beforeSchemaUpdate();
+ return true;
+ }
+
+ public function onCheckSchema()
+ {
+ $schema = Schema::get();
+ $schema->ensureTable('deleted_notice', Deleted_notice::schemaDef());
+ return true;
+ }
+
+ protected function getActionTitle(ManagedAction $action, $verb, Notice $target, Profile $scoped)
+ {
+ // FIXME: switch based on action type
+ return _m('TITLE', 'Notice moderation');
+ }
+
+ protected function doActionPreparation(ManagedAction $action, $verb, Notice $target, Profile $scoped)
+ {
+ // pass
+ }
+
+ protected function doActionPost(ManagedAction $action, $verb, Notice $target, Profile $scoped)
+ {
+ switch (true) {
+ case ActivityUtils::compareVerbs($verb, array(ActivityVerb::DELETE)):
+ // do whatever preparation is necessary to delete a verb
+ $target->delete();
+ break;
+ default:
+ throw new ServerException('ActivityVerb POST not handled by plugin that was supposed to do it.');
+ }
+ }
+
+ public function deleteRelated(Notice $notice)
+ {
+ if ($notice->getProfile()->hasRole(Profile_role::DELETED)) {
+ // Don't save a new Deleted_notice entry if the profile is being deleted
+ return true;
+ }
+
+ // For auditing purposes, save a record that the notice was deleted.
+ return Deleted_notice::addNew($notice);
+ }
+
+ /**
+ * This is run when a 'delete' verb activity comes in.
+ *
+ * @return boolean hook flag
+ */
+ protected function saveObjectFromActivity(Activity $act, Notice $stored, array $options=array())
+ {
+ // Let's see if this has been deleted already.
+ $deleted = Deleted_notice::getKV('uri', $act->id);
+ if ($deleted instanceof Deleted_notice) {
+ return $deleted;
+ }
+
+ common_debug('DELETING notice: ' . $act->objects[0]->id);
+ $target = Notice::getByUri($act->objects[0]->id);
+
+ $deleted = new Deleted_notice();
+
+ $deleted->id = $target->getID();
+ $deleted->profile_id = $target->getProfile()->getID();
+ $deleted->uri = Deleted_notice::newUri($target->getProfile(), $target);
+ $deleted->act_uri = $target->getUri();
+ $deleted->act_created = $target->created;
+ $deleted->created = common_sql_now();
+
+ common_debug('DELETING notice, storing Deleted_notice entry');
+ $deleted->insert();
+
+ common_debug('DELETING notice, actually deleting now!');
+ $target->delete();
+
+ return $deleted;
+ }
+
+ public function activityObjectFromNotice(Notice $notice)
+ {
+ $object = Deleted_notice::fromStored($notice);
+ return $object->asActivityObject();
+ }
+
+ protected function getActivityForm(ManagedAction $action, $verb, Notice $target, Profile $scoped)
+ {
+ if (!$scoped instanceof Profile || !($scoped->sameAs($target->getProfile()) || $scoped->hasRight(Right::DELETEOTHERSNOTICE))) {
+ throw new AuthorizationException(_('You are not allowed to delete other user\'s notices'));
+ }
+ return DeletenoticeForm($action, array('notice'=>$target));
+ }
+}
diff --git a/plugins/ActivityModeration/classes/Deleted_notice.php b/plugins/ActivityModeration/classes/Deleted_notice.php
new file mode 100644
index 0000000000..c450c0eb63
--- /dev/null
+++ b/plugins/ActivityModeration/classes/Deleted_notice.php
@@ -0,0 +1,224 @@
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+/**
+ * Table Definition for deleted_notice
+ */
+
+class Deleted_notice extends Managed_DataObject
+{
+ public $__table = 'deleted_notice'; // table name
+ public $id; // int(4) primary_key not_null
+ public $profile_id; // int(4) not_null
+ public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
+ public $act_uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
+ public $created; // datetime() not_null
+ public $deleted; // datetime() not_null
+
+ public static function schemaDef()
+ {
+ return array(
+ 'fields' => array(
+ 'id' => array('type' => 'int', 'not null' => true, 'description' => 'identity of notice'),
+ 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'author of the notice'),
+ 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI of the deleted notice'),
+ 'act_uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI of the delete activity, may exist in notice table'),
+ 'act_created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was created'),
+ 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was deleted'),
+ ),
+ 'primary key' => array('id'),
+ 'unique keys' => array(
+ 'deleted_notice_act_uri_key' => array('act_uri'),
+ ),
+ 'indexes' => array(
+ 'deleted_notice_profile_id_idx' => array('profile_id'),
+ ),
+ );
+ }
+
+ public static function addNew(Notice $notice)
+ {
+ $actor = $notice->getProfile();
+
+ if ($actor->hasRole(Profile_role::DELETED)) {
+ // Don't emit notices if the user is deleted
+ return true;
+ }
+
+ $act = new Activity();
+ $act->type = ActivityObject::ACTIVITY;
+ $act->verb = ActivityVerb::DELETE;
+ $act->time = time();
+ $act->id = TagURI::mint('deleted_notice:%d:%d:%s',
+ $actor->getID(),
+ $notice->getID(),
+ common_date_iso8601(common_sql_now()));
+
+ $act->content = sprintf(_m('<a href="%1$s">%2$s</a> deleted notice <a href="%3$s">{{%4$s}}</a>.'),
+ htmlspecialchars($actor->getUrl()),
+ htmlspecialchars($actor->getBestName()),
+ htmlspecialchars($notice->getUrl()),
+ htmlspecialchars($notice->getUri())
+ );
+
+ $act->actor = $actor->asActivityObject();
+ $act->target = new ActivityObject();
+ $act->target->id = $notice->getUri();
+ $act->objects = array(clone($act->target));
+
+ $url = $notice->getUrl();
+ $act->selfLink = $url;
+ $act->editLink = $url;
+
+ // This will make ActivityModeration run saveObjectFromActivity which adds
+ // a new Deleted_notice entry in the database as well as deletes the notice
+ // if the actor has permission to do so.
+ $stored = Notice::saveActivity($act, $actor);
+
+ return $stored;
+ }
+
+ static public function fromStored(Notice $stored)
+ {
+ $class = get_called_class();
+ $object = new $class;
+ $object->act_uri = $stored->getUri();
+ if (!$object->find(true)) {
+ throw new NoResultException($object);
+ }
+ return $object;
+ }
+
+ public function getActor()
+ {
+ return Profile::getByID($this->profile_id);
+ }
+
+ static public function getObjectType()
+ {
+ return 'activity';
+ }
+
+ protected $_stored = array();
+
+ public function getStored()
+ {
+ $uri = $this->getTargetUri();
+ if (!isset($this->_stored[$uri])) {
+ $stored = new Notice();
+ $stored->uri = $uri;
+ if (!$stored->find(true)) {
+ throw new NoResultException($stored);
+ }
+ $this->_stored[$uri] = $stored;
+ }
+ return $this->_stored[$uri];
+ }
+
+ public function getTargetUri()
+ {
+ return $this->uri;
+ }
+
+ public function getUri()
+ {
+ return $this->act_uri;
+ }
+
+ public function asActivityObject(Profile $scoped=null)
+ {
+ $actobj = new ActivityObject();
+ $actobj->id = $this->getUri();
+ $actobj->type = ActivityUtils::resolveUri(self::getObjectType());
+ $actobj->actor = $this->getActorObject();
+ $actobj->target = new ActivityObject();
+ $actobj->target->id = $this->getTargetUri();
+ $actobj->objects = array(clone($actobj->target));
+ $actobj->verb = ActivityVerb::DELETE;
+ $actobj->title = ActivityUtils::verbToTitle($actobj->verb);
+
+ $actor = $this->getActor();
+ $actobj->content = sprintf(_m('<a href="%1$s">%2$s</a> deleted notice {{%3$s}}.'),
+ htmlspecialchars($actor->getUrl()),
+ htmlspecialchars($actor->getBestName()),
+ htmlspecialchars($actor->getTargetUri())
+ );
+
+ return $actobj;
+ }
+
+ static function newUri(Profile $actor, Managed_DataObject $object, $created=null)
+ {
+ if (is_null($created)) {
+ $created = common_sql_now();
+ }
+ return TagURI::mint(strtolower(get_called_class()).':%d:%s:%d:%s',
+ $actor->getID(),
+ ActivityUtils::resolveUri(self::getObjectType(), true),
+ $object->getID(),
+ common_date_iso8601($created));
+ }
+
+ static public function beforeSchemaUpdate()
+ {
+ $table = strtolower(get_called_class());
+ $schema = Schema::get();
+ $schemadef = $schema->getTableDef($table);
+
+ // 2015-10-03 We change the meaning of the 'uri' field and move its
+ // content to the 'act_uri' for the deleted activity. act_created is
+ // added too.
+ if (isset($schemadef['fields']['act_uri'])) {
+ // We already have the act_uri field, so no need to migrate to it.
+ return;
+ }
+ echo "\nFound old $table table, upgrading it to contain 'act_uri' and 'act_created' field...";
+
+ $schemadef['fields']['act_uri'] = array('type' => 'varchar', 'not null' => true, 'length' => 191, 'description' => 'URI of the delete activity, may exist in notice table');
+ $schemadef['fields']['act_created'] = array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was created');
+ unset($schemadef['unique keys']);
+ $schema->ensureTable($table, $schemadef);
+
+ $deleted = new Deleted_notice();
+ $result = $deleted->find();
+ if ($result === false) {
+ print "\nFound no deleted_notice entries, continuing...";
+ return true;
+ }
+ print "\nFound $result deleted_notice entries, aligning with new database layout: ";
+ while($deleted->fetch()) {
+ $orig = clone($deleted);
+ $deleted->act_uri = $deleted->uri;
+ // this is a fake URI just to have something to put there to avoid NULL
+ $deleted->uri = TagURI::mint(strtolower(get_called_class()).':%d:%s:%s:%s',
+ $deleted->profile_id,
+ ActivityUtils::resolveUri(self::getObjectType(), true),
+ 'unknown',
+ common_date_iso8601($deleted->created));
+ $deleted->act_created = $deleted->created; // we don't actually know when the notice was created
+ $deleted->updateWithKeys($orig, 'id');
+ print ".";
+ }
+ print "DONE.\n";
+ print "Resuming core schema upgrade...";
+ }
+
+}
diff --git a/lib/deletenoticeform.php b/plugins/ActivityModeration/forms/deletenotice.php
index d06004613a..d06004613a 100644
--- a/lib/deletenoticeform.php
+++ b/plugins/ActivityModeration/forms/deletenotice.php
diff --git a/scripts/upgrade.php b/scripts/upgrade.php
index 126ef29036..06a2f74771 100755
--- a/scripts/upgrade.php
+++ b/scripts/upgrade.php
@@ -88,6 +88,7 @@ function updateSchemaPlugins()
{
printfnq("Upgrading plugin schema...");
+ Event::handle('BeforePluginCheckSchema');
Event::handle('CheckSchema');
printfnq("DONE.\n");