-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
123 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
# Custom user plugins | ||
|
||
## Overview | ||
RaspAP's `PluginManager` provides a framework for developers to create custom plugins to extend the application's functionality. To facilitate this, the `SamplePlugin` [repository](https://github.com/RaspAP/SamplePlugin) exists to give developers an entry point into creating their own plugins. Getting started with the `SamplePlugin` is described in the following sections. | ||
|
||
## The SamplePlugin | ||
The `SamplePlugin` implements a `PluginInterface` and is automatically loaded by RaspAP's `PluginManager`. | ||
|
||
![sample-plugin](https://github.com/user-attachments/assets/5a6ecf40-2b14-4502-8b63-5e85f9ade6f6){: style="width:420px"} | ||
|
||
|
||
Several common plugin functions are demonstrated in `SamplePlugin`, as well as a method for persisting session data in plugin instances. Each plugin has its own namespace, meaning that classes and functions are organized to avoid naming conflicts. Plugins are self-contained and render their own templates from inside the `/templates` directory. | ||
|
||
## Getting started | ||
The `SamplePlugin` requires an installation of RaspAP, either via the [Quick install](quick.md) method or with a [Docker container](docker.md). The default application path `/var/www/html` is used here. If you've chosen a different install location, substitute this in the steps below. | ||
|
||
1. Begin by creating a fork of the [SamplePlugin repository](https://github.com/RaspAP/SamplePlugin). | ||
2. SSH into the device hosting RaspAP, change to the install location and create a `/plugins` directory. | ||
``` | ||
cd /var/www/html | ||
sudo mkdir plugins | ||
``` | ||
3. Change to the `/plugins` directory and clone your `SamplePlugin` fork: | ||
``` | ||
cd plugins | ||
sudo git clone https://github.com/[your-username]/SamplePlugin | ||
``` | ||
4. The `PluginManager` will autoload the plugin. A **:fontawesome-solid-plug: Sample Plugin** item will appear in the sidebar. | ||
5. Proceed with customizing the plugin by using the tips in the next sections. | ||
|
||
## Scope of functionality | ||
The `SamplePlugin` implements the server-side methods needed to support basic plugin functionality. It initalizes a `Sidebar` object and adds a custom navigation item. User input is processed with `handlePageAction()` and several common operations are performed, including: | ||
|
||
1. Saving plugin settings | ||
2. Starting a sample service | ||
3. Stopping a sample service | ||
|
||
Template data is then collected in `$__template_data` and rendered by the `main.php` template file located in `/templates`. Property get/set methods are demonstrated with `apiKey` and `serviceStatus` values. A method is then used in `persistData()` to save the `SamplePlugin` object data. | ||
|
||
!!! tip "Tip" | ||
Importantly, `SamplePlugin` does _not_ use the PHP `$_SESSION` object as this super global can lead to conflicts with other user plugins. | ||
|
||
On the front-end, Bootstrap's [form validation](https://getbootstrap.com/docs/5.3/forms/validation/) is used to validate user input. A custom JavaScript function is used to generate a random `apiKey` value. The `sample.service` LED indicator is functional, as are the service stop/start form buttons. | ||
|
||
## Customizing | ||
The `SamplePlugin` demonstrates basic plugin functions without being overly complex. It's designed with best practices in mind and made to be easily modified by developers. | ||
|
||
### Unique plugin names | ||
Most plugin authors will probably begin by renaming `SamplePlugin` to something unique. The `PluginManager` expects the plugin folder, file, namespace and class to follow the same naming convention. When renaming the `SamplePlugin` ensure that each of the following entities uses the same plugin name: | ||
|
||
|
||
| Entity | Type | | ||
|--------------------------------------------------|------------| | ||
| `plugins/SamplePlugin` | folder | | ||
| `plugins/SamplePlugin/SamplePlugin.php` | file | | ||
| `namespace RaspAP\Plugins\SamplePlugin` | namespace | | ||
| `class SamplePlugin implements PluginInterface` | class | | ||
|
||
That is, replace each occurrence of `SamplePlugin` with your plugin name in these entities. | ||
|
||
### Plugin logic and templates | ||
Plugin classes and functions are contained in `SamplePlugin.php`. The parent template `main.php` and child tab templates are used to render template data. | ||
|
||
``` | ||
├── SamplePlugin/ | ||
│ ├── SamplePlugin.php | ||
│ └── templates/ | ||
│ ├── main.php | ||
│ └── tabs/ | ||
│ ├── about.php | ||
│ ├── basic.php | ||
│ └── status.php | ||
``` | ||
|
||
You may wish to omit, modify or create new tabs. This is done by editing `main.php` and modifying the contents of the `/tabs` directory. | ||
|
||
### Sidebar item | ||
The `PluginInterface` exposes an `initalize()` method that is used to create a unique sidebar item. The properties below can be customized for your plugin: | ||
|
||
``` | ||
$label = _('Sample Plugin'); | ||
$icon = 'fas fa-plug'; | ||
$action = 'plugin__'.$this->getName(); | ||
$priority = 65; | ||
``` | ||
|
||
You may specify any icon in the [Font Awesome 6.6 free library](https://fontawesome.com/icons) for the sidebar item. The priority value sets the position of the item in the sidebar (lower values = a higher priority). | ||
|
||
### Permissions | ||
For security reasons, the `www-data` user which the `lighttpd` web service runs under is not allowed to start or stop daemons or execute commands. RaspAP's installer adds the `www-data` user to [sudoers](https://www.sudo.ws/about/intro/), but with restrictions on what commands the user can run. If your plugin requires execute permissions on a Linux binary not present in RaspAP's [sudoers file](https://github.com/RaspAP/raspap-webgui/blob/master/installers/raspap.sudoers), you will need to add this yourself. To edit this file, the `visudo` command should be used. This tool safely edits `sudoers` and performs basic validity checks before installing the edited file. | ||
|
||
Execute `visudo` and edit RaspAP's sudoers file like so: | ||
|
||
``` | ||
sudo visudo /etc/sudoers.d/090_raspap | ||
``` | ||
|
||
An example of adding entries to support a plugin's service is shown below: | ||
|
||
``` | ||
www-data ALL=(ALL) NOPASSWD:/bin/systemctl start sample.service | ||
www-data ALL=(ALL) NOPASSWD:/bin/systemctl stop sample.service | ||
www-data ALL=(ALL) NOPASSWD:/bin/systemctl status sample.service | ||
``` | ||
|
||
Wildcards ('`*`') and regular expressions are supported by `sudoers` but [care should be taken when using them](https://www.sudo.ws/posts/2022/03/sudo-1.9.10-using-regular-expressions-in-the-sudoers-file/). | ||
|
||
## Multiple instances | ||
The `PluginManager` is a managerial class responsible for locating, instantiating and coordinating plugins. Through the use of namespaces and object data persistence in `SamplePlugin`, any number of user plugins may be installed to `/plugins` and run concurrently. | ||
|
||
![multiple-plugins](https://github.com/user-attachments/assets/2d156efe-8cfc-49e7-b682-219d2db4eeee){: style="width:640px"} | ||
|
||
As previously noted, developers should avoid using PHP's `$_SESSION` object in their plugins to prevent conflicts with other plugin instances. An alternative method for session data storage is provided in the `SamplePlug` `persistData()` function. | ||
|
||
!!! note "Note" | ||
The `persistData()` function writes serialized data to the volatile `/tmp` directory which is cleared on each system boot. For this reason, it should not be used as a permanent method of data persistence. However, this functionality roughly approximates PHP's `$_SESSION` object; the difference being that each plugin's data is isolated from other plugin instances. | ||
|
||
|
||
## Publishing your plugin | ||
The `SamplePlugin` contains an "About" tab where you may provide author information, a description and link to your project. If you've authored a plugin you feel would be useful to the RaspAP community, you're encouraged to share it in the `SamplePlugin` repo's [Discussions](https://github.com/RaspAP/SamplePlugin/discussions). | ||
|
||
## Discussions | ||
Questions or comments about creating user plugins? Join the [discussion here](https://github.com/RaspAP/raspap-webgui/discussions/). |