Saturday, September 25, 2010

Hello World - PyroCMS Module

/*---------------------------------------------------------------------------------------------
Update 10/16/2011:

This tutorial has been here for older version (< 1.0.x) of PyroCMS. Latest Stable PyroCMS release is v1.3.2
Please download and use following tutorial files which are for PyroCMS 1.3.2:

PyroCMS Hello World Module tutorial pdf for v1.3.2
Download Sample Hello World Module PyroCMS 1.3.2

Pyro docs have been significantly improved over the year. You can refer it too. If you need developers to work on your PyroCMS project you can contact us here - Semicolon Developers.

(Many thanks to Alan Martin from South Africa who forwarded me these updates.)

---------------------------------------------------------------------------------------------*/
PyroCMS Version: 0.0.9.9.x
What’s here:
------------------------------------------------------------
PyroCMS Module – Folder Location?
PyroCMS Module – Files Required & Folder Structure?
PyroCMS Module – Frontend/Backend Snapshot (of what we are going to build)
PyroCMS Module – Coding the Hello World module with full MVC implementation 
Why "<![CDATA  MySql stuffs     ]]>" required to be in correct format in details.xml ?
Hello world module Code
Summary
----------------------------------------------------------
With this text, I assume your familiarity with PyroCMS folder structure and theming, and you are testing on xampp.  I would explain how to make PyroCMS Module with very basic concepts from Codeigniter and PyroCMS.  Do double check what i have highlight here,  you may get errors installing/running the module we create, otherwise.
PyroCMS is good, but its documentation is not enough/not clear. I mean, once you know stuffs, its very easy to understand things at one shot, but it is rarely the case with beginners, that’s why i thought i would put the example on it.
Don’t copy-paste the code from the example below rather download source code from github, in copy pasting you might face quotes errors. mysql query might not work.

PyroCMS Module – Folder Location?

PyroCMS Core Modules
…..xampp\htdocs\pyrocms\application\modules\pages
”pages” is pyrocms core module that handles the pyroCMS page element

Custom Modules / Third Party Module / we are creating one now

Custom Modules are placed under projectname\third_party\modules  folder
xampp\htdocs\pyrocms\third_party\modules\helloworld
pyrocms – our project name
helloworld – the module we are going to create.
Remember Core and Third Party modules and their directories.

PyroCMS Module – Files Required?

PyroCMS requires at least a details.xml and one controller inside the module-name folder to be a module. I will explain file requirements for different types of module creation. Filenames and folders, are understood easily if you know Codeigniter MVC conventions. If you are not familiar with CI and trying to build PyroCMS module, still its easy to grasp the folder structure with one easy attempt like this. You can have following folders inside a custom module folder - config, controllers, helpers, libraries, models, views, js, css, img.
1. helloworld\details.xml
-> its basic structure is explained in detail below
-> this file contains module name, description, version etc.. and some useful configuration options that help PyroCMS understand our new module. Say Hello! Introduce your module here.
2. helloworld\controllers\helloworld.php
-> the name of the controller (file name & class name) keep them same as module name
-> If you need module just for front end and nothing required in backend, you’d need at least one controller, and extend the controller class from Public_Controller.
-> if you need to access these module from backend (like CRUD interface in backend), you’ll need  a controller class helloworld\controllers\admin.php which would extent Admin_Controller
When i write a file name like helloworld\controllers\helloworld.php i expect, you have created a helloworl.php controller inside controllers folder inside our helloworld module.
3. helloworld\views\helloworld.php
-> view file which would be used for displaying data from module controller
->If you don’t need to access database for your module these 3 files will suffice your need for creating module, otherwise you need model class too. Take your time to understand the folder structure.
-> if you thought to put backend admin controllers in setp 2 above, then think about placing its views too.

let’s place some view files as for admin side in a views\admin folder for easy structure: 

helloworld\views\admin\index.php
-> pass data to this view from admin controller
helloworld\views\admin\sidebar.php
-> sidebar partial that’s shown in left sidebar of the default backend theme.
-> partial is just few tags and php codes together. think of it as general “view file” . For eg. the box in the sidebar of other modules you see while browsing in admin.
4. helloworld\models\helloworld_m.php

So when we require database access or to implement business logic, we create models :
helloworld_m.php  <— this is a convention of underscore m, model class name would be Helloworld_m . You can create more model files as per your need.
Don’t forget to place these file on respective Models/Views/Controllers folder according the folder structure in the figure (at end of this section below)
1. helloworld\details.xml
2. helloworld\controllers\helloworld.php
      ----> helloworld\controllers\admin.php  [controller for admin interface]
3. helloworld\views\view_file.php
    -------> helloworld\views\admin\index.php –> admin main view
    ------> helloworld\views\admin\sidebar.php –> admin sidebar partial
4. helloworld\models\helloworld_m.php
If your module is complex, you end with more files, that’s okay.
PyroCMS module file/folder structure is MVC, just bundled within a module folder. That’s what makes PyroCMS HMVC ( Hierarchical Model View Controller). PyroCMS is codeigniter based, if you know codeigniter you have an option to understand HMVC and build your own CMS ;) Good Luck.

Here’s the PyroCMS helloworld module folder structure:
module_structure

PyroCMS Module – Frontend/Backend Snapshot

Let’s have a look at what our module will look in front-end and backend

Frontend: <h1><?php echo $msg; ?></h1> that’s it
front
(to see a module in action, we’d after installing helloworld module link one header navigation to our module)

Section of what you see when you add new navigation link:
link_module
Backend:

Module installed and listed in the Admin –> Modules-> Third party Section (we don’t create view for this listing, PyroCMS automatically lists our new module there if successfully installed, information is taken from details.xml)
modulelist
We have – index.php and  sidebar.php  for admin side view,
when we build template from these two view that will look as below on the admin side.
Hello World Module Page on admin side: 
 admin
noticed the – left sidebar? main content? helloworld link on the admin navigation, link to hello world module in the dropdown modules list ?

PyroCMS Module – Coding the module

The code is just for illustration purpose. My idea is to help you create and install your own custom module.

1. helloworld\details.xml
2. helloworld\controllers\helloworld.php
      a. ----> helloworld\controllers\admin.php  [controller for admin interface]
3. helloworld\views\view_file.php
      a. -------> helloworld\views\admin\index.php –> admin main view 
      b. ------> helloworld\views\admin\sidebar.php –> admin sidebar partial
4. helloworld\models\helloworld_m.php
we’ll create 1 (details.xml) + 2 (controllers) + 3 (views) + 1 (model file) = 7 files in total and respective folders Models\Views\Controllers and place them inside MVC folder structure.

1. helloworld\details.xml

A. basic file / when no backend and MySQL required:

-> When we don’t need custom table for our module in database or when we are just working on PyroCMS tables to access data, we can have a minimal details.xml as follows.
<?xml version="1.0" encoding="UTF-8"?>
<module version="0.1">
  <name>   
    <en>Helloworld</en>   
  </name>
  <description> 
    <en>Displays Hello World! Message to user</en>   
  </description>  
  <skip_xss>1</skip_xss>
  <is_frontend>1</is_frontend> 
  <is_backend>0</is_backend>
  <is_backend_menu>0</is_backend_menu>   
</module>

where:
<name> &  <description>  will help list the module in admin->modules->third party modules list.
<skip_xss>1</skip_xss> – skip_xss true
<is_frontend>1</is_frontend>  - front end of the module exists <is_backend>0</is_backend>  - module has no backend support
<is_backend_menu>0</is_backend_menu>   - module will not be listed in backend admin navigation
B. Advanced file / when backend of module exists and our module will have its own database table to operate:
<?xml version="1.0" encoding="UTF-8"?>
<module version="0.1">
  <name>   
    <en>Helloworld</en>   
  </name>
  <description> 
    <en>Displays Hello World! Message to user</en>   
  </description>  
  <skip_xss>1</skip_xss>
  <is_frontend>1</is_frontend> 
  <is_backend>1</is_backend>
  <is_backend_menu>1</is_backend_menu>   
    <controllers>
        <controller name="admin">
            <method>index</method>           
        </controller>
    </controllers> 
    <install>
<![CDATA[  
DROP TABLE IF EXISTS hello_world;
-- command split --
CREATE TABLE hello_world (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`msg` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL
) ENGINE = MYISAM ; 
-- command split --
INSERT INTO `hello_world` (`id`,`msg`)
VALUES (NULL , 'hello world from PyroCMS module, from db'),
(NULL , 'Greetings from @bhu1st');
]]>
  </install>
<uninstall>
<![CDATA[
  DROP TABLE IF EXISTS hello_world;
]]>
</uninstall>
</module>
Did you see the changes??
is_backend set to one. i.e module accessible in backend. If database table are used just for storing information and no backend access required this can be set to zero.
<is_backend>1</is_backend>   - we are going to build module backend accessible
<is_backend_menu>1</is_backend_menu>  - module will be listed in admin side navigation menu  

Name of the admin controller and its methods

We add code  below when we create module that have backend interface.
<controller name=”admin”> tells we are going to create a
helloworld\controllers\admin.php  - controller
The controller that will extend Admin_Controller and we would have one method named index in it.
<controllers>
       <controller name="admin">
           <method>index</method>            
       </controller>
</controllers> 

MySQL Queries required at module install time
Before explaining this i want you to remember that details.xml file is a XML document.
Let me explain, What is this "<![CDATA  MySql stuffs     ]]>" doing here?

Everything inside a CDATA section is ignored by the XML parser, but will be used by PyroCMS module install routine.
XML Document Parser will parse all <element></element> but, characters inside the "<![CDATA  -----------    ]]>"  are not parsed by XML parsers and they are taken as raw string of characters.  So the advantage is we keep our required MySQL queries inside it, as normal storage. At XML document parse time (module installation),  the content between <install></install> is taken, split by the separator -- command split --  and executed over the PyroCMS database that we setup in PyroCMS installation process.
Remember:
a. Include your MySQL queries inside "<![CDATA” &    “ ]]>"
b. Separate each MySQL query by the separator
          -- command split --
c. the CDATA end marker ]]>" cannot contain spaces or line breaks before it. Just place it before </install> as

]]>
</install>

d. last query doesn’t need the -- command split --  separator
e. CDATA can not be nested 

So the code below contains two queries,  clear to see:

<install>
<![CDATA[  
DROP TABLE IF EXISTS hello_world;
-- command split --
CREATE TABLE hello_world (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`msg` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL
) ENGINE = MYISAM ; 
-- command split --
INSERT INTO `hello_world` (`id`,`msg`)
VALUES (NULL , 'hello world from PyroCMS module, from db'),
(NULL , 'Greetings from @bhu1st');
]]>
</install>
In this CDATA section, we first drop the table “hello_world” if exists in database, then we create and insert some data in the hello_world table. That’s it. Our table is created and populated with the supplied data, if module installation went fine, you should be able to see the hello_world table in PyroCMS database you setup before.
MySQL Queries required at module uninstall time
  <uninstall>
<![CDATA[
  DROP TABLE IF EXISTS hello_world;
]]>
</uninstall>
If you grasped the CDATA concept, only thing that concerns us in <uninstall></uninstall> code is the MySQL query “DROP TABLE IF EXISTS hello_world;”, yup, this will be triggered when you uninstall the module.  That’s fine. Tables created during install time will be deleted.

C. When your module is just accessed in backend, like Newsletter module etc..

 
<is_frontend>0</is_frontend>
<is_backend>1</is_backend>
<is_backend_menu>0</is_backend_menu>
refer code above with these settings for full details.xml code..

2. helloworld\controllers\helloworld.php

Code/comment explains itself, you may refer above sections too.
<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');
/*
|-------------------------------------
| Publich Controller of our module
|-------------------------------------
| accessed from front end
|
| extends Public_Controller
*/
class Helloworld extends Public_Controller
{
    function __construct()
    {
        parent::Public_Controller();
    }
    function index()
    {
        //load model
        $this->load->model('helloworld_m');
        //get message from model
        $message = $this->helloworld_m->getHelloMsg();
        //pass message and build template
        $this->data->msg = $message;       
        $this->template->build('helloworld', $this->data);
    }
}

2. a. helloworld\controllers\admin.php  [controller for admin interface]

The controller file name “admin.php” since we specified so in our details.xml

<controllers>
       <controller name="admin">
           <method>index</method>            
       </controller>
</controllers> 

The Code:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/*
|-------------------------------------
| Admin Controller of our module
|-------------------------------------
| accessed from back end
| extends Admin_Controller
|
*/
class Admin extends Admin_Controller
{
    public function __construct()
    {
        parent::Admin_Controller();       
        //load model
        $this->load->model('helloworld_m');   
        //set views/admin/sidebar as 'sidebar' partial,
        //that is to be shown on Sidebar section of backend
        $this->template->set_partial('sidebar', 'admin/sidebar');
    }
    //Show Helloworld message to admin
    function index()
    {           
        //function triggred when we click on the module name in backend in the menu
        //get message from model
        $message = $this->helloworld_m->getHelloMsg();
        //pass message and build template
        $this->data->msg = $message;           
        $this->template->build('admin/index', $this->data);
    }
}
?>

3. helloworld\views\view_file.php

<h1><?php echo $msg; ?></h1>

3.a. helloworld\views\admin\index.php - admin main view 

Hi,
<br/> <br/>
<h2><?php echo $msg; ?></h2>

3.b, helloworld\views\admin\sidebar.php –> admin sidebar partial

see how it is set as sidebar partial in constructor of our Admin controller 2. a. helloworld\controllers\admin.php

  $this->template->set_partial('sidebar', 'admin/sidebar');

this view file would contain following html:
<div class="box">
    <h3>About Me</h3>   
    <div class="box-container">
        Bhupal Sapkota<br/>
        Kathmandu, Nepal<br/>
    </div>
</div>
i’ve put a little bit info about me, never mind. Put any <html> or Pyro stuff you like there.

4. helloworld\models\helloworld_m.php – our model class

<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/*
|-------------------------------------
| Model Class of our module
|-------------------------------------
| for database access
|
*/
class Helloworld_m extends Model
{
    function __construct()
    {
        parent::Model();
    }
    function getHelloMsg()
    {   
        //get hello_world table
        $query = $this->db->get('hello_world');
        //if module successfully installed and data exists in table, grab it, return it
        if($query->num_rows() > 0){
            $result = $query->row_array();
            return $result['msg'];   
        }else { //otherwise return simple hello message       
            return "Hello world from PyroCMS Module!";
        }
    }
}
?>


Install Helloworld Module

->Download code from Helloworld PyroCMS Module in Github

zip your module, and upload it from the Modules –> Third party section, Upload link
If your MySQL queries had no problems, you’d see hello_world table added in PyroCMS database. Helloworld module will be listed in admin navigation, modules list, in third party section.

When you uninstall

module gets deleted from third_party\modules\ folder. think of having a backup before uninstall.

Module In Action

Access module from Admin Navigation menu or type this in browser:

Backend:
http://localhost/projectname/admin/modulename

 http://localhost/pyrocms/admin/helloworld
Frontend:
Assign one frontend navigation link to point our helloworld module
http://localhost/projectname/modulename
 http://localhost/pyrocms//helloworld

Output Check

I have added validation in our model Helloworld_m,
function getHelloMsg()
    {   
        //get hello_world table
        $query = $this->db->get('hello_world');
        //if module successfully installed and data exists in table, grab it, return it
        if($query->num_rows() > 0){
            $result = $query->row_array();
            return $result['msg'];   
        }else { //otherwise return simple hello message       
            return "Hello world from PyroCMS Module!";
        }
    }
If SQL executed successfully during module install, the $msg in view renders “hello world from PyroCMS module, from db” otherwise  "Hello world from PyroCMS Module!";

Conclusion

1. follow the file/folder structures and naming conventions.
2. Understand the module creation requirement and accordingly settings in details.xml
3. CDATA section of the details.xml needs to carefully saved. Check your queries in database before saving them in CDATA section.
4. Upload your module to install it.  [ remember to upload zip file, zip file name same as module name ]
[helloworld.zip] or [yourmodule.zip]
5. If upload failed, delete raw upload from third_party modules folder, database column if any, check your settings, check queries again, zip it, and re-upload.
5. If still couldn’t install it:
--> i think you now understand the module creation flow
--> insert few queries in database  [details.xml]
-> add your module settings in modules table in database [details.xml]
-> don’t wait for any other documentation ;) the kickass CMS, created over the kickass framework is really easy to grasp in one shot back tracing, 2-3 hours max  
-> hack it use it. contribute back if any.
-> if you are familiar with Codeigniter and not with HMVC  <------ read

0 comments :

Post a Comment