FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Plugin Base · AquilaCMS/AquilaCMS Wiki · GitHub

This repository was archived by the owner on Oct 9, 2025. It is now read-only.
/ AquilaCMS Public archive

Plugin Base

MaelVB edited this page Jul 30, 2021 · 2 revisions

Plugin Creation

Welcome to the guide "How to create a plugin for AquilaCMS"

Author : Nans / n4n5


Things to know

  • After downloading the base-plugin-aquila here, you can see the following files :
/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 !

I. Configuration page of the plugin

We are going to :

  • add a controller to the HTML page
  • add interaction between controller and HTML page
  • view different API calls to save information

Here is a diagram to make it easier to understand :

I.1. Add a controller to the page

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.

I.2. Add interaction between controller and HTML page

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

I.3. Differents API call to save informations

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

I.3.1. Create your own call to the API

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.

I.3.2. Setup the receive part (the API), linked to a service

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 !

I.3.3. Set up a service

In the try{}, you have two choices :

  • Use a predefined function from the services
  • Use a custom function (and create it)

I.3.3.1. Use a predefined function

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()

I.3.3.2. Use a custom function

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};

I.3.4. Use the service in the controller (with the API call)

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 :

II. Add HTML to others page of Aquila

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

II.1. Creation of the decorator

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

II.2. Injection in the code

There are two solutions to inject some code :

  • with a hook
  • with the creation of a hook

II.2.1. Injection with creation of an hook

II.2.1.1 Setting up the service of the decorator

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

II.2.1.2 Setting up the injection in the controller 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

II.2.1.3 Setting up the injection in the HTML page

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>

II.2.1. Injection with a hook

II.2.1.1 Search for a good hook

You need to check in the code if there is a hook to place your code.

II.2.1.2 Use the hook

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

II.3. Setting up the directive

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

II.3.1. Setting up the controller of the directive

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

II.3.2. Setting up the HTML of the directive

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.)

III. Finish your plugin

Congrats ! You have finished your plugin, but not really. There are 3 things left to do :

III.3. Add translations

See Plugin Creation > Translations

III.2. Change the name

After doing all this, your plugin name is still base-aquila-plugin, you now need to change the name.

Things to remember :

  • don't forget every name of each file in your the plugin
  • replace all base-plugin-aquila in every file
  • change name of base variable
  • change name in info.json and initAfter.js

Finally, you can create the README.md of your plugin !

III.3. Test 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 !

Congrats ! You've made it

Here are some bonus articles to improve your module :

Wiki pages Pages 43

Clone this wiki locally


Back | FazBrowse Home | New Git URL