Verslag JUG Eindhoven 9 april
Het is een natte frisse avond in april. Zonnetje heeft vandaag al een aantal keer lekker kunnen schijnen. Hans Kuijpers laat in een demo zien hoe je GTM kunt gebruiken voor de koppeling naar GA4.
Google Tag Manger
Middels een demo laat Hans Kuijpers zien hoe je Google Tag Manager (GTM) kunt gebruiken om bijvoorbeeld Google Analytics (GA4) te koppelen aan je website. Het is ook mogelijk om Matamo Analytics of andere analytics tool via GTM aan je site te koppelen.
Stap 1. GTM account en container
- Ga naar https://tagmanager.google.com en log in met een Google account.
- Klik op de button _Account Aanmaken_ en doorloop de stappen waarna je ook een _Container_ aangemaakt hebt voor je website.
- Je eindigt met een popup waar een stukje code in staat om te plaatsen in de `<head>` van je site en eentje direct na het starten van de `<body>`. Bewaar de GTM ID. De verwerking van de code doen we in stap 2 op een andere manier.
Stap 2. Je website verbinden met GTM
De connectie tussen de website en GTM wordt gemaakt met een eigen plugin. Deze plugin doet twee dingen:
- plaatst eerdergenoemde code van GTM
- plaatst de initiële status van de Consent Mode. (nogal verplicht bij Google tegenwoordig.) Zie ook het verslag van de vorige JUG040 bijeenkomst over de implementatie van consent mode
- Ga naar https://github.com/perfectwebteam/pwt-gtm en download de meest recente versie
- Ga naar Joomla Administrator > Systeem > Installeren > Extensies en installeer de zojuist gedownloade systeem plugin.
- Ga naar Joomla Administrator > Systeem > Beheren > Plugins en zoek op naam _PWT GTM_. Op de plugin om deze te bewerken:
- Wijzig status naar _Gepubliceerd_
- Voer de GTM ID uit stap 1 in
- Opslaan en Sluiten
Met deze acties is er een connectie gelegd tussen jouw website en GTM.
Let op... mocht je de Systeem Plugin _httpheaders_ actief hebben staan met een configuratie voor Content Security Policy, dan zal de GTM integratie mogelijk geblokkeerd worden door je eigen integratie. Lees in de dev tools de errors en zie waar een en ander mis gaat. Wijzig met behulp van deze info de configuratie van de CSP headers in de system plugin _httpheaders_:
- script-src-elem:
https://www.googletagmanager.com
- style-src-elem:
https://www.googletagmanager.com https://fonts.googleapis.com
- font-src:
https://fonts.gstatic.com
- img-src:
data: https://www.googletagmanager.com https://fonts.gstatic.com
Druk in GTM op de knop _Voorbeeld_ om de wijzigingen te testen voordat je op de knop _Verzenden_ klikt. Bij _voorbeeld_ is er nog niets uitgerold op de live omgeving. Alleen jouw sessie zal de wijzigingen kunnen zien. Een nieuw venster verschijnt met daarin een overzicht van de events en de tags die per event al dan niet worden uitgevoerd.
Stap 3. Aanmaken van Variabelen, Triggers en Tags
De in stap 2 geïnstalleerde plugin PWT GTM plaatst de initiële waarden van de consent op de pagina.
Dit betreft het volgende stukje code:
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
if (localStorage.getItem('consentMode') === null) {
gtag('consent', 'default', {
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'analytics_storage': 'granted',
'personalization_storage': 'granted',
'functionality_storage': 'granted',
'security_storage': 'granted',
});
} else {
gtag('consent', 'default', JSON.parse(localStorage.getItem('consentMode')));
}
</script>
Dit stukje code kijkt of de voorkeuren opgeslagen zijn in localstorage en gebruikt deze. Zo niet, dan wordt een default meegegeven.
Zie de screenshot hieronder waarbij je het resultaat ziet van het event = Consent bij het bekijken van de pagina vanuit _Voorbeeld_
Vervolgens in GTM een nieuwe tag aanmaken van type = _Aangepaste HTML_. Plaats daarin de volgende block code om een Cookie Consent Popup te kunnen maken:
De meest recente versie van de code staat op https://gist.github.com/hans2103/13243293eb5e72b6e2c87751a42da458
<button type="button" class="cc-modal__button--settings" onclick="openCCModal('cc-accept')" aria-label="cookie settings">
cookie
</button>
<div id="cc-accept" class="cc-modal">
<div role="dialog" class="cc-modal__dialog">
<div class="cc-modal__content">
<div class="cc-modal__header">
<h2>Deze website maakt gebruik van cookies</h2>
</div>
<div class="cc-modal__body">
<p>
Deze website maakt gebruik van cookies om de functionaliteit en effectiviteit te verbeteren. Sommige cookies worden geplaatst door diensten van derden. Door op ‘accepteren en doorgaan’ te klikken ga je akkoord met het gebruik van cookies zoals omschreven in onze privacyverklaring. Je kunt je eigen voorkeuren aanpassen door op ‘zelf instellen’ te klikken.</p>
<details>
<summary>Wat zijn cookies?</summary>
<div>
<p>Cookies zijn kleine tekstbestanden die door websites kunnen worden gebruikt om gebruikerservaringen efficiënter te maken.</p>
<p>Volgens de wet mogen wij cookies op jouw apparaat opslaan als ze strikt noodzakelijk zijn voor het gebruik van de site. Voor alle andere soorten cookies hebben we je toestemming nodig.</p>
<p>Deze website maakt gebruik van verschillende soorten cookies. Sommige cookies worden geplaatst door diensten van derden die op onze pagina's worden weergegeven.</p>
<p>Je kunt je toestemming op elk moment wijzigen of intrekken.</p>
</div>
</details>
</div>
<div class="cc-modal__footer">
<button id="cc-reject-all" class="cc-modal__button">Weiger</button>
<button onclick="openCCModal('cc-settings')" class="cc-modal__button">Zelf instellen</button>
<button id="cc-accept-all" class="cc-modal__button cc-modal__button--active">Accepteren</button>
</div>
</div>
</div>
<div id="cc-settings" class="cc-modal">
<div role="dialog" class="cc-modal__dialog">
<div class="cc-modal__content">
<div class="cc-modal__header">
<button class="cc-modal__button-close" onclick="closeCCModal('cc-settings')" aria-label="Close">X</button>
<h2>Cookie Settings</h2>
<p>Welke cookies mogen we plaatsen?</p>
</div>
<div class="cc-modal__body">
<h3>Noodzakelijke cookies</h3>
<p>Noodzakelijke cookies helpen een website bruikbaarder te maken, door basisfuncties als paginanavigatie en toegang tot beveiligde gedeelten van de website mogelijk te maken. Zonder deze cookies kan de website niet naar behoren werken.</p>
<label><input id="consent-necessary" type="checkbox" value="cookiesNecessary" checked disabled/>Noodzakelijke cookies (altijd aan)</label>
<hr/>
<h3>Persoonlijke instellingen</h3>
<p>Voorkeurscookies zorgen ervoor dat een website informatie kan onthouden die van invloed is op het gedrag en de vormgeving van de website, zoals de taal van je voorkeur of de regio waar je woont.</p>
<label><input id="consent-preferences" type="checkbox" value="cookiesPreferences" checked />Voorkeurscookies</label>
<hr/>
<h3>Statistische cookies</h3>
<p>Statistische cookies helpen eigenaren van websites begrijpen hoe bezoekers hun website gebruiken, door anoniem gegevens te verzamelen en te rapporteren.</p>
<label><input id="consent-analytics" type="checkbox" value="cookiesAnalytics" checked />Statistiche cookies</label>
<hr/>
<h3>Marketing cookies</h3>
<p>Marketingcookies worden gebruikt om bezoekers te volgen wanneer ze verschillende websites bezoeken. Hun doel is advertenties weergeven die zijn toegesneden op en relevant zijn voor de individuele gebruiker. Deze advertenties worden zo waardevoller voor uitgevers en externe adverteerders.</p>
<label><input id="consent-marketing" type="checkbox" value="cookiesMarketing" />Marketing cookies</label>
</div>
<div class="cc-modal__footer">
<button id="cc-accept-selection" class="cc-modal__button cc-modal__button--active">Settings opslaan</button>
</div>
</div>
</div>
</div>
</div>
<style>
.cc-modal {
position: fixed;
inset: 0;
z-index: 1050;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
outline: 0;
transition: opacity .15s linear;
font-size: 16px;
line-height: 1.5555555556;
font-weight: 400;
letter-spacing: normal;
background-color:rgba(20,20,20,.75);
font-family: system-ui, sans-serif;
}
.cc-modal:not(.open) {
display: none;
opacity: 0;
}
.cc-modal.open {
overflow-x: hidden;
overflow-y: auto;
}
.cc-modal h2 {
font-size: 2em;
}
.cc-modal .cc-modal__dialog {
transition: transform .3s ease-out;
transform: translate(0,-50px);
position: relative;
margin: 1.75em auto;
inline-size: calc(100% - 1em);
pointer-events: none;
max-inline-size: 42em;
max-block-size:80vh;
}
.cc-modal.open .cc-modal__dialog {
transform: none;
}
.cc-modal__content {
position: relative;
display: flex;
flex-direction: column;
inline-size: 100%;
pointer-events: auto;
background-color: #f8f9fa;
background-clip: padding-box;
border: 1px solid rgba(0,0,0,.2);
border-radius: 0.3em;
outline: 0;
}
.cc-modal__content > * {
padding: 1em;
}
.cc-modal__content > * > * {
margin-block: unset;
}
.cc-modal__header {
border-bottom: 1px solid;
}
.cc-modal__body > * + * {
margin-top: .5em;
}
.cc-modal__footer {
display: flex;
align-items: stretch;
gap: .5em;
border-top: 1px solid;
}
.cc-modal__button {
flex: 1;
color: #1a73e8;
background-color: #ffffff;
border: 1px solid #dadce0;
border-radius: .25em;
padding: 1em;
cursor: pointer;
transition: background-color .2s,box-shadow .2s,color .2s;
}
.cc-modal__button:hover,
.cc-modal__button:focus {
background-color: #f6f9fe;
border-color: #1a73e8;
color: #174ea6;
}
.cc-modal__button--active {
color: #ffffff;
background-color: #1a73e8;
border-color: #1a73e8;
}
.cc-modal__button--active:hover,
.cc-modal__button--active:focus {
box-shadow: 0 1px 2px 0 rgba(60,64,67,.3), 0 1px 3px 1px rgba(60,64,67,.15);
background-color: #185abc;
color: #ffffff;
}
.cc-modal__button--settings {
position: fixed;
display: flex;
justify-content: center;
align-items: center;
inset-block-end: .5em;
inset-inline-start: .5em;
border: none;
background: transparent;
}
.cc-modal__button--settings svg {
inline-size: 1em;
block-size: 1em;
}
.cc-modal__button-close {
border: none;
background: transparent;
float: right;
}
.cc-modal details {
padding: .5em;
border: 1px solid #dadce0;
border-radius: .25em;
}
.cc-modal details summary {
cursor: pointer;
}
.cc-modal details summary > * {
display: inline;
}
details[open] summary {
border-bottom: 1px solid #dadce0;
margin-bottom: 0.5em;
}
.cc-modal details > div > * {
margin-bottom: unset;
}
.cc-modal details > div > * + * {
margin-top: .5em;
}
</style>
<script>
function openCCModal(id) {
document.getElementById(id).classList.add('open');
document.body.classList.add('cc-modal-open');
if (localStorage.getItem('consentMode') !== null) {
restoreFromLocalStorage();
}
}
// close currently open modal
function closeCCModal(id) {
document.getElementById(id).classList.remove('open');
document.body.classList.remove('cc-modal-open');
}
// close all open modals
function closeCCModals() {
document.querySelector('.cc-modal.open').classList.remove('open');
document.body.classList.remove('cc-modal-open');
}
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
// accept all cookies
function acceptAllCookies() {
setConsent(
{
adStorage: true,
analyticsStorage: true,
personalizationStorage: true,
functionalityStorage: true,
securityStorage: true,
adUserData: true,
adPersonalization: true
}
);
closeCCModals();
};
// accept selection
function acceptSelectedCookies() {
setConsent(
{
adStorage: document.getElementById('consent-marketing').checked,
analyticsStorage: document.getElementById('consent-analytics').checked,
personalizationStorage: document.getElementById('consent-preferences').checked,
functionalityStorage: document.getElementById('consent-preferences').checked,
securityStorage: true,
adUserData: document.getElementById('consent-marketing').checked,
adPersonalization: document.getElementById('consent-marketing').checked,
}
);
closeCCModals();
}
// accept only necessary
function acceptNoCookies() {
setConsent(
{
adStorage: false,
analyticsStorage: false,
personalizationStorage: false,
functionalityStorage: false,
securityStorage: true,
adUserData: false,
adPersonalization: false
}
);
closeCCModals();
}
// set consent
function setConsent(consent) {
var consentMode = {
'ad_storage': consent.adStorage ? 'granted' : 'denied',
'analytics_storage': consent.analyticsStorage ? 'granted' : 'denied',
'personalization_storage': consent.personalizationStorage ? 'granted' : 'denied',
'functionality_storage': consent.functionalityStorage ? 'granted' : 'denied',
'security_storage': consent.securityStorage ? 'granted' : 'denied',
'ad_user_data': consent.adUserData ? 'granted' : 'denied',
'ad_personalization': consent.adPersonalization ? 'granted' : 'denied',
};
gtag('consent', 'update', consentMode);
localStorage.setItem('consentMode', JSON.stringify(consentMode));
dataLayer.push({event: 'gtm_consent_update'});
}
// load settings
function restoreFromLocalStorage() {
var consent = JSON.parse(localStorage.getItem('consentMode'));
if (consent.adStorage === 'granted'
|| consent.adUserData === 'granted'
|| consent.adPersonalization === 'granted') {
document.getElementById('consent-marketing').setAttribute('checked', 'checked')
}
if (consent.analyticsStorage === 'granted') {
document.getElementById('consent-analytics').setAttribute('checked', 'checked')
}
if (consent.personalizationStorage === 'granted'
|| consent.functionalityStorage === 'granted') {
document.getElementById('consent-preferences').setAttribute('checked', 'checked')
}
if (consent.securityStorage === 'granted') {
document.getElementById('consent-necessary').setAttribute('checked', 'checked')
}
}
// Store the cookie preferences into local storage
if (localStorage.getItem('consentMode') === null) {
document.getElementById('cc-accept').classList.add('open');
}
document.getElementById('cc-accept-all').addEventListener('click', function (ev) {
acceptAllCookies()
});
document.getElementById('cc-accept-selection').addEventListener('click', function (ev) {
acceptSelectedCookies();
});
document.getElementById('cc-reject-all').addEventListener('click', function (ev) {
acceptNoCookies();
});
</script>
De trigger voor deze tag = _Consent Initialization - All Pages_. Dit houdt in dat deze tag op alle pagina's vrijwel als eerste ingeladen wordt. Heel mooi... want deze tag zorgt voor de cookie popup als er nog geen cookie voorkeuren ingesteld zijn of als deze gewijzigd moeten worden.
Met de consent mode actief in GTM kun je deze tag toewijzen aan _Geen aanvullende toestemming nodig_.
Trigger `gtm_consent_update` aanmaken
Op moment de voorkeuren opgeslagen zijn wordt het event `gtm_consent_update` afgevuurd. (dit staat zo geprogrammeerd in de functie setConsent in de code hierboven). Voor dit event maken we een trigger aan.
- GTM > Triggers > Nieuw
- Titel = GTM Consent Update
- Triggerconfiguratie > Type = _Aangepaste Gebeurtenis_, naam van gebeurtenis = `gtm_consent_update`
Variable `GA4 Measurement ID`
Om te voorkomen dat je waarden op meerdere plaatsen in moet vullen werk ik graag met variabelen. Zo ook voor de Measurement ID van Google Analytics. Dit ID krijg je in beeld na het aanmaken van een nieuw GA4 property en heb je nodig bij het configureren van de GTM variabele
- GTM > Variabelen > Nieuw
- Titel = GA4 Measurement ID
- Variabelenconfiguratie > Type = _Lookup Table_.
- reden voor een lookup table is dat je per hostname (live, test en acceptatie) andere GA4 Measurement ID's kunt hanteren.
- invoervariabele = `{{Page HosteName}}`
- tabel eronder vul je invoer (hostname) en uitvoer (GA4 Measurement ID) in
Tag `Google Tag`
Data van de website naar Google Analytics stuur je via de Tag _Google Tag_.
- GTM > Tags > Nieuw
- Titel = Google Tag
- Tagconfiguratie > Type = _Google Tag_
- Tag ID = `{{GA4 Measurement ID}}`
- Triggers = `GTM Consent Update` => omdat het pas mag worden uitgevoerd zodra er consent gegeven is.
Testen en verzenden
Zoals ook beschreven in _stap 2_, eerst testen en daarna publiceren. Geef bij het publiceren een duidelijk korte beschrijving wat er voor wijzigingen in deze versie zitten.
Tadaa!! GTM is bij deze gebruikt om GA4 aan je website te koppelen. Maar niet alleen een. koppeling naar Google Analytics is mogelijk. Ook Matamo, floodlink, Google Ads en meer. Zelfs eigen html, css en scripts, zoals hierboven al beschreven is.
TinyMCE uitbreiden
Bij het toevoegen van een link een className toevoegen... dat zou mooi zijn. De editor JCE heeft dit en meer in zich, maar wat als je zo veel mogelijk bij de core van Joomla wil blijven en niet zit te wachten op alle andere mogelijkheden van JCE? Dan ben je aangewezen op TinyMCY als WYSIWYG editor en die heeft die optie standaard niet in zicht zitten.
Nicholas van Akeeba heeft een plugin geschreven die het mogelijk maakt uitbreidingen te maken op TinyMCE. Deze plugin is te downloaden via GitHub: https://github.com/nikosdion/plg_system_tinymod
Na installatie van deze plugin is deze terug te vinden met de naam "System - TinyMCE Configuration Modfier". Activeer deze plugin en configureer het text veld.
Voorbeeld voor het toevoegen van button classNames
{
"style_formats_merge": false,
"link_class_list": [
{
"title": "None",
"value": ""
},
{
"title": "Primary buttons",
"menu": [
{
"title": "btn btn-primary",
"value": "btn btn-primary"
},
{
"title": "btn btn-sm btn-primary",
"value": "btn btn-sm btn-primary"
},
{
"title": "btn btn-lg btn-primary",
"value": "btn btn-lg btn-primary"
},
{
"title": "btn btn-outline-primary",
"value": "btn btn-outline-primary"
},
{
"title": "btn btn-sm btn-outline-primary",
"value": "btn btn-sm btn-outline-primary"
},
{
"title": "btn btn-lg btn-outline-primary",
"value": "btn btn-lg btn-outline-primary"
}
]
},
{
"title": "Secondary buttons",
"menu": [
{
"title": "btn btn-secondary",
"value": "btn btn-secondary"
},
{
"title": "btn btn-sm btn-secondary",
"value": "btn btn-sm btn-secondary"
},
{
"title": "btn btn-lg btn-secondary",
"value": "btn btn-lg btn-secondary"
},
{
"title": "btn btn-outline-secondary",
"value": "btn btn-outline-secondary"
},
{
"title": "btn btn-sm btn-outline-secondary",
"value": "btn btn-sm btn-outline-secondary"
},
{
"title": "btn btn-lg btn-outline-secondary",
"value": "btn btn-lg btn-outline-secondary"
}
]
},
{
"title": "Info buttons",
"menu": [
{
"title": "btn btn-info",
"value": "btn btn-info"
},
{
"title": "btn btn-sm btn-info",
"value": "btn btn-sm btn-info"
},
{
"title": "btn btn-lg btn-info",
"value": "btn btn-lg btn-info"
},
{
"title": "btn btn-outline-info",
"value": "btn btn-outline-info"
},
{
"title": "btn btn-sm btn-outline-info",
"value": "btn btn-sm btn-outline-info"
},
{
"title": "btn btn-lg btn-outline-info",
"value": "btn btn-lg btn-outline-info"
}
]
},
{
"title": "Success buttons",
"menu": [
{
"title": "btn btn-success",
"value": "btn btn-success"
},
{
"title": "btn btn-sm btn-success",
"value": "btn btn-sm btn-success"
},
{
"title": "btn btn-lg btn-success",
"value": "btn btn-lg btn-success"
},
{
"title": "btn btn-outline-success",
"value": "btn btn-outline-success"
},
{
"title": "btn btn-sm btn-outline-success",
"value": "btn btn-sm btn-outline-success"
},
{
"title": "btn btn-lg btn-outline-success",
"value": "btn btn-lg btn-outline-success"
}
]
},
{
"title": "Warning buttons",
"menu": [
{
"title": "btn btn-warning",
"value": "btn btn-warning"
},
{
"title": "btn btn-sm btn-warning",
"value": "btn btn-sm btn-warning"
},
{
"title": "btn btn-lg btn-warning",
"value": "btn btn-lg btn-warning"
},
{
"title": "btn btn-outline-warning",
"value": "btn btn-outline-warning"
},
{
"title": "btn btn-sm btn-outline-warning",
"value": "btn btn-sm btn-outline-warning"
},
{
"title": "btn btn-lg btn-outline-warning",
"value": "btn btn-lg btn-outline-warning"
}
]
},
{
"title": "Danger buttons",
"menu": [
{
"title": "btn btn-danger",
"value": "btn btn-danger"
},
{
"title": "btn btn-sm btn-danger",
"value": "btn btn-sm btn-danger"
},
{
"title": "btn btn-lg btn-danger",
"value": "btn btn-lg btn-danger"
},
{
"title": "btn btn-outline-danger",
"value": "btn btn-outline-danger"
},
{
"title": "btn btn-sm btn-outline-danger",
"value": "btn btn-sm btn-outline-danger"
},
{
"title": "btn btn-lg btn-outline-danger",
"value": "btn btn-lg btn-outline-danger"
}
]
},
{
"title": "Light buttons",
"menu": [
{
"title": "btn btn-light",
"value": "btn btn-light"
},
{
"title": "btn btn-sm btn-light",
"value": "btn btn-sm btn-light"
},
{
"title": "btn btn-lg btn-light",
"value": "btn btn-lg btn-light"
},
{
"title": "btn btn-outline-light",
"value": "btn btn-outline-light"
},
{
"title": "btn btn-sm btn-outline-light",
"value": "btn btn-sm btn-outline-light"
},
{
"title": "btn btn-lg btn-outline-light",
"value": "btn btn-lg btn-outline-light"
}
]
},
{
"title": "Dark buttons",
"menu": [
{
"title": "btn btn-dark",
"value": "btn btn-dark"
},
{
"title": "btn btn-sm btn-dark",
"value": "btn btn-sm btn-dark"
},
{
"title": "btn btn-lg btn-dark",
"value": "btn btn-lg btn-dark"
},
{
"title": "btn btn-outline-dark",
"value": "btn btn-outline-dark"
},
{
"title": "btn btn-sm btn-outline-dark",
"value": "btn btn-sm btn-outline-dark"
},
{
"title": "btn btn-lg btn-outline-dark",
"value": "btn btn-lg btn-outline-dark"
}
]
},
{
"title": "Link buttons",
"menu": [
{
"title": "btn btn-link",
"value": "btn btn-link"
},
{
"title": "btn btn-sm btn-link",
"value": "btn btn-sm btn-link"
},
{
"title": "btn btn-lg btn-link",
"value": "btn btn-lg btn-link"
},
{
"title": "btn btn-outline-link",
"value": "btn btn-outline-link"
},
{
"title": "btn btn-sm btn-outline-link",
"value": "btn btn-sm btn-outline-link"
},
{
"title": "btn btn-lg btn-outline-link",
"value": "btn btn-lg btn-outline-link"
}
]
}
],
"link_list": [
{
"title": "Tiny Blog",
"value": "https:\/\/www.tiny.cloud\/blog"
}
],
"block_formats": "Div=div; Paragraph=p; Heading 1=h1; Heading 2=h2; Heading 3=h3; Heading 4=h4; Heading 5=h5; Heading 6=h6; Preformatted=pre; Code=code",
"noneditable_class": "nonedit",
"style_formats": [
{
"title": "Red header",
"block": "h1",
"styles": {
"color": "#ff0000"
}
},
{
"title": "Houders",
"items": [
{
"title": "Locked element",
"block": "div",
"wrapper": "true",
"merge_siblings": "false",
"classes": "nonedit"
},
{
"title": "article",
"block": "article",
"wrapper": "true",
"merge_siblings": "false"
},
{
"title": "aside",
"block": "aside",
"wrapper": "true",
"merge_siblings": "false"
},
{
"title": "section",
"block": "section",
"wrapper": "true",
"merge_siblings": "false"
}
]
}
]
}
Vanaf nu zal het modal popup bij het plaatsen van een link ook het veld Class bevatten.
Volgende keer
De volgende JUG Eindhoven is op de dinsdag 14 mei 2024. Het onderwerp van deze meeting en de mogelijkheid tot aanmelden is terug te vinden op https://joomlacommunity.nl/agenda/event/759-jug040-bijeenkomst-eindhoven
Vragen voor Dr. Joomla kun je kwijt als reactie onder de aankondiging.