How to create Osclass Plugins - Madhouse

How to create Osclass plugins – part 1

Osclass, Plugins, Tutorials

This tutorial lays the foundation gives step-by-step explanations on how to develop a good plugin for Osclass. This is no absolute specifications but guidelines to make better-organized and coherent plugins for Osclass.

The official documentation on How to create a plugin for Osclass might be a little too short and this tutorial will give you a quite advanced overview on the subject.

Introduction

This tutorial is a three part tutorial:

For the sake of this example, we will be building a basic plugin called Madhouse HelloWorld whose code is available on Github. Download Madhouse HelloWorld on Github


  1. Conventions & guidelines
  2. Architecture of Osclass plugins
  3. Create the plugin itself
  4. Build the model layer!
  5. Build the controller layer!
  6. Build the view layer!
  7. Conclusion

1- Conventions and Guidelines

You’re about to develop a new plugin for Osclass, but first let’s establish some conventions on names and coding-style in Osclass.

1.1- Team name and prefix

Don’t use ‘osc_’ prefix!

Choose your own prefix to develop your plugins for Osclass: it’s cooler and safer because using osc_ as prefix for your helpers could get you into troubles like name clashes with Osclass functions or classes and that is really not cool!

Our team is called Madhouse, therefore we’re have chosen the mdh_ prefix. Your team name and prefix can be almost everything: name of your company, your favorite band, your own name… As long as it does not clash with another developer or with Osclass, you can Let your imagination flow.

1.2- Choose a plugin name

To choose a good and unique (and working) plugin name, please follow those rules:

  • Use only lowercase letters a-z, numbers 0-9 and underscore _;
  • Don’t use Osclass such as osc_something or osclass_thing;
  • Be creative! A cool name is what will get attention from the people;
  • Use your team name if you feel another plugin may have the same name, eg. Madhouse Facebook;

1.3- Coding-style

Follow the standard defined in the PSR-1 and PSR-2 documents.

Some more Osclass-specific guidelines:

  • Helper names:
    • MUST be lowercase, words separated by underscores _
    • MUST be prefixed with your unique prefix, remember, ours is mdh_
  • When using Preference:
    • Preference section MUST be plugin_{your_plugin_name}
    • Preference name MUST be lowercase and separated by underscores _

The general rule would be:

Try to be consistent with what Osclass use in the development of your plugin.

We will only differ from Osclass on classes names: using PSR-0 or PSR-4 depending if you use namespace (which you should) or not.

2- Directory structure of an Osclass Plugin

We have defined a directory structure for plugins – still inspired by what Osclass does – and improved by other sources and general PHP conventions.

An exemple of directory structure for an Osclass plugin

assets/ Resources used by the plugin. We will typically found sub-folders for CSS, Javascript, images, SQL, dependencies, etc.
classes/ Pretty much all the PHP code. We’ll mainly found sub-folders for models, controllers.
helpers/ helpers functions offered by the plugin.
index.php main plugin file. Contains plugin definition, hooks, routes, register / enqueue of scripts and styles, customizations of administration menu…
main.php Entrypoint of our plugin, no longer required if on Osclass 3.2 and greater. We will get back at this later.
oc-load.php PHP file which defines constants and loads (requires) classes, controllers, models, helpers, etc.
views/ folder for views file, both for frontend (web/) and backend (admin/)

3- Create the plugin itself

UPDATE: You don’t need Madhouse Utils anymore!

Osclass finds plugins by reading the index.php of each plugin folder located in oc-content/plugins/.

Lets create a folder /oc-content/plugins/madhouse_helloworld/ – where madhouse_helloworld is the name of our plugin. Create an index.php file in this directory containing the following informations:

/*
Plugin Name: Madhouse HelloWorld
Short Name: madhouse_helloworld
Plugin URI: https://wearemadhouse.wordpress.com/2013/10/11/how-to-develop-osclass-plugins/
Description: Example and starter plugin for Osclass, designed by Madhouse.
Version: 1.2.0
Author: Madhouse
Author URI: https://wearemadhouse.wordpress.com/
*/

/*
 * ==========================================================================
 * LOADING
 * ==========================================================================
 */

require_once __DIR__ . "/oc-load.php";

/*
 * ==========================================================================
 * INSTALL / UNINSTALL
 * ==========================================================================
 */

/**
 * (hook: install) Make installation operations
 * It creates the database schema and sets some preferences.
 * @returns void.
 */
function mdh_helloworld_install() {
}
osc_register_plugin(osc_plugin_path(__FILE__), 'mdh_helloworld_install');

/**
 * (hook: uninstall) Make un-installation operations
 * It destroys the database schema and unsets some preferences.
 * @returns void.
 */
function mdh_helloworld_uninstall() {
}
osc_add_hook(osc_plugin_path(__FILE__) . '_uninstall', 'mdh_helloworld_uninstall');

Create an empty file at the root of your plugin called oc-load.php that will contain constants and imports (require_once) of the PHP code of your plugin.

Now, if you go to the Osclass plugins page (Plugins > Manage Plugins), you should have a new plugin ready to be installed. You can try to install it. Congrats, it works right away but the plugin does not do anything yet: this is the simplest plugin for Osclass possible.

4- Build the model layer

Osclass is MVC – eg. Model-View-Controller – at its core and offer tools to build plugin that are to. Therefore, we will build our plugin following the same principles.

Note: You might not need the database for your plugin, if so, skip this step.

4.1- Create the database schema

First, let’s create an SQL file to define our database schema and its direct counter-part, a script to destroy this schema when uninstalling the plugin.

Create a SQL file under madhouse_helloworld/assets/model/install.sql:

CREATE TABLE IF NOT EXISTS /*TABLE_PREFIX*/t_mdh_helloworld_message (
    `pk_i_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `s_content` VARCHAR(30) NOT NULL,
    PRIMARY KEY(`pk_i_id`)
) ENGINE = InnoDB DEFAULT CHARACTER SET 'UTF8' COLLATE 'UTF8_GENERAL_CI';

INSERT INTO /*TABLE_PREFIX*/t_mdh_helloworld_message(s_content) VALUES('first message');
INSERT INTO /*TABLE_PREFIX*/t_mdh_helloworld_message(s_content) VALUES('second message');

Create another SQL file under the same madhouse_helloworld/assets/model/uninstall.sql directory:

DROP TABLE IF EXISTS /*TABLE_PREFIX*/t_mdh_helloworld_message;

_Note: /*TABLE_PREFIX*/ will automatically be replaced with the prefix defined in the config.php of your Osclass installation – by default oc_._

4.2- Create a DAO class

Now, ever heard of DAO class? This introduction on how to create a DAO for Osclass should give you the key points.

We’ll create our own DAO for Madhouse HelloWorld, so, create a new PHP file in madhouse_helloworld/classes/Madhouse/HelloWorld/Models/Message.php:

/**
 * Data Access Object (DAO) for messages.
 *
 * Performs operations on database for our plugin.
 *
 * @author Madhouse
 * @since 1.00
 */
class Madhouse_HelloWorld_Models_Message extends DAO
{
    function __construct() {
        parent::__construct();
        $this->setTableName('t_mdh_helloworld_message');
        $this->setPrimaryKey('pk_i_id');
        $this->setFields(array('pk_i_id', 's_content'));
    }

    /**
     * Singleton.
     */
    private static $instance;

    /**
     * Singleton constructor.
     * @return an MadhouseMessengerDAO object.
     */
    public static function newInstance() {
            if(!self::$instance instanceof self) {
                    self::$instance = new self;
            }
            return self::$instance;
    }
}

We extend the class DAO.php from Osclass core.

4.3- Load our DAO class

Remember that empty madhouse_helloworld/oc-load.php file that I made you create ? Add the proper require directive for our DAO class in this file to load our new class.

madhouse_helloworld/oc-load.php:

require_once __DIR__ . "/classes/Madhouse/HelloWorld/Models/Message.php";

4.4- Install & uninstall

To create our database schema and make our DAO class usable, we will update our install and uninstall procedure of our plugin.

Check the Github repository of Madhouse HelloWorld to find mdh_import_sql in the helpers/hUtils.php file. Copy this file and add the require in the oc-load.php file.

madhouse_helloworld/index.php:

/**
 * (hook: install) Make installation operations
 *      It creates the database schema and sets some preferences.
 * @returns void.
 */
function mdh_helloworld_install() {
    mdh_import_sql(mdh_current_plugin_path("assets/model/install.sql", false));
}
osc_register_plugin(osc_plugin_path(__FILE__), 'mdh_helloworld_install');

/**
 * (hook: uninstall) Make un-installation operations
 *      It destroys the database schema and unsets some preferences.
 * @returns void.
 */
function mdh_helloworld_uninstall() {
    mdh_import_sql(mdh_current_plugin_path("assets/model/uninstall.sql", false));
}
osc_add_hook(osc_plugin_path(__FILE__) . '_uninstall', 'mdh_helloworld_uninstall');

4.5- Model layer is done!

Everything is ready for the model layer of our plugin because extending the Osclass core class DAO.php gives us a whole lot of operations that we can do:

function findByPrimaryKey($value)
function updateByPrimaryKey($values, $key)
function deleteByPrimaryKey($value)
function listAll()
function insert($values)
function update($values, $where)
function delete($where)
function setTableName($table)
function getTableName()
function setPrimaryKey($key)
function getPrimaryKey()
function setFields($fields)
function getFields()
function checkFieldKeys($aKey)
function getTablePrefix()
function getErrorLevel()
function getErrorDesc()
public function count()

5- Build the controller layer!

Now we will create our controller layer to use our previously created model layer. Osclass has 3 base controller classes that you can extend for your plugin controllers.

 – Use for… Description
BaseModel Web Simple controller, without security check.
WebSecBaseModel Web Ensure user is logged in or redirect to login page.
AdminSecBaseModel Admin Ensure user is logged in and is an administrator or redirect to admin login page.

For the sake of this example, our HelloWorld plugin will require the user to be logged in. Therefore, our new controller class will extend the WebSecBaseModel.php.

If you have a recent version of Osclass (3.3 or superior), just continue reading. Otherwise, you should definitely update your Osclass, but in the meantime you can jump to 5.2 section below.

5.1- Osclass 3.3 and superior

Starting from Osclass 3.3, the controller layer of a plugin is made up of 3 things:

  • one controller or more;
  • one route or more;
  • one function added to the custom_controller hook;

5.1.1- Define a route for our plugin

Routes are available since Osclass v3.3 and have been a great improvements.

Update the madhouse_helloworld/index.php file to add the following code:

osc_add_route(
    mdh_current_plugin_name() . "_show",
    'helloworld/show/?',
    'helloworld/show/',
    mdh_current_plugin_name() . '/views/web/show.php'
);

This defines a route named madhouse_helloworld_show accessible on /helloworld/show/ and which displays a file /views/web/show.php that we will create later.

5.1.2- Create our controller class

madhouse_helloworld/classes/controllers/Web.php:

<?php

/**
 * Web controller, handling all logic for web (frontend).
 *
 *  It extends WebSecBaseModel by default which requires user to be logged in.
 *  You could choose to extend BaseModel for non-secure actions (ie. public profile)
 *
 * @since 1.00
 * @author -
 */
class Madhouse_HelloWorld_Controllers_Web extends WebSecBaseModel {

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

    /**
     * Business control for Madhouse HelloWorld Plugin for OSClass.
     */
    public function doModel() {
        switch(Params::getParam("route")) {
            case mdh_current_plugin_name() . "_show":
                // Do something !
            break;
            default:
                // Don't know what to do. Pretend not to exist.
                osc_add_flash_error_message(__("Oops! We got confused at some point. Try to refresh the page.", mdh_current_plugin_name()));
                $this->redirectTo(osc_base_url());
            break;
        }
    }
}

Here’s a really simple controller using the HTTP parameter route and where the first case is the name of the route we created. We will update the doModel() method to actually do something.

madhouse_helloworld/classes/controllers/Web.php:

/**
 * Business control for Madhouse HelloWorld Plugin for OSClass.
 */
public function doModel() {
    switch(Params::getParam("route")) {
        case mdh_current_plugin_name() . "_show":
            // Get our first message from our model layer.
            $message = Madhouse_HelloWorld_Models_Message::newInstance()->findByPrimaryKey(1);
            // Exports it to make it available to the view.
            View::newInstance()->_exportVariableToView(
                "mdh_helloworld_message",
                $message["s_content"]
            );
        break;
        default:
            // Don't know what to do. Pretend not to exist.
            osc_add_flash_error_message(__("Oops! We got confused at some point. Try to refresh the page.", mdh_current_plugin_name()));
            $this->redirectTo(osc_base_url());
        break;
    }
}

As we have done with our DAO class, you need to add this new class to the oc-load.php file.

5.1.3- Use the custom_controller hook

Last bit of the controller layer of the plugin – and probably the most important -, the custom_controller hook has been introduced to make the glue between our route and our controller.

function mdh_helloworld_controller()
{
    if(mdh_is_helloworld()) {
        $do = new Madhouse_HelloWorld_Controllers_Web();
        $do->doModel();
    }
}
osc_add_hook("custom_controller", "mdh_helloworld_controller");

Osclass runs this hook whenever a route is called, and plugins can use it to process each HTTP requests and do something. So, in the function, we check if the current page is our plugin route, and if it is, we call our controller to handle the request. Simple as that!

Let’s jump to 5.3- section to put the final touch to our controller layer.

5.2- Osclass 3.2 and inferior

(deprecated)

The controller layer of Osclass plugins is made up of two things:

  • one or more controllers;
  • an entrypoint file at the root of the plugin folder – main.php;

5.2.1- Create a controller class

Since it’s an older version of Osclass we’re dealing with, our controller will be called Legacy.

madhouse_helloworld/classes/controllers/WebLegacy.php:

<?php

/**
 * Web controller, handling all logic for web (frontend).
 * For Osclass 3.2 and inferior !
 * It extends WebSecBaseModel by default which requires user to be logged in.
 * You could choose to extend BaseModel for non-secure actions (ie. public profile)
 *
 * @since 1.20
 * @author -
 */
class Madhouse_HelloWorld_Controllers_WebLegacy extends WebSecBaseModel {

    public function __construct() {
        parent::__construct();
        $this->ajax = true;
    }

    /**
     * Business control for Madhouse HelloWorld Plugin for OSClass.
     */
    public function doModel() {
        switch(Params::getParam("do")) {
            case "show":
                // Do something !
            break;
            default:
                // Don't know what to do. Pretend not to exist.
                osc_add_flash_error_message(__("Oops! We got confused at some point. Try to refresh the page.", mdh_current_plugin_name()));
                $this->redirectTo(osc_base_url());
            break;
        }
    }

    /**
     * Makes the right file be rendered.
     *  This function seems to be duplicated everywhere in the controllers.
     * @param $file relative path of the required file (starting from the current web theme path).
     * @see oc-includes/osclass/controller.
     */
    public function doView($file) {
        Madhouse_Utils_Controllers::doView($file);
    }
}

It does not do much. It is a pretty basic controller extending WebSecBaseModel.php Osclass core class. Users (to access this controller, users will have to log in).

We will now add some logic, adding a case “show” at the doModel() method of our controller. We want to retrieve the first message from the model and make it available to the view (show.php, that will be created later on).

    /**
     * Business control for Madhouse HelloWorld Plugin for OSClass.
     */
    public function doModel() {
        switch(Params::getParam("do")) {
            case "show":
                // Get our first message from our model layer.
                $message = Madhouse_HelloWorld_Models_Message::newInstance()->findByPrimaryKey(1);
                // Exports it to make it available to the view.
                View::newInstance()->_exportVariableToView(
                    "mdh_helloworld_message",
                    $message["s_content"]
                );

                // Call the view.
                $this->doView("show.php");
            break;
            default:
                // Don't know what to do. Pretend not to exist.
                osc_add_flash_error_message(__("Oops! We got confused at some point. Try to refresh the page.", "madhouse_helloworld"));
                $this->redirectTo(osc_base_url());
            break;
        }
    }

Now that it’s ready: load this class in your oc-load.php.

5.2.2- Entrypoint : main.php

Now, we need make the controller available to Osclass by creating a main.php file at the root of the plugin folder, as follow:

if(! mdh_plugin_is_ready(mdh_current_plugin_name())) {
    mdh_handle_error_ugly();
}

switch(Params::getParam("page")) {
    case "ajax":
        $do = new Madhouse_HelloWorld_Controllers_WebLegacy();
        $do->doModel();
    break;
    default:
        mdh_handle_error_ugly();
    break;
}

That’s a little bit tricky to explain. We use the page=ajax&action=custom to be able to call our entrypoint file (main.php). This is just a trigger for our previous work, just ensuring that the request is correct and forwarding to our custom controller.

5.3- Helpers

Controller layer is almost finished. Before going further and create our first view file, we will create helpers to make it easier for theme developers.

In a new file madhouse_helloworld/helpers/hHelloWorld.php:

/**
 * url for our "show" view.
 * @return string the url to the show page.
 * @since 1.00
 */
function mdh_helloworld_show_url()
{
    if(osc_version() >= 330) {
        return osc_route_url(mdh_current_plugin_name() . "_show");
    }
    return osc_ajax_plugin_url(mdh_current_plugin_name() . "/main.php") . "&do=show";
}

/**
 * gets the exported message to display.
 * @return string the message.
 * @since 1.00
 */
function mdh_helloworld_get_message()
{
    return view::newinstance()->_get("mdh_helloworld_message");
}

/**
 * tells if the current page belongs to helloworld.
 * @return bool true if the current page is helloworld, false otherwise.
 * @since  1.20
 */
function mdh_is_helloworld()
{
    if(preg_match('/^' . mdh_current_plugin_name() . '.*$/', params::getparam("route"))) {
        return true;
    }
    return false;
}

Once again, you have to load this file by adding it to the oc-load.php file!

6- Build a view layer!

To display the result of our plugin, we need a view file that relates to our defined route.

6.1- Create the view file

Once again, it depends on the version of your Osclass version.

6.1.1. View file for Osclass 3.3 and superior

Create a new file madhouse_helloworld/views/web/show.php :

    <?php _e("Here's your message :", "madhouse_helloworld"); ?> 
    «<?php echo mdh_helloworld_get_message(); ?>»

Ready to test the whole thing. Go to the URL : /helloworld/show/

If you’re logged in on the frontend, you will see this :

Here's your message : «first message»

And, if you’re not logged in, you will be redirected to the login page – thanks to WebSecBaseModel.php.

6.1.2- View file for Osclass 3.2 and inferior

Create a new file madhouse_helloworld/views/web/show.php:

    <?php _e("Here's your message :", "madhouse_helloworld"); ?> 
    «<?php echo mdh_helloworld_get_message(); ?>»

Now that you have everything. Go to this URL :

/index.php?page=ajax&action=custom&ajaxfile=madhouse_helloworld/main.php&do=show

7- Conclusion

You are now officially a Kung-Fu master in the art of creating Osclass plugins. Stay tuned for more posts on plugins for Osclass!

See you chimps.

16 thoughts on “How to create Osclass plugins – part 1

  1. i tried the first step and went to admin, but did not find the plugin name?
    is that because i dont have Madhouse Utils installed? (you said “From now on, you need Madhouse Utils installed and enabled. “)
    but if you follow your link u will never find the Utils. is that just a joking?

    1. Hi,

      This tutorial works with Madhouse Utils 1.14.
      We’ll update it to use the newer version soon.

      In the meantime I can provide you the old Madhouse Utils (v1.14)😉

  2. This is great!, I’m new to OSClass but I did install it and run my own classfied ad site. It’s look professional and I really like it.
    I’m looking forward to be part of this community. Soon, I’ll create my new plugin🙂

  3. osc_add_route(
    mdh_current_plugin_name() . “_show”,
    ‘helloworld/show/?’,
    ‘helloworld/show/’,
    mdh_current_plugin_name() . ‘/views/web/show.php’
    );

    How can I use for params?
    ‘helloworld/show/2’,
    ‘/views/web/show.php?item=2’

    1. Hi,

      Pagination is a bit tricky.

      You have to make a regexp like this:

      osc_add_route(
      mdh_current_plugin_name() . “_show”,
      ‘helloworld/show/?([0-9]*)/?,
      ‘helloworld/show/{p}’,
      mdh_current_plugin_name() . ‘/views/web/show.php’
      );

      And then update the helper to get the url:

      function mdh_messenger_inbox_url($page=null, $num=null) {
      	 $params = array();
      
              if(! is_null($page)) {
                  $params["p"] = $page;
              }
      
              if(! is_null($num)) {
                  $params["n"] = $num;
              }
      
      		return osc_route_url(mdh_current_plugin_name() . "_inbox", $params);
      	}
      
  4. thanks
    but not working😦

    You have to make a regexp like this:

    osc_add_route(
    mdh_current_plugin_name() . “_show”,
    ‘helloworld/show/?([0-9]*)/?, <——– are You missed closing quote here?
    ‘helloworld/show/{p}’, <——— why this {p}?
    mdh_current_plugin_name() . ‘/views/web/show.php’
    );

    I typed in url helloworld/show/3
    but its always showing this helloworld/show
    actually I have list of item title when you click the title then how show all details of the title with using item id?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s