10 コミット 714ab0c872 ... ac654a6b6c

作者 SHA1 メッセージ 日付
  SENOO, Ken ac654a6b6c Add CHANGELOG 9 ヶ月 前
  SENOO, Ken 0558e54545 Add display count on user directory 9 ヶ月 前
  SENOO, Ken 32c8f5de68 Support `count` URL query for max value per userdirectory page 9 ヶ月 前
  SENOO, Ken 8817c95ba2 Add account/delete API and bulk action 9 ヶ月 前
  SENOO, Ken ec6acc4ddb Add account delete API base code 9 ヶ月 前
  SENOO, Ken c8d35f5d7e Rename class and date format 9 ヶ月 前
  SENOO, Ken d7f9a2e7d7 Add checkbox 10 ヶ月 前
  SENOO, Ken d782053817 Add control for common_config('site', 'closed') with apiaccountregister API. 11 ヶ月 前
  SENOO, Ken 5386dec03e Add stderr=true for testing with header 11 ヶ月 前
  SENOO, Ken 228a5ad078 Update dependency with run 'composer install' 11 ヶ月 前

+ 5 - 0
CHANGELOG.md

@@ -2,9 +2,14 @@
 
 
 ## 2.x.x - YYYY-mm-dd
 ## 2.x.x - YYYY-mm-dd
 
 
+### Added
+
+- Support bulk action and display count for user directory (/directory/users) <https://imgur.com/gallery/nei6Ij2>. And add account/delete API for bulk spam account deleteion <https://notabug.org/gnusocialjp/gnusocial/issues/20>.
+
 ### Fixed
 ### Fixed
 
 
 - Duplicated post with unlike between remote GNU social server ((URL)[https://notabug.org/gnusocialjp/gnusocial/issues/10)]).
 - Duplicated post with unlike between remote GNU social server ((URL)[https://notabug.org/gnusocialjp/gnusocial/issues/10)]).
+- Add control for common_config('site', 'closed') with apiaccountregister API.
 
 
 ## 2.0.2 - 2023-08-21
 ## 2.0.2 - 2023-08-21
 
 

+ 116 - 0
actions/apiaccountdelete.php

@@ -0,0 +1,116 @@
+<?php declare(strict_types=1);
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Delete account
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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/>.
+ *
+ * @category  API
+ * @package   GNUsocial
+ * @author    SENOO, Ken <develop@senooken.jp>
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://www.gnu.org/software/social/
+ */
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+/**
+ * Delete a account with API.
+ * 
+ * Refer to DeleteuserAction.
+ */
+class ApiAccountDeleteAction extends ApiAuthAction
+{
+    var $user = null;
+
+    /**
+     * Take arguments for running
+     *
+     * @param array $args $_REQUEST args
+     *
+     * @return boolean success flag
+     */
+    protected function prepare(array $args=array())
+    {
+        if (!parent::prepare($args)) {
+            return false;
+        }
+
+        assert($this->scoped instanceof Profile);
+
+        $profile = $this->getTargetProfile($this->arg('id'));
+        if (empty($profile)) {
+            // TRANS: Client error displayed when trying delete who's profile could not be found.
+            throw new ClientException(_('Could not delete user: user not found.'), 403);
+        }
+        $this->user = $profile->getUser();
+
+        if ($this->user->id === $this->scoped->id) {
+            throw new ClientException('Could not delete self for mistake.', 403);
+        }
+
+        if (!$this->scoped->hasRight(Right::DELETEUSER)) {
+            // TRANS: Client error displayed when trying to delete a user without having the right to delete users.
+            throw new AuthorizationException(_('You cannot delete users.'));
+        }
+
+        // Only administrators can delete other privileged users (such as others who have the right to silence).
+        if ($this->scoped->isPrivileged() && !$this->scoped->hasRole(Profile_role::ADMINISTRATOR)) {
+            // TRANS: Client error displayed when trying to delete a user that has been granted moderation privileges
+            throw new AuthorizationException(_('You cannot delete other privileged users.'));
+        }
+
+        return true;
+    }
+
+    /**
+     * Handle the request
+     *
+     * @param array $args $_REQUEST data (unused)
+     *
+     * @return void
+     */
+    protected function handle()
+    {
+        parent::handle();
+
+        if ($_SERVER['REQUEST_METHOD'] == 'DELETE') {
+            $this->handleDelete();
+        }
+    }
+
+    /**
+     * Actually delete a user.
+     *
+     * @return void
+     */
+    function handleDelete()
+    {
+        if (Event::handle('StartDeleteUser', array($this, $this->user))) {
+            // Mark the account as deleted and shove low-level deletion tasks
+            // to background queues. Removing a lot of posts can take a while...
+            if (!$this->user->hasRole(Profile_role::DELETED)) {
+                $this->user->grantRole(Profile_role::DELETED);
+            }
+
+            $qm = QueueManager::get();
+            $qm->enqueue($this->user, 'deluser');
+
+            Event::handle('EndDeleteUser', array($this, $this->user));
+        }
+    }
+}

+ 6 - 1
actions/apiaccountregister.php

@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types=1);
 /**
 /**
  * StatusNet, the distributed open-source microblogging tool
  * StatusNet, the distributed open-source microblogging tool
  *
  *
@@ -95,6 +95,11 @@ class ApiAccountRegisterAction extends ApiAction
             }
             }
         }
         }
 
 
+        if (common_config('site', 'closed')) {
+            // TRANS: Client error displayed when trying to register to a closed site.
+            $this->clientError(_('Registration not allowed.'));
+        }
+
         if (common_config('site', 'inviteonly') && empty($this->code)) {
         if (common_config('site', 'inviteonly') && empty($this->code)) {
             // TRANS: Client error displayed when trying to register to an invite-only site without an invitation.
             // TRANS: Client error displayed when trying to register to an invite-only site without an invitation.
             $this->clientError(_('Sorry, only invited people can register.'), 401);
             $this->clientError(_('Sorry, only invited people can register.'), 401);

+ 8 - 0
doc-src/twitterapi

@@ -221,6 +221,14 @@ This method is powerful when used in conjunction with users/lookup.
 
 
 ## Account resources
 ## Account resources
 
 
+### account/delete
+
+```
+DELETE /api/account/delete.json?user_id=[id]
+```
+
+Delete account (original).
+
 ### account/verify_credentials
 ### account/verify_credentials
 
 
 Returns an HTTP 200 OK response code and a representation of the requesting user if
 Returns an HTTP 200 OK response code and a representation of the requesting user if

+ 7 - 2
lib/action/action.php

@@ -1792,13 +1792,18 @@ class Action extends HTMLOutputter // lawsuit
      */
      */
     public function pagination(bool $have_before, bool $have_after, int $page, string $action, ?array $args = null): void
     public function pagination(bool $have_before, bool $have_after, int $page, string $action, ?array $args = null): void
     {
     {
+        $count = $this->trimmed('count');
+        if ($count) {
+            $pargs['count'] = $count;
+        }
+
         // Does a little before-after block for next/prev page
         // Does a little before-after block for next/prev page
         if ($have_before || $have_after) {
         if ($have_before || $have_after) {
             $this->elementStart('ul', ['class' => 'nav',
             $this->elementStart('ul', ['class' => 'nav',
                 'id' => 'pagination',]);
                 'id' => 'pagination',]);
         }
         }
         if ($have_before) {
         if ($have_before) {
-            $pargs = ['page' => $page - 1];
+            $pargs['page'] = $page - 1;
             $this->elementStart('li', ['class' => 'nav_prev']);
             $this->elementStart('li', ['class' => 'nav_prev']);
             $this->element(
             $this->element(
                 'a',
                 'a',
@@ -1811,7 +1816,7 @@ class Action extends HTMLOutputter // lawsuit
             $this->elementEnd('li');
             $this->elementEnd('li');
         }
         }
         if ($have_after) {
         if ($have_after) {
-            $pargs = ['page' => $page + 1];
+            $pargs['page'] = $page + 1;
             $this->elementStart('li', ['class' => 'nav_next']);
             $this->elementStart('li', ['class' => 'nav_next']);
             $this->element(
             $this->element(
                 'a',
                 'a',

+ 4 - 5
lib/profile/profilelist.php

@@ -54,13 +54,13 @@ class ProfileList extends Widget
         $this->action = $action;
         $this->action = $action;
     }
     }
 
 
-    function show()
+    function show(int $maxCount = PROFILES_PER_PAGE)
     {
     {
         $cnt = 0;
         $cnt = 0;
 
 
         if (Event::handle('StartProfileList', array($this))) {
         if (Event::handle('StartProfileList', array($this))) {
             $this->startList();
             $this->startList();
-            $cnt = $this->showProfiles();
+            $cnt = $this->showProfiles($maxCount);
             $this->endList();
             $this->endList();
             Event::handle('EndProfileList', array($this));
             Event::handle('EndProfileList', array($this));
         }
         }
@@ -78,12 +78,11 @@ class ProfileList extends Widget
         $this->out->elementEnd('ul');
         $this->out->elementEnd('ul');
     }
     }
 
 
-    function showProfiles()
+    function showProfiles(int $count = PROFILES_PER_PAGE)
     {
     {
         $cnt = $this->profile->N;
         $cnt = $this->profile->N;
         $profiles = $this->profile->fetchAll();
         $profiles = $this->profile->fetchAll();
-
-        $max = min($cnt, $this->maxProfiles());
+        $max = min($cnt, $count);
 
 
         for ($i = 0; $i < $max; $i++) {
         for ($i = 0; $i < $max; $i++) {
             $pli = $this->newListItem($profiles[$i]);
             $pli = $this->newListItem($profiles[$i]);

+ 1 - 1
lib/util/framework.php

@@ -35,7 +35,7 @@ define('GNUSOCIAL_ENGINE_URL', 'https://gnusocial.rocks/');
 define('GNUSOCIAL_ENGINE_REPO_URL', 'https://notabug.org/diogo/gnu-social/'); // Change to https://git.gnu.io/gnu/gnu-social
 define('GNUSOCIAL_ENGINE_REPO_URL', 'https://notabug.org/diogo/gnu-social/'); // Change to https://git.gnu.io/gnu/gnu-social
 
 
 define('GNUSOCIAL_BASE_VERSION', '2.0.2');
 define('GNUSOCIAL_BASE_VERSION', '2.0.2');
-define('GNUSOCIAL_LIFECYCLE', 'beta0'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
+define('GNUSOCIAL_LIFECYCLE', 'dev'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
 
 
 define('GNUSOCIAL_VERSION', GNUSOCIAL_BASE_VERSION . '-' . GNUSOCIAL_LIFECYCLE);
 define('GNUSOCIAL_VERSION', GNUSOCIAL_BASE_VERSION . '-' . GNUSOCIAL_LIFECYCLE);
 
 

+ 1 - 1
lib/util/htmloutputter.php

@@ -44,7 +44,7 @@ define('PAGE_TYPE_PREFS', 'text/html');
  * @copyright 2008-2019 Free Software Foundation, Inc http://www.fsf.org
  * @copyright 2008-2019 Free Software Foundation, Inc http://www.fsf.org
  * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
  * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
  */
  */
-class htmloutputter extends XMLOutputter
+class HTMLOutputter extends XMLOutputter
 {
 {
     protected $DTD = ['doctype' => 'html',
     protected $DTD = ['doctype' => 'html',
         'spec'                  => '-//W3C//DTD XHTML 1.0 Strict//EN',
         'spec'                  => '-//W3C//DTD XHTML 1.0 Strict//EN',

+ 8 - 0
lib/util/router.php

@@ -574,6 +574,14 @@ class Router
                         ['action' => 'ApiAccountRateLimitStatus'],
                         ['action' => 'ApiAccountRateLimitStatus'],
                         ['format' => '(xml|json)']);
                         ['format' => '(xml|json)']);
 
 
+            $m->connect('api/account/delete/:id.:format',
+                        ['action' => 'ApiAccountDelete'],
+                        ['id' => Nickname::INPUT_FMT,
+                         'format' => '(xml|json)']);
+            $m->connect('api/account/delete.:format',
+                         ['action' => 'ApiAccountDelete'],
+                         ['format' => '(xml|json)']);
+
             // blocks
             // blocks
 
 
             $m->connect('api/blocks/create/:id.:format',
             $m->connect('api/blocks/create/:id.:format',

+ 0 - 0
phpunit.xml


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません