| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Welcome to the guide "How to create a plugin for AquilaCMS"
Author : Nans / n4n5
/base-plugin-aquila
├── /app
├── /views
│ base-plugin-aquila.controllers.js
│ base-plugin-aquila.module.js
│ base-plugin-aquila.routes.js
│ base-plugin-aquila.services.js
├── /routes
│ base-plugin-aquila.js
├── /services
│ baseServices.js
├── info.json
└── initAfter.js
All of these files will be useful for the development of the plugin.
Aquila works with API calls to get and save information
After the installation of your plugin :
Make sure you read the Plugin Files page !
We are going to :
Here is a diagram to make it easier to understand :

To add a controller to a webpage, you need to set up the link between controller and page. To do that, use the file /app/base-plugin-aquila.routes.js
const BaseRoutes = angular.module('aq.base-plugin-aquila.routes', ['ngRoute']);
BaseRoutes.config(['$routeProvider',
function ($routeProvider) {
$routeProvider
.when('/base-plugin-config', {
templateUrl : 'app/base-plugin-aquila/views/base-plugin-config.html',
controller : 'BasePluginController',
resolve : {
loggedin : checkLoggedin
}
});
}
]);Legend :
- /base-plugin-config is the URL display for the user in the backoffice
- controller : 'BasePluginController' is the name of the controller
- templateUrl : 'app/base-plugin-aquila/views/base-plugin-config.html' is the 'real' link of our HTML file in Aquila
This file is the link between the HTML and the controller.
Now, you can view the HTML file in /app/views/base-plugin-config.html
You can delete everything and only keep the first three lines (and the three end tags)
<form name="form" class="form-horizontal" novalidate ng-submit="save(false)" role="form">
<ns-box data-title="base-plugin.title" title-icon="fa fa-home" close-href="#/plugins">
<ns-buttons is-edit-mode="isEditMode" save-and-quit="save(true)" disable-save="disableSave" return-path="/modules" form="form">Legend :
- note that the tags ns-box and ns-buttons are custom Aquila tags
To check if the controller is working, add an input :
<input type="text" class="form-control" ng-model="plugin.prenom" required>Now, go to the controller file : /app/base-plugin-aquila.controllers.js.
You can see here the name of the controller : BasePluginController
const BaseControllers = angular.module('aq.base-plugin-aquila.controllers', []);
BaseControllers.controller('BasePluginController', ['$scope', '$location', '$q', 'toastService',
function ($scope, $location, $q, toastService) {
$scope.plugin = {
nom : 'Chirac',
prenom : 'Jacques',
date : '29 novembre 1932'
};
}
]);Legend
- note the name of the controller BasepluginController. It's the same name in /app/base-plugin-aquila.routes.js (makes sense)
As you can see, there is a value in $scope.plugin.prenom, so the ng-model of the input (which is plugin.prenom) is defined.
Now you can check if the configuration page displays the value of the variable in the input.
Now, let's add interaction. For instance, you can do that with ns-switch (a custom switch used in Aquila).
Just add a line in the HTML file :
<ns-switch ng-model="plugin.switch" ng-change="onChange(plugin.switch)" yes-value="Actif" no-value="Non-Actif"></ns-switch>Legend :
- the yes-value and the no-value are the values displayed in the switch
- the onChange(plugin.switch) is a function that will be activated containing the value of the switch as an argument (true or false)
You now need to create the onChange() function in the controller, for example you can write :
$scope.onChange = function (valueOfSwitch){
if(valueOfSwitch){
//do something, example :
toastService.toast("success", "true");
}else{
//do others things, example :
toastService.toast("danger", "false");
}
}Legend
- in this example, a pop up will show true when the ng-switch is on, and false when it is off
One function which is crucial to add in the controller is the $scope.save(), because the HTML code contains ng-submit and save-and-quit, that both use the function $scope.save()
<form name="form" class="form-horizontal" novalidate ng-submit="save(false)" role="form">
<ns-box data-title="base-plugin.title" title-icon="fa fa-home" close-href="#/plugins">
<ns-buttons is-edit-mode="isEditMode" save-and-quit="save(true)" disable-save="disableSave" return-path="/modules" form="form">Legend :
- note the ng-submit="save(false)" in the HTML code
- note the save-and-quit="save(true)" in the HTML code
This function needs a return path if the user wants to save & quit, for example :
$scope.save = function(isQuit){
//save the configuration of the plugin
if(isQuit){
return $location.path(`/modules`);
}
}Legend
- the return instruction returns the user to the path you wrote
First of all, a simple API call can be made with $http, you just need to inject the dependency $http like the toastService dependency in the controller parameters :
BaseControllers.controller('BasePluginController', ['$scope', '$http', '$location', '$q', 'toastService',
function ($scope, $http, $location, $q, toastService) {
//The code of the controller
}
]);Legend :
- you can see $http is put twice to avoid mistakes during minification
- Be aware of the order of the parameters ! It needs to be the same order
A call to the API with this method is good but not the best.
You can use it for predefined API calls. For example, a call to have the ReadMe of the plugin :
$http.post('/v2/modules/md', {
moduleName: "base-plugin-aquila"
}).then(function (response) {
$scope.md = response.data.html
});Legend
- with this API call, you just need the name of the plugin pluginName to get the ReadMe
- remember to add HTML code to use the $scope.md
First, you need to modify /app/base-plugin-aquila.services.js
Start by changing the name of the factory (choose a name corresponding to your plugin and write the first letter in uppercase to identify it correctly).
If you change the name of the factory now, it will be easier later.
const BaseServices = angular.module('aq.base-plugin-aquila.services', ['ngResource']);
BaseServices.factory('NameOfTheFactory', ['$resource',
function ($resource) {
return $resource('/v2/nameToIdentifyTheplugin', {}, {
query : {method: 'POST', params: {}}
});
}
]);Legend :
- note the name of the factory, here it's NameOfTheFactory
Next, you need to add some lines to the factory. Each line is a call/a query to the API, and a line looks like this :
getSomething : {method: 'POST', params: {type: 'aNameForTheAPI'}, isArray: false}Legend
- note the method here is is a POST request
- note that getSomething is the name of a function we will use later
- note that isArray is not very useful, so you can put it away
- the type: 'aNameForTheAPI' is used to know the URL when calling the API:
return $resource('/v2/nameToIdentifyTheplugin/:type', {}, { getSomething : {method: 'POST', params: {type: 'aNameForTheAPI'}, isArray: false} }Legend :
- The :type will be replaced by the object {type: 'aNameForTheAPI'}
So for now, we have a function >> a request to an URL of the API.
Now, you need to setup the receive part.
You are about to modify the API, so each change will need a restart of Aquila to be functional.
The API is made with Node.js
First of all, you need to register the API URL. To do that, edit the file /routes/base-plugin-aquila.js
It's very simple, you just need to link a request (from /v2/nameToIdentifyTheplugin/aNameForTheAPI) to a function, for example :
module.exports = function (app) {
app
.get('/v2/nameToIdentifyTheplugin/getAThing', getSomething)
.post('/v2/nameToIdentifyTheplugin/setPlugin', setConfigPlugin)
.post('/v2/nameToIdentifyTheplugin/setAThing', setSomething)
}Legend
- translate : if there is a GET on /v2/nameToIdentifyTheplugin/setAThing, activate the function setSomething
- translate : if there is a POST on /v2/nameToIdentifyTheplugin/setPlugin, activate the function setConfigPlugin
- note that a same url can take multiple types of requests (POST, GET, PUT...)
- the URL needs to match with the url in the factory
After that, declare a function below (you already have some examples in the file).
This function is used to manage the request. It's recommended to use try{}catch{} instructions in case of a potential error.
Now, it's time to set up a service !
In the try{}, you have two choices :
There are in Aquila some predefined functions, such as getConfig which is used to set the configuration of the plugin. You need to require it so you can add :
const {setConfig, getConfig} = require('../../../services/modules');Legend :
- Add this at the top of the file in /routes/base-plugin-aquila.js to use these functions
And use these function like this :
async function setConfigPlugin(req, res, next){ //the function we have declared
try{
await setConfig(info.name, req.body); //the predefined function
res.end();
}catch(err){
next(err);
}
}Legend :
- note the async and the await
- note the use of the next() function (to counter error)
- note the inject of next in parameters
- note there is no return from the setConfig function, so you need to use a res.end()
You may use the Service of the plugin
const ServicePlugin = require('../services/baseServices');Legend :
- the const ServicePlugin indicates to use the service of the plugin
The use of the service will look like that :
async function setSomething(req, res, next){
try{
return res.json(await ServicePlugin.setSmthg());
}catch(err){
next(err);
}
}Legend :
- note that the function setSomething() is used by the API
- note that the function setSmthg() is used to call a service
And now, you can create your service ! To do that, edit the file /services/baseServices.js
A good practice is to assign your function to a const, and then exports all the const.
Example : writing a file
const setFile = async function(req) {
const filePath = './nameOfTheFile.xml';
if (await fs.access(filePath)) {
await fs.writeFile(filePath, req.content);
}
};Legend :
- note that you need to require fs to do that (or fsp, a custom fs)
const fs = require('../../../utils/fsp');Legend :
- add this at the top of /routes/baseServices.js to use fsp or fs
After your function, remember to exports the const
module.exports = {setFile};To summarize, we have done : a function >> a request to the API >> the API receiving part >> the service >> the result sent back
note : remember, if your service doesn't return anything, you need to add res.end(); in the function of the API
To use the service in the controller, add :
NameOfTheFactory.nameOfTheRequestFunction({/*Your data in object format, if there are*/}, function(response){
//the callback with the response (if there are)
});Example :
NameOfTheFactory.getSomething({}, function(response){
$scope.thing = response;
});Legend :
- after receiving the result, you assigned a $scope variable to the response, that way, you can save the result and use it later
A diagram of what we did :

Summary :
We are going to use a decorator and to complicate things, we are going to create a directive linked to this decorator to display something in a page.
Here is a diagram to represent this :
[decorator] >> is insert into html by a service
[HTML code of decorator] >> is remplaced by the HTML of the directive with the good name >> which is controlled by the controller of the directive
To do that, we need to create a decorator. A decorator has already been created in the file /app/base-plugin-aquila.module.js
A decorator is, to simplify, a link between two things : a service to inject the code and the HTML code we want to inject.
We are going to use a directive. A directive is a linker between an HTML TAG, some HTML code and a controller
angular.plugin('adminCatagenApp').config(['$provide', function ($provide) {
$provide.decorator('HookPageProduct', [
'$delegate',
function myServiceDecorator($delegate) {
$delegate = $delegate.concat([
{
label : 'Poids',
component_template :
'<div class="col-sm-10">'
+ '<input type="number" ng-model="client.weight" class="form-control" />'
+ '</div>'
}
]);
return $delegate;
}
]);
}]);Legend :
- HookPageProduct is the name of the service used by the decorator
- component_template is the HTML you want to inject in the page you want to change
- in this case, you are using a directive, so component_template is not HTML but just a custom tag (see below)
In the file /app/base-plugin-aquila.module.js , the decorator has HTML code in string format, but in this case its going to be different. Use a simple HTML tag :
{
label : 'Poids',
component_template : '<custom-tag></custom-tag>'
}Legend :
- The name of the tag is very important and is linked with the directive name
There are two solutions to inject some code :
Go the services.js of the page we want to change, and to then add this code (in this example we are going to modify the Products page) :
ProductServices.service('HookPageProduct', function ()
{
return [];
});Legend :
- the keyword HookPageProduct needs to be as same as in the decorator
- ProductServices is the name of the controller of the product page
Now to add the service, add some code in the controller of the page we want to change. Then, change the HTML code of the page
Now, go to the controller of the page to inject the decorator (here HookPageProduct). Then, add the injection in the scope, e. g :
ProductControllers.controller("nsProductGeneral", [
"$scope", "$filter", "HookPageProduct", "SetAttributesV2", "AttributesV2", "$modal", "ProductsV2",
function ($scope, $filter, HookPageProduct, SetAttributesV2, AttributesV2, $modal, ProductsV2) {
//maybe some code of the controller here
$scope.hook = HookPageProduct; // add a value to the scope
//maybe some more code of the controller here
}]);Legend :
- note the HookPageProduct in the parameters of the controller, also note that it is there twice
Finally, some HTML to use the value we added to the scope :
<span bind-html-compile="oneHook.component_template" ng-repeat="oneHok in hook"></span>Legend :
- the ng-repeat is just a loop (it is useful when you have multiple elements)
- the bind-html-compile to take the componant_template of the element oneHook (used in the loop) and then compile and place it. In our case, it places <custom-tag></custom-tag>
You need to check in the code if there is a hook to place your code.
The ng-repeat directive is used in the HTML, so add a value to the variable that is used in the loop.
ProductControllers.controller("nsProductGeneral", [
"$scope", "$filter", "HookPageProduct", "TheSecondInjection", "SetAttributesV2", "AttributesV2", "$modal", "ProductsV2",
function ($scope, $filter, HookPageProduct, TheSecondInjection, SetAttributesV2, AttributesV2, $modal, ProductsV2) {
//maybe some code of the controller here
$scope.hook = [ HookPageProduct, TheSecondInjection]; //add a value to the scope
//maybe some more code of the controller here
}]);Legend :
- You just made a array with two values : the old HookPageProduct and the new TheSecondInjection
- note that the decorator and the service.js need to be added just like above
The directive will replace the HTML code (the HTML tag) that has been injected. To do that, create a new file in /app/ named base-plugin-aquila.directives.js
const directivesPluginBase = angular.plugin('aq.base-plugin-aquila.directives', []);
directivesPluginBase.directive('customTag', [
function () {
return {
restrict : 'E',
templateUrl : 'app/base-plugin-aquila/views/htmlOfDirective.html',
controller: 'newControllerFromPlugin',
scope : false
};
}
]);Legend
- note the name of the directive customTag is the same writing of the HTML tag that we put in the decorator. The change is that uppercase letter are now lowercase with a dash ( customTag <> custom-tag)
- templateUrl stores a link to the final HTML that you want to inject
- controller is the controller name for the HTML which is located in the templateUrl
- note the const name for the directives, choose it wisely
To set up the controller, go to /app/base-plugin-aquila.controllers.js and add a new controller below the first controller :
// here code of the first controller
BaseControllers.controller('newControllerFromPlugin', [
'$scope', '$http', '$modal', '$rootScope', 'toastService',
function($scope, $http, $modal, $rootScope, toastService) {
//the code to control and interact the HTML
}]);Legend
- newControllerFromPlugin keyword needs to be the same as in the directive
- $modal is not useful if you don't use it
Finally, create the file in base-plugin-aquila/app/views/htmlOfDirective.html, the HTML you want to add to the page
The controller can now interact and control the HTML you have injected (the same as in steps I.1. and I.2.)
Congrats ! You have finished your plugin, but not really. There are 3 things left to do :
See Plugin Creation > Translations
After doing all this, your plugin name is still base-aquila-plugin, you now need to change the name.
Things to remember :
Finally, you can create the README.md of your plugin !
The last step is, of course, testing your plugin.
Try to import your plugin to check if it's working correctly, and maybe add more features to your plugin !
Here are some bonus articles to improve your module :
Installation
Get started
Core reference
Themes
Images
Plugin Creation
Hook
Updating
Testing
| Back | FazBrowse Home | New Git URL |