Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions administrator/language/en-GB/plg_system_schemaorg.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ PLG_SYSTEM_SCHEMAORG_BASETYPE_DESCRIPTION="Choose whether your website represent
PLG_SYSTEM_SCHEMAORG_BASETYPE_LABEL="Base Type"
PLG_SYSTEM_SCHEMAORG_BASETYPE_OPTION_ORGANIZATION="Organization"
PLG_SYSTEM_SCHEMAORG_BASETYPE_OPTION_PERSON="Person"
PLG_SYSTEM_SCHEMAORG_CONTACT_LABEL="Select Contact"
PLG_SYSTEM_SCHEMAORG_FIELD_SCHEMA_DESCRIPTION="Structured data is a standardised format for organising and representing information on the web. It provides a way to describe the content and meaning of data in a structured manner, making it easier for search engines and other applications to understand and process the information. <a href=\"https://schema.org\" target=\"_blank\" rel=\"noopener noreferrer\">More information on schema.org</a>."
PLG_SYSTEM_SCHEMAORG_FIELD_SCHEMA_DESCRIPTION_NOT_CONFIGURED="To use the schema.org functionality, you have to configure the plugin first. Please contact an administrator of the page to get it configured."
PLG_SYSTEM_SCHEMAORG_FIELD_SCHEMA_DESCRIPTION_NOT_CONFIGURED_ADMIN="To use the schema.org functionality, you have to configure the plugin first. Please select <a href=\"index.php?option=com_plugins&amp;task=plugin.edit&amp;extension_id=%s\" target=\"_blank\">this link to open the plugin</a>, configure and save."
PLG_SYSTEM_SCHEMAORG_FIELD_SCHEMA_EXTEND_JED_DESC="<strong>Need more Schema types?</strong> Extend with <a href=\"https://extensions.joomla.org/category/core-enhancements/schema-plugins\" target=\"_blank\" rel=\"noopener noreferrer\">Schema Plugins from Joomla! Extension Directory</a>."
PLG_SYSTEM_SCHEMAORG_FIELD_SCHEMA_LABEL="Schema"
PLG_SYSTEM_SCHEMAORG_FIELD_SCHEMA_TYPE_LABEL="Schema Type"
PLG_SYSTEM_SCHEMAORG_IMAGE_LABEL="Image"
PLG_SYSTEM_SCHEMAORG_INHERIT_CONTACT="Inherited from contact"
PLG_SYSTEM_SCHEMAORG_INHERIT_DEFAULT_CONTACT="Inherited from default contact"
PLG_SYSTEM_SCHEMAORG_NAME_LABEL="Name"
PLG_SYSTEM_SCHEMAORG_SOCIALMEDIA_LABEL="Social Media Accounts"
PLG_SYSTEM_SCHEMAORG_SOCIALMEDIA_URL_LABEL="URL"
Expand Down
20 changes: 20 additions & 0 deletions build/media_source/plg_system_schemaorg/joomla.asset.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
"name": "plg_system_schemaorg",
"version": "__DEPLOY_VERSION__",
"description": "Joomla CMS",
"license": "GPL-2.0-or-later",
"assets": [
{
"name": "plg_system_schemaorg.contact",
"type": "script",
"uri": "plg_system_schemaorg/schemaorg-contact.js",
"dependencies": [
"core"
],
"attributes": {
"type": "module"
}
}
]
}
195 changes: 195 additions & 0 deletions build/media_source/plg_system_schemaorg/js/schemaorg-contact.es6.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/**
* @copyright (C) 2025 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
/**
* schemaorg-contact.js
* Populate placeholders from injected initialContact by baseName.
*/
((Joomla, document) => {
"use strict";

class SchemaOrgContactHandler {
constructor(options) {
this.options = Object.assign(
{
fieldMappings: {
name: "name",
email: "email_to",
url: "webpage",
streetAddress: ["address", "street"],
addressLocality: ["suburb", "state", "address"],
postalCode: "postcode",
addressRegion: "state",
addressCountry: "country",
telephone: "telephone",
mobile: "mobile",
fax: "fax",
misc: "misc",
position: "position",
},
},
options
);

// nested fields that must go under [address]
this.nestedFields = {
streetAddress: "address",
postalCode: "address",
addressLocality: "address",
addressRegion: "address",
addressCountry: "address",
};

// read injected contact from Joomla options or global
const opt =
(Joomla &&
Joomla.getOptions &&
Joomla.getOptions("plg_system_schemaorg")) ||
window.plg_system_schemaorg ||
{};
this.initialContact = opt.initialContact || null;

this._initOnce = false;
this.init();
}

init() {
if (this._initOnce) return;
this._initOnce = true;

if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => this.bindEvents());
} else {
this.bindEvents();
}
}

bindEvents() {
// Scope to the current form
const form = document.querySelector("form");
if (!form) return;

// Contact title inputs in the form
const contactNameFields = form.querySelectorAll(
"input.js-input-title[name*='[contact]']"
);
let isPopulated = false;
contactNameFields.forEach((field) => {
if (field.value && field.value.trim() && this.initialContact) {
this.populatePlaceholders(field, this.initialContact);
isPopulated = true;
}
});
if (!isPopulated) {
contactNameFields.forEach((field) => {
if (this.initialContact) {
this.populatePlaceholders(field, this.initialContact);
isPopulated = true;
}
});
}
}

populatePlaceholders(contactField, contactData) {
if (!contactField || !contactData) return;

// Normalize simple contact object and trim strings
const contact = Object.assign({}, contactData);
let isDefaultContact = false;
Object.keys(contact).forEach((k) => {
if (typeof contact[k] === "string") contact[k] = contact[k].trim();
if (k === "isDefaultContact") {
isDefaultContact = Boolean(contact[k]);
}
});

// find the role container (use nearby grouping)
const roleContainer = contactField.closest(
".subform-repeatable-group, fieldset, form"
);
if (!roleContainer) return;

// Parse baseName from contact field name
const contactName = contactField.name || "";
const baseMatch = contactName.match(/^(.*)\[contact\]$/);
if (!baseMatch) {
console.debug("schemaorg: contact baseName not found for", contactName);
return;
}
const baseName = baseMatch[1]; // e.g. jform[schema][Book][illustrator]

// For each mapping, compute target name and set placeholder
Object.entries(this.options.fieldMappings).forEach(
([schemaField, contactKey]) => {
let value = "";

if (Array.isArray(contactKey)) {
for (const k of contactKey) {
if (contact[k]) {
value = contact[k];
break;
}
}
} else {
value = contact[contactKey] || "";
}

if (!value) return;
const parentKey = this.nestedFields[schemaField] || null;
const targetName = parentKey
? `${baseName}[${parentKey}][${schemaField}]`
: `${baseName}[${schemaField}]`;

// find the input within the same roleContainer
const selector = `input[name='${targetName}'], textarea[name='${targetName}'], select[name='${targetName}']`;
const targetField =
roleContainer.querySelector(selector) ||
document.querySelector(selector);
let newValue = value;
const defaultContactText = Joomla.Text._(
"PLG_SYSTEM_SCHEMAORG_INHERIT_DEFAULT_CONTACT"
);

const contactText = Joomla.Text._(
"PLG_SYSTEM_SCHEMAORG_INHERIT_CONTACT"
);

if (isDefaultContact) {
newValue += ` — ${defaultContactText} "${contact.name}"`;
} else {
newValue += ` — ${contactText} "${contact.name}"`;
}
if (targetField) {
targetField.placeholder = newValue;
targetField.classList.add("has-contact-placeholder");
} else {
// debug if not found
console.debug(
"schemaorg: could not find target field for",
targetName
);
}
}
);

// emit event
document.dispatchEvent(
new CustomEvent("schemaorg:contact:loaded", {
detail: {
contactId: contact.id,
contactData: contact,
container: roleContainer,
},
})
);
}
}

// Initialize once
if (typeof Joomla !== "undefined") {
if (!Joomla.SchemaOrgContactHandler) {
Joomla.SchemaOrgContactHandler = new SchemaOrgContactHandler();
}
}
})(window.Joomla, document);
12 changes: 11 additions & 1 deletion plugins/system/schemaorg/schemaorg.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<fieldset name="basic" addfieldprefix="Joomla\Component\Contact\Administrator\Field">
<field
name="baseType"
type="list"
Expand All @@ -42,6 +42,16 @@
default="0"
/>

<field
name="defaultContact"
type="modal_contact"
label="PLG_SYSTEM_SCHEMAORG_CONTACT_LABEL"
select="true"
new="true"
edit="true"
clear="true"
/>

<field
name="name"
type="text"
Expand Down
Loading