# Putting the pages together

The Fat-Free Framework pulls together template files to create the pages:

- layout.html – primary template that pulls in the other templates:
  - head.html – the HTML head section – same template for all pages
  - notifications.html – conditional text for unsupported versions of Internet Explorer & the EU cookie notice – same template for all pages
  - navigation.html – logo and navbar – same template for all pages
  - content.html – page content and layout is supplied in these files (each page has a unique content file with a unique file name)
  - footer.html – same template for all pages
  - scripts.html – same template for all pages
- sitemap.xml

The head, notifications, navigation, and script sections are all generic and receive their information from the hive via the controllers (data is stored before calling layout.html). The footer content is supplied from the dict files (it’s the same content, just translated). The content sections are specific to each page.

## layout template

The layout template sets up the html page and calls the other templates:

    <!DOCTYPE html>
    <html lang="{{ @langCode }}" prefix="og: http://ogp.me/ns#">
    <include href="head.html" />
    <body{{ @isHomePage ?
        ' data-spy="scroll" data-target="#main-nav" data-offset="60" id="page-top"'
        : ''  }}>
        <div class="offcanvas-right">
    <include href="notifications.html" />
    <include href="navigation.html" />
    <include href="{{ @pageContent }}" />
        </div><!--offcanvas offcanvas-right-->

    <include href="footer.html" />
    <include href="script.html" />
    </body>
    </html>

Layout handles the body tag differently for the home page by including the setup for Bootstrap 4’s Scrollspy.

## head template

The head template sets up:

- dns-prefetch for Google Analytics
- Site meta data
- Google analytics
- Loading the CSS files
- Loading the icons

The dns-prefetch contacts the Domain Name System to obtain the IP address to download the Google Analytics JavaScript file, if analytics is enabled. Doing a dns-prefetch does not make contact with the Google server, so there is no violation of the user’s privacy. the Google Analytics JavaScript file is only loaded if the user has accepted cookies.

The head template loads the required CSS file based on the language being used. The logic for selecting which CSS file to load is a series of separate `if` statements, which are easier to change than a series of nested if/true/false statements, but there is no default setting.

## Notifications template

The notifications template has the notice for older version of Internet Explorer, the EU cookie/privacy notice, and the skip-link:

- IE 8 or older: Displays a notice that the site does not support older versions of Internet Explorer although the content is visible:
  - For IE 8 or older, I block the normal CSS and JavaScript files using IE conditionals, and instead supply those older browsers with Christian Oliff’s CSS for IE 8 and a custom CSS file to hide some elements that don’t display properly on IE 8.
- EU cookie/privacy: Notice to comply with the European requirement to let users know that the site uses cookies. The notice includes a way to opt-out of Google Analytics and a link to the Terms of Use/Privacy page.
- Skip-link: Link for users that are navigating with the tab key to skip over the navigation links and go directly to the content (for accessibility).

### Note on support for Internet Explorer

Although Microsoft has discontinued support for Internet Explorer, it is still in use. While less than 1% of the visitors to my business site use Internet Explorer (0.34% during Q2 2022), 5% of the visitors to an education related site I help support still use Internet Explorer, include a few using IE 7 (schools often do not have the budget to upgrade their systems).

As every visitor is important, I use Bootstrap 4, which supports IE 10 & 11, rather than making the switch to Bootstrap 5. For IE 9, I provide Christian Oliff’s CSS & JavaScript for IE 9.

Note: Oliff does have a CSS & JavaScript package to enable Bootstrap 5 to work with Internet Explorer 11.

## navigation template

The naviation.html template works for four types of pages:

- Home page: https://www.mysite.com/en/home – has a single page structure
- Detail pages: https://www.mysite.com/en/fatfree – have a multiple page structure
- Terms page: the Terms of use page
- Error pages: 404 and 5XX pages – the directory will vary

All addresses (href and src) use absolute links as it avoids any problem with being in the wrong directory for a relative link to work (e.g. the 404 response page).

One complication – when on the home page, the home page links need to be in the form `href="#overview"` (just the page links). They can’t be `href="../en/home#overview"` or `href="https://www.mysite.com/en/home#overview"` as Bootstrap 4’s Scrollspy seems to have a problem with anything other than just the page link. When on any other page, the home page links should be absolute links in the form `href="https://www.mysite.com/en/home#overview"`.

My solution is to set in index.php a hive variable, `schemeHost`, for the scheme and the host together (e.g. http:// and www.mysite.com) that I use for links to things like CSS files and images, and I assemble that value with the langSubdirectory variable (set in the language controllers) to form a base address for the language pages. When assembling addresses for the home pages, I use the isHomePage flag to control how the addresses are handled.

## content templates

For most sites, data is in a database. For a website with five pages in three languages that won’t change much, a database seems like overkill. Using files is a simpler solution, with the primary place for information being a unique content template for each page.

## footer template

The footer includes:

- Loading spinner – a CSS animation that’s displayed on small screens (which are usually mobile devices) to indicate something is happening after clicking a menu link to another page.
- Scroll-to-top button
- Three footer sections (About this site, Contact, and About me) – the content for the sections comes from the language dictionary
  - Contact email address: I use Albion Research’s email address obfuscator to convert the contact email address to percent encoded, which can be read by the browser, but most spambots won’t expend the energy to look for percent encoded strings and convert them to text to see if they represent an email address.
- Link to the Terms of Use – the address for the related language terms page is assembled in the footer template. (Note: As I use the English terms page for all three lanageus, the routes.ini file points to the same controller, but it could be different.)
- Copyright notice – the notice uses a PHP function to compare the current year to the yearBuilt value in config.ini for echoing the copyright string (using a function eliminates the need to update the copyright string each New Year’s Eve).

## script template

The script template loads the JavaScript files, either as separate files (unminified versions for the files that I created for easier troubleshooting), or one bundled file and embedded JavaScript content for files that are used on just one page. I no longer use content delivery networks for supplying files due to privacy concerns.

## sitemap template

The sitemap is based on the route aliases. To not include pages in the sitemap (e.g. the contact pages), I add an x to the end of the route name (e.g. en_contactx) and filter out those routes from Fat-Free’s ALIASES variable before passing the route names and paths to the sitemap template.

The sitemap template loops through each route name, using an anonymous function connected to a framework variable to get the date the content template for that route was last modified.

The anonymous function to get the file date:

    $f3->set('fdate',
        function($fileName) {
            if (file_exists('app/ui/'.$fileName.'.html')) {
                return date ('Y-m-d', filemtime('app/ui/'.$fileName.'.html'));
                } else {
                    return 'Error';
                }
        }
    );

Calling the function from the sitemap template:

    <lastmod>{{ @fdate(@route_name) }}</lastmod>

For each route, the template loops through the array of languages from config.ini and generates a list of all of the alternate languages for that route (including the route itself per Google’s information on sitemaps).

The sitemap control function is in the UtilitiesController rather than the CommonPageController as the sitemap function calls its own instance for the Fat-Free Framework render function.

Note: Google (and other search engines) will be able to locate the pages I’m leaving off of the sitemap as there will be links to those pages in the navigation menu and the footer, but as those pages all have the tag `<meta name="robots" content="noindex,nofollow">`, Google will not index those pages. (Google is polite about complying with the robots meta tag, but not all search engines may be as nice.)