I have been struggling with a problem with Joomla!, lately, and figured out a nifty solution I wanted to share and see if others had ideas. Joomla!'s standard Components, Modules, and Plugins just didn't fit the requirements of this Intranet application I am building. I created a very flexible architecture of
Portals->Themes->Positions->Portlets->Layouts->Resources with nice customization on the frontend by users, the ability to create groups and share resources easily, and a standard data model and set of processing that is used for all content, whether it's images, or text, or private messages.
To output the Portlet Layouts, I created a series of classes that use "JDoc-like" statements in the Template to drive processing. The application collects resource requirements (JS, CSS) from the Template and Portlets during processing and combines/minifies the files at the end. I have my own head process where output is available via layouts and easier to adjust, as needed.
The problem I ended up having was that Joomla! was unaware of the Portlet processing since it only handles normal Modules, Components and Plugins. For that reason, some times, Joomla! would finish before all of the portlets were processed, and before the resources were combined and minified.
Today, I realized that what I needed was two new HTML Renderers, one for my position/portlet output, and the other for my head replacement. After some hacking around, I figured out a way to load a custom Renderer, and then use my own JDoc statements to get Joomla! to drive the Portlet and Head processes, just like it does with Modules, Components, and Plugins.
1. loadRenderer FunctionI had to copy the loadRenderer Function from the
JDocument class since the path was unfortunately hardcoded and I didn't want to add any files to the core libraries. This function can be added to any appropriate class. As you can see, my class is named TamkaDocumentHTML.
Note how I changed the path to my library location (I could have made that more flexible by passing in the folder location.) Also, it is important to note that the
JDocumentRenderer.$type class must be followed.
class TamkaDocumentHTML
{
function __construct( ) {}
/**
* Load a renderer
*
* @access public
* @param string The renderer type
* @return object
* @since 1.5
*/
function loadRenderer( $type )
{
$null = null;
$class = 'JDocumentRenderer'.$type;
if( !class_exists( $class ) ) {
// $path = dirname(__FILE__).DS.$this->_type.DS.'renderer'.DS.$type.'.php';
$path = dirname(__FILE__).DS.'renderer'.DS.$type.'.php';
if(file_exists($path)) {
require_once($path);
} else {
JError::raiseError(500,JText::_('Unable to load renderer class'));
}
}
if ( !class_exists( $class ) ) {
return $null;
}
$instance = new $class($this);
return $instance;
}
}
2. Create the Renderers and place into the position specified, above.Next, I created a Renderer for the production of the Position Portlets and also a Renderer for the Head Statement. The name of the class must be JDocumentRenderer + the value you will use as your JDoc Type statement. Also, the name of the Renderer file must be the same as the Type Value.
class JDocumentRendererTamkaPosition
{
/**
* render
* @param object $module
* @param object $params [optional]
* @param object $content [optional]
* @return
*/
function render( $module, $params = array(), $content = null )
{
$_SESSION['TAMKA_RENDER_POSITION'] = '';
require_once TAMKA_CONSTANT_INCLUDE_THEMES.'position.php';
$tamkaPosition = new TamkaPosition($module);
return $_SESSION['TAMKA_RENDER_POSITION'];
}
}
The Header Renderer Class:class JDocumentRendererTamkaHead
{
public function render( $head, array $params = array(), $content = null ) {
/**
* Create Resource File Includes and possible minification
*/
require_once TAMKA_CONSTANT_INCLUDE_THEMES.'resources.php';
$tamkaResources = new TamkaResources();
$tamkaResources->addDocumentResources();
/**
* Create Head and begin outputting buffer
*/
ob_start();
echo $_SESSION['TAMKA_LAYOUT_OUTPUT_SITE_HEAD'].
$_SESSION['TAMKA_RESOURCES_DOCUMENT_HEAD_CSS'].
$_SESSION['TAMKA_RESOURCES_DOCUMENT_HEAD_JS'].
$_SESSION['TAMKA_RESOURCES_DOCUMENT_HEAD_JS_INLINE'].
'</head>.;
unset($_SESSION['TAMKA_LAYOUT_OUTPUT_SITE_HEAD']);
unset($_SESSION['TAMKA_RESOURCES_DOCUMENT_HEAD_CSS']);
unset($_SESSION['TAMKA_RESOURCES_DOCUMENT_HEAD_JS']);
unset($_SESSION['TAMKA_RESOURCES_DOCUMENT_HEAD_JS_INLINE']);
$contents = ob_get_contents();
ob_end_clean();
require_once TAMKA_CONSTANT_INCLUDE_THEMES.'meta.php';
$tamkaMeta = new TamkaMeta();
$tamkaMeta->unsetMeta ();
return $contents;
}
}
3. Added a System Plugin that fires on onAfterInitialise to load the new Renderers
require_once TAMKA_CONSTANT_INCLUDE_LIBRARY_BASE.'utilities'.DIRECTORY_SEPARATOR.'html'.DIRECTORY_SEPARATOR.'html.php';
$tamkaDocument = new TamkaDocumentHTML ();
$tamkaDocument->loadRenderer( 'TamkaHead' );
$tamkaDocument->loadRenderer( 'TamkaPosition' )
4. Custom JDoc Statements in your Template index.php file to drive the new Renderers.The following two JDoc statements are now available to my application. It's very much like normal JDoc Statements, except these statements drive my architectural elements *and* do so in a way that Joomla! is aware, and listening for completion before continuing to the next step.
<jdoc:include type="tamkaposition" name="6" /><jdoc:include type="tamkahead" />This could be useful anytime you want to integrate an application of any nature that doesn't fit the normal architecture. I think it has a lot of potential.
I looked at nooku, as well, and it has the same inflexibility with the
loadRenderer function. Much of nooku is still the standard Joomla! framework, with many improvements. In both Joomla! and nooku, it would be nice to make it possible to use
JDocument::loadRenderer to extend this capability by simply using the class.
Hope this helps someone. If you have done something similar, or know a better way, I am very interested in hearing about it.!
You need to be a member of All Together, As A Whole to add comments!
Join All Together, As A Whole