Code Modules — Docs
Pages Module
Pages Module
Overview of Page Module
Each top level HTML page calls the Page module entry file which defines all the global variables and then hands off to that page’s doppelgänger which either contains the content for the page or pulls it from various other modules.
The Pages module is a high level module, which varies from Markup modules in some ways. For one thing, there’s only one Pages module per website. Also, the Pages module defines the global variables; since every top level HTML file calls the Pages module entry file, that’s where the global variables are defined. The Pages module doesn’t use Markup modules’ internal structure of templates/
, lists/
, items/
, and types/
folders. In the Pages module, everything is within the templates/
folder.
Top Level HTML Page Calls Page Module
The Code Modules system treats every page on the website as an include with a doppelgänger in the app/modules/pages/templates/
folder.
Here’s how the Sample Website’s Pages module is organized:
1pages/2├── _notes.txt3├── entry.shtml4└── templates/5 ├── 404.shtml6 ├── about.shtml7 ├── blog-buns.shtml8 ├── blog-home-1.shtml9 ├── blog-home-2.shtml10 ├── blog-post.shtml11 ├── blog-sausages.shtml12 ├── blog-toppings.shtml13 ├── contact.shtml14 ├── faq.shtml15 ├── full-width.shtml16 ├── home.shtml17 ├── parts/18 │ ├── bottom.shtml19 │ ├── breadcrumbs.shtml20 │ ├── carousel.shtml21 │ ├── header-title.shtml22 │ ├── top-default.shtml23 │ ├── top-home.shtml24 │ └── top.shtml25 ├── portfolio-1-col.shtml26 ├── portfolio-2-col.shtml27 ├── portfolio-3-col.shtml28 ├── portfolio-4-col.shtml29 ├── portfolio-elements.shtml30 ├── portfolio-markup.shtml31 ├── portfolio-nav.shtml32 ├── portfolio-page.shtml33 ├── portfolio-tech.shtml34 ├── pricing.shtml35 ├── services.shtml36 └── sidebar.shtml
Each top level HTML page has a corresponding page in templates/
. There’s a templates/parts subfolder which has some common shared code between most pages; the home page has a carousel while the other pages do not.
“About” Page Example
The top-of-the-chain HTML calling file is the one served to the public. On the Sample Website, these files live in public/
root. Before being built, the top level HTML page just contains the DOCTYPE (which must be the first line of the source code for a page), and the call to the Pages module. The top level HTML file always calls the default entry for the Pages module — /pages/entry.shtml
— with the #TEMPLATE# variable set to the basename of its doppelgänger template. The totality of the home page index.html
in the public root might look like the following prior to being built:
1<!DOCTYPE html>2<!-- #bbinclude '/pages/entry.shtml'3#bbincludeoptions#="inline=true"4#TEMPLATE# = 'home'5-->6<!-- end bbinclude -->
Similarly, the about.html top level file might look like this prior to being built:
1<!DOCTYPE html>2<!-- #bbinclude '/pages/entry.shtml'3#bbincludeoptions#="inline=true"4#TEMPLATE# = 'about'5-->6<!-- end bbinclude -->
After being built, all the content returned from the call will be placed between the two parts of the persistent include, between lines 5 and 6 in the examples above. That content will be completely replaced on each subsequent build; any changes to the content must be made in the doppelgänger file rather than in the top level HTML file.
These top level HTML pages call the entry file for the Pages module. The #TEMPLATE# variable selects the home.shtml
or about.shtml
pages found in the Pages module’s templates/
folder.
Global Site Variables
Every page of the site always calls the Pages module entry first. It’s the one file that all pages must pass through during the include update process. That makes this app/modules/page/entry.shtml
file the right place to put global site variables that can be used further down the chain. I put a client’s name, address, main email address, main phone number, domain name, logo image, motto, Google Analytics code, CSS and JS cache busting variables, and many other global items in this file. Downstream, I use the variable name in the markup; it gets replaced with the variable value when the include is called by the parent. When a client’s phone number changes, I can change it in this one place and be sure that all the other pages using that variable have the updated value. It also helps make modules reusable from one client to another, although one needs to be sure to define all the variables upon which a module depends.
Where Should Variables Be Defined?
It’s not always obvious whether to put a variable in the global file or, further down, in a Markup module item itself. If a variable is used in more than one module, it’s an easy decision to keep the variable with the globals. But, for example, the Google Map Link could be contained in a variable in the Elements module include where it’s used, or in the global default file. My approach, for these types of single use variables, is usually to treat them as a global. It’s marginally easier to find and change a variable in the global entry.shtml
file than in the various modules. It can help with the reuse of the module, but the module won’t work unless the global variable it needs is properly defined. Sometimes, in a Module’s entry file, I'll put a line like #GOOGLE-MAP-LINK# = '#GOOGLE-MAP-LINK#'
to make it clear which global variables the module depends upon.
On the Sample Website, the #GOOGLE-MAP-LINK# variable is defined globally. Ultimately, the choice doesn’t matter much; what’s important is to give each piece of data a single “source of truth” in a variable somewhere. See the discussion of the Terminal utility AG
on the Other page for tips about how to easily locate the file containing a deeply nested variable.
Entry Page Example with Global Variables
1<!-- #bbinclude "/pages/templates/#TEMPLATE#.shtml"2#bbincludeoptions#="inline=true"3#COMPANY# = 'Code Modules'4#NAME# = 'Code Modules'5#NAV-NAME# = 'Code Modules — Sample'6#MAIN-TITLE# = 'Code Modules — Sample Files'7#TITLE-PHRASE# = 'Code Modules Sample Files'8#LICENSE# = 'MIT License'9
10#COMMENT# = 'Code Modules Sample Files Website has not yet launched. Check back after October 1, 2021.'11
12#URL-ROOT# = 'https://sample.codemodules.net'13#URL-DOCUMENTATION# = 'https://codemodules.net'14#DOCUMENTATION-TARGET# = 'Documentation'15#COMMENT# = 'no trailing slash'16
17#URL-SAMPLE-REPO# = 'https://github.com/ChristopherWerby/code-modules'18#SAMPLE-REPO-TARGET# = 'GitHub'19#COMMENT# = 'Sample repo will be public.'20#URL-DOCS-REPO# = 'https://gitlab.com/ChristopherWerby/code-modules-docs'21#DOCS-REPO-TARGET# = 'GitLab'22#COMMENT# = 'Docs repo to remain private.'23
24#LOGO# = '/images/logos/Logo-Code_Modules-OW-256-scaled-fgBlack-bgTrans-600x600.png'25#COMMENT# = 'From URL Root'26
27#PAGE-IMAGE# = 'Logo-Code_Modules-OW-256-scaled-fgBlack-bgTrans-600x600.png'28#PAGE-IMAGE-PATH# = '/images/logos'29#PAGE-IMAGE-ALT# = 'Page Image for Code Modules'30
31#BASE-DESCRIPTION# = 'This sample website is a demonstration of the Code Modules system in use.'32#LONG-DESCRIPTION# = 'Code Modules is an MIT licensed open source project maintained by Pipsqueak Productions LLC. Source code for this website can be found on GitHub. Documentation for Code Modules can be found at codemodules.net.'33#COMMENT# = 'Note: The base description is used at the beginning of each description type. When the long description is used, it is compound of the base and long description. The base description is the entirety of the short description.'34
35#AUTHOR-DESCRIPTION# = 'Site Design and Maintenance by Pipsqueak Productions, LLC. If you’re looking at the code, you must care. So do we. Pipsqueak.com'36
37#EMAIL# = '<a href="mailto:info@example.com">info@example.com</a>'38#COMMENT# = 'encoded: <a href="mailto:info@example.com">info@example.com</a>'39
40#COMMENT# = 'encoded: info@example.com'41
42#RELEASE-DATE# = 'October 1, 2021'43
44#PHONE# = '(415) 555-1212'45#PHONE-LABEL# = 'main'46#PHONE-MARKUP# = '<a class="noWrap" href="tel:+14155551212">(415) 555-1212</a>'47#STREET1# = 'Palace of the Legion of Honor'48#STREET2# = '100 34th Avenue'49#CITY# = 'San Francisco'50#STATE# = 'CA'51#ZIP# = '94121'52
53#URL-COPYRIGHT# = 'https://pipsqueak.com'54#AUTHOR# = 'Pipsqueak Productions, LLC'55
56#VERIFICATION-GOOGLE# = 'baHVvnxq7WqCDN3edWSTnZPoO0wOWwgBBiO9Nk9DA0s'57#VERIFICATION-MICROSOFT# = 'DA6A1633A5DB646EB79EB58E4DC11EF7'58
59#GOOGLE-MAP-LINK# = 'https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3045.8267028129117!2d-122.50295383731373!3d37.784474796290105!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x808587acd6e2ba69%3A0xb9ffd88c77154d2c!2sLegion%20of%20Honor!5e0!3m2!1sen!2sus!4v1629506249997!5m2!1sen!2sus'60#LINKEDIN# = 'https://www.linkedin.com/company/example/'61#FACEBOOK# = 'https://www.facebook.com/example/'62#TWITTER# = 'https://twitter.com/example'63#YOUTUBE# = 'https://www.youtube.com/user/example'64
65#TECH-HEAD-TEMPLATE# = 'head'66#COMMENT# = 'TECH-HEAD-TEMPLATE is head by default but sometimes head-home'67
68#PAGE# = '#TEMPLATE#'69#COMMENT# = 'TEMPLATE can be overwritten down the calling chain. Save the value in the PAGE variable.'70
71#TYPE# = 'default'72#DESCRIPTION-ITEM# = 'default'73#DROPDOWN-DIVIDER# = 'null'74
75#ICON-CLASS# = 'fa-1x'76#COMMENT# = 'ICON-CLASS is optional; defaults to fa-1x. Can be fa-lg, or fa-2x, fa-3x, fa-4x, fa-5x for size. Can include fa-fw for fixed width. Can include fa-li for use in lists. Can include fa-spin or fa-pulse to animate. Can include classes to flip and rotate: fa-rotate-90, fa-rotate-180, fa-rotate-270, fa-flip-horizontal, fa-flip-vertical.'77
78#INDENT# = ' '79
80#COLUMNS# = '4'81#COMMENT# = 'Default columns is for three items across; 12 ÷ 4 = 3'82
83-->84<!-- end bbinclude -->
Entry Page Calls Template for Each Page
The Pages module’s entry.shtml
calls a different template page for each top level HTML page using the #TEMPLATE# variable in the entry file’s calling path to its child to redirect the flow to the correct template.
1<!-- #bbinclude "/pages/templates/#TEMPLATE#.shtml"2[…]3-->4<!-- end bbinclude -->
“About” Template Example
1<!-- #bbinclude "parts/top.shtml"2#bbincludeoptions#="inline=true"3#PAGE-TITLE# = "About Us"4#SUBHEAD# = "Fake Company Profile"5#DESCRIPTION-ITEM# = "#PAGE#"6#KEYWORD-ITEM# = "#PAGE#"7#CANONICAL-URL# = "/about.html"8#DROPDOWN# = "none"9#ID# = "aboutPage"10#HIERARCHY# = "secondary"11-->12<!-- end bbinclude -->13 <div class="row">14 <div class="col-lg-6">15 <img class="img-fluid rounded mb-4" src="/images/editorial/Conference_Room-40-1100x643.jpg" alt="A fake conference room by Christina Wocintechchat via Unsplash">16 </div>17<!-- /.col-lg-6 -->18 <div class="col-lg-6">19<!-- #bbinclude "/elements/entry.shtml"20#bbincludeoptions# ="inline=true"21#ITEM# = 'about-intro'22#INDENT# = ' '23-->24<!-- end bbinclude -->25 </div>26<!-- /.col-lg-6 -->27 </div>28<!-- /.row -->29 <h2>30 Our (Fake) Team31 </h2>32<!-- #bbinclude "/markup/team/entry.shtml"33#bbincludeoptions#="inline=true"34#TEMPLATE# = 'default'35#COLUMNS-LG# = '6'36#COLUMNS-MD# = '6'37#COLUMNS-SM# = '12'38#INDENT# = " "39-->40<!-- end bbinclude -->41 <h2>42 Our (Fake) Customers43 </h2>44<!-- #bbinclude "/markup/customers/entry.shtml"45#bbincludeoptions#="inline=true"46#INDENT# = " "47-->48<!-- end bbinclude -->49#bbinclude "parts/bottom.shtml"
The About
page template contains some markup unique to this page, including Bootstrap grid containers and columns, but most of the content is abstracted away in modules. Because the content doesn’t get in the way, it’s easier to see and modify Bootstrap’s grid structure code. I tend to leave headlines and subheads in the Pages template files rather than deeper in the modules that they call. This helps make clear the content that is returned from each call, and it makes it easier to change the organization of a page.
Top and Bottom Parts
The top and bottom of the web page, usually similar from page to page, are also abstracted away into their own includes.
Templates call a parts/top.shtml
file and a parts/bottom.shtml
file. The top.shtml
file is further abstracted so that the home page can be different (no breadcrumbs and the addition of a carousel) from the other pages.
1#bbinclude 'top-default.shtml'2<!-- Page Content -->3 <div class="container">4#bbinclude 'header-title.shtml'5#bbinclude 'breadcrumbs.shtml'
1<!--[if IE 9]> <html lang="en" class="ie9"> <![endif]-->2<!--[if gt IE 9]> <html lang="en" class="ie"> <![endif]-->3<!--[if !IE]><!-->4<html lang="en">5<!--<![endif]-->6 <head data-fold="true">7<!-- #bbinclude "/tech/entry.shtml"8#bbincludeoptions#="inline=true"9#TEMPLATE# = 'head'10#INDENT# = ' '11-->12<!-- end bbinclude -->13 </head>14 <body id="#ID#" class="#HIERARCHY#" data-preloader="2">15<!-- #bbinclude "/nav/entry.shtml"16#bbincludeoptions#="inline=true"17#SIMPLE-TYPE# = 'simple'18#TERTIARY-NAV-TYPE# = 'dropdown-item'19#DROPDOWN-DIVIDER# = 'dropdown-divider'20#TEMPLATE# = 'default'21#INDENT# = ' '22-->23<!-- end bbinclude -->
1<!-- #bbinclude "/markup/breadcrumbs/entry.shtml"2#bbincludeoptions#="inline=true"3#TEMPLATE# = '#HIERARCHY#'4#DROPDOWN# = '#DROPDOWN#'5#INDENT# = " "6-->7<!-- end bbinclude -->
1</div>2<!-- /.container -->3<!-- #bbinclude "/markup/footer/entry.shtml"4#bbincludeoptions#="inline=true"5#INDENT# = ' '6-->7<!-- end bbinclude -->8<!-- #bbinclude "/tech/entry.shtml"9#bbincludeoptions#="inline=true"10#TEMPLATE# ='foot'11#INDENT# = ' '12-->13<!-- end bbinclude -->14 </body>15</html>
Note that top-default
and bottom
both call the high level Tech module. The top-default
file also calls the high level Nav module. Both call various Markup modules.
Since every page on the site uses these modules, a modification in one will ripple throughout the entire site.