+++ /dev/null
-<?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'),
- ),
- );
- }
-}
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
'Group_block',
'Group_alias',
'Session',
- 'Deleted_notice',
'Config',
'Profile_role',
'Location_namespace',
array('core' => array(
'ActivityVerb' => array(),
'ActivityVerbPost' => array(),
+ 'ActivityModeration' => array(),
'AuthCrypt' => array(),
'Cronish' => array(),
'Favorite' => array(),
+++ /dev/null
-<?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.'));
- }
-}
--- /dev/null
+<?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));
+ }
+}
--- /dev/null
+<?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...";
+ }
+
+}
--- /dev/null
+<?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.'));
+ }
+}
{
printfnq("Upgrading plugin schema...");
+ Event::handle('BeforePluginCheckSchema');
Event::handle('CheckSchema');
printfnq("DONE.\n");