Wiki

Clone wiki

my_plugin / Home

Make a plugin in Osclass

How to make a plugin in Osclass? or better yet, what is the correct way to make a plugin in Osclass?

The content of this tutorial answers that question by showing the development of a plugin prototype with basic functionalities (CRUD), but following the same Osclass Model-View-Controller (MVC), you will get a complete starter template that you can download for free at the end and use it as a guide. Doing this the right way you will discover that everything makes sense, it will make things much easier for you when climbing development and teamwork. And by direct consequence you will also better understand the Osclass classified system.

Files structure:

|_ oc-content:
		|_ plugins:
				|_ my_plugin:
					struct.sql
					model:
						|_ MyPlugin.php
					helpers:
						|_ hUtils.php
					classes:
						|_ datatables:
							|_ CrudDataTable.php
					controller:
						|_ admin:
							|_ crud.php
							|_ settings.php
					views:
						|_ admin:
							|_ crud.php
							|_ settings.php
					oc-load.php
					parts:
						|_ public:
							|_ my_plugin_content.php
					index.php
					languages:
						|_ en_EN:
							|_ messages.po
						|_ es_ES:
							|_ messages.po
							|_ messages.mo

In Osclass a plugin is a folder that is located within the plugins directory, create a folder there and name it my_plugin. You can create all these empty files and develop them in the specific order shown here.

struct.sql

CREATE TABLE /*TABLE_PREFIX*/t_plugin_table_one (
	pk_i_id INT NOT NULL AUTO_INCREMENT,
	s_name VARCHAR(60) NULL,
	i_num INT NULL,
	dt_pub_date DATETIME NOT NULL,
	dt_date DATETIME NOT NULL,
	s_url TEXT NOT NULL,
	b_active BOOLEAN NOT NULL DEFAULT FALSE,

	PRIMARY KEY (pk_i_id)
)	ENGINE=InnoDB DEFAULT CHARACTER SET 'UTF8' COLLATE 'UTF8_GENERAL_CI';

CREATE TABLE /*TABLE_PREFIX*/t_plugin_table_two (
	pk_i_id INT NOT NULL AUTO_INCREMENT,
	fk_i_one_id INT NULL,

	PRIMARY KEY (pk_i_id),
	INDEX (fk_i_one_id),
	FOREIGN KEY (fk_i_one_id) REFERENCES /*TABLE_PREFIX*/t_plugin_table_one (pk_i_id)
)	ENGINE=InnoDB DEFAULT CHARACTER SET 'UTF8' COLLATE 'UTF8_GENERAL_CI';

As its name indicates, it is the sql structure of the plugin tables that store the collected data as it interacts with the functions of the plugin (in case it requires it, a plugin sometimes does not require its own tables of database).

Meanwhile, this tutorial uses a basic structure with two tables 't_plugin_table_one' and 't_plugin_table_two'; In the second table (t_plugin_table_two) there is a foreign sql relationship with the first.

NOTE: The structure of this prototype plugin is designed only to demonstrate the manipulation of different types of data and their relationship between them does not meet any specific logical objective.

MyPluginModel.php

To set the model, you must create a class (MyPlugin) that extends to the Osclass DAO class. It is important that this class has the singleton pattern so that it can be instantiated directly anywhere in the plugin.

<?php 
/**
* Model of My Plugin
*/
class MyPlugin extends DAO
{
    private static $instance;

    /**
    * Singleton Pattern
    */
    public static function newInstance()
    {
        if(!self::$instance instanceof self) {
            self::$instance = new self;
        }
        return self::$instance;
    }

    function __construct()
    {
        parent::__construct();
    }

The singleton pattern instantiates the class in this way MyPlugin()::newInstance()->someMethod(); .

- Installation methods of the [model/MyPlugin.php] plugin:

Other important parts of this section are the methods that allow the loading of the database that is related to the installation of the plugin itself.

    /**
    * Import tables to database using sql file
    */
    public function import($file)
    {
        $sql  = file_get_contents($file);

        if(!$this->dao->importSQL($sql)) {
            throw new Exception("Error importSQL::MyPlugin".$file);
        }
    }

    /**
    * Config the plugin in osclass database, settings the preferences table 
    * and import sql tables of plugin from struct.sql
    */
    public function install()
    {
        $this->import(MY_PLUGIN_PATH.'struct.sql');
        osc_set_preference('version', '1.0.0', 'my_plugin', 'STRING');
        osc_set_preference('field_one', '1', 'my_plugin', 'BOOLEAN');
        osc_set_preference('field_two', '0', 'my_plugin', 'BOOLEAN');
        osc_set_preference('field_three', '', 'my_plugin', 'STRING');
        osc_run_hook('my_plugin_install');
    }

The constant MY_PLUGIN_PATH is explained in index.php.

- Uninstall method [model/MyPlugin.php]:

This method is responsible for doing the complete dismantling of the tables in the database and the rest of the plugin uninstallation. NOTE: Remember to do the DROP TABLE in a manner contrary to the order in which the tables were installed, in case there is a foreign relationship.

    /**
    * Delete all fields from the 'preferences' table and also delete all tables of plugin
    */
    public function uninstall()
    {
        $this->dao->query(sprintf('DROP TABLE %s', $this->getTable_table_two()));
        $this->dao->query(sprintf('DROP TABLE %s', $this->getTable_table_one()));
        Preference::newInstance()->delete(array('s_section' => 'my_plugin'));
        osc_run_hook('my_plugin_uninstall');
    }

helpers/hUtils.php

This is a file of general functions of the plugin that serve as support, such as URL validators or date generators. NOTE: The "helpers" should not necessarily exist in the project, but in this case I have left a couple and you can see how they work within the plugin, also in case they are useful.

classes/datatables/CrudDataTable.php

The classes subdirectory is usually also where third-party libraries are stored, an example would be: classes / MCrypt.php. In this case it was necessary to create another subdirectory inside, a folder called datatables to contain the DataTables generating files there, since a project could have more than one. In this entry DataTable in Osclass the operation of the DataTables will be explained.

controller/admin/crud.php

<?php
// Controller of My Plugin CRUD
class CAdminMyPluginCRUD extends AdminSecBaseModel
{

    // Business Layer...
    public function doModel()
    {
        switch (Params::getParam('plugin_action')) {
            case 'done':
                // Form process...
                
                osc_add_flash_ok_message(__('Ok custom message!', 'my_plugin'), 'admin');
                
                ob_get_clean();

                $this->redirectTo($_SERVER['HTTP_REFERER']);
                //$this->redirectTo(osc_route_admin_url('my-plugin-admin-settings'));
                break;

            default:
                $var = "Hello world";
                $this->_exportVariableToView('var', $var);
                break;
        }
    }
    
}

To create a controller in which it will be used within the administration (oc-admin) you must start by creating a class that extends AdminSecBaseModel. The inside of doModel() is evaluated with the help of the switch structure if an action parameter is being received, if it is 'done', it executes what is inside that case, if it receives nothing then the 'switch' is found in default, notice that from there use $this->_exportVariableToView('var', $var); to prepare the value of $ var to the scope of the views and can then be received from a view with the get() function.

views/admin/crud.php

<?php
// For get vars from controller
$var = __get('var');
?>

<h2 class="render-title"><?php _e("Title of this view", 'my_plugin'); ?></h2>

<!-- Form... -->
<?php echo $var; ?>

<!-- DataTable -->

<script>
	// JavaScript function
	function function_name(var) {
	    return false;
	}
</script>

The views receive the variables that are passed from the controller, to obtain its value use get(). Here you can develop everything as regards the front-end issue, if you want to see the full content of this part, download the files of this prototype plugin (My Plugin). Translatable text strings are contained within the typical _e() function which indicates that Osclass uses the translation system with .po and .mo files (at the end of the tutorial that topic is discussed).

oc-load.php

<?php
// Model
require_once MY_PLUGIN_PATH . "model/MyPlugin.php";

// Helpers
require_once MY_PLUGIN_PATH . "helpers/hUtils.php";

// Controllers
require_once MY_PLUGIN_PATH . "controller/admin/crud.php";
require_once MY_PLUGIN_PATH . "controller/admin/settings.php";

It is a file that loads the remains of plugin components logically hierarchically and separately. That order must be respected, otherwise you will have problems.

parts/public/my_plugin_content.php

The parts directory is used to locate pieces of views or dynamic HTML content with PHP that is embedded in the content using the osc_add_hook() or osc_add_filter() function, these types of content would be separated into folders that would be named admin, user or public , depending on whether the use is within the administration (oc-admin) or in some region of the template (public or user), that is, the same logic is followed as in views. Inside these files it is recommended to avoid instantiating some kind of model directly, use helpers for it, index.php explains how to register these files.

index.php

It is the main file of a plugin, even depending on the project, it could be the only file of the plugin. In this case it consists of several parts.

- Plugin letterhead [index.php]:

<?php
/*
Plugin Name: My Plugin
Plugin URI: https://www.website.com/my_plugin
Description: My Plugin description
Version: 1.0.0
Author: My Name
Author URI: https://www.website.com/
Short Name: my-plugin
Plugin update URI: https://www.website.com/my_plugin/update
*/

osclass-1.png

- Plugin folder path [index.php]:

// Paths
define('MY_PLUGIN_FOLDER', 'my_plugin/');
define('MY_PLUGIN_PATH', osc_plugins_path() . MY_PLUGIN_FOLDER);

The plugin needs to know how is called its own folder that contains it and therefore the full path of where it is located, this to be able to install, or uninstall, later reference is made to these functionalities.

- Load components [index.php]:

// Prepare model, controllers and helpers
require_once MY_PLUGIN_PATH . "oc-load.php";

Load the oc-load.php file.

- URL paths [index.php]:

// URL routes
osc_add_route('my-plugin-admin-crud', MY_PLUGIN_FOLDER.'views/admin/crud', MY_PLUGIN_FOLDER.'views/admin/crud', MY_PLUGIN_FOLDER.'views/admin/crud.php');
osc_add_route('my-plugin-admin-settings', MY_PLUGIN_FOLDER.'views/admin/settings', MY_PLUGIN_FOLDER.'views/admin/settings', MY_PLUGIN_FOLDER.'views/admin/settings.php');

osclass-2.png

It also directly loads the views.

- Headings in the administration panel [index.php]:

// Headers in the admin panel
osc_add_hook('admin_menu_init', function() {
    osc_add_admin_submenu_divider(
        "plugins", __("My Plugin", 'my_plugin'), "my_plugin", "administrator"
    );

    osc_add_admin_submenu_page(
        "plugins", __("CRUD", 'my_plugin'), osc_route_admin_url("my-plugin-admin-crud"), "my-plugin-admin-crud", "administrator"
    );

    osc_add_admin_submenu_page(
        "plugins", __("Settings", 'my_plugin'), osc_route_admin_url("my-plugin-admin-settings"), "my-plugin-admin-settings", "administrator"
    );
});

osclass-4.png

- Load controllers [index.php]:

// Load the controllers, depend of url route
function my_plugin_admin_controllers() {
	switch (Params::getParam("route")) {
		case 'my-plugin-admin-crud':
			$filter = function($string) {
                return __("CRUD - My Plugin", 'my_plugin');
            };

            // Page title (in <head />)
            osc_add_filter("admin_title", $filter, 10);

            // Page title (in <h1 />)
            osc_add_filter("custom_plugin_title", $filter);

            $do = new CAdminMyPluginCRUD();
            $do->doModel();
			break;

		case 'my-plugin-admin-settings':
			$filter = function($string) {
                return __("Settings - My Plugin", 'my_plugin');
            };

            // Page title (in <head />)
            osc_add_filter("admin_title", $filter, 10);

            // Page title (in <h1 />)
            osc_add_filter("custom_plugin_title", $filter);

            $do = new CAdminMyPluginSettings();
            $do->doModel();
			break;
	}
}
osc_add_hook("renderplugin_controller", "my_plugin_admin_controllers");

Here, load the controllers with their respective views that depend on the URL path, and at the same time add the page title to the view.

- Headers or page titles [index.php]:

$filter = function($string) {
    return __("CRUD - My Plugin", 'my_plugin');
};

// Page title (in <head />)
osc_add_filter("admin_title", $filter, 10);

// Page title (in <h1 />)
osc_add_filter("custom_plugin_title", $filter);

2459523164-osclass-5.png

- Controller [index.php]:

$do = new CAdminMyPluginCRUD();
$do->doModel();

- Registration of functions or parts of dynamic HTML code with PHP [index.php]:

function my_plugin_content() {
	include MY_PLUGIN_PATH . 'parts/public/my_plugin_content.php';
}
// Add the function in a hook, you run on the template with: osc_run_hook('my_plugin');
osc_add_hook('my_plugin', 'my_plugin_content');

In this case the contents of the parts/public/my_plugin_content.php file will run in any region with the following function <?php osc_run_hook ('my_plugin'); ?>.

- Functions for installation, configuration and uninstallation link [index.php]:

// 'Configure' link
function my_plugin_configure_admin_link() {
	osc_redirect_to(osc_route_admin_url('my-plugin-admin-settings'));
}
// Show 'Configure' link at plugins table
osc_add_hook(osc_plugin_path(__FILE__)."_configure", 'my_plugin_configure_admin_link');


// Call uninstallation method from model (model/MyPlugin.php)
function my_plugin_uninstall() {
	MyPlugin::newInstance()->uninstall();
}
// Show an Uninstall link at plugins table
osc_add_hook(osc_plugin_path(__FILE__) . '_uninstall', 'my_plugin_uninstall');


// Call the process of installation method
function my_plugin_install() {
	MyPlugin::newInstance()->install();
}
// Register plugin's installation
osc_register_plugin(osc_plugin_path(__FILE__), 'my_plugin_install');

osclass-6.png

Languages

languages/en_EN/messages.po

The .po files are used to collect all translatable text strings that are contained in functions such as _e("Hello", 'my_plugin') or ("Hello", 'my_plugin'), and then rewritten to a different language . The languages folder contains the directories of each language, these folders are named by the language code, for English it is used in_EN, for Spanish it would be es_ES.

- Letterhead [languages/en_EN/messages.po]:

msgid ""
msgstr ""
"Project-Id-Version: My Plugin\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-23 01:00+0000\n"
"PO-Revision-Date: \n"
"Last-Translator: author <author@mail.com>\n"
"Language: en_US\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: _e;__\n"
"X-Poedit-Basepath: .\n"
"X-Generator: Poedit 1.8.7\n"

These files consist of two parts, one is the letterhead where the version of the program that edited the file is described, creation date, revision date, last translator, language code and the other part are the collection of translation variables.

- Translation variables [languages/en_EN/messages.po]:

msgid "Settings"
msgstr ""

msgid "Settings - My Plugin"
msgstr ""

msgid "Edit register"
msgstr ""

msgid "Add new register"
msgstr ""

In the case of the English language, which is the main and default language of the Osclass project, its messages.po file will only contain the original English variables (msgid), but the variables where a respective translation (msgstr) would hurt would be empty, This means that this language as a section is not really necessary for some functionality in the plugin but serves as a template to facilitate the creation of other languages.

languages/es_ES/messages.po

msgid "Settings"
msgstr "ConfiguraciĆ³n"

msgid "Settings - My Plugin"
msgstr "ConfiguraciĆ³n - My Plugin"

msgid "Edit register"
msgstr "Editar registro"

msgid "Add new register"
msgstr "Agregar nuevo registro"

Here it is up to the msgstr variables to contain the translation.

languages/es_ES/messages.mo

de12 0495 0000 0000 3700 0000 1c00 0000
d401 0000 4900 0000 8c03 0000 0000 0000
b004 0000 2100 0000 b104 0000 2300 0000
d304 0000 1f00 0000 f704 0000 0c00 0000

The .mo files are the compiled and binary version of the .po, this is obtained through the Poedit program when saving the messages.po file. All languages for them to work must have their .mo version except the English language folder, because it is not necessary.

Installation

After having developed each of the above files, you can try installing the plugin in Manage Plugins from the Osclass Administration Panel, if you prefer you can move the folder of this plugin to another part, compress it in zip and upload it from the Panel of Administration, then click on 'Install'.

NOTE: Change name of main folder to my_plugin to be installable and work!

Updated