Skip to content

Concrete5 Mod: getBlockURL in block_controller

August 14th, 2010

Today, as I was building a package for the Concrete5 marketplace, I found it difficult to get the block’s URL within the block controller’s on_page_view method. I was wanting to inject some javascript into the header using $this->addHeaderItem(). This is easy enough in a block view, but not in the controller. For some reason the getBlockURL() method hides in block_view.php, and is not found in the core block_controller.php file.

To solve this, I copied the method from block_view.php and put it in my custom block’s controller.php with one minor change:


$obj = $this->blockObj;

becomes


$obj = BlockType::getByHandle($this->btHandle);

It worked great. Ultimately, I’d like to see this added to the core block_controller.php file, so all blocks could access it.

The full method looks like this, in the block’s controller.php file:


	public function getBlockURL($filename = null) {

		$obj = BlockType::getByHandle($this->btHandle); // had to modify this line

		if ($obj->getPackageID() > 0) {
			if (is_dir(DIR_PACKAGES_CORE . '/' . $obj->getPackageHandle())) {
				$base = ASSETS_URL . '/' . DIRNAME_PACKAGES . '/' . $obj->getPackageHandle() . '/' . DIRNAME_BLOCKS . '/' . $obj->getBlockTypeHandle();
			} else {
				$base = DIR_REL . '/' . DIRNAME_PACKAGES . '/' . $obj->getPackageHandle() . '/' . DIRNAME_BLOCKS . '/' . $obj->getBlockTypeHandle();
			}
		} else if (file_exists(DIR_FILES_BLOCK_TYPES . '/' . $obj->getBlockTypeHandle() . '/' . $filename)) {
			$base = DIR_REL . '/' . DIRNAME_BLOCKS . '/' . $obj->getBlockTypeHandle();
		} else {
			$base = ASSETS_URL . '/' . DIRNAME_BLOCKS . '/' . $obj->getBlockTypeHandle();
		}

		return $base;
	}

Concrete5 Mod: Form Helper Tweaks

August 3rd, 2010

As we were developing the new Peoria County Government website, we encountered several places where the core FormHelper class fell short of our needs. We tweaked it and present it here for your (download delight). The tweaks are described below.

The Select Helper

The standard select() helper does not allow for a <select> element to have multiple selected values. This modified version does, and keeps everything backwards-compatible.

Basically, in order to use multiple selections, you just have to flip-flop the third and fourth parameters.

The code looks like this:


$form = Loader::helper('form');
print $form->select('mySelect[]', $options, array('multiple' => true), $selections);


Explanation: Concrete5 assumes that if the third argument is an array, then it’s $miscFields and there is no selected value (although it does check $_POST for a selected value later–but either way, the selected value is not allowed to be an array of multiple values). Also, if the third argument is an array, the helper assumes no further arguments are supplied. We can use this to our advantage by supplying an array of multiple selections as the fourth parameter.

The Checkbox Helper

The standard checkbox helper assumes you want the name and id attributes of the input tag to be identical, but this assumption does not always hold true. There have been many times—especially in grid situations—when we wanted the name of a checkbox to be something like ‘selected[]‘ but the id to be unique and incremented like ‘selected0′, ‘selected1′, ‘selected2′, etc., so these elements could be scripted easily.

Our modified checkbox helper does this. All that’s needed is to make sure one of the misc fields has a key of ‘id’.

The code looks like this:


$form = Loader::helper('form');
for ($n = 0; $n < 5; $n++) {
	print $form->checkbox('selected[]', $selected[$n], false, array('id' => 'selected' . $n));
}


The File Helper

This mod simply allows for class names to be attached to the <file> tag. Usage is very straightforward, just submit the className string as the second argument of the helper.

Concrete5 Mod: Searchable HTML block

July 29th, 2010

This mod is simple and should be incorporated into the core ASAP. It enables Concrete5′s search functionality to index textual content within the HTML block type.

Just add the following code to /root/concrete/blocks/html/controller.php, starting at line 20:


	public function getSearchableContent() {
		return strip_tags($this->content);
	}


Voila!

Great quote about technology vs. creativity

July 11th, 2010

“Effects these days are in the hands of everyman. You can go shoot a movie on your own, of high quality—broadcast quality, with a camcorder. But it doesn’t necessarily mean we’re seeing better movies. Shakespeare didn’t have a word-processor. When we got word-processors we didn’t get Shakespeares. We’ve got to separate the two out. There’s creativity, there’s technology. And the two are inter-related, but technology is not necessarily creative.”
~ Harrison Ellenshaw

Lighting: Future of Forestry Concert

July 3rd, 2010

One Hat Provided concert stage lighting for Future of Forestry yesterday. The band complimented the lighting saying it was “very balanced” and tasteful.

More pictures of this concert may be viewed in our gallery.

Graphic Design: Real Life, Real Joy Poster

April 23rd, 2010

Just copleted a poster for a Hand of Love Ministry event entitled: Real Life, Real Joy!

Click image at left to download medium-res version.

Site Launch: Ate Up Clothing Company

April 22nd, 2010

Just launched a new small website for Ate Up Clothing Company. This site features some custom animation, a “crucial” design, and is our first website built on top of the Concrete5 content management system!

Congratulations Casey, on your site launch. (Now it’s time to fill it with some great content!)

Concrete5 and Dreamhost – How to get it working

April 16th, 2010

Great post on the Concrete5 forums about getting Concrete5 to work on Dreamhost servers using PHP and FastCGI.

Basically, it boils down to adding a cgi-bin directory, then creating a dispatcher.fcgi file inside it. After that, the .htaccess file needs to be tweaked a bit.

Concrete5: When and Where to Include Your JS

April 7th, 2010

It’s important to include Javascript in the correct places and in the correct order when building a Concrete5 site. So when and where should we include our Javascript files?

1. header_required.php

This file normally resides at /concrete/elements/header_required.php. In order to modify it, we’ll have to first copy it to the root /elements folder. Within this file, we’ll stick our Javascript base libraries and other global JS code.

Base libraries

This is where we want to include whatever base Javascript library (YUI, jQuery, EXT, mootools, etc.) we’ll use on the site.

Hint: If you don’t use jQuery as a base library on your site, it might be helpful to introduce a switch to determine whether the user is logged in or not. If he is, send the stock Concrete5 base elements (so the in-context editing will work); if not, just send your custom base libraries instead.

Note: The syntax shown here uses our Flexible JS and CSS mod.


if ($u->isRegistered()) {
   // concrete5 libraries
   $this->addHeaderItem($html->css('ccm.base.css'), 'CORE');
   $this->addHeaderItem($html->javascript('jquery.js'), 'CORE');
   $this->addHeaderItem($html->javascript('ccm.base.js'), 'CORE');
}
// your base libraries
$this->addHeaderItem($html->css(array(
   'url' => 'js/ext/resources/css/ext-all.css'
)), 'CORE');
$this->addHeaderItem($html->javascript(array(
   'url' => 'js/ext/adapter/ext/ext-base.js'
)), 'CORE');
$this->addHeaderItem($html->javascript(array(
   'url' => 'js/ext/ext-all.js'
)), 'CORE');


Other global scripts

This is also a good place to put any custom Javascript code your whole site depends upon, regardless of which template the user chooses. For instance, our practice is to always include a script here which sets up a custom Javascript namespace.


$this->addHeaderItem($html->javascript(array(
   'url' => 'js/global/onehat.js'
)));

// This script contains something along the lines of:
// var ONEHAT = {
//    app: {},
//    browser: {},
//    util: {},
//    widget: {}
// };


All the custom apps and widgets we create fit under this one global ONEHAT object so we can be sure our code plays nicely with code from other sources.

2. Templates

Each template file should include whatever scripts are unique to that template. This might include user-interface items like navigation menus or animations.


if (!$c->isEditMode()) {
   $this->addHeaderItem($html->javascript(array(
      'url' => 'navigation.js'
   )));
}


Notice that our practice is to place template-specific Javascript in a conditional block so that it only loads when the page is not in edit mode. This is so custom user-interface eye-candy doesn’t interfere with the in-context editing features of Concrete5.

Also, be aware that any Javascript inserted within a template needs to be placed before the following code (otherwise it won’t work, and will fail silently):


<?php Loader::element('header_required'); ?>


3. Page view

Various page types might have specific Javascript that needs to be inserted—single pages, especially. This can be done anywhere within the page view using $this->addHeaderItem().

4. Page controller

Page controllers are an even better place to stick Javascript includes than the view—especially if you only need it in certain cases, like when an action is being run. Most controller methods can contain a call to $this->addHeaderItem(), including __construct() (although this is usually not the best place to put it), on_start(), on_before_render() (which is where I usually put it), and view().

5. Block controller

Unfortunately, blocks are rendered after the View::outputHeaderItems() method has been run. That means any block Views which contain calls to $this->addHeaderItem() will fail silently! No exceptions will be thrown. No errors will be logged in the dashboard. It just won’t work.

This means that if a block wants to insert Javascript into the header, it must do so within that block controller’s on_page_view() method.


class FooBlockController extends BlockController {
   public function on_page_view() {
      Loader::helper('html');
      $html = new SiteHtmlHelper();
      $this->addHeaderItem($html->javascript(array(
         'url' => 'blocks/foo/bar.js',
         'inline' => false,
         'minify' => true
      )), 'VIEW');
   }

   // This must be defined or else header items will be added twice...
   // once in the header and once at the bottom
   public function outputAutoHeaderItems() { }

}


Note: It is possible for your block’s View to insert Javascript inline (without using addHeaderItem()). But if you followed our advice of moving JS to the bottom of the HTML source, you will encounter the problem of your inline JS appearing in the HTML source before the base libraries or your global namespace object have loaded. Thus, it’s likely that your code will break.

Final Thoughts

It’s important to keep your code modular and loosely-coupled. Code for blocks should not interfere with one another or be dependent on each other—they should not even be aware that the other blocks exist. Code for templates should be totally self-contained and only apply within that template.

Concrete5 Mod: Moving JS to the Bottom

March 31st, 2010

In our previous post, we talked about a mod to Concrete5 which would allow for flexible JS and CSS includes. In this post, we’re going to expand upon that idea with another mod which will allow us to move our Javascript code to the bottom of the HTML source, thereby providing better front-end performance for our websites.

Note: This mod only takes effect when used in conjunction with the Flexible JS and CSS Includes mod, and will have no effect on core Concrete5 Javascript includes. Thus its true benefit—at least right now—is for the public-facing end of your website, not the contextual-editing part of Concrete5.

Eventually, it would be nice to see this functionality incorporated into the Concrete5 core. Also it would be nice to see all core Javascript includes migrated to use these methods. That way, all JS scripts would be loaded last on the page but in the correct order, preserving dependencies, etc.

Instructions for Concrete v5.4

Step 1:

Copy the /concrete/libraries/view.php file to your /libraries folder.

Step 2:

Within view.php, add a private property, $footerItems, immediately after $headerItems.


private $footerItems = array();


Step 3:

Still within view.php, replace the stock outputHeaderItems() method with the following:


public function outputHeaderItems() {
   $items = $this->getHeaderItems();
   foreach($items as $item) {
      if ($item instanceof SiteJavaScriptOutputObject) {
         $this->footerItems[] = $item;
      } else {
         print $item;
         print "\n";
      }
   }
}


This is where the magic happens. We are going through the headerItems array and checking to see if there are any Javascript items that have used our modified HtmlHelper class. If so, we pull them aside and stick them in a footerItems array for later inclusion.

Step 4:

Still within view.php, add the following method:


public function outputFooterItems() {
   $items = $this->footerItems;
   foreach($items as $item) {
      print $item;
      print "\n";
   }
}


Step 5:

Create a new file in your /elements directory called footer_required.php. Insert the following code:


<?php
print $this->outputFooterItems();
echo Config::get('SITE_TRACKING_CODE');
?>


Step 6:

Now, inside your template, insert the following code, just before the closing </body> tag:


<?php Loader::element('footer_required'); ?>


Step 7:

Finally, whenever you include Javascript code, use the following syntax:


$this->addHeaderItems($html->javascript(array(
   'url' => 'myScript.js'
   // and any of the other optional parameters here
)));


That’s it! Your Javascript code will now appear last in the HTML source, and you will likely notice an improvement in front-end performance.

Hints

Modify header_required.php to use the new HtmlHelper methods

But only do this when the user is not logged in (as it will break the Concrete5 UI when the user is logged in).


if ($u->isRegistered()) {
   $this->addHeaderItem($html->css('ccm.base.css'), 'CORE');
   $this->addHeaderItem($html->javascript('jquery.js'), 'CORE');
   $this->addHeaderItem($html->javascript('ccm.base.js'), 'CORE');
} else {
   $this->addHeaderItem($html->css('ccm.base.css'), 'CORE');
   $this->addHeaderItem($html->javascript(array('url' => 'jquery.js'), 'CORE'));
   $this->addHeaderItem($html->javascript(array('url' => 'ccm.base.js'), 'CORE'));
}


And as mentioned elsewhere, don’t even bother to include the jQuery library in this file unless you’re using jQuery elsewhere on your site.

Put your JS and CSS code inline if on the home page, otherwise link externally

Usually, front pages profit from having the JS and CSS code put inline, thereby cutting down on initial HTTP requests. Internal pages to a website, on the other hand, usually benefit from the caching provided by linking to external files.


$this->addHeaderItem($html->css(array(
   'url' => 'styles/globalLayout.css',
   'inline' => $this->getCollectionParentID() ? false : true
)));


If we are on the home page (the collection’s parentID == 0, which is falsey) then our code is inserted inline. If not, we use an external link.

Use addHeaderItem() to add footer items?

It may seem a little strange to be using addHeaderItems in order to add Javascript to the footer. But we’re doing this for the sake of backward-compatibility. Adding a new method of addFooterItems would not work with existing code.

©2009 One Hat Design Studio, llc   Blog Admin

map index index map index map index map index map index map index map index map