Category Archives: Expression Engine

Expression Engine How To

Git is magnificent.

Git is magnificent. No single tool has saved me as much time, effort, and sanity as Git and I recommend it’s use to every developer, designer, and power-user I know.

What’s so great about Git?

I had planned to sit down this evening and write an article extolling the virtues of git and version control, but I’ve discovered loads of articles who have done it much better than I ever could. So instead of reinventing the wheel, I will merely redirect you to to these articles for an introduction to git, then make a few additional points.

Required reading:
Designers read Git: A Designer’s Perspective.
Developers read the article above and Git for the nervous developer.

A SlideShare presentation on the more technical aspects of Git.

Git options on Mac OS X: 

  • GitTower – Commercial gui client ($69)
  • git-osx-installer – open-source installation of command-line git
  • port install git-core – using mac ports (formerly darwin) package manager

Git options for Windows users:

  • TortiseGit - open-source client with Windows Explorer integration
  • MSysGit - open-source command-line client
  • SmartGit - commercial ($69)

Git repository hosting:

  • Github – Packages geared toward many users and better collaboration, less repositories
  • Beanstalk – Repository hosting with packages geared toward fewer developers, but more repositories per account tier.
  • Unfuddle – Git and SVN hosting, geared toward project managements (has wikis, gantt charts, etc). Silly name.

More resources:

Pro Git – A free git bible.

Expression Engine How To

Adding a “Prepend” Option to URLs generated by the {exp:structure:nav} Tag

In mod.structure.php function nav() insert the highlighted line:

[php highlight="81" firstline="76"]

// Miscellaneous parameters
$depth = $this->EE->TMPL->fetch_param(‘limit_depth’, 1);
$exclude = $this->EE->TMPL->fetch_param(‘exclude’, NULL);
$mode = $this->EE->TMPL->fetch_param(‘mode’, ‘sub’);
$prepend = $this->EE->TMPL->fetch_param(‘prepend’, NULL);


And before the end of the function, replace the line setting $html with the following highlighted line:

[php highlight="124" firstline="120"]

if ($entry_id === FALSE)
$entry_id = 0;

$html = $this->sql->generate_nav($entry_id, $current_id, $depth, $exclude, $mode, $prepend);

return $html;


Now open sql.structure.php.

Locate the generate_nav() function (around line 479) and change the function definition to the first highlighted line below. Also update the function call to get_selective_data() (around line 489) to pass through that $prepend value as shown in the second line below.

[php highlight="479,489" linestart="474"]

* Get the HTML code for an unordered list of the tree
* @return string HTML code for an unordered list of the whole tree
function generate_nav($entry_id, $current_id = FALSE, $depth = 1, $exclude = NULL, $mode, $prepend=null)

$html = ”;
$separator = $this->EE->config->item(‘word_separator’) != "dash" ? ‘_’ : ‘-’;

// Fallback to entry_id if no current_id (e.g. sitemap usage)
if ($current_id === FALSE)
$current_id = $entry_id;

$pages = $this->get_selective_data($entry_id, $depth, $exclude, $current_id, $mode, $prepend);
$pages = $this->add_attributes($pages, $current_id, $mode);


Now we update get_selective_data() (around line 139) to make use of the passed data:

[php linestart="130" highlight="139"]
* Get selective data on all Structure Channels
* The Godfather of all Structure queries
* "I’m gonna make you a query you can’t refuse."
* @return array
function get_selective_data($parent_id = NULL, $depth = 1, $exclude = NULL, $current_id = NULL, $mode = NULL, $prepend = NULL)
$pages = $this->get_site_pages();


Please take a moment and go back to thoroughly review the inline comment.

Finally, in the same get_selective_data() function find the code which sets “$data[$row['entry_id']]['uri']” (around line 286) and replace it with the highlighted code:

[php linestart="281" highlight="287"]

// build out the main data array
foreach ($page_data as $row)
$data[$row['entry_id']] = $row;
//$data[$row['entry_id']]['uri'] = $this->EE->functions->create_page_url($pages['url'] .’/’. $prepend, $pages['uris'][$row['entry_id']]);
$data[$row['entry_id']]['uri'] = $this->EE->functions->create_page_url($pages['url'] .’/’. $prepend, $pages['uris'][$row['entry_id']]);
$data[$row['entry_id']]['slug'] = $pages['uris'][$row['entry_id']];
$data[$row['entry_id']]['classes'] = array();
$data[$row['entry_id']]['ids'] = array();


That does it. You should now be able to add an arbitrary string to the beginning of the path portion of a URL.

Example code:

[php htmlscript="true" wraplines="true"]

{exp:structure:nav mode="sub" lang="{site_lang}" start_from="/{segment_1}" }


Note: {site_lang} is a snippet I’ve created in the EE back end that always echos the current site “stub”. Also, the “start_from” attribute isn’t required.

You can use the same principles in this post to prepend strings to the Structure breadcrumbs and other parts of Structure. Please be aware, however, if you modify the core code the Structure guys are not going to be able to help you with any problems you might encounter.

Expression Engine How To

Adding a Custom Field to the Structure Page Control Panel

A recent project with POWERSHiFTER had us designing an Expression Engine-driven site with a ridiculous amount of content (110,000 words / 500+ printed pages). Luckily the content was very well organized and came complete with internal addressing system for each of the sections to improve the quality of life for out copy editors.

Out-of-the-box page hierarchy on the Structure control panel

Now for all of our Expression Engine sites we immediately install Structure, a badass module that completely reinvents how EE handles pages. Basically, it’s the best thing ever.. it’s got an intuitive design and saves us (me) from messing around trying to hack a “page structure” into EE, and lists site content pages in a hierarchy on a single view on the admin control panel which makes it super-easy to find or edit pages. Awesome! But even with all those perks, managing all that content in Structure became a bit cumbersome as everything in our copy was referenced by the “internal address” I mentioned above, not by the page titles that appear on the Structure control panel.

The solution? Add our internal document address system custom field to the Structure control panel page!

After adding custom field data to the page hierarchy

Keep in mind that this should work for any custom field attached to a Structure page (provided the field exists), but I’m not making any guarantees or warranty that this will work.. even though it should. Basically, it works on my machine using an install of ExpressionEngine 2.1.3 but, as with everything in life, YMMV.

Further Warning: This will modify the core code for Structure. Don’t expect Travis or Jack (the guys behind Structure) to support your extension if these modifications break your site, cause you mental anguish, or murder your goldfish. You’ve been warned.

Step 0:


Step 1:

Get the Field ID for the custom field you want to include on the Structure Control Panel page. You can get this by going to Admin -> Channel Administration -> Custom Fields. Click “Add/Edit Custom Fields” for the appropriate Field Group, then click on the custom field you will be using.

Now look at the URL, you should see an address like this:

Write down the number that comes after “field_id=” in the URL; in this case it’s “21″. We will be using this later.

Step 2

Open the file “expressionengine/third_party/stricture/sql.structure.php” and find the “get_data()” function, on-or-around line 83. We are going to update the SQL query used by this function.

The SQL query at the time of writing is the following:

[php title="Original SQL"]
$sql = "SELECT node.*, (COUNT(parent.entry_id) – 1) AS depth, expt.title, expt.status
FROM exp_structure AS node
INNER JOIN exp_structure AS parent
ON node.lft BETWEEN parent.lft AND parent.rgt
INNER JOIN exp_channel_titles AS expt
ON node.entry_id = expt.entry_id
WHERE parent.lft > 1
AND node.site_id = {$this->site_id}
AND parent.site_id = {$this->site_id}
GROUP BY node.entry_id
ORDER BY node.lft";

And we’re going to update it to the following:

[php highlight="2,8" title="Modified SQL Query"]
$sql = "SELECT node.*, (COUNT(parent.entry_id) – 1) AS depth, expt.title, expt.status
,expd.field_id_21 AS content_address
FROM exp_structure AS node
INNER JOIN exp_structure AS parent
ON node.lft BETWEEN parent.lft AND parent.rgt
INNER JOIN exp_channel_titles AS expt
ON node.entry_id = expt.entry_id
INNER JOIN exp_channel_data as expd ON node.entry_id = expd.entry_id
WHERE parent.lft > 1
AND node.site_id = {$this->site_id}
AND parent.site_id = {$this->site_id}
GROUP BY node.entry_id
ORDER BY node.lft";

Notice the additions I’ve made on the 2nd and 8th lines. Let me explain:

  • Line 2: Replace “21″ with the Field ID you noted from Step 1. This will return the data you care about. You can also add “AS your_field_name” bit to your SQL to help you identify the field in the next step.
  • Line 8: This INNER JOIN connects the page title we’re returning to the custom field data for the page. You don’t need to edit this line.

Save and close the file.

Step 3:

Open the file “expressionengine/third_party/structure/views/index.php” and find following code (on or around line 69):

<a class="page-edit" href="<?=$edit_url;?>"><?=$page['title'];?></a>

Now update the code to add in your custom field data, similar to the following:

<a class="page-edit" href="<?=$edit_url;?>"><?=$page['content_address'];?>&nbsp;<?=$page['title'];?></a>

Save and close the file. Done!