Sunday, September 26, 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

6 comments:

Francisco on March 20, 2011 3:14 AM said...

Why the modules section just gives me this message:

Could not install the module.



It's about permissions or the XML it's bad. I Hope you can help me!

@bhu1st on March 25, 2011 12:29 AM said...

@Francisco did you download code from github link? Code is same in both places but if you copied it from blog post above you will most probably have bad xml,(extra spaces issues). Also, zip your module folder, give it same name as your module name and upload the zip file via module upload section. Please re-check the CDATA instruction above once again. If that didn't fix the problem, share me the code link, i would love to help.

Nikhil on May 9, 2011 7:41 PM said...

Hi bhu1st,

I am also getting the same message, 'Could Not Install the module'.
I tried my own code and also downloaded from the GIT but both give the same error.
I zipped the helloworld folder and kept it inside /third_party/modules folder and trying to upload it from the admin panel of pyro. I even tried uploading from the /third_party folder but both give the same error.
Please help me where am i going wrong ??
Thanks in advance

@bhu1st on May 9, 2011 9:26 PM said...

@Nikhil try this:

1. remove the module from third_party folder,
2. remove the entry for this module from "modules" table of pyrocms db.
3. re-upload the module.

If you still get the error, here are two things you can try:

Since you followed the code above and i assume you are on old pyrocms (i.e ver 0.0.9.9.x), there are two folders in old pyrocms where you can place your module:

1. pyrocms\application\modules (core modules)
2. pyrocms\third_party\modules (third party)

try putting your module in core module folder and check if it works.

or

simply copy/paste/rename any light weight module (you can check the module functionality in backend eg. categories module, navigation module) from already existing core or third party modules in these folder and try renaming controllers/modules as i described above, that should work.

If you are starting a new project you better start with new pyrocms version. current release is 1.1 i guess. In new version's module, details.xml is changed to details.php, other flow remains quite similar to the one i described above.

Nikhil on May 12, 2011 12:03 PM said...

bhu1st,

I figured out the problem, it was with details.php and i was using details.xml.
Thanks for your help.

I have another issue here,
I ve developed a module which is working fine except for one functionality.
I want a search functionality as it is there in the blog module using javascript, with the change of the dropdown the search should happen,
can you suggest some way in which i can achieve this ??
and can you pleasee elaborate on this and if possible recommend some sites which has good pyro cms docs or good forums discussing pyrocms.

Thanks a lot for your time.
:)

@bhu1st on May 12, 2011 1:05 PM said...

@Nikhil,
Glad it helped.

For the dropdown/select box you can add some JS redirection code on the onchange event of the select box. redirect to search action grabing the search keyword from select option value.

this might help: http://stackoverflow.com/questions/3687976/php-page-redirect-dependant-on-select-box-choice/3687999#3687999

PyroCMS Forum is preety active Nikhil, you can refer that: http://pyrocms.com/forums

Post a Comment

 
Everyone started programming with Hello World. Who found a passion did magic.
I work at a semicolon developers. We called it semicolon developers because the "hello world" we wrote didn't work without semicolon (;). Remember our team for a quality web, desktop or android apps development. My contact information is in my website.

My Latest Tweets

Project Blogging

Friends

Hello World! Copyright © 2009 Blogger Template Designed by Bie Blogger Template