From 92388e173492fdf54ff29984cab1fe7a7e62c659 Mon Sep 17 00:00:00 2001 From: Diogo Cordeiro Date: Sun, 11 Aug 2019 03:16:50 +0100 Subject: [PATCH] [SamplePlugin] Review and update with the latest GNU social best practices --- lib/urlmapper.php | 13 +- plugins/Sample/SamplePlugin.php | 140 +++++++++--------- plugins/Sample/actions/hello.php | 128 ++++++++-------- .../Sample/classes/User_greeting_count.php | 110 +++++++------- 4 files changed, 203 insertions(+), 188 deletions(-) diff --git a/lib/urlmapper.php b/lib/urlmapper.php index f2a93b2fbe..d37b0fb337 100644 --- a/lib/urlmapper.php +++ b/lib/urlmapper.php @@ -65,15 +65,16 @@ class URLMapper * Route creation. * $acceptHeaders should be set to true when, for whatever reason, * a path is being re-connected. The $headers list is still optional, - * in this case, given that being empty means "accept everything". + * in this case, given that being empty means "accept everything". * * @author Evan Prodromou * @author Bruno Casteleiro - * @param string $path route path - * @param array $args route action and, if needed, action settings - * @param array $paramPatterns regex patterns for path's parameters - * @param bool $acceptHeaders whether a path is being re-connected - * @param array $headers headers that should be set for route creation + * @param string $path route path + * @param array $args route action and, if needed, action settings + * @param array $paramPatterns regex patterns for path's parameters + * @param bool $acceptHeaders whether a path is being re-connected + * @param array $headers headers that should be set for route creation + * @throws Exception If can't connect * @return void */ public function connect(string $path, array $args, array $paramPatterns = [], bool $acceptHeaders = false, array $headers = []) diff --git a/plugins/Sample/SamplePlugin.php b/plugins/Sample/SamplePlugin.php index 6d92694723..88026860b3 100644 --- a/plugins/Sample/SamplePlugin.php +++ b/plugins/Sample/SamplePlugin.php @@ -1,39 +1,32 @@ . + /** - * StatusNet - the distributed open-source microblogging tool - * Copyright (C) 2009, StatusNet, Inc. + * A sample plugin to show best practices for GNU social plugins * - * A sample Plugin to show best practices for StatusNet plugins - * - * PHP version 5 - * - * 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 . - * - * @category Sample - * @package StatusNet + * @package GNU social * @author Brion Vibber * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 - * @link http://status.net/ + * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org + * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ -if (!defined('STATUSNET')) { - // This check helps protect against security problems; - // your code file can't be executed directly from the web. - exit(1); -} +// This check helps protect against security problems; +// your code file can't be executed directly from the web. +defined('GNUSOCIAL') || die(); /** * Sample plugin main class @@ -87,38 +80,37 @@ if (!defined('STATUSNET')) { * main StatusNet distribution go in 'plugins' and third-party or local ones go * in 'local'. * - * Simple plugins can be implemented as a single Plugin. Others are more complex - * and require additional Plugins; these should use their own directory, like + * Simple plugins can be implemented as a single plugin. Others are more complex + * and require additional plugins; these should use their own directory, like * 'local/plugins/{$name}/'. All files related to the plugin, including images, - * JavaScript, CSS, external libraries or PHP Plugins should go in the plugin + * JavaScript, CSS, external libraries or PHP plugins should go in the plugin * directory. * * @category Sample - * @package StatusNet + * @package GNU social * @author Brion Vibber * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 - * @link http://status.net/ + * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org + * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ class SamplePlugin extends Plugin { - const PLUGIN_VERSION = '2.0.0'; + // Versions start at 0.1.0 in Semver + const PLUGIN_VERSION = '0.1.0'; /** * Plugins are configured using public instance attributes. To set * their values, site administrators use this syntax: * - * addPlugin('Sample', array('attr1' => 'foo', 'attr2' => 'bar')); + * addPlugin('Sample', ['attr1' => 'foo', 'attr2' => 'bar']); * * The same plugin class can be initialized multiple times with different * arguments: * - * addPlugin('EmailNotify', array('sendTo' => 'evan@status.net')); - * addPlugin('EmailNotify', array('sendTo' => 'brionv@status.net')); + * addPlugin('EmailNotify', ['sendTo' => 'evan@status.net']); + * addPlugin('EmailNotify', ['sendTo' => 'brionv@status.net']); * */ - public $attr1 = null; public $attr2 = null; @@ -128,9 +120,9 @@ class SamplePlugin extends Plugin * Plugins overload this method to do any initialization they need, * like connecting to remote servers or creating paths or so on. * - * @return boolean hook value; true means continue processing, false means stop. + * @return bool hook value; true means continue processing, false means stop. */ - function initialize() + public function initialize(): bool { return true; } @@ -141,9 +133,9 @@ class SamplePlugin extends Plugin * Plugins overload this method to do any cleanup they need, * like disconnecting from remote servers or deleting temp files or so on. * - * @return boolean hook value; true means continue processing, false means stop. + * @return bool hook value; true means continue processing, false means stop. */ - function cleanup() + public function cleanup(): bool { return true; } @@ -162,12 +154,11 @@ class SamplePlugin extends Plugin * However, they need to remember to run that script after installing or * upgrading a plugin! * + * @return bool hook value; true means continue processing, false means stop. * @see Schema * @see ColumnDef - * - * @return boolean hook value; true means continue processing, false means stop. */ - function onCheckSchema() + public function onCheckSchema(): bool { $schema = Schema::get(); @@ -186,12 +177,15 @@ class SamplePlugin extends Plugin * * @param URLMapper $m path-to-action mapper * - * @return boolean hook value; true means continue processing, false means stop. + * @return bool hook value; true means continue processing, false means stop. + * @throws Exception If it can't connect our required routes */ - public function onRouterInitialized(URLMapper $m) + public function onRouterInitialized(URLMapper $m): bool { - $m->connect('main/hello', - ['action' => 'hello']); + $m->connect( + 'main/hello', + ['action' => 'hello'] + ); return true; } @@ -208,31 +202,45 @@ class SamplePlugin extends Plugin * @param Action $action The current action handler. Use this to * do any output. * - * @return boolean hook value; true means continue processing, false means stop. + * @return bool hook value; true means continue processing, false means stop. * + * @throws Exception * @see Action */ - function onEndPrimaryNav($action) + public function onEndPrimaryNav(Action $action): bool { // common_local_url() gets the correct URL for the action name // we provide - $action->menuItem(common_local_url('hello'), - // TRANS: Menu item in sample plugin. - _m('Hello'), - // TRANS: Menu item title in sample plugin. - _m('A warm greeting'), false, 'nav_hello'); + $action->menuItem( + common_local_url('hello'), + // TRANS: Menu item in sample plugin. + _m('Hello'), + // TRANS: Menu item title in sample plugin. + _m('A warm greeting'), + false, + 'nav_hello' + ); return true; } + /** + * Plugin version information/meta-data + * + * @param array $versions + * @return bool hook true + * @throws Exception + */ public function onPluginVersion(array &$versions): bool { - $versions[] = array('name' => 'Sample', - 'version' => self::PLUGIN_VERSION, - 'author' => 'Brion Vibber, Evan Prodromou', - 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/Sample', - 'rawdescription' => - // TRANS: Plugin description. - _m('A sample plugin to show basics of development for new hackers.')); + $versions[] = [ + 'name' => 'Sample', + 'version' => self::PLUGIN_VERSION, + 'author' => 'Brion Vibber, Evan Prodromou', + 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/Sample', + 'rawdescription' => + // TRANS: Plugin description. + _m('A sample plugin to show basics of development for new hackers.') + ]; return true; } } diff --git a/plugins/Sample/actions/hello.php b/plugins/Sample/actions/hello.php index 37bae3a4dd..c9660421cc 100644 --- a/plugins/Sample/actions/hello.php +++ b/plugins/Sample/actions/hello.php @@ -1,55 +1,48 @@ . + /** * Give a warm greeting to our friendly user * - * PHP version 5 - * - * @category Sample - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 - * @link http://status.net/ - * - * StatusNet - the distributed open-source microblogging tool - * Copyright (C) 2009, StatusNet, 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 . + * @package GNU social + * @author Brion Vibber + * @author Evan Prodromou + * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org + * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ -if (!defined('STATUSNET')) { - exit(1); -} +defined('GNUSOCIAL') || die(); /** - * Give a warm greeting to our friendly user - * * This sample action shows some basic ways of doing output in an action * class. * * Action classes have several output methods that they override from * the parent class. * - * @category Sample - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 - * @link http://status.net/ + * @category Sample + * @package GNU social + * @author Evan Prodromou + * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org + * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ class HelloAction extends Action { - var $user = null; - var $gc = null; + public $user = null; + public $gc = null; /** * Take arguments for running @@ -64,16 +57,17 @@ class HelloAction extends Action * * @param array $args $_REQUEST args * - * @return boolean success flag + * @return bool success flag + * @throws ClientException */ - function prepare(array $args = array()) + public function prepare(array $args = []) { parent::prepare($args); $this->user = common_current_user(); if (!empty($this->user)) { - $this->gc = User_greeting_count::inc($this->user->id); + $this->gc = User_greeting_count::inc($this->user->getID()); } return true; @@ -87,11 +81,12 @@ class HelloAction extends Action * by the time handle() is called the action should be * more or less ready to go. * - * @param array $args $_REQUEST args; handled in prepare() - * * @return void + * @throws ClientException + * @throws ReflectionException + * @throws ServerException */ - function handle() + public function handle() { parent::handle(); @@ -104,15 +99,16 @@ class HelloAction extends Action * Override this method to show a custom title. * * @return string Title of the page + * @throws Exception */ - function title() + public function title() { if (empty($this->user)) { // TRANS: Page title for sample plugin. return _m('Hello'); } else { // TRANS: Page title for sample plugin. %s is a user nickname. - return sprintf(_m('Hello, %s!'), $this->user->nickname); + return sprintf(_m('Hello, %s!'), $this->user->getNickname()); } } @@ -127,24 +123,38 @@ class HelloAction extends Action * This method also demonstrates use of a plural localized string. * * @return void + * @throws Exception */ - function showContent() + public function showContent() { if (empty($this->user)) { - $this->element('p', array('class' => 'greeting'), - // TRANS: Message in sample plugin. - _m('Hello, stranger!')); + $this->element( + 'p', + ['class' => 'greeting'], + // TRANS: Message in sample plugin. + _m('Hello, stranger!') + ); } else { - $this->element('p', array('class' => 'greeting'), - // TRANS: Message in sample plugin. %s is a user nickname. - sprintf(_m('Hello, %s'), $this->user->nickname)); - $this->element('p', array('class' => 'greeting_count'), - // TRANS: Message in sample plugin. - // TRANS: %d is the number of times a user is greeted. - sprintf(_m('I have greeted you %d time.', - 'I have greeted you %d times.', - $this->gc->greeting_count), - $this->gc->greeting_count)); + $this->element( + 'p', + ['class' => 'greeting'], + // TRANS: Message in sample plugin. %s is a user nickname. + sprintf(_m('Hello, %s'), $this->user->getNickname()) + ); + $this->element( + 'p', + ['class' => 'greeting_count'], + // TRANS: Message in sample plugin. + // TRANS: %d is the number of times a user is greeted. + sprintf( + _m( + 'I have greeted you %d time.', + 'I have greeted you %d times.', + $this->gc->greeting_count + ), + $this->gc->greeting_count + ) + ); } } @@ -161,9 +171,9 @@ class HelloAction extends Action * * @param array $args other arguments, if RO/RW status depends on them. * - * @return boolean is read only action? + * @return bool is read only action? */ - function isReadOnly($args) + public function isReadOnly($args) { return false; } diff --git a/plugins/Sample/classes/User_greeting_count.php b/plugins/Sample/classes/User_greeting_count.php index 6196820af6..6f4869ba0e 100644 --- a/plugins/Sample/classes/User_greeting_count.php +++ b/plugins/Sample/classes/User_greeting_count.php @@ -1,42 +1,33 @@ . + /** * Data class for counting greetings * - * PHP version 5 - * - * @category Data - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 - * @link http://status.net/ - * - * StatusNet - the distributed open-source microblogging tool - * Copyright (C) 2009, StatusNet, 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 . + * @package GNU social + * @author Brion Vibber + * @author Evan Prodromou + * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org + * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ -if (!defined('STATUSNET')) { - exit(1); -} - -require_once INSTALLDIR . '/classes/Memcached_DataObject.php'; +defined('GNUSOCIAL') || die(); /** - * Data class for counting greetings - * - * We use the DB_DataObject framework for data classes in StatusNet. Each + * We use the DB_DataObject framework for data classes in GNU social. Each * table maps to a particular data class, making it easier to manipulate * data. * @@ -44,11 +35,11 @@ require_once INSTALLDIR . '/classes/Memcached_DataObject.php'; * extension of DB_DataObject that provides caching, internationalization, * and other bits of good functionality to StatusNet-specific data classes. * - * @category Action - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 - * @link http://status.net/ + * @category Action + * @package GNU social + * @author Evan Prodromou + * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org + * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later * * @see DB_DataObject */ @@ -57,23 +48,23 @@ class User_greeting_count extends Managed_DataObject public $__table = 'user_greeting_count'; // table name public $user_id; // int(4) primary_key not_null public $greeting_count; // int(4) - public $created; // datetime() not_null - public $modified; // datetime not_null default_0000-00-00%2000%3A00%3A00 + public $created; // datetime() not_null default_0000-00-00%2000%3A00%3A00 + public $modified; // datetime() not_null default_CURRENT_TIMESTAMP public static function schemaDef() { - return array( - 'fields' => array( - 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id'), - 'greeting_count' => array('type' => 'int', 'not null' => true, 'description' => 'the greeting count'), - 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'), - 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), - ), - 'primary key' => array('user_id'), - 'foreign keys' => array( - 'user_greeting_count_user_id_fkey' => array('user', array('user_id' => 'id')), - ), - ); + return [ + 'fields' => [ + 'user_id' => ['type' => 'int', 'not null' => true, 'description' => 'user id'], + 'greeting_count' => ['type' => 'int', 'not null' => true, 'description' => 'the greeting count'], + 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'], + 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'], + ], + 'primary key' => ['user_id'], + 'foreign keys' => [ + 'user_greeting_count_user_id_fkey' => ['user', ['user_id' => 'id']], + ], + ]; } /** @@ -85,15 +76,16 @@ class User_greeting_count extends Managed_DataObject * @param integer $user_id ID of the user to get a count for * * @return User_greeting_count instance for this user, with count already incremented. + * @throws Exception */ - static function inc($user_id) + public static function inc($user_id) { $gc = User_greeting_count::getKV('user_id', $user_id); if (empty($gc)) { $gc = new User_greeting_count(); - $gc->user_id = $user_id; + $gc->user_id = $user_id; $gc->greeting_count = 1; $result = $gc->insert(); @@ -101,21 +93,25 @@ class User_greeting_count extends Managed_DataObject if (!$result) { // TRANS: Exception thrown when the user greeting count could not be saved in the database. // TRANS: %d is a user ID (number). - throw new Exception(sprintf(_m('Could not save new greeting count for %d.'), - $user_id)); + throw new Exception(sprintf( + _m('Could not save new greeting count for %d.'), + $user_id + )); } } else { $orig = clone($gc); - $gc->greeting_count++; + ++$gc->greeting_count; $result = $gc->update($orig); if (!$result) { // TRANS: Exception thrown when the user greeting count could not be saved in the database. // TRANS: %d is a user ID (number). - throw new Exception(sprintf(_m('Could not increment greeting count for %d.'), - $user_id)); + throw new Exception(sprintf( + _m('Could not increment greeting count for %d.'), + $user_id + )); } }