Skip to content

Commit bca83a9

Browse files
committed
Merge branch '5.4-dev' into 6.0-dev-upmerge-2025-09-26
2 parents 47243a2 + 81a8284 commit bca83a9

File tree

11 files changed

+111
-60
lines changed

11 files changed

+111
-60
lines changed

.github/workflows/create-translation-pull-request-v5.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ jobs:
2626
if: ${{ github.repository == 'joomla-translation-bot/joomla-cms' && github.ref == 'refs/heads/translation5' }}
2727

2828
steps:
29-
- uses: actions/checkout@v3
29+
- uses: actions/checkout@v5
3030
# We need the full depth to create / update the pull request against the main repo
3131
with:
3232
ref: translation5
3333
fetch-depth: 0
34-
- uses: actions/setup-node@v3
34+
- uses: actions/setup-node@v5
3535
with:
36-
node-version: 20
36+
node-version: 24
3737

3838
- name: Fetch latest cms changes
3939
run: |

.github/workflows/create-translation-pull-request-v6.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ jobs:
2626
if: ${{ github.repository == 'joomla-translation-bot/joomla-cms' && github.ref == 'refs/heads/translation6' }}
2727

2828
steps:
29-
- uses: actions/checkout@v3
29+
- uses: actions/checkout@v5
3030
# We need the full depth to create / update the pull request against the main repo
3131
with:
3232
ref: translation6
3333
fetch-depth: 0
34-
- uses: actions/setup-node@v3
34+
- uses: actions/setup-node@v5
3535
with:
36-
node-version: 20
36+
node-version: 24
3737

3838
- name: Fetch latest cms changes
3939
run: |

build/build-modules-js/compress.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ async function getFiles(path) {
1313
// Get files within the current directory
1414
return (await readdir(path, { withFileTypes: true, recursive: true }))
1515
.filter((file) => !file.isDirectory() && ['.js', '.css'].includes(extname(file.name)))
16-
.map((file) => `${file.path}/${file.name}`);
16+
.map((file) => `${file.parentPath}/${file.name}`);
1717
}
1818

1919
/**

build/build-modules-js/css-versioning.mjs renamed to build/build-modules-js/stylesheets/css-versioning.mjs

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { readdir, readFile, writeFile } from 'node:fs/promises';
33
import { existsSync, readFileSync } from 'node:fs';
44
import { dirname, extname, resolve } from 'node:path';
55
import { transform, composeVisitors } from 'lightningcss';
6-
import { Timer } from './utils/timer.mjs';
6+
import { Timer } from '../utils/timer.mjs';
77

88
const RootPath = process.cwd();
99
const skipExternal = true;
@@ -37,17 +37,13 @@ function version(urlString, fromFile) {
3737
* @param {from: String} - the filepath for the css file
3838
* @returns {import('lightningcss').Visitor} - A visitor that replaces the url
3939
*/
40-
function urlVersioning(fromFile) {
41-
return {
42-
/**
43-
* @param {import('lightningcss').Url} url - The url object to transform
44-
* @returns {import('lightningcss').Url} - The transformed url object
45-
*/
46-
Url(url) {
47-
return { ...url, ...{ url: version(url.url, fromFile) } };
48-
},
49-
};
50-
}
40+
const urlVersioning = (fromFile) => ({
41+
/**
42+
* @param {import('lightningcss').Url} url - The url object to transform
43+
* @returns {import('lightningcss').Url} - The transformed url object
44+
*/
45+
Url: (url) => ({ ...url, url: version(url.url, fromFile) }),
46+
});
5147

5248
/**
5349
* Adds a hash to the url() parts of the static css
@@ -57,8 +53,25 @@ function urlVersioning(fromFile) {
5753
*/
5854
const fixVersion = async (file) => {
5955
try {
56+
let content = await readFile(file, { encoding: 'utf8' });
57+
// To preserve the licence the comment needs to start at the beginning of the file
58+
const replaceUTF8String = file.endsWith('.min.css') ? '@charset "UTF-8";' : '@charset "UTF-8";\n';
59+
content = content.startsWith(replaceUTF8String) ? content.replace(replaceUTF8String, '') : content;
60+
61+
// Preserve a leading license comment (/** ... */)
62+
const firstLine = content.split(/\r?\n/)[0] || '';
63+
if (firstLine.includes('/*') && !firstLine.includes('/*!')) {
64+
const endCommentIdx = content.indexOf('*/');
65+
if (endCommentIdx !== -1
66+
&& (content.substring(0, endCommentIdx).includes('license')
67+
|| content.substring(0, endCommentIdx).includes('copyright'))
68+
) {
69+
content = firstLine.includes('/**') ? content.replace('/**', '/*!') : content.replace('/*', '/*!');
70+
}
71+
}
72+
6073
const { code } = transform({
61-
code: await readFile(file),
74+
code: Buffer.from(content),
6275
minify: file.endsWith('.min.css'),
6376
visitor: composeVisitors([urlVersioning(file)]),
6477
});
@@ -76,13 +89,15 @@ const fixVersion = async (file) => {
7689
*
7790
* @returns {Promise<void>}
7891
*/
79-
export const cssVersioning = async () => {
92+
const cssVersioningVendor = async () => {
8093
const bench = new Timer('Versioning');
8194

82-
const cssFiles = (await readdir(`${RootPath}/media`, { withFileTypes: true, recursive: true }))
95+
const cssFiles = (await readdir(`${RootPath}/media/vendor`, { withFileTypes: true, recursive: true }))
8396
.filter((file) => (!file.isDirectory() && extname(file.name) === '.css'))
84-
.map((file) => `${file.path}/${file.name}`);
97+
.map((file) => `${file.parentPath}/${file.name}`);
8598

8699
Promise.all(cssFiles.map((file) => fixVersion(file)))
87100
.then(() => bench.stop());
88101
};
102+
103+
export { urlVersioning, cssVersioningVendor };

build/build-modules-js/stylesheets/handle-css.mjs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,52 @@
11
import { dirname, sep } from 'node:path';
22

33
import pkg from 'fs-extra';
4-
import { transform as transformCss } from 'lightningcss';
4+
import { transform as transformCss, composeVisitors } from 'lightningcss';
5+
import { urlVersioning } from './css-versioning.mjs';
56

67
const {
7-
copy, readFile, writeFile, ensureDir,
8+
readFile, writeFile, ensureDir,
89
} = pkg;
910

1011
export const handleCssFile = async (file) => {
1112
const outputFile = file.replace(`${sep}build${sep}media_source${sep}`, `${sep}media${sep}`);
1213
try {
13-
// CSS file, we will copy the file and then minify it in place
14+
// CSS file, we will process the file and then minify it in place
1415
// Ensure that the directories exist or create them
1516
await ensureDir(dirname(outputFile), { recursive: true, mode: 0o755 });
1617

18+
let content = await readFile(file, { encoding: 'utf8' });
19+
20+
// To preserve the licence the comment needs to start at the beginning of the file
21+
content = content.startsWith('@charset "UTF-8";\n') ? content.replace('@charset "UTF-8";\n', '') : content;
22+
1723
if (file !== outputFile) {
18-
await copy(file, outputFile, { preserveTimestamps: true, overwrite: true });
24+
const { code: css } = transformCss({
25+
code: Buffer.from(content),
26+
minify: false,
27+
visitor: composeVisitors([urlVersioning(file)]), // Adds a hash to the url() parts of the static css
28+
});
29+
30+
// Save optimized css file
31+
await writeFile(
32+
outputFile,
33+
content.startsWith('@charset "UTF-8";')
34+
? css
35+
: `@charset "UTF-8";
36+
${css}`,
37+
{ encoding: 'utf8', mode: 0o644 },
38+
);
1939
}
2040

21-
const content = await readFile(file, { encoding: 'utf8' });
22-
const { code } = transformCss({
41+
// Process the file and minify it in place
42+
const { code: cssMin } = transformCss({
2343
code: Buffer.from(content),
2444
minify: true,
45+
visitor: composeVisitors([urlVersioning(outputFile)]), // Adds a hash to the url() parts of the static css
2546
});
2647

27-
// Ensure the folder exists or create it
28-
await writeFile(outputFile.replace('.css', '.min.css'), `@charset "UTF-8";${code}`, { encoding: 'utf8', mode: 0o644 });
48+
// Save minified css file
49+
await writeFile(outputFile.replace('.css', '.min.css'), `@charset "UTF-8";${cssMin}`, { encoding: 'utf8', mode: 0o644 });
2950

3051
console.log(`✅ CSS file copied/minified: ${file}`);
3152
} catch (err) {

build/build-modules-js/stylesheets/handle-scss.mjs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import { dirname, sep } from 'node:path';
33

44
import rtlcss from 'rtlcss';
55
import { ensureDir } from 'fs-extra';
6-
import { transform as transformCss, Features } from 'lightningcss';
6+
import { transform as transformCss, Features, composeVisitors } from 'lightningcss';
77
import { compileAsync } from 'sass-embedded';
8+
import { urlVersioning } from './css-versioning.mjs';
89

910
const getOutputFile = (file) => file.replace(`${sep}scss${sep}`, `${sep}css${sep}`).replace('.scss', '.css').replace(`${sep}build${sep}media_source${sep}`, `${sep}media${sep}`);
1011

@@ -26,24 +27,43 @@ export const handleScssFile = async (file) => {
2627
contents = rtlcss.process(contents);
2728
}
2829

30+
// To preserve the licence the comment needs to start at the beginning of the file
31+
contents = contents.startsWith('@charset "UTF-8";\n') ? contents.replace('@charset "UTF-8";\n', '') : contents;
32+
2933
// Ensure the folder exists or create it
3034
await ensureDir(dirname(cssFile), {});
35+
36+
const { code: css } = transformCss({
37+
code: Buffer.from(contents),
38+
minify: false,
39+
exclude: Features.VendorPrefixes,
40+
visitor: composeVisitors([urlVersioning(file)]), // Adds a hash to the url() parts of the static css
41+
});
42+
43+
// Save optimized css file
3144
await writeFile(
3245
cssFile,
33-
`@charset "UTF-8";
34-
${contents}`,
46+
contents.startsWith('@charset "UTF-8";')
47+
? css
48+
: `@charset "UTF-8";
49+
${css}`,
3550
{ encoding: 'utf8', mode: 0o644 },
3651
);
3752

3853
const { code: cssMin } = transformCss({
3954
code: Buffer.from(contents),
4055
minify: true,
4156
exclude: Features.VendorPrefixes,
57+
visitor: composeVisitors([urlVersioning(cssFile)]), // Adds a hash to the url() parts of the static css
4258
});
4359

4460
// Ensure the folder exists or create it
4561
await ensureDir(dirname(cssFile.replace('.css', '.min.css')), {});
46-
await writeFile(cssFile.replace('.css', '.min.css'), `@charset "UTF-8";${cssMin}`, { encoding: 'utf8', mode: 0o644 });
62+
await writeFile(
63+
cssFile.replace('.css', '.min.css'),
64+
`@charset "UTF-8";${cssMin}`,
65+
{ encoding: 'utf8', mode: 0o644 },
66+
);
4767

4868
console.log(`✅ SCSS File compiled: ${cssFile}`);
4969
};

build/build.mjs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
* node build.mjs --com-media will compile the media manager Vue application
1414
* node build.mjs --watch-com-media will watch and compile the media manager Vue application
1515
* node build.mjs --gzip will create gzip files for all the minified stylesheets and scripts.
16-
* node build.mjs --cssversioning will update all the url entries providing accurate versions for stylesheets.
1716
* node build.mjs --versioning will update all the joomla.assets.json files providing accurate versions for stylesheets and scripts.
1817
*/
1918

@@ -34,10 +33,10 @@ import { recreateMediaFolder } from './build-modules-js/init/recreate-media.mjs'
3433
import { watching } from './build-modules-js/watch.mjs';
3534
import { mediaManager, watchMediaManager } from './build-modules-js/javascript/build-com_media-js.mjs';
3635
import { compressFiles } from './build-modules-js/compress.mjs';
37-
import { cssVersioning } from './build-modules-js/css-versioning.mjs';
3836
import { versioning } from './build-modules-js/versioning.mjs';
3937
import { Timer } from './build-modules-js/utils/timer.mjs';
4038
import { compileCodemirror } from './build-modules-js/javascript/build-codemirror.mjs';
39+
import { cssVersioningVendor } from './build-modules-js/stylesheets/css-versioning.mjs';
4140

4241
const require = createRequire(import.meta.url);
4342

@@ -103,10 +102,6 @@ Program.allowUnknownOption()
103102
)
104103
.option('--gzip', 'Compress all the minified stylesheets and scripts.')
105104
.option('--prepare', 'Run all the needed tasks to initialise the repo')
106-
.option(
107-
'--cssversioning',
108-
'Update all the url() versions on their relative stylesheet files',
109-
)
110105
.option(
111106
'--versioning',
112107
'Update all the .js/.css versions on their relative joomla.assets.json',
@@ -130,6 +125,7 @@ if (cliOptions.copyAssets) {
130125
.then(() => cleanVendors())
131126
.then(() => localisePackages(options))
132127
.then(() => patchPackages(options))
128+
.then(() => cssVersioningVendor())
133129
.then(() => minifyVendor())
134130
.catch((error) => handleError(error, 1));
135131
}
@@ -185,11 +181,6 @@ if (cliOptions.versioning) {
185181
versioning().catch((err) => handleError(err, 1));
186182
}
187183

188-
// Update the url() versions in the .css files
189-
if (cliOptions.cssversioning) {
190-
cssVersioning().catch((err) => handleError(err, 1));
191-
}
192-
193184
// Prepare the repo for dev work
194185
if (cliOptions.prepare) {
195186
const bench = new Timer('Build');
@@ -201,6 +192,7 @@ if (cliOptions.prepare) {
201192
.then(() => minifyVendor())
202193
.then(() => createErrorPages(options))
203194
.then(() => stylesheets(options, Program.args[0]))
195+
.then(() => cssVersioningVendor())
204196
.then(() => scripts(options, Program.args[0]))
205197
.then(() => mediaManager())
206198
.then(() => bootstrapJs())

build/build.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,10 +342,9 @@ function capture_or_fail(string $command): string
342342
exit(1);
343343
}
344344

345-
run_and_check('npm install --unsafe-perm');
346-
345+
// Install dependencies and build the media assets
347346
// Create version entries of the urls inside the static css files
348-
run_and_check('npm run cssversioning');
347+
run_and_check('npm ci');
349348

350349
// Create gzipped version of the static assets
351350
run_and_check('npm run gzip');

installation/language/be-BY/joomla.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ INSTL_LANGUAGES_WARNING_BACK_BUTTON="Назад"
142142

143143
; Automated Updates Opt Out
144144

145+
; Automated Updates Opt Out
146+
145147
; Default language view
146148
INSTL_DEFAULTLANGUAGE_ADMINISTRATOR="Мова панэлі кіравання"
147149
INSTL_DEFAULTLANGUAGE_ADMIN_COULDNT_SET_DEFAULT="Не ўдалося задаць мову як стандартную. Англійская мова будзе заданая ў якасці стандартнай для панэлі кіравання."

installation/language/en-GB/joomla.ini

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ INSTL_INSTALL_JOOMLA="Install Joomla"
105105
INSTL_ADMIN_EMAIL_DESC="Enter the email address of the website Super User."
106106
INSTL_ADMIN_PASSWORD_DESC="Set the password for your Super User account."
107107
INSTL_ADMIN_PASSWORD_LENGTH="Enter at least 12 characters."
108-
INSTL_ADMIN_USER_DESC="Enter the real name of your Super User."
109108
INSTL_ADMIN_USERNAME_DESC="Set the username for your Super User account."
109+
INSTL_ADMIN_USER_DESC="Enter the real name of your Super User."
110110
INSTL_LOGIN_DATA="Login Data"
111111
INSTL_SETUP_SITE_NAME="Setup Site Name"
112112
INSTL_SITE="Main Configuration"
@@ -118,15 +118,15 @@ INSTL_PUBLIC_FOLDER_DESC="Relative or absolute path to the public folder"
118118
INSTL_PUBLIC_FOLDER_DESC_SHORT="Relative or absolute path to the public folder"
119119

120120
; Complete view
121-
INSTL_COMPLETE_ERROR_FOLDER_DELETE="The \"%s\" folder could not be deleted. Please manually delete the folder."
122-
INSTL_COMPLETE_REMOVE_FOLDER="Remove \"%s\" folder"
123-
INSTL_COMPLETE_CONGRAT="Congratulations!"
124-
INSTL_COMPLETE_TITLE="Congratulations! Your Joomla site is ready."
125-
INSTL_COMPLETE_SITE_BTN="Open Site"
121+
INSTL_COMPLETE_ADD_EXTRA_LANGUAGE="Install Additional Languages"
126122
INSTL_COMPLETE_ADMIN_BTN="Open Administrator"
123+
INSTL_COMPLETE_CONGRAT="Congratulations!"
124+
INSTL_COMPLETE_ERROR_FOLDER_DELETE="The \"%s\" folder could not be deleted. Please manually delete the folder."
127125
INSTL_COMPLETE_FINAL="Installation is Complete"
128126
INSTL_COMPLETE_FINAL_DESC="Your Joomla installation is now complete and ready to use."
129-
INSTL_COMPLETE_ADD_EXTRA_LANGUAGE="Install Additional Languages"
127+
INSTL_COMPLETE_REMOVE_FOLDER="Remove \"%s\" folder"
128+
INSTL_COMPLETE_SITE_BTN="Open Site"
129+
INSTL_COMPLETE_TITLE="Congratulations! Your Joomla site is ready."
130130
INSTL_REMOVE_INST_FOLDER="Are you sure you want to delete? Confirming will permanently delete the \"%s\" folder."
131131

132132
; Languages view
@@ -139,23 +139,23 @@ INSTL_LANGUAGES_DESC="The Joomla interface is available in several languages. Ch
139139
INSTL_LANGUAGES_MESSAGE_PLEASE_WAIT="This operation will take up to 10 seconds per language to complete<br>Please wait while we download and install the languages &hellip;"
140140
INSTL_LANGUAGES_NO_LANGUAGE_SELECTED="No languages have been selected to be installed."
141141
INSTL_LANGUAGES_SELECTED="Install Selected Languages"
142+
INSTL_LANGUAGES_WARNING_BACK_BUTTON="Return to last installation step"
142143
INSTL_LANGUAGES_WARNING_NO_INTERNET="Joomla was unable to connect to the languages server. Please finish the installation process."
143144
INSTL_LANGUAGES_WARNING_NO_INTERNET2="Note: You will be able to install languages later using the Joomla Administrator."
144-
INSTL_LANGUAGES_WARNING_BACK_BUTTON="Return to last installation step"
145145

146146
; Automated Updates Opt Out
147147
INSTL_AUTOMATED_UPDATES="Automated Updates"
148148
INSTL_AUTOMATED_UPDATES_DESC="Joomla automatically updates itself for minor and patch releases.<br><br><strong>Please note:</strong> Automated Updates will register your site at the respective service provided by the Joomla! project. The registration allows the Joomla project to access information about your site and server environment, specifically Site Url, PHP version, Database type and version, CMS version and Server OS type and version. This information is only used to improve the service.<br><br>Alternatively you can disable automated updates using the button below."
149149
INSTL_AUTOMATED_UPDATES_DISABLE="Disable Automated Updates"
150-
INSTL_DISABLE_AUTOUPDATE="Are you sure you want to disable automated updates?"
151150
INSTL_COMPLETE_ERROR_AUTOMATED_UPDATES_DISABLE="Could not disable automated updates."
151+
INSTL_DISABLE_AUTOUPDATE="Are you sure you want to disable automated updates?"
152152

153153
; Default language view
154154
INSTL_DEFAULTLANGUAGE_ADMINISTRATOR="Default Administrator language"
155155
INSTL_DEFAULTLANGUAGE_ADMIN_COULDNT_SET_DEFAULT="Joomla was unable to set the language as default. English will be used as the default language for the Backend Administrator."
156156
INSTL_DEFAULTLANGUAGE_ADMIN_SET_DEFAULT="Joomla has set %s as your default ADMINISTRATOR language."
157-
INSTL_DEFAULTLANGUAGE_COLUMN_HEADER_SELECT="Select"
158157
INSTL_DEFAULTLANGUAGE_COLUMN_HEADER_LANGUAGE="Language"
158+
INSTL_DEFAULTLANGUAGE_COLUMN_HEADER_SELECT="Select"
159159
INSTL_DEFAULTLANGUAGE_COLUMN_HEADER_TAG="Tag"
160160
INSTL_DEFAULTLANGUAGE_COULD_NOT_DOWNLOAD_PACKAGE="Joomla failed to download or unpack the language pack from: %s"
161161
INSTL_DEFAULTLANGUAGE_COULD_NOT_INSTALL_LANGUAGE="Joomla was unable to install the %s language."
@@ -184,8 +184,8 @@ INSTL_DATABASE_FIX_LOWERCASE="The table prefix must be lowercase for PostgreSQL.
184184
INSTL_DATABASE_FIX_TOO_LONG="The MySQL table prefix must be a maximum of 15 characters."
185185
INSTL_DATABASE_INVALID_DB_DETAILS="The database details provided are incorrect and/or empty."
186186
INSTL_DATABASE_INVALID_MARIADB_VERSION="You need MariaDB %1$s or higher to continue the installation. Your version is: %2$s"
187-
INSTL_DATABASE_INVALID_MYSQL_VERSION="You need MySQL %1$s or higher to continue the installation. Your version is: %2$s"
188187
INSTL_DATABASE_INVALID_MYSQLI_VERSION="You need MySQL %1$s or higher to continue the installation. Your version is: %2$s"
188+
INSTL_DATABASE_INVALID_MYSQL_VERSION="You need MySQL %1$s or higher to continue the installation. Your version is: %2$s"
189189
INSTL_DATABASE_INVALID_PGSQL_VERSION="You need PostgreSQL %1$s or higher to continue the installation. Your version is: %2$s"
190190
INSTL_DATABASE_INVALID_POSTGRESQL_VERSION="You need PostgreSQL %1$s or higher to continue the installation. Your version is: %2$s"
191191
INSTL_DATABASE_INVALID_TYPE="Please select the database type."

0 commit comments

Comments
 (0)