356 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			356 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|   | Plugin Development | ||
|  | ======================= | ||
|  | 
 | ||
|  | SamplePlugin.php | ||
|  | ----------------------- | ||
|  | 
 | ||
|  | Each plugin requires a main class to interact with the GNU social system. | ||
|  | 
 | ||
|  | The main class usually extends the Plugin class that comes with GNU social. | ||
|  | 
 | ||
|  | The class has standard-named methods that will be called when certain events | ||
|  | happen in the code base. These methods have names like 'onX' where X is an | ||
|  | event name (see EVENTS.txt for the list of available events). Event handlers | ||
|  | have pre-defined arguments, based on which event they're handling. A typical | ||
|  | event handler: | ||
|  | 
 | ||
|  | ```php | ||
|  | function onSomeEvent($paramA, &$paramB) | ||
|  | { | ||
|  |     if ($paramA == 'jed') { | ||
|  |         throw new Exception(sprintf(_m("Invalid parameter %s"), $paramA)); | ||
|  |     } | ||
|  |     $paramB = 'spock'; | ||
|  |     return true; | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Event Handlers | ||
|  | ----------------------- | ||
|  | 
 | ||
|  | Event handlers must return a Boolean value. | ||
|  | 
 | ||
|  | If they return false, all other event handlers for this event (in other plug-in) | ||
|  | will be skipped, and in some cases the default processing for that event would | ||
|  | be skipped. This is great for replacing the default action of an event. | ||
|  | 
 | ||
|  | If the handler returns true, processing of other event handlers and the default | ||
|  | processing will continue. This is great for extending existing functionality. | ||
|  | 
 | ||
|  | If the handler throws an exception, processing will stop, and the exception's | ||
|  | error will be shown to the user. | ||
|  | 
 | ||
|  | Installation | ||
|  | ------------------ | ||
|  | 
 | ||
|  | To install a plugin (like this one), site admins add the following code to their | ||
|  | config.php file: | ||
|  | 
 | ||
|  | ```php | ||
|  | addPlugin('Sample'); | ||
|  | ``` | ||
|  | 
 | ||
|  | Plugins must be installed in one of the following directories: | ||
|  | 
 | ||
|  | * local/plugins/{$pluginclass}.php | ||
|  | * local/plugins/{$name}/{$pluginclass}.php | ||
|  | * local/{$pluginclass}.php | ||
|  | * local/{$name}/{$pluginclass}.php | ||
|  | * plugins/{$pluginclass}.php | ||
|  | * plugins/{$name}/{$pluginclass}.php | ||
|  | 
 | ||
|  | Here, `{$name}` is the name of the plugin, like 'Sample', and `{$pluginclass}` | ||
|  | is the name of the main class, like 'SamplePlugin'. Plugins that are part of | ||
|  | the main GNU social distribution go in 'plugins' and third-party or local ones | ||
|  | go in 'local'. | ||
|  | 
 | ||
|  | Simple plugins can be implemented as a single module. Others are more complex | ||
|  | and require additional modules; these should use their own directory, like | ||
|  | 'local/plugins/{$name}/'. All files related to the plugin, including images, | ||
|  | JavaScript, CSS, external libraries or PHP modules should go in the plugin | ||
|  | directory. | ||
|  | 
 | ||
|  | Plugin Configuration | ||
|  | ------------------ | ||
|  | 
 | ||
|  | Plugins are configured using public instance attributes. To set their values, | ||
|  | site administrators use this syntax: | ||
|  | 
 | ||
|  | ```php | ||
|  | addPlugin('Sample', array('attr1' => 'foo', 'attr2' => 'bar')); | ||
|  | ``` | ||
|  | 
 | ||
|  | The same plugin class can be initialized multiple times with different arguments: | ||
|  | 
 | ||
|  | ```php | ||
|  | addPlugin('EmailNotify', array('sendTo' => 'evan@status.net')); | ||
|  | addPlugin('EmailNotify', array('sendTo' => 'brionv@status.net')); | ||
|  | ``` | ||
|  | 
 | ||
|  | ```php | ||
|  | class SamplePlugin extends Plugin | ||
|  | { | ||
|  |     public $attr1 = null; | ||
|  |     public $attr2 = null; | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Initialization | ||
|  | ------------------ | ||
|  | 
 | ||
|  | 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. | ||
|  | 
 | ||
|  | ```php | ||
|  | function initialize() | ||
|  | { | ||
|  |     return true; | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Clean Up | ||
|  | ------------------ | ||
|  | 
 | ||
|  | Plugins overload this method to do any cleanup they need, like disconnecting from | ||
|  | remote servers or deleting temp files or so on. | ||
|  | 
 | ||
|  | ```php | ||
|  | function cleanup() | ||
|  | { | ||
|  |     return true; | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Database schema setup | ||
|  | ------------------ | ||
|  | 
 | ||
|  | Plugins can add their own tables to the GNU social database. Plugins should use | ||
|  | GNU social's schema interface to add or delete tables. The ensureTable() method | ||
|  | provides an easy way to ensure a table's structure and availability. | ||
|  | 
 | ||
|  | By default, the schema is checked every time GNU social is run (say, when a Web | ||
|  | page is hit). Admins can configure their systems to only check the schema when | ||
|  | the checkschema.php script is run, greatly improving performance. However, they | ||
|  | need to remember to run that script after installing or upgrading a plugin! | ||
|  | 
 | ||
|  | ```php | ||
|  | function onCheckSchema() | ||
|  | { | ||
|  |     $schema = Schema::get(); | ||
|  | 
 | ||
|  |     // '''For storing user-submitted flags on profiles''' | ||
|  | 
 | ||
|  |     $schema->ensureTable('user_greeting_count', | ||
|  |                           array(new ColumnDef('user_id', 'integer', null, | ||
|  |                                               true, 'PRI'), | ||
|  |                                 new ColumnDef('greeting_count', 'integer'))); | ||
|  | 
 | ||
|  |     return true; | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Load related modules when needed | ||
|  | ------------------ | ||
|  | 
 | ||
|  | Most non-trivial plugins will require extra modules to do their work. Typically | ||
|  | these include data classes, action classes, widget classes, or external libraries. | ||
|  | 
 | ||
|  | This method receives a class name and loads the PHP file related to that class. | ||
|  | By tradition, action classes typically have files named for the action, all | ||
|  | lower-case. Data classes are in files with the data class name, initial letter | ||
|  | capitalized. | ||
|  | 
 | ||
|  | Note that this method will be called for *all* overloaded classes, not just ones | ||
|  | in this plugin! So, make sure to return true by default to let other plugins, | ||
|  | and the core code, get a chance. | ||
|  | 
 | ||
|  | ```php | ||
|  | function onAutoload($cls) | ||
|  | { | ||
|  |     $dir = dirname(__FILE__); | ||
|  | 
 | ||
|  |     switch ($cls) | ||
|  |     { | ||
|  |     case 'HelloAction': | ||
|  |         include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php'; | ||
|  |         return false; | ||
|  |     case 'User_greeting_count': | ||
|  |         include_once $dir . '/'.$cls.'.php'; | ||
|  |         return false; | ||
|  |     default: | ||
|  |         return true; | ||
|  |     } | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Map URLs to actions | ||
|  | ------------------ | ||
|  | 
 | ||
|  | This event handler lets the plugin map URLs on the site to actions (and thus an | ||
|  | action handler class). Note that the action handler class for an action will be | ||
|  | named 'FoobarAction', where action = 'foobar'. The class must be loaded in the | ||
|  | onAutoload() method. | ||
|  | 
 | ||
|  | ```php | ||
|  | function onRouterInitialized($m) | ||
|  | { | ||
|  |     $m->connect('main/hello', | ||
|  |                 array('action' => 'hello')); | ||
|  |     return true; | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Modify the default menu to link to our custom action | ||
|  | ------------------ | ||
|  | 
 | ||
|  | Using event handlers, it's possible to modify the default UI for pages almost | ||
|  | without limit. In this method, we add a menu item to the default primary menu | ||
|  | for the interface to link to our action. | ||
|  | 
 | ||
|  | Action Class | ||
|  | ------------------ | ||
|  | 
 | ||
|  | The Action class provides a rich set of events to hook, as well as output methods. | ||
|  | 
 | ||
|  | ```php | ||
|  | function onEndPrimaryNav($action) | ||
|  | { | ||
|  |     // '''common_local_url()''' gets the correct URL for the action name we provide | ||
|  | 
 | ||
|  |     $action->menuItem(common_local_url('hello'), | ||
|  |                       _m('Hello'), _m('A warm greeting'), false, 'nav_hello'); | ||
|  |     return true; | ||
|  | } | ||
|  | 
 | ||
|  | function onPluginVersion(&$versions) | ||
|  | { | ||
|  |     $versions[] = array('name' => 'Sample', | ||
|  |                         'version' => STATUSNET_VERSION, | ||
|  |                         'author' => 'Brion Vibber, Evan Prodromou', | ||
|  |                         'homepage' => 'http://example.org/plugin', | ||
|  |                         'rawdescription' => | ||
|  |                         _m('A sample plugin to show basics of development for new hackers.')); | ||
|  |     return true; | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | hello.php | ||
|  | ------------------ | ||
|  | 
 | ||
|  | This section is taken directly from the 'hello.php'. ( plugins/Sample/hello.php ) | ||
|  | 
 | ||
|  | 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. | ||
|  | 
 | ||
|  | ```php | ||
|  | class HelloAction extends Action | ||
|  | { | ||
|  |     var $user = null; | ||
|  |     var $gc   = null; | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Take arguments for running | ||
|  | ------------------ | ||
|  | 
 | ||
|  | This method is called first, and it lets the action class get all its arguments | ||
|  | and validate them. It's also the time to fetch any relevant data from the database. | ||
|  | 
 | ||
|  | Action classes should run parent::prepare($args) as the first line of this | ||
|  | method to make sure the default argument-processing happens. | ||
|  | 
 | ||
|  | ```php      | ||
|  | function prepare($args) | ||
|  | { | ||
|  |     parent::prepare($args); | ||
|  | 
 | ||
|  |     $this->user = common_current_user(); | ||
|  | 
 | ||
|  |     if (!empty($this->user)) { | ||
|  |         $this->gc = User_greeting_count::inc($this->user->id); | ||
|  |     } | ||
|  | 
 | ||
|  |     return true; | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Handle request | ||
|  | ------------------ | ||
|  | 
 | ||
|  | This is the main method for handling a request. Note that most preparation | ||
|  | should be done in the prepare() method; by the time handle() is called the | ||
|  | action should be more or less ready to go. | ||
|  | 
 | ||
|  | ```php | ||
|  | function handle($args) | ||
|  | { | ||
|  |     parent::handle($args); | ||
|  | 
 | ||
|  |     $this->showPage(); | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Title of this page | ||
|  | ------------------ | ||
|  | 
 | ||
|  | Override this method to show a custom title. | ||
|  | 
 | ||
|  | ```php | ||
|  | function title() | ||
|  | { | ||
|  |     if (empty($this->user)) { | ||
|  |         return _m('Hello'); | ||
|  |     } else { | ||
|  |         return sprintf(_m('Hello, %s'), $this->user->nickname); | ||
|  |     } | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Show content in the content area | ||
|  | ------------------ | ||
|  | 
 | ||
|  | The default GNU social page has a lot of decorations: menus, logos, tabs, all | ||
|  | that jazz. This method is used to show content in the content area of the | ||
|  | page; it's the main thing you want to overload. This method also demonstrates | ||
|  | use of a plural localized string. | ||
|  | 
 | ||
|  | ```php | ||
|  | function showContent() | ||
|  | { | ||
|  |     if (empty($this->user)) { | ||
|  |         $this->element('p', array('class' => 'greeting'), | ||
|  |                        _m('Hello, stranger!')); | ||
|  |     } else { | ||
|  |         $this->element('p', array('class' => 'greeting'), | ||
|  |                        sprintf(_m('Hello, %s'), $this->user->nickname)); | ||
|  |         $this->element('p', array('class' => 'greeting_count'), | ||
|  |                        sprintf(_m('I have greeted you %d time.', | ||
|  |                                   'I have greeted you %d times.', | ||
|  |                                   $this->gc->greeting_count), | ||
|  |                                   $this->gc->greeting_count)); | ||
|  |     } | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Return true if read only. | ||
|  | ------------------ | ||
|  | 
 | ||
|  | Some actions only read from the database; others read and write. The simple | ||
|  | database load-balancer built into GNU social will direct read-only actions to | ||
|  | database mirrors (if they are configured) and read-write actions to the master database. | ||
|  | 
 | ||
|  | This defaults to false to avoid data integrity issues, but you should make sure | ||
|  | to overload it for performance gains. | ||
|  | 
 | ||
|  | ```php | ||
|  | function isReadOnly($args) | ||
|  | { | ||
|  |     return false; | ||
|  | } | ||
|  | ``` | ||
|  | 
 |