Joomla, Custom Fields and external data – powerful overrides made easy with Joomlatools Pages
by Marc Dechèvre | Woluweb
slides https://slides.woluweb.be
1. JoomlaDay USA
This presentation -which could alternatively be named
Joomlatools Pages – a beginner’s guide
was made especially for
JoomlaDay USA | 24 April 2021 | #jdayusa
Note: the video content will only be available to paid registrants of https://jdayusa.com
2. Introduction
2.1. Demo site
We have built a demo site so that you can see the results of all the actions explained step by step in this presentation
https://pages.joomlacustomfields.org/
The corresponding code is
- directly visible on the demo site: https://pages.joomlacustomfields.org/code
- and also on Github : https://github.com/woluweb/Joomlatools-Pages-Demo
2.2. Joomlatools Pages – useful links
Joomlatools Pages is free and open source (feel free to contribute/give back!)
Note:
will download the latest versions of pages or the joomla extension
2.3. Joomlatools Pages – presentations by the author himself
Johan Janssens, the father of Joomlatools Pages, has already made three general presentations:
- JoomlaDay USA | 24 April 2021 | Building a Joomla site with Joomlatools Pages
- JoomlaShack Conference | 14 Dec 2020 | The Evolution of Web Publishing from 1990 to 2020
- JoomlaShack Conference | 11 May 2020 | An Interview with Johan Janssens about Joomlatools Pages
2.4. Why this presentation
I share two qualities with Johan Janssens :
- we are both Belgians 😀
- we are both Joomla fans 😉
Johan, who is also one of the cofounders of Joomla itself, is of course an excellent developer.
I don’t consider myself as a developer (I don’t code extensions, even if I play a lot with overrides which entails some PHP knowledge).
I am more what could be called a power user: I know Joomla by heart and like to use its full capabilities.
And I am definitely a Joomla Custom Fields 'evangelist’ since it was released with J!3.7 thanks to Allon Moritz aka Laoneo.
Custom Fields are indeed a game changer and has made Joomla much more powerful, especially if you use overrides in order to customise the Layout.
So when I discovered Joomlatools Pages, I immediately saw a great opportunity because
- it makes overrides much easier
- it integrates the Custom Fields
- it gives new possibilities like fetching data from external sources (json files for example)
- and even non-coders or coders who don’t know Joomla can immediately use it
So why this presentation?
Simply because I am always happy to share my enthousiasm a.o. about everything related to Joomla Custom Fields.
And also because I like to learn by doing. So I thought a good way of showing some of the features offered by Joomlatools Pages would be to make a step by step demo.
2.5. Focus on Pages to make overrides including Custom Fields and fetching external data
Joomlatools Pages can already do so many things (plus the fact that features are regularly added/improved) that it would probably take a full day to cover them all…
So in this presentation we will 'only’ see how to make (blog view and article view) overrides including Custom Fields and fetching external data because even so there is already so much to tell!
As you will see, Pages is so powerful and yet so easy…
3. The initial setup
Before playing with our new tool, let’s prepare Joomlatools Pages and our website
3.1. Install
- Go to https://github.com/joomlatools/joomlatools-pages/releases
- Download the latest package
- Install the ZIP file the usual way :
- Go to your YOURSITE.COM/administrator > Extensions > Manage > Install
- Upload the package.
When you see “Installation successfully completed!” you are done
(don’t worry if the interface is a bit different from usual after installation: Pages uses its own installer)
For other installation methods, see https://github.com/joomlatools/joomlatools-pages/wiki/Installation
3.2. Nothing new in the administration interface…
Now that Pages is installed (together with the Joomlatools Framework), you will notice… that nothing has changed in the backend 🙂
No new Component, no Configuration, no anything.
Actually this is totally normal: the only thing you will maybe do in Joomla’s backend related to Joomlatools Pages is adding a few Menu Items.
3.3. Joomla Update Manager
Note: Pages does not use the Joomla Update Manager. So if at some point you want to check which version of Pages is installed, you can simply go to Extensions > Manage > Manage and search for “Pages”.
3.4. Create the folder
Using your File Manager or your FTP connexion, create the following folder and subfolder
/joomlatools-pages/pages/
3.5. Before starting : URL Rewriting
Oh btw nobody wants to see that /index.php/
in the url, right?
So if you have not already done so
- go to System > Global Configuration > tab 'Site’ > block 'SEO Settings’ > set option 'Use URL Rewriting’ to 'Yes’
- at the root of your website rename
htacces.txt
into.htaccess
Then the url https://pages.joomlacustomfields.org/index.php/step1 simply becomes https://pages.joomlacustomfields.org/step1
3.6. Let’s create some content with Custom Fields
- create an
Article Category
with Alias: organizations - create
3 articles
in that Category with an image (intro or full) - create a
Custom Field
(of type Text for example) with the following Name:socialbrusselsid
- create a
Custom Field
(of type URL) with the following Name:youtube
- assign those Custom Fields to the Article Category 'organizations’
- assign the following values to the Custom Field 'socialbrusselsid’: 13817, 3322, 10600 (for example)
- assign the following values to the Custom Field 'youtube’: https://www.youtube.com/watch?v=g21IiNrkhXA, https://www.youtube.com/watch?v=Gooof6e6BvQ, https://www.youtube.com/watch?v=LXeaDPmHP18 (or any other YouTube video of course 🙃 I simply chose those ones bc my daughter plays drums with her progressive rock band)
The socialbrusselsid Custom Field will allow us to combine our articles with information fetched from an external website via webservices.
Example:
- https://social.brussels/organisation/13817 ('human’ format)
- https://social.brussels/rest/organisation/13817 (json format)
3.7. See the latest version of the source code on the website itself
To make it easy, I have copied the source of each page in the slides of this presentation itself.
But regularly I bring improvements. So if you want to be sure to get the latest version, they are directly visible on the demo website:
https://pages.joomlacustomfields.org/code
Note: how can I display the source code automatically on the website? Txs to Joomlatools Pages itself.
All I have to do is to create a file
code.html.php
with the following content
---
process:
filters: [highlight]
---
<?= source('step6') ?>
where step6 refers to my file step6.html.php which is created later in this presentation.
Note: in this example and on most of the following ones, I have added
process: filters: [highlight]
in the Frontmatter. This gives automatic code highlighting.
On a production site you would probably remove it, but here I have included it bc I display on purpose the whole array of articles with
<pre><code><?= var_dump(collection()); ?></code></pre>
See https://github.com/joomlatools/joomlatools-pages/discussions/668 for more information.
4. A step by step demo
The idea of this step by step demo is to onboard everybody, even completely non technical persons.
4.1. Joomlatools Pages with HTML input
Let’s start with something super easy: a little HTML file.
4.1.1. Create the file
In the folder /joomlatools-pages/pages/
create a file named
step1.html
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step1)
---
title: A HTML page
summary: This page is a static HTML page.
---
<h2>This is a pure HTML file</h2>
<p>This is a pure HTML file in the filesystem that Pages renders as if it were an Article.</p>
<p><a href="https://github.com/joomlatools/joomlatools-pages/wiki" target="_blank">Joomlatools Pages' Wiki</a></p>
4.1.2. See the result
Go to https://pages.joomlacustomfields.org/step1 and see the result: step1.html
is displayed as if it were a regular Joomla article!
So actually, so far we already have a kind of Flat File CMS 🙂
But the nice thing is that you still benefit from
- your template with its design (CSS, …), its modules, …
- and all the other Joomla features
4.1.3. Metadata
Do you want to customize the Title and the Description of the page ?
This can also be done very easily by adding this at the beginning of your file
---
title: A HTML page
summary: This page is a static HTML page.
---
See https://github.com/joomlatools/joomlatools-pages/wiki/Metadata for more information
Note: the part between ---
and ---
is called the Frontmatter
4.1.4. Creating a menu item – optional
The following step is not required, however if you want Joomla to show a menu item for your page you can do so by creating one.
In the administrator interface, go to your menu manager and create a new menu item. The Menu Item Type should be 'Pages > Default’. Add the title and alias, making sure that the alias matches the file you created. For example:
- Title: Hello World
- Alias: step1
Note: you could of course also create a Menu Item of type 'System Links > URL’ and set the Link field to /step1
4.1.5. Multilingual websites
Once you have played with Joomlatools Pages it will seem obvious to you: if you have a multilingual website and the url of a page in English is YOURSITE.COM/en/step1 then the corresponding file should be
/joomlatools-pages/pages/en/step1.html
and not
/joomlatools-pages/pages/step1.html
4.2. Joomlatools Pages with MarkDown input
You don’t know what MarkDown is ?
- see https://en.wikipedia.org/wiki/Markdown
- in a few words, it is a lightweight markup language for creating formatted text using a plain-text editor
4.2.1. Create the file
In the folder /joomlatools-pages/pages/
create a file named
step2.html.md
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step2)
---
title: A Markdown page
summary: This page is rendered by the Markdown engine.
---
## Hello World!
Welcome to my new page in **MarkDown** format.
4.2.2. See the result
Go to https://pages.joomlacustomfields.org/step2 and see the result: step2.html.md
is displayed as if it were a regular Joomla article!
All my presentations (including this one) are written in MarkDown format (.md file). This means that I could now very easily display on my website a nice HTML version of those Presentations.
4.3. Joomlatools Pages with PHP input
Let’s start with a simple PHP example
4.3.1. Create the file
In the folder /joomlatools-pages/pages/
create a file named
step3.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step3)
---
title: A PHP page
summary: This page is rendered by the PHP engine.
---
<h2><?= $title ?></h2>
<? $greeting = 'Hello World' ?>
<p><?= sprintf('%s from PHP', $greeting); ?></p>
<p>Today is <?= date('l h:i', 'now'); // Prints something like: Monday 10:56 ?></p>
<ktml:partial format="md">
**Why not** even put some *markdown* written directly in our PHP file?
</ktml:partial>
Note the short syntax
<h2><?= $title ?></h2>
instead of what we usually have in Joomla files
<h2><?php echo $title; ?></h2>
4.3.2. See the result
Go to https://pages.joomlacustomfields.org/step3 and see the result.
4.3.3. Insert markdown directly in your PHP file
With Pages you can also insert directly markdown (or other types of file) in your PHP file with the following syntax:
<ktml:partial format="md">
**Why not** put some *markdown* written directly in our file?
</ktml:partial>
4.4. Joomlatools Pages with PHP input fetching external data
And now a killing feature: how to get data from an external website (in this case in json format) in 1 (one!) line of code.
For this demo, let’s use the open data made from https://social.brussels/ which gives all the details (name, address, phone number, …) of all Social organizations in Brussels.
4.4.1. How does social.brussels work
On that website, if you search and select a given Organization, you will get such an url. Example:
https://social.brussels/organisation/13817
where 13817 is the ID of that organization.
And if you add /rest/
after the domain name, you actually get the same information… in json format
https://social.brussels/rest/organisation/13817
You will note that most of the fields have either a suffix Fr
(for French) or Nl
for Dutch since our country is multilingual.
Note: if you open the latter url in Firefox then you don’t even need an add-on in the browser to see the json in a structured way and with code highlighting.
4.4.2. Create the file
In the folder /joomlatools-pages/pages/
create a file named
step4.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step4)
---
title: A PHP page fetching external data from json file
summary: This page is rendered by the PHP engine.
---
<h2>Fetching data from external json</h2>
<p>directly from <a target="_blank" href="https://social.brussels/rest/organisation/13817">https://social.brussels/rest/organisation/13817</a></p>
<h3>In Dutch</h3>
<h4>->nameOfficialNl<h4>
<p><pre><code><?= data('https://social.brussels/rest/organisation/13817', '1day')->nameOfficialNl; ?></code></pre></p>
<h4>->legalStatus->labelNl<h4>
<p><pre><code><?= data('https://social.brussels/rest/organisation/13817', '1day')->legalStatus->labelNl; ?></code></pre></p>
<h3>In French</h3>
<h4>->nameOfficialFr<h4>
<p><pre><code><?= data('https://social.brussels/rest/organisation/13817', '1day')->nameOfficialFr; ?></code></pre></p>
<h4>->legalStatus->labelFr<h4>
<p><pre><code><?= data('https://social.brussels/rest/organisation/13817', '1day')->legalStatus->labelFr; ?></code></pre></p>
<h3>That was easy peasy!</h3>
4.4.3. See the result
Go to https://pages.joomlacustomfields.org/step4 and see the result.
4.4.4. Caching
And the good news is: external data can be easily cached and you can choose the duration easily.
For more info see: https://github.com/joomlatools/joomlatools-pages/pull/512
By default caching is enabled and the data will not be revalidated.
But thanks to the data([url], [cache])
template function you can customize this.
Example if you want to cache for 24 hours:
<? $data = data([url], '1day'); ?>
In this example we make multiple calls to the same resources.
So the question is: what does happen with the Cache ?
Each time you make a call to data, Pages will check if the resource it has cached is still valid. If it is it will use it from cache, if not it will update. So multiple data() functions to the same url with a diff cache time the lowest time wins.
4.5. Blog view using table=content from database
It is now time to build a simple Blog view.
Pages allow indeed to query directly any Joomla table
4.5.1. Create the file
In the folder /joomlatools-pages/pages/
create a file named
step5.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step5)
---
collection:
model: database?table=content
---
<h2>Blog view using database?table=content</h2>
<p>We list all articles directly from the database</p>
<hr>
<div class="well">
<? foreach(collection() as $article): ?>
<div style="background: white; padding: 20px;">
<? $article_images = json_decode($article->images);?>
<h3><b>Title:</b> <?= $article->title ?></h3>
<p><img src="<?= $article_images->image_intro; ?>" alt="<?= $article_images->image_intro_alt; ?>" title="<?= $article_images->image_intro_caption; ?>"></p>
<p><b>Published on:</b> <?= date('d M Y', $article->published_date); // // Prints something like: 08 Jun 2021 ?></p>
<p><b>Category ID:</b> <?= $article->catid ?></p>
<p><b>Introtext:</b> <?= $article->introtext ?></p>
</div>
<hr>
<? endforeach; ?>
</div>
<p>And here is a little <code>var_dump(collection())</code> so that you can see all what is immediately available in the array itself:</p>
<pre><code><?= var_dump(collection()); ?></code></pre>
4.5.2. See the result
Go to https://pages.joomlacustomfields.org/step5 and see the result.
4.5.3. Images options are stored in JSON format in Joomla
If you have already seen Joomla’s native code or made overrides, you know about this: the Article Image’s options are stored in the database in JSON format. So to get the path/name of an image, we must first json_decode it:
<? $article_images = json_decode($article->images);?>
<img src="<?= $article_images->image_intro; ?>
4.6. Blog view using ext:joomla.model.articles and links with the Joomla router
Accessing directly a table from the database was already nice but it is possible to do much more thanks to Joomlatools Pages’ Joomla Extensions
4.6.1. Install Joomlatools Pages’ Joomla Extensions
- go to https://github.com/joomlatools/joomlatools-pages > Releases (latest)
- in the Assets, download ext_joomla.zip
- unzip it
- create the following folder
joomlatools-pages/extensions/
- upload the files into that newly created folder
So actually with Pages 'installing’ extensions is just 'uploading’ them…
4.6.2. Create the file
In the folder /joomlatools-pages/pages/
create a file named
step6.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step6)
---
collection:
model: ext:joomla.model.articles
state:
published: 1
category: [organizations]
sort: date
order: asc
limit: 3
---
<h2>Blog view using <samp>ext:joomla.model.articles</samp> and links with the Joomla router</h2>
<p>Note: I am just adding a few CSS lines in my PHP file in order to display the Articles as nice Cards. That CSS is put into the <samp>head</samp> of the page automatically by Pages.</p>
<style>
ul.pages-cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 20px;
margin-left: 0; /* otherwise we have the natural left margin of the Unordered List */
}
ul.pages-cards > li {
background: white;
display: flex;
flex-direction: column;
padding: 10px;
border: 1px solid lightgray;
box-shadow: 3px 3px 2px 1px rgba(0, 0, 0, 0.2);
transition: 0.5s;
}
ul.pages-cards > li:hover {
box-shadow: 3px 3px 2px 1px rgba(0, 0, 0, 0.4);
}
</style>
<div class="well">
<ul class="pages-cards">
<? foreach(collection() as $article): ?>
<? $data = data('https://social.brussels/rest/organisation/'. $article->fields->get('socialbrusselsid')->value, '1day') ?>
<li>
<h3>aa<a href="<?= route($article) ?>"><?= $article->title; ?></a></h3>
<p><img src="<?= $article->image->url; ?>" alt="<?= $article->image->alt; ?>" title="<?= $article->image->caption; ?>"></p>
<p><b>Category: <?= $article->category->title; ?></b></p>
<p><small><?= 'This article was published on ' . date('d M Y', $article->published_date) . ' for the demo'; ?></small></p>
<p><b>Excerpt (ex-introtext):</b></p><?= $article->excerpt ?>
<p><b>Text (ex-fulltext):</b></p><?= $article->text ?>
<p><b>(ex-introtext + ex-fulltext):</b></p><?= $article ?>
<p><b>Custom Field Label and Value:</b><br /><?= $article->fields->socialbrusselsid->label ?>: <?= $article->fields->socialbrusselsid->value ?></p>
<p><b>Name from external json:</b><br /><?= $data->nameOfficialNl ?></p>
<p><b>Address from external json:</b><br /><?= $data->address->number ?> <?= $data->address->streetNl ?> <?= $data->address->postalCodeNl ?></p>
</li>
<? endforeach ?>
</ul>
</div>
<hr>
<p>And here is a little <code>var_dump(collection())</code> so that you can see all what is immediately available in the array itself:</p>
<pre><code><?= var_dump(collection()); ?></code></pre>
4.6.3. See the result
Go to https://pages.joomlacustomfields.org/step6 and see the result.
4.6.4. Filtering and sorting
In the Frontmatter we can very easily filter and sort our content thanks to the state
:
- we filtered on published articles: 1
- we filtered on the category: alias or ID will do
- we sort by ascending date
- we can also limit the number of articles: 3
Very easy to visualize and write…
Note: if you want to filter on multiple categories, simply use the following syntax:
category: [organizations, uncategorised]
4.6.5. Some customized styling with CSS Grid and/or Flexbox
For the HTML/CSS of this demo, we did not want to bother with Bootstrap because
- most of the recent templates use BS4
- some websites’templates still use BS3
- Protostar is based on BS2
- … and Joomla4 will use BS5
So if I had used Bootstrap, you could not necessarily simply copy/paste the code to get the same result if your Bootstrap version is different (or if you are using another framework like UIKit, Tailwind, …).
Instead, to get something easier, lighter and more universal, we simply use here CSS Grid and Flexbox.
If you have no clue what it is, you should definitely have a look at it. Especially for CSS Grid since Joomla4 is based on CSS Grid, which is a (very) good thing.
Note: as you can see, I am using here 'inline CSS’ with a style
tag directly inside my HTML.
You don’t even need to put it in your template 'custom css’ because Pages will automatically put the CSS into the <header>
of your site!
4.6.6. Using the Joomla Router
You want the Title to have a link to the Article itself?
Using the native Joomla Router the following single line will do:
<a href="<?= $article->getRoute() ?>"><?= $article->title; ?></a>
4.6.7. A direct access to the image path/name
If you look at the code you will notice that, using ext:joomla.model.articles
, we don’t need to play with json_decode to get the image path/name
. It is indeed immediately available:
<?= $article->image->url; ?>
Note 1: actually, in the code itself I should first check that there is an image before displaying it. So here is the code to display an Article image (with its Title and Description) only if it exists:
<? if( !empty( $article->image ) ): ?>
<img src="<?= $article->image->url; ?>" alt="<?= $article->image->alt; ?>" title="<?= $article->image->caption; ?>">
<? endif; ?>
Note 2: is it the intro image or the full text image ? See the answer here: https://github.com/joomlatools/joomlatools-pages/discussions/673
4.6.8. Category title/alias
If you look at the code you will notice that, using ext:joomla.model.articles
, we can access the Category title/alias
directly:
<?= $article->category->title; ?>
(whereas using database?table=content
we could only access the Category ID since only the ID is mentioned in the content table)
4.6.9. introtext/fulltext
If you look at the code you will notice that, using ext:joomla.model.articles
, we don’t use introtext/fulltext
but exerpt/text
.
For more information, see https://github.com/joomlatools/joomlatools-pages/discussions/667
4.6.10. Custom Fields
Custom Fields are sooooo easy to display:
Custom Field's label: <?= $article->fields->socialbrusselsid->label ?>
Custom Field's rawvalue (ie the value in the database): <?= $article->fields->socialbrusselsid->value ?>
Custom Field's rendering (ie using Joomla's default rendering with the CF's options): <?= $article->fields->socialbrusselsid ?>
Note: using the simple $article->fields->customfieldalias
(so without referring to label
or value
) is very nice because it will use the Joomla’s rendering.
This is particularly useful if you are using third-party Custom Fields allowing to easily display Maps, Videos, Sound etc (see for example those proposed by Tassos: https://www.tassos.gr/joomla-extensions/advanced-custom-fields)
But beware: if you have spaces in the Title of your Custom Fields, Joomla will by default suggest a Name with dashes.
This is OK but then you will have to use one of these syntaxes
$article->fields->get('with-dash')->value
$article->fields->{'with-dash'}->value
instead of the short syntax
$article->fields->withoutdash->value
So to keep things simple I suggest to avoid dashes in the Names of the CF.
4.6.11. External JSON
How do we fetch information from an external website ?
- we have a Custom Field with Name 'socialbrusselsid’
- the value of that CF for a given article is 13817
- the following url contains all the information we want to fetch: https://social.brussels/rest/organisation/13817
- let’s suppose we want to display the Street of that organization in Dutch, namely: ->address->streetNl
- and let’s suppose we want to cache that information for 1 day
- then the following single line does it all!
<?= data('https://social.brussels/rest/organisation/'. $article->fields->get('socialbrusselsid')->value, '1day')->address->streetNl ?>
4.6.12. More generally, retrieving data from a SAAS service
See https://github.com/joomlatools/joomlatools-pages/discussions/505
Retrieving data from a SAAS service with Pages is very easy. Pages handles data sources in a transparent way. It doesn’t make a difference if you connect to a database, a webservice or a file on the filesystem. They all work in exactly the same way.
To work with data Pages uses collections and specifically for webservices, there is a base webservice collection you can use.
Every collection can be: filtered / sorted / searched / paginated
Pages offers out of the box support for JSON and can even read CSV from a webservice, which is a great way to integrate with things like Google Spreadsheets.
An example would be
---
collection:
model: webservice?url=http://example.com/api/endpoint
---
<? foreach(collection() as $item) : ?>
<?= $item->title ?>
<? endforeach; ?>
assuming that the service returns a title
property
4.6.13. Comparison with a typical native Joomla override
Of course, the scope is not exactly the same (I did not include some Article Options to keep it short for the presentation), but see how simple/concise/readable this code is compared to the code of a typical native Joomla override:
https://github.com/joomla/joomla-cms/tree/staging/components/com_content/views/category/tmpl
See for example blog.php and blog_item.php
4.7. Blog view using ext:joomla.model.articles and links with the Joomla router – and partials for Custom Fields
Let’s put some parts of the code in little files (called partials) so that
- they are more readable
- they are easier to maintain
- they can be reused at different places
This will allow us for example to easily transform
- a simple Text Custom Field into a block of text made of many fields taken from an external json
- a simple URL Custom Field containing the URL of a YouTube video into a player with the desired options
4.7.1. Create the file
In the folder /joomlatools-pages/pages/
create a file named
step7.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step7)
---
collection:
model: ext:joomla.model.articles
state:
published: 1
category: [organizations]
sort: date
order: asc
process:
filters: highlight
---
<h2>Blog view using <samp>ext:joomla.model.articles</samp> and links with the Joomla router (and partials for Custom Fields)</h2>
<style>
ul.pages-cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 20px;
margin-left: 0; /* otherwise we have the natural left margin of the Unordered List */
}
ul.pages-cards > li {
background: white;
display: flex;
flex-direction: column;
padding: 10px;
border: 1px solid lightgray;
box-shadow: 3px 3px 2px 1px rgba(0, 0, 0, 0.2);
transition: 0.5s;
}
ul.pages-cards > li:hover {
box-shadow: 3px 3px 2px 1px rgba(0, 0, 0, 0.4);
}
</style>
<div class="well">
<ul class="pages-cards">
<? foreach(collection() as $article): ?>
<li>
<h3><a href="<?= route($article) ?>"><?= $article->title; ?></a></h3>
<p><img src="<?= $article->image->url; ?>" alt="<?= $article->image->alt; ?>" title="<?= $article->image->caption; ?>"></p>
<p><b>Category: <?= $article->category->title; ?></b></p>
<p><small><?= 'This article was published on ' . date('d M Y', $article->published_date) . ' for the demo'; ?></small></p>
<p><b>Excerpt (ex-introtext):</b></p><?= $article->excerpt ?>
<p><b>Text (ex-fulltext):</b></p><?= $article->text ?>
<p><b>(ex-introtext + ex-fulltext):</b></p><?= $article ?>
<p><?= partial('/embeds/socialbrussels.html', ['id' => $article->fields->socialbrusselsid->value]) ?></p>
<p><?= partial('/embeds/url.html', ['url' => $article->fields->youtube->value]) ?></p>
<p><?= partial('/embeds/youtube.html', ['url' => $article->fields->youtube->value]) ?></p>
</li>
<? endforeach ?>
</ul>
</div>
<hr>
<p>And here is a little <code>var_dump(collection())</code> so that you can see all what is immediately available in the array itself:</p>
<pre><code><?= var_dump(collection()); ?></code></pre>
4.7.2. Create the partials for the Custom Fields
In Joomla when you want to create a new type of Custom Field you typically have to create a little plugin.
This is fairly easy for someone who can code a bit.
But it becomes even much easier with Pages since all you need to to is to create a little file containing the rendering.
4.7.2.1. A partial for TEXT Custom Field – socialbrusselsid
The main file refers to a partial transforming the socialbrusselsid
Custom Field into a block of several informations fetched from the external json file.
/partials/embeds/socialbrussels.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step7articleembedsocialbrussels)
<? $data = data('https://social.brussels/rest/organisation/'. $id, '1day') ?>
<h4>social.brussels data (using a reusable 'partial')</h4>
<ul>
<li><b>id:</b> <a href="<?= 'https://social.brussels/rest/organisation/'. $id ?>" target="_blank"><?= $id ?></a></li>
<li><b>FRENCH</b></li>
<ul>
<li><b>nameOfficialFr:</b> <?= $data->nameOfficialFr ?></li>
<li><b>legalStatus->labelFr:</b> <?= $data->legalStatus->labelFr ?></li>
<li><b>emailFr:</b>
<ul>
<? foreach( $data->emailFr as $email){ ?>
<li><?= $email ?></li>
<? } ?>
</ul>
</li>
</ul>
<li><b>DUTCH</b></li>
<ul>
<li><b>nameOfficialNl:</b> <?= $data->nameOfficialNl ?></li>
<li><b>legalStatus->labelNl:</b> <?= $data->legalStatus->labelNl ?></li>
<li><b>emailNl:</b>
<ul>
<? foreach( $data->emailNl as $email){ ?>
<li><?= $email ?></li>
<? } ?>
</ul>
</li>
</ul>
</ul>
<?//= partial('/embeds/openstreetmap.html') ?>
4.7.2.2. A partial for URL Custom Field – button with YouTube link
The main file refers to a partial transforming the youtube
Custom Field into a button.
/partials/embeds/url.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step7articleembedurl)
<a class="btn btn-primary" href="<?= $url ?>">url transformed into a button via a Partial in embeds folder</a>
4.7.2.3. A partial for URL Custom Field – YouTube video player
The main file refers to a partial transforming the youtube
Custom Field into a customized video player.
/partials/embeds/youtube.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step7articleembedyoutube)
<? if (preg_match('%(?:youtube(?:-nocookie)?\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu\.be/)([^"&?/ ]{11})%i', $url, $match)): ?>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/<?= $match[1] ?>" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<? endif ?>
4.7.3. See the result
Go to https://pages.joomlacustomfields.org/step7 and see the result.
4.7.4. Not necessary any more to create plugins to get new CF Types
So as you can see: it is so easy to create a little file that will take care of transforming a basic native Text field into anything (video player, map, …) that you don’t necessarily need to build a plugin to create a new 'type of Custom Field’.
4.8. Blog view using ext:joomla.model.articles and links with the Pages router
Pages ships with its own router.
This means you have direct control on the slug/url of your Articles.
4.8.1. Create the files
We will only need two files
- one for the Blog View
- one for the Article View
Just for fun, in the Article View we will import a partial (ie an external file).
4.8.1.1. The Blog View
In the folder /joomlatools-pages/pages/
create a file named
step8.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step8blog)
---
collection:
model: ext:joomla.model.articles
state:
published: 1
category: [organizations]
sort: date
order: asc
process:
filters: highlight
---
<h2>Blog view using ext:joomla.model.articles and links with the Pages router (in one file)</h2>
<p><small>Of course, in a real site, when using Custom Fields we could filter by Category since not all CF are assigned to all Categories for example)</small></p>
<div class="well">
<ul>
<? foreach(collection() as $article): ?>
<li>
<a href="<?= route('/step8article', ['slug' => $article->slug]) ?>"><?= $article->title; ?></a>
</li>
<? endforeach ?>
</ul>
</div>
<hr>
<p>And here is a little <code>var_dump(collection())</code> so that you can see all what is immediately available in the array itself:</p>
<pre><code><?= var_dump(collection()); ?></code></pre>
4.8.1.2. The Article View
In the folder /joomlatools-pages/pages/
create a file named
step8article.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step8article)
---
collection:
model: ext:joomla.model.articles
state:
published: 1
category: [organizations]
sort: date
order: asc
process:
filters: highlight
---
<h2>Article View override done with Pages (in one file)</h2>
<article>
<h3><?= collection()->title ?></h3>
<p><?= collection()->text ?></p>
<?= partial('/embeds/socialbrussels.html', ['id' => collection()->fields->socialbrusselsid->value]) ?>
</article>
<h3>A var_dump of all Custom Fields with <code>var_dump(collection()->fields)</code></h3>
<pre><code><?= var_dump(collection()->fields); ?></code></pre>
4.8.1.3. A partial for TEXT Custom Field – socialbrusselsid
See above
4.8.2. See the result
Go to https://pages.joomlacustomfields.org/step8 and see the result.
4.8.3. Pointing to the Article view
In the Blog view, this is how we point to the file of the Article view (and make sure the slug will be as defined in the Article view):
<a href="<?= route('step8article', ['slug' => $article->slug]) ?>"><?= $article->title; ?></a>
4.8.4. Customizing the slug
In the Article view we define in the Frontmatter what the slug will be.
In this case, /blabla/article-alias
:
route: blabla/[:slug]
4.9. Blog view using ext:joomla.model.articles and links with the Pages router and partials
Last but not least, after having made a simple 'override’ of the Blog view and of the Article view let us play further with the partials
4.9.1. Create the files
So far we have always has all our xxx.html.php
files in the /joomlatools-pages/pages/
folder.
But actually you can of course create subfolders, subsubfolders etc. For more information, see https://github.com/joomlatools/joomlatools-pages/wiki/Folder-Structure#pages
Example: the following two options produce exactly the same result:
/joomlatools-pages/pages/test.html.php
/joomlatools-pages/pages/test/index.html.php
4.9.1.1. the Blog view
In the folder /joomlatools-pages/pages/step9/
create a file named
index.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step9blog)
---
collection:
model: ext:joomla.model.articles
state:
published: 1
category: [organizations]
sort: date
order: asc
process:
filters: highlight
---
<h2>Blog view using <samp>ext:joomla.model.articles</samp> and links with the Pages router (with partials)</h2>
<?= partial('/myorganizations/liststep9.html') ?>
4.9.1.2. the Blog partial
As can be seen in the index file, we import a file /partials/myorganizations/liststep8.html
Of course, the content of that file could have been placed directly in index.html.php
. But putting it in the partials makes it reusable and easier to maintain.
So in the folder /joomlatools-pages/partials/myorganizations
create a file named
liststep9.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step9blogpartial)
<p><small>Of course, in a real site, when using Custom Fields we could filter by Category since not all CF are assigned to all Categories for example)</small></p>
<div class="well">
<ul>
<? foreach(collection() as $article): ?>
<li>
<a href="<?= route('/step9/org', ['slug' => $article->slug]) ?>"><?= $article->title; ?></a>
</li>
<? endforeach ?>
</ul>
</div>
<hr>
<p>And here is a little <code>var_dump(collection())</code> so that you can see all what is immediately available in the array itself:</p>
<pre><code><?= var_dump(collection()); ?></code></pre>
4.9.1.3. the Article view with its router
As can be seen in the partial file, we refer to a file step9/org
In the folder joomlatools-pages/pages/step9/
create a file named
org.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step9article)
---
route: myslug/[:slug]
collection:
extend: articles
process:
filters: highlight
---
<?= partial('/myorganizations/myarticle.html') ?>
You can choose whatever you like to replace myslug
, which will be visible in the url of Article “Organization 1” as follows:
https://pages.joomlacustomfields.org/myslug/organization-1
4.9.1.4. the partial of the Article view
Just to make things more fun, we create a partial which is imported in the article view.
As can be seen in the article view, we refer to a file /partials/myorganizations/myarticle.html
In the folder /partials/myorganizations/
create a file named
myarticle.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#step9articlepartial)
<h2>Article View override done with Pages (with partials)</h2>
<article>
<h3><?= collection()->title ?></h3>
<p><?= collection()->text ?></p>
<?= partial('/embeds/socialbrussels.html', ['id' => collection()->fields->socialbrusselsid->value]) ?>
</article>
<h3>A var_dump of all Custom Fields with <code>var_dump(collection()->fields)</code></h3>
<pre><code><?= var_dump(collection()->fields); ?></code></pre>
4.9.1.5. A partial for the socialbrusselsid Custom Field
Same as in the previous step.
4.9.2. See the result
Go to https://pages.joomlacustomfields.org/step9 and see the result.
4.9.3. Could have been done with 2 files
Of course, what we have made here in 4 files could have been done in just 2 files like seen at the previous Step.
But the idea was to show that snippets of code can be imported and so reused.
And this facilitates the maintenance of the code of course.
4.10. Other features
So I am now done with what I really wanted to share with your about Pages.
But Pages ships with many other features.
4.10.1. Decoration
Let us illustrate one of those features, namely the ability to 'decorate’ (namely add content before/after/…) the component’s view, whatever it is: article, third-party extension, …
4.10.1.1. Create the file
In the folder /joomlatools-pages/pages/
create a file named
decoration.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#decoration)
---
process:
decorate: true
---
<mark>
<h1><mark>I'm decorated by Pages</mark></h1>
<p><mark>What does it mean?</mark></p>
<p><mark>Simply that there is a native menu having blabla as Alias... and by adding a file called blabla.html.php I can add my own content to the native component content.</mark></p>
<p><mark>And now starts original's component view:</mark></p>
</mark>
<div class="well">
<ktml:content>
</div>
<p><mark>PS: oh, btw, this line is also added by Pages after the original content.</mark></p>
4.10.1.2. See the result
Go to https://pages.joomlacustomfields.org/decoration and see the result.
4.10.1.3. How to name the decoration file
So actually if the URL of the page I want to decorate is MYSITE.COM/blabla
then all I need to do to decorate it is to create a file named
blabla.html.php
and customize it.
4.10.2. Search
Another feature is Searching.
This can be done directly from the Frontmatter… or even via the url if you allow it in the Frontmatter.
For more information see https://github.com/joomlatools/joomlatools-pages/discussions/506
4.10.2.1. Create the file
In the folder /joomlatools-pages/pages/
create a file named
search.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#search)
---
collection:
model: ext:joomla.model.articles
state:
sort: title
config:
search: [title, alias, date]
---
<h2>Joomlatools Pages also allows to search</h2>
<p>This can be done directly from the Frontmatter... or even via the url if you allow it in the Frontmatter.</p>
<p><small>Note: on this page we have removed <samp>category: [organizations]</samp> in order to show all articles by default</small></p>
<h3>Searching via url</h3>
<p>Click on the following links and see the list of articles changing in function of the search criteria:</p>
<ul>
<li><a href="/search">/search</a></li>
<li><a href="/search?search=title:org">/search?search=title:org</a></li>
<li><a href="/search?search=title:2">/search?search=title:2</a></li>
<li><a href="/search?search=title:Organization%201">/search?search=title:Organization%201</a></li>
</ul>
<hr>
<div class="well">
<p>List of (searched) articles:</p>
<ul>
<? foreach(collection() as $article): ?>
<li><?= $article->title ?> (<?= $article->fields->socialbrusselsid ?>)</li>
<? endforeach ?>
</ul>
</div>
<hr>
<p>For more information see <a href="https://github.com/joomlatools/joomlatools-pages/discussions/506" target="_blank">https://github.com/joomlatools/joomlatools-pages/discussions/506</a></p>
4.10.2.2. See the result
Go to https://pages.joomlacustomfields.org/search and see the result.
4.10.3. Filtering
Another feature is Filtering.
This can be done directly within your Frontmatter… or even via the url.
It works even with Custom Fields (which is not possible in Joomla without an extension)
Note: difference between Searching and Filtering:
- when you search you start with nothing and you go find something
- when you filter you start with everything and you remove items that don’t follow the constraints you provide
4.10.3.1. Create the file
In the folder /joomlatools-pages/pages/
create a file named
filter.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#filter)
---
collection:
model: ext:joomla.model.articles
state:
sort: title
category: [organizations]
---
<h2>Joomlatools Pages also allows to filter</h2>
<div class="muted">
<p>Preliminary note: difference between Searching and Filtering:
<ul>
<li>when you search you start with nothing and you go find something</li>
<li>when you filter you start with everything and you remove items that don't follow the constraints you provide</li>
</ul>
</div>
<hr>
<h3>Filtering via url</h3>
<p>Click on the following links and see the list of articles changing in function of the filter criteria.</p>
<ul>
<li><a href="/filter">/filter</a></li>
<li><a href="/filter?id=2">/filter?id=2</a> (ID 2)</li>
<li><a href="/filter?filter[id][]=2">/filter?filter[id][]=2</a> (ID 2)</li>
<li><a href="/filter?id=1,2">/filter?id=1,2</a> (ID 1 or 2)</li>
<li><a href="/filter?id[]=1&id[]=2">/filter?id[]=1&id[]=2</a> (ID 1 or 2)</li>
<li><a href="/filter?filter[id][]=gt:2">/filter?filter[id][]=gt:2</a> (ID greater than 2)</li>
<li><a class="work-in-progress" href="/filter?filter[id][]=gt:1&filter[id][]=lt:4">/filter?filter[id][]=gt:1&filter[id][]=lt:4</a></li>
</ul>
<hr>
<div class="well">
<p>List of (filtered) articles:</p>
<ul>
<? foreach(collection() as $article): ?>
<li><?= $article->title ?> (id: <?= $article->id ?>)</li>
<? endforeach ?>
</ul>
</div>
<hr>
<p>Filtering can be done via the <b>Frontmatter</b>, the <b>url</b> or the <b>code</b></p>
<p><small>Note: it works great for data coming from webservices, or from arrays, for database this is a tad harder to implement, it works for data from the same table, for related data it's harder. Still working on making it so that you don't need special states for filtering on db.</small></p>
<p>Filters work on</p>
<ul>
<li>ID</li>
<li>category</li>
<li>access (=> use userid)</li>
<li>editor (=> use userid)</li>
<li>author (=> user userid)</li>
</ul>
<p>For more information, see <a href="https://github.com/joomlatools/joomlatools-pages/pull/363">https://github.com/joomlatools/joomlatools-pages/pull/363</a></p>
<p>Filters also work with States: you can use the collection stats both in your frontmatter and in your url too.<br />States are easy to find: <a href="https://github.com/joomlatools/joomlatools-pages/blob/master/contrib/extensions/joomla/model/articles.php#L16">https://github.com/joomlatools/joomlatools-pages/blob/master/contrib/extensions/joomla/model/articles.php#L16</a></p>
<p><small>Note: 'filter' is an approach to try and make this just work, without needing much custom code. This works great for example for a database table if you use the base database collection or for a webservice, but in case of the joomla extension we are not talking one to one to the daabase table, so filter doesn'y work there that easily.</small></p>
<hr>
<?= partial('/embeds/filtering') ?>
<hr>
<p>And here is a little <code>var_dump(collection())</code> so that you can see all what is immediately available in the array itself:</p>
<pre><code><?= var_dump(collection()); ?></code></pre>
4.10.3.2. See the result
Go to https://pages.joomlacustomfields.org/filter and see the result.
4.10.4. Form
Pages is also able to create Forms. For more information, see https://github.com/joomlatools/joomlatools-pages/wiki/Cookbook-Forms
See our example on https://pages.joomlacustomfields.org/form
4.10.5. GDoc
Yet another feature of Joomlatools Pages: it is possible to load any type of structured data file by file by url.
In this example, Pages will extract the content from a published Google Doc and inject it in the page.
For more information see https://github.com/joomlatools/joomlatools-pages/pull/264
4.10.5.1. Create the file
In the folder /joomlatools-pages/pages/
create a file named
gdoc.html.php
having the following content
(latest version on https://pages.joomlacustomfields.org/code#gdoc)
---
url: https://docs.google.com/document/d/e/2PACX-1vTK9nkPIsoq6ZJeVm86ns4BX7Q0bOcMHV5jYoaGlsREXUxt22kDW2zt0Zh3wSF9mquuVQKlPFyRw9HK/pub
---
<h2>Fetching and displaying a Google Doc</h2>
<p>Data coming from: <a target="_blank" href="https://docs.google.com/document/d/e/2PACX-1vTK9nkPIsoq6ZJeVm86ns4BX7Q0bOcMHV5jYoaGlsREXUxt22kDW2zt0Zh3wSF9mquuVQKlPFyRw9HK/pub">https://docs.google.com/document/d/e/2PACX-1vTK9nkPIsoq6ZJeVm86ns4BX7Q0bOcMHV5jYoaGlsREXUxt22kDW2zt0Zh3wSF9mquuVQKlPFyRw9HK/pub</a></p>
<p>For more information see <a href="https://github.com/joomlatools/joomlatools-pages/pull/264" target="_blank">https://github.com/joomlatools/joomlatools-pages/pull/264</a></p>
<h3>Is there also a cache with Gdoc & Gsheet?</h3>
<p>Both use caching if:</p>
<ul>
<li>Joomla caching is enabeld.</li>
<li>you set <samp>'http_client_cache' => true</samp> in your config.php in the <samp>/joomlatools-pages/</samp> directory</li>
</ul>
<p>Is on now so the cache for the Gdoc and Gsheet is working, you will find a new dir called /joomlatools-pages/cache/responses, containing two files, one for the Gdoc and one for the Gsheet.<br />
The Gdoc is cached always, aka pages will not recheck of the original file has changed, the Gsheet uses pages webservice caching which is a smart cache, it will know when you update the csv.</p>
<hr>
<h3>The content of the Gdoc hereafter</h3>
<? $doc = data($url); ?>
<title><?= $doc->get('html/head/title') ?></title>
<?
$content = $doc->get('html/body/div')->filter('@attributes', ['id' => 'contents'])->remove('style')->toHtml();
foreach ($content->query('//*') as $node)
{
foreach(['style', 'class', 'id'] as $attribute) {
$node->removeAttribute($attribute);
}
}
?>
<div class="well">
<article>
<?= $content; ?>
</article>
</div>
4.10.5.2. See the result
Go to https://pages.joomlacustomfields.org/gdoc and see the result.
5. Y O U R next steps
Now time to go and play for yourself!
Curious to know even more? Have a look here to have examples of sites built with Joomlatools Pages:
- https://github.com/joomlatools/joomlatools-pages/discussions/categories/show-and-tell
- https://github.com/joomlatools/joomlatools-pages/tree/master/contrib/sites/blog
demo at Joomladay USA 2021 - https://github.com/johanjanssens/agrea.ph
website made by Johan
6. Future features
6.1. ACL
In the next version (0.21) Pages will also manage Access Control Lists (ACL).
The code is (almost) ready.
And again it is super easy to use:
<h3>Only visible for hasRole or hasGroup</h3>
<? if(user()->hasGroup(['manager', 'administrator'], true)) : ?>
<p>blabla</p>
<? endif ?>
<? if(user()->hasRole('special')) : ?>
<p>blabla</p>
<? endif ?>
6.2. Transform your site into a webservice
As you might know, Joomla!4 will ship with a Rest API.
But you don’t need to wait: with the next version (0.21) Pages will also allow you to transform your content into json:
Example:
- blog version: https://www.joomlatools.com/blog/
- json version: https://www.joomlatools.com/blog.json
The corresponding code:
Pages supports indeed rendering to different machine readable formats: CSV, JSON, RSS, XML. Support for all of those formats is included, and you do not need to write any code. Just create a file with the right format, defined a collection in it and Pages will render the data from the collection in the specific format.
For more informatoin, see https://github.com/joomlatools/joomlatools-pages/discussions/675
7. Conclusion
First of all I would like to thank Johan Jansens for different reasons
- he has always been very kind/patient with me asking questions (as a non-developer to a developer)
- he has also improved a series of Pages features following our discussions (in particular regarding Custom Fields which is my favourite area in Joomla)
Second, I should make a confession
- this presentation is already pretty long and yet I only covered one feature of Pages: Blog View 'overrides’ and Article View 'overrides’
- but actually I am pretty sure I only (dis)covered 20% of Pages is or will be 😀
8. Get in touch
Any suggestion about this presentation ?
Please feel free to contact me. I’ll be happy to keep improving it:)
Marc Dechèvre | woluweb
+32 474 37 13 12 | +32 2 772 58 69