Skip navigation

This guide will help you port your application to ExpressionEngine. If you don’t know, ExpressionEngine is a CMS built on CodeIgniter. Being built on CI makes it easy to write Web Apps using pure CodeIgniter framework, and then port it to EE with few changes.

To start off, you need to create an EE module. The module consists of the following structure:

  • module_name
    • language
      • english
        • lang.module_name.php
    • models
    • mcp.module_name.php
    • mod.module_name.php
    • upd.module_name.php
Replace module_name with your module’s name with all letters in lowercase.

Inside lang.module_name.php:

<?php
$lang = array(

"events_module_name" 		=> 'Your module name here',
"events_module_description" 	=> 'A description to your module',
);
?>

Inside mcp.module_name.php

<?php

class Events_mcp { 

	function __construct()
	{
		$this->EE =& get_instance();
	}
}
?>

Inside upd.module_name.php

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class ModuleName_upd { 

    var $version = '1.0'; 

    function __construct()
    {
		// Make a local reference to the ExpressionEngine super object
		$this->EE =& get_instance();
    }

    function install()
    {
	$this->EE->load->dbforge();

	$data = array(
		'module_name' => 'ModuleName' ,
		'module_version' => $this->version,
		'has_cp_backend' => 'n',
		'has_publish_fields' => 'y'
	);
	$this->EE->db->insert('modules', $data);
     }

    function uninstall()
    {
	return TRUE;
    }

    function update($current = '')
    {
	return FALSE;
    }

}

And finally inside mod.module_name.php:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class ModuleName {
	function __construct()
	{
                // Make a local reference to the ExpressionEngine super object
		$this->EE =& get_instance();
	}

Now place this folder inside “/expressionengine/third_party/”. If everything was done correctly your module should appear in EE’s module manager.

module manager

Module Manager

This is the first step in porting your existing CI application to EE. Next step is to put your actual code in the correct way. Briefly, the EE module corresponds to oridinary CI files as follows:

  • models  folder in EE corresponds to models folder in CI
  • mod.module_name.php corresponds to a single CI controller
  • Views in CI correspond to templates in EE

Models:

There are no required changes  to do if you want to use your CI models in EE. Just places them inside your module’s model folder under EE and they’re going to be available for you to load and use.

Controllers:

Your CI controller code should be placed inside mod.module_name.php under EE.  The controller code is not a simple copy and paste though to work. The first problem is the “load” function not being accessible from your new controller file (well since it’s not a normal CI controller). To have access to this function and others as usual, you’ll have to get instance of the CI object which would expose these functions for you to use. So the constructor should look like the following:

mod.module_name.php:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class ModuleName {
	function __construct()
	{
		// Make a local reference to the ExpressionEngine super object
		$this->EE =& get_instance();
	}
}
?>

Now replace every call to $this->load() with $this->EE->load(). Same goes for loaded models and libraries. For example:

function addpost()
{
	$this->EE->load->helper('url');
	$this->EE->load->helper('form');
	$this->EE->load->library('form_validation');
	$this->EE->form_validation
		->set_rules('post_title', 'Post Title', 'required|max_length[100]|trim|xss_clean');
}

At the end of each function you will not load a view. This is because we will be dealing with EE’s templates which works pretty much differently than a normal View.

In CodeIgniter, usually at the end of the function you’d call a view and pass some data to be rendered by that view. In EE however, templates and controllers work differently. Instead of calling a controller function which loads a view, you call an EE template which calls zero or more functions from the EE controller file (mod.module_name.php). The template also expects the data to be returned in a specific format that you’ll be forced to change your functions returned values to match that format. I’ll use an example of  a Blog module for more clarification

Assume we want fetch all posts and comments from the database and display them in a template.  Normally in  CI controller functions we would it in a single function, and pass it to the view to handle. But given the way templates in EE work where you directly call as much controller functions as you want from it, we have the following options:

  • Either break up the function into two, posts in one function and comments in other, each will be solely called from the template.
  • Have both posts and their comments placed in one array, which is returned to the template upon the controller function call.
I’ll go with the first approach since it’s simpler.
mod.posts.php:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class ModuleName {
	function __construct()
	{
		// Make a local reference to the ExpressionEngine super object
		$this->EE =& get_instance();
	}

        function get_posts()
	{
		$this->EE->load->model('post');
		$this->EE->post->db->order_by('post_id', 'asc');
		$output_data = $this->EE->post->db->get('posts')->result();
		return $this->format_output($output_data);
	}
        function get_comments()
	{
                $post_id = $this->EE->TMPL->fetch_param('post_id');
		$this->EE->load->model('comment');
		$output_data = $this->EE->comment->db->get_where('post_id',$post_id)->result();
		return $this->format_output($output_data);
	}
}
?>

Notice that the way we’re fetching posts and comments from the database is the normal CI way.  The main different things here is the first and last lines in get_comments function and their similarities in get_posts.  We’ll talk about the format_output function first.

As I previously mentioned, EE templates expects the function output to be formatted in a specific way. Normally you have only to call

return $this->EE->TMPL->parse_variables($this->EE->TMPL->tagdata, $output_data);

However, this only works given that $output_data is a multidimensional associative array.  If you are fetching data from database this way, then you’ll have to convert them to associative arrays since they are returned in form of an array of objects (should be an array of arrays) before passing it to parse_variables function. With quick dirty work I wrote a simple function that takes care of this, for 2 levels deep at most:

function format_output($objects)
{
	//prepares the data to be be used by templates
	//2 LEVEL PARSING ONLY!
	$values = array();

	foreach($objects as $o)
	{
		$vars = get_object_vars($o);
		$tmp = array();

		foreach($vars as $v=>$val)
		{
			if(gettype($o->$v) == 'object')
			{
				$subvars = get_object_vars($o->$v);
				$subtmp = array();

				foreach($subvars as $subv=>$subval)
				{
					$keyname = $subv=="id"?$v."_id":$subv;
					$subtmp[$keyname]=$o->$v->$subv;
				}
				$tmp[$v]=array(0=>$subtmp);
			}
			else
				$tmp[$v]=$o->$v;
			}
			$values[]=$tmp;
		}
		$output = $this->EE->TMPL->parse_variables($this->EE->TMPL->tagdata, $values);
		return $output;
	}

EE Template

Now create a new template that would display blog posts and their comments:

{exp:posts:get_posts}
	<h2>{post_title}:</h2>
	<p>{post_body}</p>

	<h3>Comments:</h3>

	{exp:posts:get_comments post_id={post_id}}
		<p id="{comment_id}">{comment_content}</p>
	{/exp:posts:get_comments}
{/exp:posts:get_posts}

For more information about EE modules check out this Module Tutorial on EE’s userguide.

I noticed when I download a file on chromium then try to open it or open its folder by clicking “Open in folder”,  it just opens in firefox anyway. Tracing the problem I found chromium invokes xdg-open, which is an application launcher. It’s actually a generic application launcher that tries to detect your current desktop environment and passes the call to this DE’s application launcher. So if you’re using gnome, xdg-open would eventually direct your request to gnome-open. The problem comes when you’re using a DE not familiar to the script (Fluxbox in my case). The script recognizes only Gnome, KDE, LXDE, Xfce. If your DE is not in those, then xdg-open falls back to a default handler which seems to be the Internet browser.

Luckily in my case I have gnome-open and exo-open installed as they were pulled in when I installed some gnome and xfce apps.  To force xdg-open to use one of them (assume exo-open), you’ll have to trick xdg-open into thinking you have xfce as your DE. To do so, just edit the xdg-open script:

sudo vim /usr/bin/xdg-open

find the line containing the function called “detectDE”. At the very bottom of the function and before the closing braces just add:

DE=xfce;

Also possible values (gnome,lxde,kde) depending on the launcher you have. Now whenever xdg-open is run (by chromium or whatever) it will treat my DE as if it’s xfce and directs the call to exo-open. To further edit the preferred applications that exo-open runs, just run exo-preferred-applications

Basically when you are developing for Social Engine, the best approach you’d take is to try as much as possible not to modify the pre-existing modules. That is because whenever a new SocialEngine update has been released, you’ll find hard time porting your changes to the new update. Not to mention the risk of messing up a pre-existing SE code.

Luckily Social Engine follows a Modular design pattern. That is,  according to Wikipedia:

Modular programming is a software design technique that increases the extent to which software is composed of separate, interchangeable components, called modules by breaking down program functions into modules, each of which accomplishes one function and contains everything necessary to accomplish this

Modules in SocialEngine are found in “/application/modules/”.  You’ll find a nice bunch of pre-existing modules that constitute SocialEngine functionality.

So how do you create a module? There is a nice process that takes takes care of creating the basic module structure and makes it already part of the application. The process consists of 3 parts:

  1. Creating the module
  2. Installing the module
  3. Building the module
—–

This is not an official guide on how to create a module for SE, these steps I just discovered them while playing around with SE and this is what I actually do to create a module. I hope they become helpful to you :)

 ——

Creating the module:

  1. First off, head to your website, and login with a user who has admin privileges.
  2. We need the Package Manager, so go to this link after you login: http://<SOCIAL_ENGINE_ADDR>/install/manage, replacing of course <SOCIAL_ENGINE_ADDR> with the correct one. Inside the package manager you’ll find different installed modules, and you can enable/ disable them as you wish.
  3. Switch to Developer SDK Tab and click on Create a Package
  4. As you see Social Engine can setup for you any type of package you might need. In our case we’d select package of type Module. Complete the rest of required fields and hit “Create Package” button.
Createpackage

Create Package form

By now you’ll have a compressed archive downloaded. If you look into it you’ll find the basic module file structure has been created for you. So next step is to actually make that module part of Social Engine so that you could start working on it and preview the changes immediately.

 ——

Installing the module:

Head again to http://<SOCIAL_ENGINE_ADDR>/install/manage, and click on Install New Packages link. The process of installing a new package consists of several automated steps. You just need to specify the package to install (which is the one downloaded from the previous steps), and the Step called “Enter FTP Info“. If Social Engine is running on your local server (the actual computer that you’re developing on), then you could just choose “None” for the FTP Connection Type, put in the Path to SE (it should be already there though) and mark “Search for SocialEngine Path“. Otherwise you’ll have to fill up the FTP connection fields.

FTP Info

FTP Fields

Then keep hitting continue until you’re done. You should get message like this in the end:

Successfully installed package

So now you’ve got you’re package created, installed and enabled in your SocialEngine application. You’ll find your module has appeared in SE’s modules folder and you can now modify it as you wish. If you’re still getting started with SocialEngine, these guides might be useful as well:

 ——

Building the module:

When you have finished developing your module and you’re ready to send it for example to a client, or sell it somewhere, you can have SocialEngine build an installable package for your module.

  1. “Go to the “Developer SDK Tab” in the Package Manager.
  2. Click on Build Packages
  3. Find your module an make a tick in the associated checkbox
  4. Click on “Build Packages” button.
  5. You can now click on your package to download it. This page is also accessible from “Manage Package Files” link in Developer SDK Tab under Package Manager
Manage Packages

 

Modal Window

SE Modal Window for confirmation

Social Engine provides an easy way for showing modal windows for users. Some example for its uses are the following:

  • Confirmation windows
  • Notification windows with Timeout
  • Taking a simple input on the fly, or even showing a whole form.

Modal Windows in SocialEngine 4 are created using mootools’s plugin “smoothbox”. Simply  a modal window here is just an iframe, displaying a view as a result of calling a specific action in a controller (see Controllers).  Let’s examine what happens when you try to delete an activity.

If you look at the html tag of the delete link you’ll find that it looks like the following:

<a href=”/activity/index/delete/action_id/126″ class=”smoothbox”>Delete</a>

Any anchor <a> tag that is given a class attribute with value “smoothbox”, opens the href link in a modal window (the i-frame), instead of transferring the whole page to it. This very much simulates an Ajax request with the least effort. Let’s inspect the href value to see where it leads:

 

Module: Activity, Controller: Index, Action:Delete, Parameters: action_id=126

So head up to this action. The first few lines in the action are just to check  if the user is allowed to perform the delete operation. Also notice that the same method is invoked for both, deleting an activity (AKA action) or deleting a comment on an activity. Let’s focus this time on lines concerned with deleting the activity itself. Go for the line that say:

if (!$this->getRequest()->isPost())      return;

What happens here is that the action checks if it is invoked by a POST request (that request that takes place when you submit a form of type=post), if it is not a post request, the action just returns at this point, causing the default view for this action (delete.tpl) to render (more info on Controllers). Notice the variable passed to the view (before return) carrying the action/activity Id, which in our example is 126.

$this->view->action_id  = $action_id  = $this->_getParam(‘action_id’, null);

If you open that view (delete.tpl), you’ll find a form of type=post just as expected. It contains the confirmation message to display:

Are you sure that you want to delete this activity item and all of its comments? This action cannot be undone

Form elements include:

  • A hidden field carrying the action Id passed from the view
  • A submit button.
  • A “Cancel”  link.

Cancel link just calls the instance of smoothbox in the parent window and closes it.

<a href=”javascript:void(0);” onclick=”parent.Smoothbox.close();”><?php echo $this->translate(“cancel”) ?></a>

On submitting the form, the same action handles the request, but this time it bypasses the condition checking if it’s not a POST request (since the action is called by submitting the form). The lines after that condition just check again for further permissions, then the actual deletion takes place. We’re not to worry here about the actual deletion process, this guide is only concerned with the modal window itself and how it works. So After deleting:

$this->view->status  = true;

$this->view->message = Zend_Registry::get(‘Zend_Translate’)->_(‘This activity item has been removed.’);

$this->view->smoothboxClose = true;

return $this->render(‘deletedItem’);

The status of the operation is set to true and passed to the view to indicate success, along with the success message to display. Also a flag (smoothboxClose) is set to true to tell the view to close the smoothbox. Finally the script renders “deletedItem.tpl”

I’ll break up deletedItem.tpl into 3 parts.

Part 1:

<script type=”text/javascript”>

parent.$(‘activity-item-<?php echo $this->action_id ?>’).destroy();

 

To further simulate the ajax request, after the activity item has been deleted it should be removed from the page. The above line simply does that by getting the HTML element containing that activity using its Id and removes it. The above translates to parent.$(‘activity-item-126′).destroy();

Part 2:

setTimeout(function()

{

parent.Smoothbox.close();

}, <?php echo ( $this->smoothboxClose === true ? 1000 : $this->smoothboxClose ); ?>);

</script>

parent.Smoothbox.close() is invoked after 1000 ms only if smoothboxClose passed from deleteAction was set to true. That is to allow enough time for the user to read the success message before hiding it.

And finally:

<div class=”global_form_popup_message”>

<?php echo $this->message ?>

</div>

This shows the success message passed from deleteAction().

Here is another drawing for you

deleteAction

An API file consists of functions that provide methods of communication between a module and other modules (including itself). For example to allow interaction with database tables associated with that module; instead of duplicating a basic select statement across multiple files, we could just create a function inside the API file that returns the result of that select statement.

This makes life much easier, specially when there are complex queries (like containing pagination and stuff) that need to be called from several controllers or actions. Even better, we won’t need to worry if for example a tablename has changed in an update or something, since all we will be updating is the API to match the new name.

In a module, APIs are created inside the “Api” folder. I think it’s a SE convention to name the basic/ default API for a module as “Core”. However assume we’re creating an API called “Myapi” just for the sake of learning.

Assume we’re in a module called “Mymodule”. Inside the “Api” folder create a file called “MyApi.php” that contains the following:

<?php

class Mymodule_Api_Myapi extends Core_Api_Abstract{

}

?>

Notice the naming convention of the class name: {ModuleName}_Api_{ApiName}

Now just add any function to that class so it becomes something like this

<?php

class Mymodule_Api_Myapi extends Core_Api_Abstract{

public function addNumbers($a, $b){

return $a+$b;

}

}

?>

Of course an API would contain more sophisticated functions than addition, but that is just an example. Now in order to call this function from anywhere in the application:

$myApi =  Engine_Api::_()->getApi(‘myapi’, ‘mymodule’);

$sum = $myApi->addNumbers(10, 5);

“getApi” takes API name and Module name as parameters, and returns an instance of the API.

Tahrir Square

Tahrir Square

 

“The revolution in Egypt has stunned the entire world” (mymodernmet)

“The uprising in Egypt has galvanized the world” (aolnews)

“Egypt Revolution will change the Arab World” (twocircles.net)

“Arab world gears up for domino effect” (presstv.ir)

 

The Egyptian Revolution. The Big Picture.

 


Following up with my last 2 posts about Widgets and Controllers, the past couple of days I got my hands dirty with Models. Models are created inside the Models directory in a module. A model represents an item, or an object, which is fetched from the database. To describe how things work, I think the simplest way is to use an example.

If you find anything that is inconsistent or does not work, please let me know in a comment or so.

Assume we want to allow the user to categorize his friends (like facebook friends lists). So we’re going to create a table in the database to store this.

From my understanding, the convention of SE (probably zend too) in table names goes like this:

prefix_modulename_tablename

prefix is similar for all tables. It’s actually set somewhere in config files (can’t remember where). modulename is the name of module containing the model for our table. tablename (in plural form) is the actual name of table we’re creating. So, in our example, I’ll assume we’re working inside the “User” module, so we create the table “prefix_user_friendslists“.  We will just give the table a column for primary key  (friendslist_id), creator id (user_id), and category name (list_name).

Note that you must call the primary key column (friendslist_id) as zend automatically infers that from the table name.

Now let’s get started on how to deal with this table from within our application. Inside the Models directory, there is another folder called “DbTable“. DbTable files are most probably the lowest layer files you’ll need to deal with to create models from the database.  A DBTable class is what connects your table to it’s model. So start off by creating a DBTable file (inside DBTable folder) for our table.

Friendslists.php (plural):

class User_Model_DbTable_Friendslists extends Engine_Db_Table

{

protected $_rowClass = ‘User_Model_Friendslist'; Singular

}

$_rowClass chould have class name of our model (that we will create next). This way Social Engine (or zend) would know that results obtained from “User_Friendlists” table, should be assigned to our model. Don’t be confused about singular/plural conventions. Actually if you think about it, DBTable represents the table from database, that’s why it has  plural form like the table name. While a model represent a single object from that table, so it has a singular form in ists class name.

Now in the Models directory create:

Friendslist.php (singular):

class User_Model_Friendslist extends Core_Model_Item_Abstract

{

protected $_owner_type = ‘user';

}

With $_parent_type and $_owner_type we are getting rid of a manual JOIN operation to get the creator of a category. This way whenever we call $category->getOwner(), its creator”User” object is automatically fetched using the “user_id” column.

Querying the table:

To query our table we first tell the Engine API which table we are going to query against:

$table = Engine_Api::_()->getDbtable(‘friendslists’, ‘user’);

Notice how function paramaters (tablename, modulename)

To select all friendslists a certain user created:

$select = $table->select()  ->where(“user_id = ?”, 1);

$result = $table->fetchAll($select);

foreach($result as $list)

{

echo “{$list->list_name} <br/>” ;

}

A single $category (inside the foreach loop) is an instance of the model we’ve created above. You can use it to access any column by its name.

To create a new category:

Engine_Api::_()->getDbtable(‘friendslists’, ‘user’)

->insert(array(

‘user_id’ => 1,

‘list_name’ => “Shiny new category”

));

Next to come is how to create your own module.

Since Social Engine uses the Zend Framework, the files structure are most probably the same. Social Engine uses modules. So inside the application directory you find modules folder. Each module has a separate folder, containing its associated controllers, models, views, widgets ..etc.

Controllers:

Controllers are found inside controllers folder. Each controller class has defined actions (methods) and each method must have an associated view.

Controllers’ naming convention is NameController.php. The controller’s class name becomes {modulename}_{controllername}Controller

So if we’re inside User module and looking at the Profile Controller, you’ll find:

  • Controller filename is ProfileController.php
  • Controller class name is User_ProfileController

The standard method in controller is the init()  method. This is the method that get invoked when the class is instantiated. So inside it important variables that are used within other method in the controller should be set. For example, setting the subject. I understood that socialengine uses the term “Subject” to name a variable, or an item that the user is currently looking at. For example when you view someone’s profile, the subject becomes that person. If you’re viewing a group, subject becomes that group. Subject is set in the init function so as to be available easily to all other methods in the same controller.

To set subject:

if( !Engine_Api::_()->core()->hasSubject() )    {  //Check is there is  no already set subject

$subject_temp = Engine_Api::_()->user()->getUser($id);      //set temp variable to some user, we got using his id

if( $subject_temp->getIdentity() )        {        //If temp is indeed a valid  user

Engine_Api::_()->core()->setSubject($subject_temp);      // set the subject to that user

}

}

To get subject:

$subject = Engine_Api::_()->core()->getSubject();

Methods other than init() are called actions. The take the following convention:

public function methodnameAction(){ }

inside which the action’s logic is defined.

Each  controller has a folder containing it’s views, found in views/scripts folder. For Profile Controller explained above, there is a folder called “profile“, and for each method in this controller -other than init() – there must be an associated view. So if we have a method called helloAction, there must be a file called hello.tpl within that folder.

To invoke a certain action within a certain controller, you follow this scheme in URL:

http://serveraddress/modulename/controllername/actionname/

This action gets invoked, and automatically the associated view gets rendered for display.

I’m currently working on a project that requires applying some modifications and creating plugins for SocialEngine 4. I’m new to socialengine so I’ll post here my findings during the work. Probably I’m not allowed to post the code in public, so I’m just going to post generic how tos that you could use to accomplish similar tasks.

My first task to is to allow users to post comments to other users’ profile. So the first challenge is to create a new tab where there would be a form, and other people comments displayed below it. More like the news feed, except that it shows and posts comments.

First thing to think about is where does this Widget belong? In my case, since it is displayed in a user’s profile then it should belong to the User module. So navigate to the Widgets folder inside the User module ({social_engine_root}/application/modules/User/widgets) and create a new directory with your widget’s name (mywidget for example). Create 2 files inside this directory:

  • Controller.php
  • index.tpl

As you’ve most probably figured out, SocialEngine uses the MVC architecture. So in this case the controller class lies in Controller.php and the view is in index.tpl. For the model I haven’t yet stepped into it so I’ll skip it now and come back to it when I find out more.

Controller.php:

<?php

class User_Widget_mywidgetController extends Engine_Content_Widget_Abstract {

public function indexAction()  {

$this->view->msg=”Hello Widget!”;

}
}

The indexAction() method is invoked whenever the widget is going to be displayed. So inside this method you should prepare data (if any) that should be passed to the view. To pass data to the view, use this convention $this->view->variablename=value like above, replacing variablename with any name of your choice, and value with the associated data.

Then in the view index.tpl:

<?php

echo $this->msg;

?>

Use this convention to get variables assigned in controller: $this->variablename

Now the next step is to tell the User module that it has got a shiny brand new widget. To do this edit the content file associated with User module: ({social_engine_root}/application/modules/User/settings/content.php)  and append the following at the very bottom of page (before the very last closing bracket ‘)’ )

array( ‘title’ => ‘My Widget’,

‘description’ => ‘This is a brand new User Widget’,

‘category’ => ‘User’,

‘type’ => ‘widget’,

‘name’ => ‘user.mywidget’,

),

always of course replacing “mywidget” with your widget’s name.

Now the Widget is registered with SocialEngine. I am going to add it under a new tab in User Profile. To do so, login as admin, click Admin from top menu and go to Layout Editor. Then Change Editing to Member profile, and in the Available Blocks scroll till you find the User section. If everything was done correctly you should find your widget under this section. Just drag it to the tabbed block and you’re done. Next up is how to create a model, and make the controller interact with it.

 

UPDATE:

I quote this from John Boehr’s comment:

If you want to create your own widgets, I’d recommend placing them in application/widgets instead. The widgets in the modules use a common content file, which would be replaced if you upgraded. The widgets in the application/widgets folder are designed to be atomic, so their files are not touched unless you specifically upgrade that widget (or never if it’s your own).

You can render a widget in a view script using:
echo $this->content()->renderWidget(‘user.list-online’, array(‘key’ => ‘value’));

Check out application/modules/Core/views/scripts/admin-index/index.tpl for examples in the code.

Great piece of info, thanks!

Because of my recommendation, 4 teams (20 persons in total) are using Qt for a HCI project.

  • 1 team is targeting Desktop platforms
  • 1 team is targeting mobile platforms
  • 1 team is using PyQt for Desktop targets
  • And finally my team is using QML (desktop targets as well)

I guess I do deserve my Qt Ambassador title! :P

Follow

Get every new post delivered to your Inbox.

Join 52 other followers