Overview

Every page within your website needs to belong to a containing object known as a codeframe. Codeframes are generally written in a generic way so that they can be used by many different websites. As such, they are usually stored within the default framework site (system). Occasionally, you may have a need to build a very unique layout for a site which is unlikely to be required again (by any other sites). In a case like this it is better to create the codeframe within the site itself.

Codefames vs. Layouts

Layouts are built on top of codeframes. Internally, codeframes are PHP classes and layouts are also PHP classes that extend codeframes. Although these are implemented as PHP classes they are maintained at a higher level of abstraction (like all components you build in GenHelm). You can think of a codeframe as a smart template. It is a template in the sense that it can contain static html, etc. that you want to render in your html pages but it smart in the sense that the contents of the template can be very dynamic and can be used to build code on a conditional basis. A layout is used to add content to a codeframe.

As an analogy, consider a house. The frame of the house is similar to a codeframe, it gives the house its overall dimensions but it does not provide much in the way of aesthetics. To complete the house you need to add cladding (bricks, panelling, etc.), windows, shingles, doors, etc. It is common for two different houses to use the exact same framing plan yet the houses can end up looking quite different from each other. This is the also the case with layouts. Two layouts may be based on the same codeframe, yet they may result in websites that look completely different from one another.

Simple Codeframe Example

Consider the following simple codeframe:

Simple Codeframe Example

Most of this will be quite familiar to anyone who has ever built a web page by hand. The only thing that looks "strange" is the #page(). Since codeframes can support any number of web pages they need to have a placeholder into which the current page's content will be inserted. So, in the rendered html page, the #page() placeholder will be replaced by the content associated with whatever page was invoked. So, we can see that every code frame contains two main parts:

  1. The "common" html that is generated for every page (technically, codeframes can be used to create other content besides html).
  2. The specific html that relates a single page (generally a single url of your site)

Although this example codeframe is easy to understand, it is also quite useless as a real codeframe. This is because the common code does not really do very much. Let's consider some of the things that this codeframe does not support.

  • In reality, you would want a different title for each page
  • Most web pages are going to need some stylesheets to style the content
  • For SEO (Search Engine Optimization) purposes you should at least have a meta description
  • Some pages might require JavaScript
  • You might need an onload event in the html tag to fire the JavaScript
  • Chances are you will want a common banner and maybe a common footer for your site
  • etc.

Luckily the codeframe implementation is extensive enough to support all of these capabilities and many more. The general concept is that, in addition to plain text, codeframes can contain various types of placeholders that can be called upon to add content to a webpage. There are four main types of placeholders:

  1. Objects
  2. Containers
  3. Properties
  4. Tags

Each type of placeholder has slightly different behaviour however the key objective is to allow every page of your site (the specific code) to have full control over what goes into the shared common sections of the page. As a simple example, page A might require stylesheet1 while page B requires stylesheet2 yet both pages may be using the exact same codeframe. How do we achieve this? Continue reading to learn how codeframe sections can be populated both procedurally (using specification forms) and programmatically (using $functions or method calls within server-side script).

Codeframe Objects

Codeframe object references begin with a # symbol. We have already learned about one of these objects which is #page. Most codeframes will need several other objects to facilitate complex page rendering. These objects generally handle very specific requirements. New objects can be created; however, the supplied objects will generally handle all of your requirements. Here is a summary of the supplied objects that will appear in most codeframes.

metatags

Used to generate all metatags into the head section of your served html. This object can be populated from two main sources:

1. The metatag section defined within site_settings:

Sample meta tags set at the site level

2. The metatag section defined for each page:

Metatags can be overridden or supplemented at the page level

Notice that the Merge Contents column can be used to indicate that you want to append to, prepend or replace the like named metatag set at the site level. Learn more about Meta Tags.

styles

Used to reference externally defined styles. This object is normally placed within the head section of your html codeframes. Styles can be populated using site_settings associated with a site as well as within the layouts that are derived from a codeframe and also at the page level. Programmatically, styles can be added using $style and $stylesheet as well as calling the styles object directly. Styles that are specific to a page can also be added in the Local Styling property of the page.

In cases where styles are used to style "below the fold" content, it may make sense to define these styles at the bottom of the page, rather than in the head section. Fortunately, objects can accept parameters to influence their behaviour. In the case of styles, you can pass a parameter to the styles object to indicate which styles are to be rendered at each location. The style object references will look like one of the following:

  1. #object(styles,head)
  2. #object(styles,bottom)

javascript

The javascript object reference usually appears four times within your codeframes that need to support embedded JavaScript or links to external script. Once again we will pass a parameter to this object to tell the javascript object the location from which it is being called. The object references will appear as:

  1. #object(javascript,head_top)
  2. #object(javascript,head)
  3. #object(javascript,top)
  4. #object(javascript,bottom)

JavaScript can be populated using site_settings, layout definitions and page definitions. Programmatically, script can be added using $script as well as calling the javascript object directly. JavaScript that is specific to one page can also be added within the Local Javascript parameter of the page's specification.

Note that head_top script is generally included before CSS styles and head script is included after the CSS.

Learn more about Styles and Scripts.

linktags

The linktags object handles the generation of all <link> references in the html. Link tags are normally set programmatically using $linktag.

message_area

The message_area object reserves an area on the page into which messages can be rendered. Messages are primarily used by forms to show errors and informational messages. The message area is populated using the set_message method.

Defining Other Placeholders Within a Codeframe

Besides objects, there are three different types of placeholders that you can define within a codeframe:

  1. Containers
  2. Properties
  3. Tags

Containers

Containers are placeholders within the codeframe/layout that can be filled with any content. You will want to define containers at all locations into which your various web pages might want to generate content. There is no requirement to "fill" a container, therefore you should try to anticipate all of the containers that might be needed so that they are available if required.

The content to be added to containers will usually be defined within your site_settings or your layout definitions.

Here are the container definitions that are defined in many of the built-in codeframes:

#container(l,head_bottom)

The head_bottom container be used to add miscellaneous tags to the bottom of your head section. Generally, this tag will not be used because special purpose objects are used to configure the head section. One example of a tag that is not supported by the special purpose objects in the html base tag. If you want such a tag in your head section this could be done by adding the base tag to the head_bottom container.

#container(l,body_top)

The body_top container is used to render content that must appear near the top of the page body.

#container(l,body_bottom)

The body_bottom container is used to render content that must appear near the bottom of the page body.

Notice that all of the container references are passed a value l (lower case L) in parameter 1. This is used in the processing of the Import Codeframe button when editing layouts. Recall that this button populates the layout with the various placeholders (tags, containers, etc.) that can be set for the associated codeframe. Although, technically, any placeholder can be populated within the layout, some placeholders would most likely never be assigned there. Take, for example, the title container. Since this is generally set uniquely for each page, it does not make sense to set this within the layout. Therefore, adding this container, even with an empty value, to the layout would just cause confusion.

Properties

Properties are specialized containers in that they are used to assign properties of an html tag. Consider the properties placeholder in the body tag:

<body #properties(l,body)>

Suppose that a certain page wants to execute some script after the page is loaded. This could be done by calling the $property function as part of the page content. Properties can also be set within site_settings and within layout definitions.

Tags

Tags are kind of like a combination of containers and properties. With tags you can configure the entire content of an html tag within the codeframe, that is, both the properties of the tag and the contents of the tag. This can be done in the site_settings or the layout definition or using the $tag function.

Codeframe Conditions

Sometimes you may want to "tweak" a codeframe because it does not quite represent the structure you need. For example, let's assume that most of your pages are the same structure but a couple of your pages need an extra div tag to be generated around the page object. You could create two codeframes but this introduces a new component that must managed. Codeframe conditions can be used to facilitate this requirement within one codeframe. Here we see a codeframe variable named body_div.

Codeframe condition variable

In this example, the variable only requires two states so we have declared this as a boolean condition. We have defaulted this condition to false and we have indicated that we want to allow layouts that use this codeframe to override this condition variable value. Now let's see how this condition can be used:

Codeframe condition example

Notice that there are two rows in the codeframe that are conditional upon the body_div variable. Since this is a boolean condition we only need to reference the condition. As you can see, for string conditions, such as message_location, you can also form an expression as part of the condition column.

Using the body_div condition we simply need to set the condition to true in cases where we want to generate (and close) the extra div tag. Let's see how this condition can be set in a layout that uses this condition.

Setting codeframe condition from layout

As part of the layout that uses this codeframe we are setting the body_div condition to true. Now the pages that use this layout will generate the extra div and closing div lines.

Defining Code and Properties Within a Codeframe

In most cases you will not have a need to define new properties or code within your codeframe, so you will leave these grids empty. One of the supplied codeframes that uses this feature is named html2pdf_codeframe. The purpose of this codeframe is to render PDF documents that have been created from HTML pages. This utilizes the html2pdf utility developed by Laurent MINGUET which is described in github.

We need to override the generate method for this codeframe in order to call the html2pdf framework to generate a pdf document. Here we see the code that was written for this purpose.

function generate() {
  	// Override the generate method to convert the HTML into a PDF using Html2Pdf
  	$content = parent::generate(); // Get the html for the page
	// Get the values used to instanciate Htlm2Pdf from the config settings
	$orientation = $this->site->settings->get_config('page', 'html2pdf', 'orientation', 'P', 'string');
	$format = $this->site->settings->get_config('page', 'html2pdf', 'format', 'Letter', 'string');
	$lang = $this->site->settings->get_config('page', 'html2pdf', 'lang', 'en', 'string');
	$unicode = $this->site->settings->get_config('page', 'html2pdf', 'unicode', true, boolean);
	$encoding = $this->site->settings->get_config('page', 'html2pdf', 'encoding', 'UTF-8', 'string');
	$margins = explode(',',$this->site->settings->get_config('page', 'html2pdf', 'margins', '5,10,5,10', 'string'));
	$pdfa = $this->site->settings->get_config('page', 'html2pdf', 'pdfa', false, 'boolean');
	try {
  		$html2pdf = new Html2Pdf($orientation,$format,$lang,$unicode,$encoding,$margins,$pdfa);
  		$html2pdf->writeHTML($content);
		$pdf = $html2pdf->output('','S'); // Return the PDF content
		if (strtoupper($encoding) !== 'UTF-8') {
			$this->site->set_charset($encoding);
		}
		return $pdf;
	} catch (\Spipu\Html2Pdf\Exception\HtmlParsingException | 
			 \Spipu\Html2Pdf\Exception\HtmlParsingException | 
			 \Spipu\Html2Pdf\Exception\ImageException | 
			 \Spipu\Html2Pdf\Exception\LongSentenceException | 
			 \Spipu\Html2Pdf\Exception\TableException $e) {
    	$formatter = new \Spipu\Html2Pdf\Exception\ExceptionFormatter($e);
    	$message = $formatter->getHtmlMessage();
		// Adjust the encoding so that the errors are visible in the browser
		$this->site->set_mime_type('html/plain');
		return 'Error converting HTML to a PDF '.$message.' The HTML attempted to be converted is: '.$content;
	}
}

Notice that the code above calls the site's get_config method to obtain certain values that are used to initialize Html2Pdf. The defaults for these values can be defined within the codeframe definition, itself, as we show here:

Codeframe config settings

By pulling this information from config settings, rather than hardcoding these values, the layouts and/or pages that use this codeframe can override these parameters as required.

Sample spec snippet for codeframe model
🡇
Sample rendered content