Joomla Developers

2014-02-26

WordPress

  • Pretty, easy to use, great for blogs and basic sites that don't need too much customization.
  • Has a good built-in SEF URLs (search engine friendly URLs) system that's easy to configure and use, as long as you are coo with the same URL pattern for all blog posts / pages.
  • Many plugins that can extend its functionality. However, the best WordPress installations rely on < 10 plugins. More than that and you're likely to face some upgrade issues eventually. Choose your plugins wisely.
  • Automatic updates. This is a great feature; just make sure you also have a backup system in place in case the automatic upgrade breaks your site. We haven't seen it happen yet, but it could.
  • Mobile-friendly admin interface and tons of responsive themes
  • Again, it offers loads of plugins and themes, but many of these are commercial and require commercial support. 

Joomla

  • Version 3+ is pretty, pretty easy to use
  • Can be used as a CMS (mainly) but also for blogging.
  • The URL aliasing system still leaves something to be desired. This can be partly overcome with addons like sh404SEF.
  • Mobile-friendly admin interface and a decent selection of responsive templates
  • Joomla itself is free, but many of the best add-ons are commercial, and require subscriptions in order to keep them secure and up to date. You should be using the best commercial addons because there are many shady third-party add-ons out there, and while free, they will cost you much more in the long run because they will wreak havoc on your site during your next major upgrade. Choose your Joomla extensions wisely!

Drupal

  • Its name is Dutch, meaning "water drop". So that's an advantage over WordPress or Joomla right there. Admittedly, we're biased. Plethora Design's founder, Casper Voogt, was born in Amsterdam. 
  • Super extensible. At its core is a system of content "types" and fields, so you can design your own content types, whether it's for news, blog posts, products, user profiles, and so on. 
  • It has Views. Here at Plethora Design we LOVE Views. And it ships as part of Drupal 8's core! You might be wondering what this is. It's a query builder that comes with many display options. You can display a list of content as a table, responsive divs, bulleted list, slideshow, iCal feed, CSV export, and so on. This allows you to reuse content items in a myriad ways, all the while exercising full control over the code output, so that you can style specific content fields discretely. Joomla and Wordpress, by and large, do not offer this granular level of control. It can be added, but takes custom coding to accomplish in most cases. 
  • Features an awesome ACL / permissions system. It is incredibly granular, such that every add-on can add its own specific permissions as it sees fit. Joomla 2.5 and 3 offer similar ACL controls, though these have been baked into Drupal for over a decade, whereas Joomla is still absorbing these new features.
  • A critique of Drupal has long been its seeming lack of good themes when compared to WordPress and Joomla. This is partly true, because Drupal is more developer and enterprise oriented, so there is a certain expectation that the functionality and theme will be custom-built or at least heavily customized. As such, there are not many commercial ready-made Drupal themes out there, but there are some truly wonderful "base themes" such as Zen and Omega, which are the only ones we work with on Drupal sites. The beauty of using such frameworks is that the framework is free, and enables you to leverage a community of many thousands of dedicated developers, versus relying on a commercial entity to provide support. It's a great choice if you need to exercise a great deal of control over layout, but don't want to reinvent the wheel. All the bells and whistles you might see on WordPress and Joomla sites can be done with Drupal, and then some. It's just that Drupal is geared more towards those who have a need to heavily customize workflows, content types, layouts, and so on.
  • Out of the three, Drupal has the strictest coding standards. It has unit tests and processes in place that modules (add-ons) must pass before they are approved for general use. There is a spirited community that reviews modules, and as a result Drupal modules tend to be capable of more robust functionality than many (not all!) Joomla / WordPress add-ons. And if you don't like some aspect of the user interface you have the freedom to change it. As they say in Drupal-land, "there's a module for that". 
  • Debugging Drupal modules is more straightforward. This speaks to how Drupal attracts more developers than either Joomla or WordPress. Drupal has automated testing and patch testing in place to ensure code is up to snuff. This avoids much of the ridiculous forum discusssions on Joomla and WordPress forums, which often involve hard-to-reproduce issues which are subsequently no tracked as bugs. With Drupal, the default position is to track issues as bugs and resolve the in a an orderly fashion that benefits the community at large.

OK, let's be honest. We favor Drupal. But Joomla and WordPress can be very good choices for some projects, and we do recommend these for new projects all the time. When planning a project, just make sure to plan for eventual 'enhancements' and extra functionality, because more often tban not these things will happen.

Out of the three, Drupal may have the highest learning curve, but it does offer the most flexibility in terms of handling any complex requirement you may throw its way. For even greater flexibility you could build your own home-grown CMS, and in many ways that is what Django does, but that is for another post.

2014-01-30

We've been revamping some customer Joomla sites, moving them from Joomla 1.5 to 3.2. The migration process means a move to Joomla 2.5 and a subsequent move to 3.2. It's a bear, but it's worth it. Joomla 3.2's admin interface is far superior to even Joomla 2.5's. I personally LOVE the Isis administrative template. It's mobile-friendly, and pretty intuitive. Much of the mundane, repetitive work of managing articles, modules, etc. is now much easier. Batch processing has been made available throughout.

Now the bad: if you want to be able to add custom content fields you will still need to use something like K2 (getk2.org), Zoo, Seblod, etc... see http://extensions.joomla.org/extensions/authoring-a-content/content-construction. K2 is the only one we have used and could recommend. The lack of CCK within Joomla core is indicative of Joomla being more of an out-of-the-box CMS than a 'framework'. Drupal has had CCK built-in since its inception, and has very powerful entity handling for individual fields. Each field can be formatted and displayed differently, and this can be done via an admin interface (using default Structure > Content Type > Display, or using Display Suite), or by custom-coding the display in your template (TPL) files, or using custom display formatters (plugins). You can even combine data from several fields into one, using Computed Field.

Drupal 8 is still in the works, and promises full mobile support as well as editing-in-place, with no need for page refreshes. If you don't want to wait for eight, you can install the Edit module .. see https://drupal.org/project/edit. I'm not yet sure how this interacts with the Autosave module; it may not save auto-saves the same way as before.

2011-09-12

FPSS is a great slideshow addon for Joomla, Drupal, and standalone slideshows. They have a new version for Joomla 1.7, with a much improved interface. When it comes to linking slides, it seems you can choose to display links for all slides, or not. But what if you want links on some slides but not others? In such a case, if the user did not supply a URL, the link will say "http://www.yoursite.com/URL". You could enter "#" or "javascript:;" as the URL, but one cannot expect non web developers to remember to do this. The user should not be required to take additional action just to NOT link a slide.

We wrote a quick fix for this. Open up the default.php template file for the FPSS layout you're using. We used JJ-Oobs so we opened up /modules/mod_fpss/tmpl/JJ-Obs/default.php. Right after "foreach($slides as $slide):" insert the following:

if($slide->link == 'URL'){
$slide->link = 'javascript:;';
}

We used a "javascript:;" URL rather than "#" because this way if you slideshow is far down the page, clicking it will not take you back to the top of the page ... using "#" can conflict with named anchors but "javascript:;" does not.

2011-04-27

VirtueMart is an e-commerce component for Joomla, and it comes with a module called Product Categories (mod_product_categories). This module comes with several display options, such as a link list, CSS menu, and some JavaScript menus. Strangely, though, it does not come with a dropdown select option (a <select> menu). This post will show you how to add that option. This VirtueMart core modification also adds a "selected" attribute to the currently selected <option>.

Rather than writing it as a separate module, I have extended the the normal product categories module to have this as an option in addition to all the other options such as Transmenu, Links List, and so on. This should make it easier to incorporate in the next release since the work's already done.

The following is in use on a site running Joomla 1.5.23 and VirtueMart 1.1.8. You can download VirtueMart here. Download the "complete package", unzip it, and look in the modules directory for mod_product_categories_1.1.8.j15.zip. This is the module that need to be modified.

After making the following modifications, you will need to edit your categories module and set the category display type to "Dropdown select menu".

There are two file modifications and one new file:
- modules/mod_product_categories/mod_product_categories.php (modified)
- modules/mod_product_categories/mod_product_categories.xml (modified)
- modules/mod_virtuemart/vm_select.php (new file).

Modify modules/mod_product_categories/mod_product_categories.php:

After:
<br />case 'tigratree':<br />/* TigraTree script to display structured categories */<br />include( $vm_path . '/vm_tigratree.php' );<br />break;<br />

Paste this:
<br />//product categories select mod<br />case 'select':<br />include( $vm_path . '/vm_select.php' );<br />break;&nbsp;<br />//<br /> 

Modify modules/mod_product_categories/mod_product_categories.xml:

After:
<br />&lt;option value="dtree"&gt;dTree&lt;/option&gt;<br /> 

Paste this:

<br />&lt;!--product categories select mod--&gt;<br />&lt;option value="select"&gt;Dropdown select menu&lt;/option&gt;<br />&lt;!--end--&gt;<br />

Paste this into a new file at modules/mod_virtuemart/vm_select.php:

<?php
//plethora mod

if( !defined( '_VALID_MOS' ) && !defined( '_JEXEC' ) ) die( 'Direct Access to '.basename(__FILE__).' is not allowed.' );

global $mosConfig_live_site, $mosConfig_absolute_path;

if( vmIsJoomla('1.5')) {
   
$live_module_dir = $mosConfig_live_site.'/modules/mod_virtuemart';
   
$absolute_module_dir = $mosConfig_absolute_path.'/modules/mod_virtuemart';
} else {
   
$live_module_dir = $mosConfig_live_site.'/modules';
   
$absolute_module_dir = $mosConfig_absolute_path.'/modules';
}
/*change following:*/
$params->set( 'module_name', 'ShopMenu' );
$params->set( 'module', 'vm_transmenu' );
$params->set( 'absPath', $absolute_module_dir . '/' . $params->get( 'module' ) );
$params->set( 'LSPath', $live_module_dir . '/' . $params->get( 'module' ) );

//include_once( $params->get( 'absPath' ) .'/Shop_Menu.php' );

global $my, $db;

//$mbtmenu= new Shop_Menu($db, $params);

//$mbtmenu->genMenu();
require_once(CLASSPATH.'ps_product_category.php');
$ps_product_category = new ps_product_category();

function get_category_tree_select( $category_id=0,
               
$links_css_class="mainlevel",
               
$list_css_class="mm123",
               
$highlighted_style="font-style:italic;" ) {
        global
$sess;

        $categories = ps_product_category::getCategoryTreeArray(); // Get array of category objects
       
$result = ps_product_category::sortCategoryTreeArray($categories); // Sort array of category objects
       
$row_list = $result['row_list'];
       
$depth_list = $result['depth_list'];
       
$category_tmp = $result['category_tmp'];
       
$nrows = sizeof($category_tmp);

        // Copy the Array into an Array with auto_incrementing Indexes
       
$key = array_keys($categories); // Array of category table primary keys
       
       
$nrows = $size = sizeOf($key); // Category count

        $html = "";

        // Find out if we have subcategories to display
       
$allowed_subcategories = Array();
        if( !empty(
$categories[$category_id]["category_parent_id"] ) ) {
           
// Find the Root Category of this category
           
$root = $categories[$category_id];
           
$allowed_subcategories[] = $categories[$category_id]["category_parent_id"];
           
// Loop through the Tree up to the root
           
while( !empty( $root["category_parent_id"] )) {
               
$allowed_subcategories[] = $categories[$root["category_child_id"]]["category_child_id"];
               
$root = $categories[$root["category_parent_id"]];
            }
        }
       
// Fix the empty Array Fields
       
if( $nrows < count( $row_list ) ) {
           
$nrows = count( $row_list );
        }

        // Now show the categories
       
for($n = 0 ; $n < $nrows ; $n++) {

            if( !isset( $row_list[$n] ) || !isset( $category_tmp[$row_list[$n]]["category_child_id"] ) )
            continue;
            if(
$category_id == $category_tmp[$row_list[$n]]["category_child_id"] )
           
$style = $highlighted_style;
            else
           
$style = "";

            $allowed = false;
            if(
$depth_list[$n] > 0 ) {
               
// Subcategory!
               
if( isset( $root ) && in_array( $category_tmp[$row_list[$n]]["category_child_id"], $allowed_subcategories )
                ||
$category_tmp[$row_list[$n]]["category_parent_id"] == $category_id
               
|| $category_tmp[$row_list[$n]]["category_parent_id"] == @$categories[$category_id]["category_parent_id"]) {
                   
$allowed = true;

                }
            }
            else
            $allowed = true;
           
$append = "";
            if(
$allowed ) {
                if(
$style == $highlighted_style ) {
                   
$append = 'id="active_menu"';
                }
                if(
$depth_list[$n] > 0 ) {
                   
$css_class = "sublevel";
                }
                else {
                   
$css_class = $links_css_class;
                }

                $catname = shopMakeHtmlSafe( $category_tmp[$row_list[$n]]["category_name"] );

                if ($_REQUEST['category_id'] == $category_tmp[$row_list[$n]]["category_child_id"]){
               
$selected = ' selected="selected"';
                }
                else{
               
$selected = '';
                }
               
$Itemid = (int) vmRequest::getInt( 'Itemid', '' );
               
$html .= '
                <option value="'
.$sess->url(URL."index.php?page=shop.browse&amp;category_id=".
               
$category_tmp[$row_list[$n]]["category_child_id"].'&option=com_virtuemart&Itemid='.$Itemid).'" label="'.$catname.'" id="'.
               
$category_tmp[$row_list[$n]]["category_child_id"].'"'. $selected. '>'
               
. str_repeat("&nbsp;&nbsp;&nbsp;",$depth_list[$n]) . $catname
               
. ps_product_category::products_in_category( $category_tmp[$row_list[$n]]["category_child_id"] )
                .
'</option>';
            }
        }

        return $html;
    }
?>
<script type="text/javascript">
<!--
function MM_jumpMenuGo(objId,targ,restore){ //v9.0
  var selObj = null;  with (document) {
  if (getElementById) selObj = getElementById(objId);
  if (selObj) eval(targ+".location='"+selObj.options[selObj.selectedIndex].value+"'");
  if (restore) selObj.selectedIndex=0; }
}
function MM_jumpMenu(targ,selObj,restore){ //v3.0
  eval(targ+".location='"+selObj.options[selObj.selectedIndex].value+"'");
  if (restore) selObj.selectedIndex=0;
}
//-->
</script>
<form id="vmcatselectform" name="vmcatselectform" action="">
  <select name="vmcatselect" id="vmcatselect" onchange="MM_jumpMenu('parent',this,0)">
   <?php
echo get_category_tree_select( $category_id, $class_mainlevel );
?>

  </select>
  <input type="button" name="go_button" id="go_button" value="Go" onclick="MM_jumpMenuGo('vmcatselect','parent',0)" />
</form>
<?php
 
//end
 
?>

2011-01-11

This article is about K2, which works with Joomla.

We created menu items pointing to “generic” tag views. Each tag has its own Itemid, which is fine. However, when you click on “read more” for any of the items on the generic tag page, the ‘read more’ link uses the default K2 Itemid rather than the Itemid of the current page / menu item. This does seem like a bug to me. The read more links should use the Itemids of the current page or at least the menu item that brought the viewer to that ‘read more’ link.

In our case, K2′s default Itemid is 17 (as set in the menu).  We also have a menu item for a tag view, with an Itemid of 37.
The ‘read more’ URLs should have been in this format:

index.php?option=com_k2&view=itemlist&layout=generic&tag=Health%20Care&task=tag&Itemid=37

.. but were showing up as:

index.php?option=com_k2&view=itemlist&layout=generic&tag=Health%20Care&task=tag&Itemid=17

We have written a small override that solved this for us, since I could find no other way to solve this. We dislike core modifications but in this case it seemed to be the only way:

In /components/k2/models/item.php around line 62 (search for “read more link” .. it should be the first one), add this:

2010-12-28

We are in the middle of designing a new version of plethoradesign.com using Joomla and K2. Most images in our web design portfolio have a large version that can be clicked and enlarged, but in some cases this was not desirable; for example for logos. There was no way to turn off this feature (not easily anyway), so it was time to add this option to K2.

Thanks to a post over at the K2 Forums, we customized our K2 installation so it is now possible to turn off the popup modal link on an item-by-item basis.

Download: k2-item-image-link-option.zip

Changed files;
administrator/com_k2/models/item.xml
components/com_k2/templates/default/item.php

The code in item.php is set up so items will show popup links on images by default, but if the item options have been set to disable the popup, it will not be linked.

2010-06-23

For the longest time I have been frustrated that the standard banner component doesn't let banner clients login and view theirbanner statistics. I had been using OpenX but it is a resource hog and is really overkill when all I want is to be able to show a client how many clicks they've had so far. I don't want them to just take my word for it.

Well, after trying out various solutions besides OpenX, I thought to myself 2 hours ago, "why can't I just use Contact field in thebanner client area and enter a Joomla username, and then write a custom model for com_banners to display the statistics if the current Joomla user is the same as the one listed as the contact for a client?"

So that's what I did. It is actually two queries. #1 checks the current username and selects all clients with that username (should be just one .. I limited it to one in the query), and selects the correct cid (client id). #2 selects all active banners for a given cid and spits them out, showing the banner, clicks, impressions, and CTR. I also added in some custom HTML to display a PayPal button … ideally this should be done in a View, not a Model, but the Banners component doesn't seem to follow the MVC structure 100% so I didn't either.

I did have to modify a few other files. Here are my modifications – this is on Joomla 1.5.18 but should work for any 1.5 release.

It's as easy as 1-2-3;

/components/com_banners/controller.php:

Add this after the end of function click():

</p>
<p>function bannerstats(){</p>
<p>$model = $this-&gt;getModel( 'Bannerstats' );</p>
<p>$model-&gt;bannerstats();  }</p>
<p>

Note that some older Joomla code might read:

</p>
<p>$model = &amp;$this-&gt;getModel( 'Bannerstats' );</p>
<p>

PHP 5 requires:

</p>
<p>$model = $this-&gt;getModel( 'Bannerstats' );</p>
<p>

 

/components/com_banners/bannerstats.php
This is a new file – containing this code:

// Create the controller $controller = new BannersController( array('default_task' =&gt; 'bannerstats') ); // Perform the Requested task $controller-&gt;execute(JRequest::getVar('task', null, 'bannerstats', 'cmd')); // Redirect if set by the controller $controller-&gt;redirect();

/components/com_banners/models/bannerstats.php

This is a new file containing this code:

// Check to ensure this file is included in Joomla! defined('_JEXEC') or die( 'Restricted access' ); $document = &amp;JFactory::getDocument(); jimport( 'joomla.application.component.model' ); jimport( 'joomla.application.component.helper' ); /** * @package Joomla * @subpackage Banners */ class BannersModelBannerstats extends JModel { function bannerstats(){ $banneruser = &amp;JFactory::getUser(); $dbgetclient = &amp;$this-&gt;getDBO(); echo "
<h2>".$banneruser-&gt;name."'s Ads</h2>
"; echo "
<p>Already a customer? If your ad is about to reach the paid for number of clicks, you can make another payment using this button:</p>
"; echo '
<p>form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank"&gt; <input type="hidden" value="_xclick" name="cmd" /> <input type="hidden" value="US" name="lc" /> <input type="hidden" value="youremail@yourdomain.com" name="business" /> <input type="hidden" value="4UXZK24C297M6″&gt; &lt;input name=" name="pal" /> <input width="62″ height=" border="0″ type=" alt="Make payments with PayPal – it\'s fast, free and secure!" name="submit" src="/sites/default/files/images/x-click-but01.gif" /> <br /> '; $dbgetclient = &amp;$this-&gt;getDBO(); echo "</p>
<h2>Active ads for: ".$banneruser-&gt;username.":</h2>
"; $getclient = "SELECT cid FROM #__bannerclient WHERE contact='".$banneruser-&gt;username."' LIMIT 1″; $dbgetclient-&gt;setQuery( $getclient ); $getclientresult = $dbgetclient-&gt;loadObjectList(); for($i = 0; $i &lt; count($getclientresult); $i++) { $cid = $getclientresult[$i]-&gt;cid; //echo "cid: ".$cid."<br />"; } $dbgetbanner = &amp;$this-&gt;getDBO(); $getbanner = "SELECT * FROM #__banner WHERE cid=".$cid.""; $dbgetbanner-&gt;setQuery( $getbanner ); $getbannerresult = $dbgetbanner-&gt;loadObjectList(); for($i = 0; $i &lt; count($getbannerresult); $i++) { echo "<img />imageurl."" _mce_src="".JURI::root()."images/banners/".$getbannerresult[$i]-&gt;imageurl."" border='0′&gt;<br />"; echo 'URL: '.$getbannerresult[$i]-&gt;clickurl.'<br />'; echo 'Clicks: '.$getbannerresult[$i]-&gt;clicks.'<br />'; echo 'Views: '.$getbannerresult[$i]-&gt;impmade.'<br />'; echo 'CTR (clickthrough rate): '.number_format((100*($getbannerresult[$i]-&gt;clicks)/($getbannerresult[$i]-&gt;impmade)),2).' %
<hr />
'; } } }

You can then create a link in the User Menu pointing to index.php?option=com_banners&amp;task=bannerstats.

It will detect the current logged-in user's username, check the "contact" field of the banner clients, and select the active banners for that client. Needless to say, the contact field should contain that person's username and nothing else, or it will not work.

In the future maybe there can be a special field allowing us to choose a username or enter one, instead of using the Contact field which is not really meant for that. I had considered making this a separate component, but it is really something I would like to see become part of the regular com_banners component.

Feedback is welcome – as far as I can tell this is secure and works, but others may have a better idea about that.

2010-06-17

It is often desirable to monitor a site for file changes, so that you can be alerted to unwanted file changes. Those could indicate hacker activity. There is server-side software that can do this for sites, but since our focus is Joomla, we wanted to take a moment to recommend the Eyesite component: http://www.lesarbresdesign.info/extensions/eyesite. To get the most out of this, you will need to set up a cron job so that it will email you whenever there are outstanding file changes.

2010-06-01

Sometimes when coding a Joomla template it may be handy to know the section ID of the article or category the user is viewing.

Here is some PHP code you could insert in your template to achieve this (for Joomla 1.5);

<br />$db =&amp;JFactory::getDBO(); <br />if(JRequest::getVar(‘view’)==’article’){ <br />$query = "SELECT sectionid FROM #__content WHERE id=".JRequest::getVar(‘id’).""; <br />$sectionid = $db-&gt;loadResult();</div>
<div class="post-198 post type-post hentry category-sundry">}<br />if(JRequest::getVar(‘view’)==’category’){ <br />$query = "SELECT sectionid FROM #__content WHERE catid=".JRequest::getVar(‘id’).""; <br />$sectionid = $db-&gt;loadResult();</div>
<div class="post-198 post type-post hentry category-sundry">}<br />

Using this, you could for example control which graphics or CSS file are loaded depending on the section of the article/category.

Pages