Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Custom theme which presents the front-end UI to all users. .
We use 2 custom themes, one which presents the backend and one which presents the front-end.
Breadcrumbs are an informative device which appear on many pages on the site. Breadcrumbs provide the user a sense of location within the site and a way to logically navigate back to the homepage.
A breadcrumb is an ordered collection of crumbs, with each crumb having a title and a link.
Drupal has a built-in breadcrumbs methodology, which will attempt to build out a pathway based on the URI (e.g. /departments/housing/metrolist
) defined by the pages (i.e. nodes) URL Alias.
It does not matter if the URL Alias is set manually or automatically, the value shown in the back-end editor form once the node is saved is used to build out the breadcrumb.
The Drupal core process creates the breadcrumb by scanning the path represented by the URI, and testing if a local page exists for each path element. It stops adding crumbs when a path element does not resolve.
FOR EXAMPLE an article is created with a URI (as defined in its URL Alias):
/departments/housing/boston/housing-information-in-boston.
When the page is rendered, Drupal scans the articles URI and
if we have a breadcrumb setting which stipulates that the homepage should always be shown as the first crumb, then a crumb of home
with a link to https://site
is created, then
checks if /departments
is a valid URI. https://site/departments
is a valid URI, so it creates a crumb of "departments" with a link to https://site/departments
, then
checks if /departments/housing
is a valid URI. https://site/departments/housing
is a valid URI, so it creates a crumb of "housing" with a link to https://site/department/housing
, then
checks if /departments/housing/boston
is a valid URI. https://site/departments/housing/boston
is NOT a valid URI - there is no page with that name on https://site
so the breadcrumb scanner stops evaluating at this point, but
if we have a breadcrumb setting to display the actual page in the breadcrumb then a final crumb of housing information in boston
is added, with no link (because this is the page showing).
The final breadcrumb in this instance would be HOME > DEPARTMENTS > HOUSING > HOUSING INFORMATION IN BOSTON with links on the first 3 crumbs.
When evaluating if a page exists on the site, Drupal only considers URL Aliases and does not check URL Redirects.
So in the example above, the boston
crumb/link still would not appear in the breadcrumb even if a place_profile
page for Boston existed with the URL Alias of /places/boston
and a URL Redirect for /departments/housing/boston
.
Where Drupal core cannot build out its own breadcrumb trail, there is some additional custom code intended to help make a logical breadcrumb.
The custom breadcrumb code only functions when it determines that Drupal has not built out the entire breadcrumb.
If Drupal has been able to build out all parts of the URI path, then the Drupal breadcrumb is used.
The custom code scans URL redirects as well as URL Aliases when building out the breadcrumbs.
Care: Redirects which are manually made on the page admin/config/search/redirect
are usually considered "external" by default. Breadcrumbs which use an external link may behave unexpectedly when clicked.
Example: the breadcrumb on d8-dev.boston.gov may open a page on www.boston.gov when clicked.
Solution: Do not create redirects for internal (i.e. Drupal hosted) pages on in the admin/config/search/redirect
page. Instead create redirects using the redirect function on the "advanced" tab of the editor form for a page.
Some URI paths are hard-coded to build specific breadcrumbs.
For example pages which have a URI path starting with government/cabinet
. The custom code ignores the "government/cabinets" part of the path and then build the breadcrumb from the remainder of the path.
The custom breadcrumb object is built here: bos_theme/bos_theme.theme::bos_theme_preprocess_breadcrumb()
The breadcrumb is styled here: bos_theme/templates/navigation/breadcrumb.html.twig
CoB custom modules - usually taxonomy, nodes and paragraphs.
The following development conventions are being followed in developing boston.gov modules.
City of Boston have the following naming and grouping conventions for custom modules:
Templates for the component should be saved in:
To add a customized template, select a suggestion for the base (node, field, region etc), then
Save the template in the folder above
In the module_name_theme()
hook in module_name.module
add the following:
If a new suggestion is needed, then add the following:
Where XXX is the appropriate entity type (node, field, region, etc etc) to add a suggestion to.
Wherever possible, the style provided from the patterns library should be used. In practice this means that boston.gov can be styled by a Drupal developer ensuring that the twig template files provide HTML structured and given classes that the patterns library expects.
Should the need arise, then the patterns library style sheets can be overridden. Typically this is done at the module level, although if multiple modules will use the override, consider placing it in the bos_theme
theme.
To add overrides,
Create the style sheet module_name.css
and appropriate markup in the relevant template (see above section),
Save the stylesheet in:
Update (or create) the module_name.libraries.yml
file with the following:
Using a module_name_preprocess_HOOK()
hook in module_name.module
attach the css where and only when it is required. For example:
Wherever possible, JavaScript should not be used on boston.gov. This is to maintain compatibility with as many browsers as possible, and to maximize accessibility for screen readers etc.
Should the need arise, then a JavaScript library can be created and deployed. Typically this is done at the module level, although if multiple modules will use the override, consider placing it in the bos_theme
theme.
To add overrides,
Create the JavaScript library module_name.js
,
Save the library in:
Update (or create) the bos_modulename.libraries.yml
with the JavaScript directive - for example you could add the following:
Using a bos_modulename_preprocess_HOOK()
hook in bos_modulename.module
attach the JavaScript library where and only when it is required. For example:
Drupal 8 defines settings and configurations in YML files with the actual "current" settings and configurations being stored in the Drupal (MySQL) database.
When the website is deployed or the web-server is restarted, configurations are re-read from the database. To reload the configuration and settings from yml files requires a manual (usually drush
) process to be run by a developer.
Clearing the sites caches causes cached configurations and settings to be replaced with values from the database. Clearing caches does not reload yml files.
YML files in a modulesdocroot/modules/custom/ ... /module_name/config/install
folder will be imported into the database when a module is first installed.
YML files in the docroot/../config/default/
folder will be imported into the database when the configuration is imported via the Drupal UI, or the drush
command.
Current (run-time) settings and configurations in the database can be exported to the docroot/../config/default/
folder via the Drupal UI, or the drush
command.
If the config_devel
module is enabled then a modules configuration can be exported to the modules config/install
folder.
The dependent configurations are defined in the module_name.info.yml
file as follows:
To export these configurations to the config/install
folder use the following drush command:
Modules should try to reuse field.storage.entity-type.field_name
configurations wherever possible.
field.storage.entity-type.field_name
configurations should be:
1. saved in the modules parent module (e.g. bos_components
or bos_content
)to enable sharing, and
2. added to the parents config_devel
section of the .info.yml
file.
Custom theme which presents the back-end UI to content authors and editors. .
Boston.gov use Drupal core workflow and moderation modules.
CoB use the following modules for moderation:
Content Moderation: [core] Provides moderation states for content.
Workflows: [core] Provides UI and API for managing workflows. This module can be used with the Content moderation module to add highly customizable workflows to content.
Moderation Note: [contrib] Provides the ability to notate elements of a moderated Entity.
Moderation Sidebar: [contrib] Provides a frontend sidebar for Content Moderation.
Developer notes for content type (node) design and implementation.
Modules can define multiple content types (nodes) grouped by similar function.
A good example module can be found at:
Module naming convention is to call the module module_name
. The "module_name" should be indicative of the node/s contained within the module.
Sub-pages in this section assumes an example module is to be named module_name
and therefore the module folder would be:
Notes on bos_admin theme for UX when adding content via admin pages.
To keep a clear and clean editor experience which uniform across the site, the form display configuration for nodes will contain groups.
There will be a root (parent) group of type tabs
. This group will contain child groups of type tab
. Each tab group will contain the nodes fields.
Recommended Grouping Layout:
1. Required: Create a parent tabs
group called group_main
(the name is not important).
2. Create child tabs
groups with the following layout:
- Basic Information: Contains custom fields required by the new content-type,
- Sidebar Components: Single Entity reference revisions
field for sidebar paragraphs,
- Components: Single Entity reference revisions
field for main page paragraphs,
.. then other tab
groups is needed (try to minimise if possible).
The use of further nested groups is discouraged, except for grouping which occurs within paragraph components that are exposed in Components or Sidebar Components tabs.
If other groups are required to help clarify the form display, they should be details
type groups, and should be set to be collapsible, and be collapsed by default.
IMPORTANT:
For site consistency, ensure any and all Entity reference revisions
(i.e. paragraphs) on the node are set to "Paragraphs (EXPERIMENTAL)"
in the form display.
The bos_admin
theme makes some changes to the node administration forms.
Config settings provided by drupal core and drupal contributed modules are moved into a tab
called advanced, and are set as children of the tabs
group as defined above.
This manipulation is done in the hookbos_admin_form_alter()
found in bos_admin.theme
file at themes/custom/bos_admin
.
The moderation state, revision log note and save / preview / delete buttons are grouped together in a details group and moved to the right sidebar area of the administration form.
This manipulation is done in the hookbos_admin_form_alter()
found in bos_admin.theme
file at themes/custom/bos_admin
When you make an html.twig
file and add it to the templates folder of a custom theme you are pretty much done (after refreshing caches!). The Drupal theme rendering processes detect the template and uses it in preference to any template of the same name from a parent or default theme. You don't really have to do anything more than add the file and refresh cache.
But, if you add a template to a custom module -even if your intent is just to override a theme default template (e.g.field.html.twig
) or to provide a suggested template, there are a few extra things you must do.
Using the example of a custom content type (node) called "node_landing_page", the steps below fully implement a template to be used to render the nodes full
display.
Note: Drupal automatically generates the suggestion fornode__landing_page__full
which can be used for rendering the "default" (i.e. "full") display.
You can generate other suggestions using the hook_theme_suggestions_hook
hook.
Create the twig template you wish to use, and give it a name that matches an existing Drupal theme suggestion with ".html.twig" as the extension.
In rare cases you may want to create a new template suggestion. Do this by returning an array of suggestions from ahook_theme_suggestions_hook()
in your custom module (see last example below).
Convention is to name the template using an "entity breadcrumb" style, with "--"'s between entities and no spaces.
Save the template file in a folder called templates
in your custom modules root folder. In our example docroot/modules/custom/node_landing_page/templates
.
- You could organize files by creating a sub-folder tree - but if you do, you will then have to specify the path
to your template in the hook_theme
- see step 3 below.
In the hook_theme
of your module you must define your new template. This hook is read by the Drupal core theme engine and loaded into a template cache (aka register). Whenever a change is made to this hook you need to clear all caches to load your changes into the cache.
In hook_theme
return an assoc array with key-value pair nested arrays for each template you wish to define.
- The outer keys (template-keys) should be one for each of the templates you are defining. Keep it simple and traceable by setting the the template-key name to be the template filename without the ".html.twig". Important: Replace all "-"'s with "_"'s in the template-key string. (in our example the template-key is node__landing_page_full
)
- The value for the key (template-key) is an array with a required base_hook
and several other optional fields.
The base_hook
should define the entity type this template is used to render (in our case node
but other common entities we theme are field, region, block, paragraph, taxonomy_term
) .
[optional] The render element
defaults to elements
if not specified.
[optional] If you wish to use a template file which is not the same name as the suggestion (with "_"'s replaced with "-"'s) then you must specify its name in the template
field. Omit the "html.twig" extension. This could be useful if you want 2 display to share the same template.
[optional] If you want to use a custom path to the template file (i.e. not the default templates folder) then use the path
field.
(see bos_link_collections_theme
in boston.gov for example)
(see "Our Example hook_theme" below for the complete hook)
[optional] Once the cache is cleared you can then catch pre-process events using hook_preprocess_hook
in our example this would be node_landing_page_preprocess_node
(to catch all node pre-process events) or node_landing_page_preprocess_node__landing_page__full
(to catch only this new template pre-process events) - notice that the hook uses the template-key
defined in the hook_theme
array.
[optional] You can also catch template_preprocess_hook
events (in our example this is template_preprocess_node__landing_page__full
).
This hook is commonly used to create a content
variable which contains all the rendered (or renderable) elements of the elements
(or whatever the field is named in the templates render element
) array.
Our Example template file:
Our Example hook_theme:
Our Example hook_preprocess_hook (version 1):
Our Example hook_preprocess_hook (version 2):
Our Example template_preprocess_hook:
Our Example hook_theme_suggestions_hook:
Modules can contain multiple paragraphs grouped by similar function.
A good example module can be found at:
Module naming convention is to call the module bos_moduleName
. The "moduleName" should be indicative of the paragraph/s contained within the module.
Sub-pages in this section assumes an example module is to be named bos_module_name
- with the module folder:
Converting D7 structures to D8
Login to the website and go to the paragraphs admin page (/admin/structure/paragraphs_type
) and delete the paragraph you want to work on
Step 1 above may delete some of the field.storage dependencies (field definitions), so just re-import all the bos_component module config to make sure you get all the shared config back into the database: lando drush config-import --partial --source=/app/docroot/modules/custom/bos_components/config/install
Create the module scaffolding using drush, for example: lando drush componetize bos_discussion_topic --components=discussion_topic
Add hook_theme() to .module file to connect to the paragraph template
Copy the corresponding paragraph template from boston.gov-d8/docroot/themes/preConversion/component
and put it in the scaffolding that the drush command from step 3 created: docroot/modules/custom/bos_components/modules/bos_discussion_topic/templates
Enable the module: lando drush en bos_discussion_topic
In the Drupal UI, add the new bundle to the field_components
paragraph types list for the Test Component Page content type: /admin/structure/types/manage/test_component_page/fields/node.test_component_page.field_components
Create a test page with the component added to review admin UI and display
Importing a single config file:
Exporting database config directly to your module (Important: the config file needs to be referenced in your module's info file under the config-devel
key): lando drush config-devel-export bos_cabinet
Custom nodes deployed in boston.gov have a navigation menu which sits below the introduction text on each page.
The in-page menu requires the node to embed paragraphs, the node--xxxx.html.twig to contain a <div> and for each embedded paragraph to have a key field.
If the node has components (paragraphs) embedded, then the node will have a field called field_components
and this field will be of a type Entity reference revisions
. The field will allow only paragraphs, and will specify the paragraph types that are allowed on the node.
To enable in-page navigation, each paragraph must have a (text field) field_short_title
, and to reduce confusion for content editors, that field should be named "Navigation Title".
To make the menu look nice and work well on mobile devices, content editors and authors should be encouraged to keep the content added to the Navigation Title to 20 chars or less.
To enable the in-page navigation menu, the nodes template should include the following:
This block should ideally be located below the title and intro-text sections.
When there is more than 1 paragraphs embedded in a nodes web page, an in-page navigation menu should appear on the page. The menu should be styled from the patterns library.
UX Desktop: When the page first loads, the menu should display above the fold. As the user scrolls down the page, the menu should collapse into a fixed toolbar at the top of the page, below the seal menu with the seal retracted. Theme should come from patterns.
UX Mobile: Menu should appear as a collapsed set of drawers with a chevron icon to expand. Css from patterns controls the collapse across the responsive page width.
In either UX, when the user clicks on the menu, the page should scroll smoothly down to the correct paragraph display on the webpage.
The twig template (e.g. node--xxx.html.twig
) for the node is responsible for locating the menu on the node. The code required is described above.
On-page menu elements are rendered from the bos_theme_preprocess_node()
and bos_theme_preprocess_field()
hooks in bos_theme.theme
found in /themes/custom/bos_theme/
.
The page click and scrolling is provided by component-navigation.boston.js
which is found in /themes/custom/bos_theme/js/
.
To make a paragraph include itself in the in-page navigation menu, it just needs to contain a text field named field_short_title
(and for that field to be included in the display being used on the node).
Modules can contain a single vocabulary taxonomy.
A good example module can be found at:
Module naming convention is to call the module vocab_moduleName
. The "moduleName" should be indicative of the taxonomy contained within the module.
Sub-pages in this section assumes an example module is to be namedvocab_module_name
- with module folder at:
City of Boston support development of discrete React (and other JS framework) WebApps. Because these services will be hosted on Drupal there is a custom Drupal webapp launcher and some conventions to
Have stable local build of Drupal 8 website running on your machine.
Make sure you are “logged in” or have “admin” access to view the CMS and add new content / nodes.
Using Drush: lando drush uli
Scroll to bottom of the page and add content item by clicking “Add Content”, . Select “Listing Page” content type.
Give new Page content a Title. This is required.
Click on the “Components” tab on the left menu
Find the dropdown Select menu to add “new component” and select “Web App” from the list.
Name the Web App something appropriate as it relates to your project. (i.e. Metrolist or My Neighborhood)
Click “Save” near the bottom or side of the page to save and create a new page / node. This will serve as the container page / component for your new web app.
Navigate to the “bos_web_app” directory of the drupal 8 repository that is checked out to your local machine /docroot/modules/custom/bos_components/modules/bos_web_app.
Locate the “apps” folder / directory. If one doesn’t exist, please create. /docroot/modules/custom/bos_components/modules/bos_web_app/apps
Inside this “apps” directory create an empty folder and name it the same name you called your Web App in Step 6 of Part 1 above. /docroot/modules/custom/bos_components/modules/bos_web_app/apps/my_neighborhood NOTE: Any spaces in your app name should be treated with underscores. For example, My Neighborhood would have a folder name of “my_neighborhood”.
Locate and open the libraries yml file named “bos_web_app.libraries.yml”. This file will serve as the pointer and compiler that will tell Drupal to attach and bunde all your JS and CSS files for your application. /docroot/modules/custom/bos_components/modules/bos_web_app/bos_web_app.libraries.yml
Once you have libraries file setup, go create the files needed, OR first create the files you’d like and then add them to the libraires.yml as laid out in Part 2 - Step 5 above.
It’s important to note that any time you add a new attached file to libraires.yml, the Drupal cache will have to be cleared for changes to take effect. You can clear the cache either through the Drupal CMS or via Drupal drush CLI
Drupal CMS: navigate to admin/config/development/performance, and click button at top of the page labeled “clear all caches”
Using Drush CLI: drush cr
After clearing the cache, you should now see your application load on the Drupal page you created and saved in Part 1 - Step 7. NOTE: You will NOT have to clear the Drupal cache every time you make a change to a CSS or JS file. This is only for new items in the libraires.yml file.
Using Drupal web login:
Navigate to Content menu item (make sure you are logged into Drupal to view)
Once you have the libraries file open, add an entry with the name of your application and add / attach necessary items to your application. For example, the application “My Neighborhood” would have a library entry as such…
See an example libraries.yml file on GitHub for a project that is currently being developed. Drupal also has good documentation on using libraries and attaching files.