]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Some work on ActivityModeration with notice deletion
authorMikael Nordfeldth <mmn@hethane.se>
Sat, 3 Oct 2015 00:02:37 +0000 (02:02 +0200)
committerMikael Nordfeldth <mmn@hethane.se>
Sat, 3 Oct 2015 00:02:37 +0000 (02:02 +0200)
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.

classes/Deleted_notice.php [deleted file]
classes/Notice.php
db/core.php
lib/default.php
lib/deletenoticeform.php [deleted file]
plugins/ActivityModeration/ActivityModerationPlugin.php [new file with mode: 0644]
plugins/ActivityModeration/classes/Deleted_notice.php [new file with mode: 0644]
plugins/ActivityModeration/forms/deletenotice.php [new file with mode: 0644]
scripts/upgrade.php

diff --git a/classes/Deleted_notice.php b/classes/Deleted_notice.php
deleted file mode 100644 (file)
index 23bbea1..0000000
+++ /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'),
-            ),
-        );
-    }
-}
index 44d5adb1b7856d46d5832b0ecdbe5b32594c9bf1..737f186a107b6c7d0f63b18ca69acc4080747fdb 100644 (file)
@@ -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
index d779717fd4a4447efbeec38231f9b6bb08bd7de7..f654d79d996754977dbb7ab9a8fc555b85b6791b 100644 (file)
@@ -72,7 +72,6 @@ $classes = array('Schema_version',
                  'Group_block',
                  'Group_alias',
                  'Session',
-                 'Deleted_notice',
                  'Config',
                  'Profile_role',
                  'Location_namespace',
index 38b8bcb1af838cf05c94611214a827ddf892ab40..554d3ae63ceff4f518dcfc25b3abaf2e723ac084 100644 (file)
@@ -306,6 +306,7 @@ $default =
         array('core' => array(
                             'ActivityVerb' => array(),
                             'ActivityVerbPost' => array(),
+                            'ActivityModeration' => array(),
                             'AuthCrypt' => array(),
                             'Cronish' => array(),
                             'Favorite' => array(),
diff --git a/lib/deletenoticeform.php b/lib/deletenoticeform.php
deleted file mode 100644 (file)
index d060046..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-if (!defined('GNUSOCIAL')) { exit(1); }
-
-class DeletenoticeForm extends Form
-{
-    protected $notice = null;
-
-    function __construct(HTMLOutputter $out=null, array $formOpts=array())
-    {
-        if (!array_key_exists('notice', $formOpts) || !$formOpts['notice'] instanceof Notice) {
-            throw new ServerException('No notice provided to DeletenoticeForm');
-        }
-
-        parent::__construct($out);
-
-        $this->notice = $formOpts['notice'];
-    }
-
-    function id()
-    {
-        return 'form_notice_delete-' . $this->notice->getID();
-    }
-
-    function formClass()
-    {
-        return 'form_settings';
-    }
-
-    function action()
-    {
-        return common_local_url('deletenotice', array('notice' => $this->notice->getID()));
-    }
-
-    function formLegend()
-    {
-        $this->out->element('legend', null, _('Delete notice'));
-    }
-
-    function formData()
-    {
-        $this->out->element('p', null, _('Are you sure you want to delete this notice?'));
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        $this->out->submit('form_action-no',
-                      // TRANS: Button label on the delete notice form.
-                      _m('BUTTON','No'),
-                      'submit form_action-primary',
-                      'no',
-                      // TRANS: Submit button title for 'No' when deleting a notice.
-                      _('Do not delete this notice.'));
-        $this->out->submit('form_action-yes',
-                      // TRANS: Button label on the delete notice form.
-                      _m('BUTTON','Yes'),
-                      'submit form_action-secondary',
-                      'yes',
-                      // TRANS: Submit button title for 'Yes' when deleting a notice.
-                      _('Delete this notice.'));
-    }
-}
diff --git a/plugins/ActivityModeration/ActivityModerationPlugin.php b/plugins/ActivityModeration/ActivityModerationPlugin.php
new file mode 100644 (file)
index 0000000..1494833
--- /dev/null
@@ -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 (file)
index 0000000..c450c0e
--- /dev/null
@@ -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/plugins/ActivityModeration/forms/deletenotice.php b/plugins/ActivityModeration/forms/deletenotice.php
new file mode 100644 (file)
index 0000000..d060046
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+class DeletenoticeForm extends Form
+{
+    protected $notice = null;
+
+    function __construct(HTMLOutputter $out=null, array $formOpts=array())
+    {
+        if (!array_key_exists('notice', $formOpts) || !$formOpts['notice'] instanceof Notice) {
+            throw new ServerException('No notice provided to DeletenoticeForm');
+        }
+
+        parent::__construct($out);
+
+        $this->notice = $formOpts['notice'];
+    }
+
+    function id()
+    {
+        return 'form_notice_delete-' . $this->notice->getID();
+    }
+
+    function formClass()
+    {
+        return 'form_settings';
+    }
+
+    function action()
+    {
+        return common_local_url('deletenotice', array('notice' => $this->notice->getID()));
+    }
+
+    function formLegend()
+    {
+        $this->out->element('legend', null, _('Delete notice'));
+    }
+
+    function formData()
+    {
+        $this->out->element('p', null, _('Are you sure you want to delete this notice?'));
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        $this->out->submit('form_action-no',
+                      // TRANS: Button label on the delete notice form.
+                      _m('BUTTON','No'),
+                      'submit form_action-primary',
+                      'no',
+                      // TRANS: Submit button title for 'No' when deleting a notice.
+                      _('Do not delete this notice.'));
+        $this->out->submit('form_action-yes',
+                      // TRANS: Button label on the delete notice form.
+                      _m('BUTTON','Yes'),
+                      'submit form_action-secondary',
+                      'yes',
+                      // TRANS: Submit button title for 'Yes' when deleting a notice.
+                      _('Delete this notice.'));
+    }
+}
index 126ef290360b27a7d8b0abfcf3963816555b18ef..06a2f74771bb8e2be374a38434e30a45f1f9b098 100755 (executable)
@@ -88,6 +88,7 @@ function updateSchemaPlugins()
 {
     printfnq("Upgrading plugin schema...");
 
+    Event::handle('BeforePluginCheckSchema');
     Event::handle('CheckSchema');
 
     printfnq("DONE.\n");