Merge pull request #3879 from dpalou/MOBILE-3947

Mobile 3947
main
Pau Ferrer Ocaña 2023-12-13 16:43:46 +01:00 committed by GitHub
commit 96d0b5a1ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 464 additions and 170 deletions

1
.gitignore vendored
View File

@ -70,6 +70,7 @@ Thumbs.db
/src/assets/lib /src/assets/lib
/src/assets/lang/* /src/assets/lang/*
/src/assets/env.json /src/assets/env.json
/src/assets/fonts/icons.json
/moodle.config.*.json /moodle.config.*.json
!/moodle.config.example.json !/moodle.config.example.json

View File

@ -0,0 +1,118 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
const { writeFile, readdirSync, statSync, readFileSync } = require('fs');
const FONTS_PATH = 'src/assets/fonts';
const ICONS_JSON_FILE_PATH = 'src/assets/fonts/icons.json';
/**
* Get object with the map of icons for all fonts.
*
* @returns Icons map.
*/
function getIconsMap() {
const config = JSON.parse(readFileSync('moodle.config.json'));
let icons = {};
const fonts = readdirSync(FONTS_PATH);
fonts.forEach(font => {
const fontPath = `${FONTS_PATH}/${font}`;
if (statSync(fontPath).isFile()) {
// Not a font, ignore.
return;
}
icons = {
...icons,
...getFontIconsMap(config.iconsPrefixes, font, fontPath),
};
});
return icons;
}
/**
* Get object with the map of icons for a certain font.
*
* @param prefixes Prefixes to add to the icons.
* @param fontName Font name.
* @param fontPath Font path.
* @returns Icons map.
*/
function getFontIconsMap(prefixes, fontName, fontPath) {
const icons = {};
const fontLibraries = readdirSync(fontPath);
fontLibraries.forEach(libraryName => {
const libraryPath = `${fontPath}/${libraryName}`;
if (statSync(libraryPath).isFile()) {
// Not a font library, ignore.
return;
}
const libraryPrefixes = prefixes?.[fontName]?.[libraryName];
if (!libraryPrefixes || !libraryPrefixes.length) {
console.warn(`WARNING: There is no prefix for the library ${fontName}/${libraryName}. Adding icons without prefix is ` +
'discouraged, please add a prefix for your library in moodle.config.json file, in the iconsPrefixes property.');
}
const libraryIcons = readdirSync(libraryPath);
libraryIcons.forEach(iconFileName => {
if (!iconFileName.endsWith('.svg')) {
// Only accept svg files.
return;
}
if (iconFileName.includes('_')) {
throw Error(`Icon ${libraryPath}/${iconFileName} has an invalid name, it cannot contain '_'. `
+ 'Please rename it to use \'-\' instead.');
}
const iconName = iconFileName.replace('.svg', '');
const iconPath = `${libraryPath}/${iconFileName}`.replace('src/', '');
if (!libraryPrefixes || !libraryPrefixes.length) {
icons[iconName] = iconPath;
return;
}
libraryPrefixes.forEach(prefix => {
icons[`${prefix}-${iconName}`] = iconPath;
});
});
});
return icons;
}
/**
* Task to build a JSON file with the list of icons to add to Ionicons.
*/
class BuildIconsJsonTask {
/**
* Run the task.
*
* @param done Function to call when done.
*/
run(done) {
const icons = getIconsMap();
writeFile(ICONS_JSON_FILE_PATH, JSON.stringify(icons), done);
}
}
module.exports = BuildIconsJsonTask;

View File

@ -15,6 +15,7 @@
const BuildLangTask = require('./gulp/task-build-lang'); const BuildLangTask = require('./gulp/task-build-lang');
const BuildBehatPluginTask = require('./gulp/task-build-behat-plugin'); const BuildBehatPluginTask = require('./gulp/task-build-behat-plugin');
const BuildEnvTask = require('./gulp/task-build-env'); const BuildEnvTask = require('./gulp/task-build-env');
const BuildIconsJsonTask = require('./gulp/task-build-icons-json');
const PushTask = require('./gulp/task-push'); const PushTask = require('./gulp/task-push');
const OverrideLangTask = require('./gulp/task-override-lang'); const OverrideLangTask = require('./gulp/task-override-lang');
const Utils = require('./gulp/utils'); const Utils = require('./gulp/utils');
@ -47,6 +48,10 @@ gulp.task('env', (done) => {
new BuildEnvTask().run(done); new BuildEnvTask().run(done);
}); });
gulp.task('icons', (done) => {
new BuildIconsJsonTask().run(done);
});
// Build a Moodle plugin to run Behat tests. // Build a Moodle plugin to run Behat tests.
if (BuildBehatPluginTask.isBehatConfigured()) { if (BuildBehatPluginTask.isBehatConfigured()) {
gulp.task('behat', (done) => { gulp.task('behat', (done) => {
@ -63,6 +68,7 @@ gulp.task(
gulp.parallel([ gulp.parallel([
'lang', 'lang',
'env', 'env',
'icons',
...(BuildBehatPluginTask.isBehatConfigured() ? ['behat'] : []) ...(BuildBehatPluginTask.isBehatConfigured() ? ['behat'] : [])
]), ]),
); );

View File

@ -111,5 +111,16 @@
"long": 3500, "long": 3500,
"sticky": 0 "sticky": 0
}, },
"disableTokenFile": false "disableTokenFile": false,
"iconsPrefixes": {
"font-awesome": {
"brands": ["fab"],
"regular": ["far"],
"solid": ["fa", "fas"]
},
"moodle": {
"font-awesome": ["fam"],
"moodle": ["moodle"]
}
}
} }

View File

@ -1,6 +1,8 @@
{ {
"ad": "rectangle-ad", "ad": "rectangle-ad",
"add": "plus", "add": "plus",
"address-book-o": "address-book",
"address-card-o": "address-card",
"adjust": "circle-half-stroke", "adjust": "circle-half-stroke",
"air-freshener": "spray-can-sparkles", "air-freshener": "spray-can-sparkles",
"allergies": "hand-dots", "allergies": "hand-dots",
@ -30,34 +32,37 @@
"arrow-rotate-backward": "arrow-rotate-left", "arrow-rotate-backward": "arrow-rotate-left",
"arrow-rotate-forward": "arrow-rotate-right", "arrow-rotate-forward": "arrow-rotate-right",
"arrow-turn-right": "share", "arrow-turn-right": "share",
"arrows": "arrows-up-down-left-right",
"arrows-alt": "up-down-left-right",
"arrows-alt-h": "left-right", "arrows-alt-h": "left-right",
"arrows-alt-v": "up-down", "arrows-alt-v": "up-down",
"arrows-alt": "up-down-left-right",
"arrows-h": "arrows-left-right", "arrows-h": "arrows-left-right",
"arrows-v": "arrows-up-down", "arrows-v": "arrows-up-down",
"arrows": "arrows-up-down-left-right",
"asl-interpreting": "hands-asl-interpreting", "asl-interpreting": "hands-asl-interpreting",
"assistive-listening-systems": "ear-listen", "assistive-listening-systems": "ear-listen",
"atlas": "book-atlas", "atlas": "book-atlas",
"automobile": "car", "automobile": "car",
"backspace": "delete-left", "backspace": "delete-left",
"balance-scale": "scale-balanced",
"balance-scale-left": "scale-unbalanced", "balance-scale-left": "scale-unbalanced",
"balance-scale-right": "scale-unbalanced-flip", "balance-scale-right": "scale-unbalanced-flip",
"balance-scale": "scale-balanced",
"band-aid": "bandage", "band-aid": "bandage",
"bank": "building-columns", "bank": "building-columns",
"bar-chart": "chart-bar", "bar-chart": "chart-bar",
"bar-chart-o": "chart-column",
"baseball-ball": "baseball", "baseball-ball": "baseball",
"basketball-ball": "basketball", "basketball-ball": "basketball",
"bathtub": "bath", "bathtub": "bath",
"battery": "battery-full",
"battery-0": "battery-empty", "battery-0": "battery-empty",
"battery-2": "battery-quarter", "battery-2": "battery-quarter",
"battery-3": "battery-half", "battery-3": "battery-half",
"battery-4": "battery-three-quarters", "battery-4": "battery-three-quarters",
"battery-5": "battery-full", "battery-5": "battery-full",
"battery-car": "car-battery", "battery-car": "car-battery",
"battery": "battery-full",
"beer": "beer-mug-empty", "beer": "beer-mug-empty",
"bell-o": "bell",
"bell-slash-o": "bell-slash",
"bible": "book-bible", "bible": "book-bible",
"biking": "person-biking", "biking": "person-biking",
"birthday-cake": "cake-candles", "birthday-cake": "cake-candles",
@ -65,17 +70,24 @@
"blind": "person-walking-with-cane", "blind": "person-walking-with-cane",
"book-dead": "book-skull", "book-dead": "book-skull",
"book-reader": "book-open-reader", "book-reader": "book-open-reader",
"bookmark-o": "bookmark",
"border-style": "border-top-left", "border-style": "border-top-left",
"boxes-alt": "boxes-stacked",
"boxes": "boxes-stacked", "boxes": "boxes-stacked",
"boxes-alt": "boxes-stacked",
"briefcase-clock": "business-time", "briefcase-clock": "business-time",
"broadcast-tower": "tower-broadcast", "broadcast-tower": "tower-broadcast",
"building-o": "building",
"burn": "fire-flame-simple", "burn": "fire-flame-simple",
"bus-alt": "bus-simple", "bus-alt": "bus-simple",
"cab": "taxi", "cab": "taxi",
"cake": "cake-candles", "cake": "cake-candles",
"calendar-alt": "calendar-days", "calendar-alt": "calendar-days",
"calendar-check-o": "calendar-check",
"calendar-minus-o": "calendar-minus",
"calendar-o": "calendar",
"calendar-plus-o": "calendar-plus",
"calendar-times": "calendar-xmark", "calendar-times": "calendar-xmark",
"calendar-times-o": "calendar-xmark",
"camera-alt": "camera", "camera-alt": "camera",
"cancel": "ban", "cancel": "ban",
"car-alt": "car-rear", "car-alt": "car-rear",
@ -85,24 +97,28 @@
"caret-square-right": "square-caret-right", "caret-square-right": "square-caret-right",
"caret-square-up": "square-caret-up", "caret-square-up": "square-caret-up",
"carriage-baby": "baby-carriage", "carriage-baby": "baby-carriage",
"chain": "link",
"chain-broken": "link-slash", "chain-broken": "link-slash",
"chain-slash": "link-slash", "chain-slash": "link-slash",
"chain": "link",
"chalkboard-teacher": "chalkboard-user", "chalkboard-teacher": "chalkboard-user",
"check-circle": "circle-check", "check-circle": "circle-check",
"check-circle-o": "circle-check",
"check-square": "square-check", "check-square": "square-check",
"check-square-o": "square-check",
"chevron-circle-down": "circle-chevron-down", "chevron-circle-down": "circle-chevron-down",
"chevron-circle-left": "circle-chevron-left", "chevron-circle-left": "circle-chevron-left",
"chevron-circle-right": "circle-chevron-right", "chevron-circle-right": "circle-chevron-right",
"chevron-circle-up": "circle-chevron-up", "chevron-circle-up": "circle-chevron-up",
"child-rifle": "child-combatant", "child-rifle": "child-combatant",
"circle-o": "circle",
"clinic-medical": "house-chimney-medical", "clinic-medical": "house-chimney-medical",
"clock-four": "clock", "clock-four": "clock",
"clock-o": "clock",
"close": "xmark", "close": "xmark",
"cloud-download-alt": "cloud-arrow-down",
"cloud-download": "cloud-arrow-down", "cloud-download": "cloud-arrow-down",
"cloud-upload-alt": "cloud-arrow-up", "cloud-download-alt": "cloud-arrow-down",
"cloud-upload": "cloud-arrow-up", "cloud-upload": "cloud-arrow-up",
"cloud-upload-alt": "cloud-arrow-up",
"cny": "yen-sign", "cny": "yen-sign",
"cocktail": "martini-glass-citrus", "cocktail": "martini-glass-citrus",
"coffee": "mug-saucer", "coffee": "mug-saucer",
@ -110,7 +126,10 @@
"cogs": "gears", "cogs": "gears",
"columns": "table-columns", "columns": "table-columns",
"comment-alt": "message", "comment-alt": "message",
"comment-o": "comment",
"commenting": "comment-dots", "commenting": "comment-dots",
"commenting-o": "comment-dots",
"comments-o": "comments",
"compress-alt": "down-left-and-up-right-to-center", "compress-alt": "down-left-and-up-right-to-center",
"compress-arrows-alt": "minimize", "compress-arrows-alt": "minimize",
"concierge-bell": "bell-concierge", "concierge-bell": "bell-concierge",
@ -135,26 +154,30 @@
"dolly-flatbed": "cart-flatbed", "dolly-flatbed": "cart-flatbed",
"donate": "circle-dollar-to-slot", "donate": "circle-dollar-to-slot",
"dot-circle": "circle-dot", "dot-circle": "circle-dot",
"dot-circle-o": "circle-dot",
"drafting-compass": "compass-drafting", "drafting-compass": "compass-drafting",
"drivers-license": "id-card", "drivers-license": "id-card",
"earth-america": "earth-americas", "drivers-license-o": "id-card",
"earth": "earth-americas", "earth": "earth-americas",
"earth-america": "earth-americas",
"edit": "pen", "edit": "pen",
"ellipsis-h": "ellipsis", "ellipsis-h": "ellipsis",
"ellipsis-v": "ellipsis-vertical", "ellipsis-v": "ellipsis-vertical",
"envelope-o": "envelope",
"envelope-open-o": "envelope-open",
"envelope-square": "square-envelope", "envelope-square": "square-envelope",
"eur": "euro-sign", "eur": "euro-sign",
"euro": "euro-sign", "euro": "euro-sign",
"exchange-alt": "right-left",
"exchange": "arrow-right-arrow-left", "exchange": "arrow-right-arrow-left",
"exchange-alt": "right-left",
"exclamation-circle": "circle-exclamation", "exclamation-circle": "circle-exclamation",
"exclamation-triangle": "triangle-exclamation", "exclamation-triangle": "triangle-exclamation",
"expand-alt": "up-right-and-down-left-from-center", "expand-alt": "up-right-and-down-left-from-center",
"expand-arrows-alt": "maximize", "expand-arrows-alt": "maximize",
"external-link-alt": "up-right-from-square",
"external-link-square-alt": "square-up-right",
"external-link-square": "square-arrow-up-right",
"external-link": "arrow-up-right-from-square", "external-link": "arrow-up-right-from-square",
"external-link-alt": "up-right-from-square",
"external-link-square": "square-arrow-up-right",
"external-link-square-alt": "square-up-right",
"eye-dropper-empty": "eye-dropper", "eye-dropper-empty": "eye-dropper",
"eyedropper": "eye-dropper", "eyedropper": "eye-dropper",
"facebook-square": "square-facebook", "facebook-square": "square-facebook",
@ -166,28 +189,51 @@
"fighter-jet": "jet-fighter", "fighter-jet": "jet-fighter",
"file-alt": "file-lines", "file-alt": "file-lines",
"file-archive": "file-zipper", "file-archive": "file-zipper",
"file-archive-o": "file-zipper",
"file-audio-o": "file-audio",
"file-clipboard": "paste", "file-clipboard": "paste",
"file-code-o": "file-code",
"file-download": "file-arrow-down", "file-download": "file-arrow-down",
"file-edit": "file-pen", "file-edit": "file-pen",
"file-excel-o": "file-excel",
"file-image-o": "file-image",
"file-medical-alt": "file-waveform", "file-medical-alt": "file-waveform",
"file-movie-o": "file-video",
"file-o": "file",
"file-pdf-o": "file-pdf",
"file-photo-o": "file-image",
"file-picture-o": "file-image",
"file-powerpoint-o": "file-powerpoint",
"file-sound-o": "file-audio",
"file-text": "file-lines", "file-text": "file-lines",
"file-text-o": "file-lines",
"file-upload": "file-arrow-up", "file-upload": "file-arrow-up",
"file-video-o": "file-video",
"file-word-o": "file-word",
"file-zip-o": "file-zipper",
"files-o": "copy",
"fire-alt": "fire-flame-curved", "fire-alt": "fire-flame-curved",
"first-aid": "kit-medical", "first-aid": "kit-medical",
"fist-raised": "hand-fist", "fist-raised": "hand-fist",
"flag-o": "flag",
"floppy-o": "floppy-disk",
"flushed": "face-flushed", "flushed": "face-flushed",
"folder-blank": "folder", "folder-blank": "folder",
"folder-o": "folder",
"folder-open-o": "folder-open",
"football-ball": "football", "football-ball": "football",
"frown-open": "face-frown-open",
"frown": "face-frown", "frown": "face-frown",
"frown-o": "face-frown",
"frown-open": "face-frown-open",
"funnel-dollar": "filter-circle-dollar", "funnel-dollar": "filter-circle-dollar",
"futbol-ball": "futbol", "futbol-ball": "futbol",
"futbol-o": "futbol",
"gauge-med": "gauge", "gauge-med": "gauge",
"gauge-simple-med": "gauge-simple", "gauge-simple-med": "gauge-simple",
"gbp": "sterling-sign", "gbp": "sterling-sign",
"glass-cheers": "champagne-glasses", "glass-cheers": "champagne-glasses",
"glass-martini-alt": "martini-glass",
"glass-martini": "martini-glass-empty", "glass-martini": "martini-glass-empty",
"glass-martini-alt": "martini-glass",
"glass-whiskey": "whiskey-glass", "glass-whiskey": "whiskey-glass",
"globe-africa": "earth-africa", "globe-africa": "earth-africa",
"globe-americas": "earth-americas", "globe-americas": "earth-americas",
@ -196,49 +242,62 @@
"globe-oceania": "earth-oceania", "globe-oceania": "earth-oceania",
"golf-ball": "golf-ball-tee", "golf-ball": "golf-ball-tee",
"grimace": "face-grimace", "grimace": "face-grimace",
"grin": "face-grin",
"grin-alt": "face-grin-wide", "grin-alt": "face-grin-wide",
"grin-beam-sweat": "face-grin-beam-sweat",
"grin-beam": "face-grin-beam", "grin-beam": "face-grin-beam",
"grin-beam-sweat": "face-grin-beam-sweat",
"grin-hearts": "face-grin-hearts", "grin-hearts": "face-grin-hearts",
"grin-squint-tears": "face-grin-squint-tears",
"grin-squint": "face-grin-squint", "grin-squint": "face-grin-squint",
"grin-squint-tears": "face-grin-squint-tears",
"grin-stars": "face-grin-stars", "grin-stars": "face-grin-stars",
"grin-tears": "face-grin-tears", "grin-tears": "face-grin-tears",
"grin-tongue": "face-grin-tongue",
"grin-tongue-squint": "face-grin-tongue-squint", "grin-tongue-squint": "face-grin-tongue-squint",
"grin-tongue-wink": "face-grin-tongue-wink", "grin-tongue-wink": "face-grin-tongue-wink",
"grin-tongue": "face-grin-tongue",
"grin-wink": "face-grin-wink", "grin-wink": "face-grin-wink",
"grin": "face-grin",
"grip-horizontal": "grip", "grip-horizontal": "grip",
"h-square": "square-h", "h-square": "square-h",
"hamburger": "burger", "hamburger": "burger",
"hand-grab-o": "hand-back-fist",
"hand-holding-usd": "hand-holding-dollar", "hand-holding-usd": "hand-holding-dollar",
"hand-holding-water": "hand-holding-droplet", "hand-holding-water": "hand-holding-droplet",
"hand-lizard-o": "hand-lizard",
"hand-paper": "hand", "hand-paper": "hand",
"hand-paper-o": "hand",
"hand-peace-o": "hand-peace",
"hand-pointer-o": "hand-pointer",
"hand-rock": "hand-back-fist", "hand-rock": "hand-back-fist",
"hand-rock-o": "hand-back-fist",
"hand-scissors-o": "hand-scissors",
"hand-spock-o": "hand-spock",
"hand-stop-o": "hand",
"hands-american-sign-language-interpreting": "hands-asl-interpreting", "hands-american-sign-language-interpreting": "hands-asl-interpreting",
"hands-helping": "handshake-angle", "hands-helping": "handshake-angle",
"hands-wash": "hands-bubbles", "hands-wash": "hands-bubbles",
"handshake-alt-slash": "handshake-simple-slash",
"handshake-alt": "handshake-simple", "handshake-alt": "handshake-simple",
"handshake-alt-slash": "handshake-simple-slash",
"handshake-o": "handshake",
"hard-hat": "helmet-safety", "hard-hat": "helmet-safety",
"hard-of-hearing": "ear-deaf", "hard-of-hearing": "ear-deaf",
"hat-hard": "helmet-safety", "hat-hard": "helmet-safety",
"haykal": "bahai", "haykal": "bahai",
"hdd": "hard-drive", "hdd": "hard-drive",
"hdd-o": "hard-drive",
"header": "heading", "header": "heading",
"headphones-alt": "headphones-simple", "headphones-alt": "headphones-simple",
"heart-broken": "heart-crack", "heart-broken": "heart-crack",
"heart-music-camera-bolt": "icons", "heart-music-camera-bolt": "icons",
"heart-o": "heart",
"heartbeat": "heart-pulse", "heartbeat": "heart-pulse",
"hiking": "person-hiking", "hiking": "person-hiking",
"history": "clock-rotate-left", "history": "clock-rotate-left",
"home-alt": "house",
"home-lg-alt": "house",
"home-lg": "house-chimney",
"home-user": "house-user",
"home": "house", "home": "house",
"home-alt": "house",
"home-lg": "house-chimney",
"home-lg-alt": "house",
"home-user": "house-user",
"hospital-alt": "hospital", "hospital-alt": "hospital",
"hospital-o": "hospital",
"hospital-symbol": "circle-h", "hospital-symbol": "circle-h",
"hospital-wide": "hospital", "hospital-wide": "hospital",
"hot-tub": "hot-tub-person", "hot-tub": "hot-tub-person",
@ -246,9 +305,11 @@
"hourglass-2": "hourglass-half", "hourglass-2": "hourglass-half",
"hourglass-3": "hourglass-end", "hourglass-3": "hourglass-end",
"hourglass-empty": "hourglass", "hourglass-empty": "hourglass",
"hourglass-o": "hourglass",
"house-damage": "house-chimney-crack", "house-damage": "house-chimney-crack",
"hryvnia": "hryvnia-sign", "hryvnia": "hryvnia-sign",
"id-card-alt": "id-card-clip", "id-card-alt": "id-card-clip",
"id-card-o": "id-card",
"ils": "shekel-sign", "ils": "shekel-sign",
"indian-rupee": "indian-rupee-sign", "indian-rupee": "indian-rupee-sign",
"info-circle": "circle-info", "info-circle": "circle-info",
@ -256,22 +317,25 @@
"institution": "building-columns", "institution": "building-columns",
"journal-whills": "book-journal-whills", "journal-whills": "book-journal-whills",
"jpy": "yen-sign", "jpy": "yen-sign",
"keyboard-o": "keyboard",
"kiss": "face-kiss",
"kiss-beam": "face-kiss-beam", "kiss-beam": "face-kiss-beam",
"kiss-wink-heart": "face-kiss-wink-heart", "kiss-wink-heart": "face-kiss-wink-heart",
"kiss": "face-kiss",
"krw": "won-sign", "krw": "won-sign",
"ladder-water": "water-ladder", "ladder-water": "water-ladder",
"landmark-alt": "landmark-dome", "landmark-alt": "landmark-dome",
"laptop-house": "house-laptop", "laptop-house": "house-laptop",
"laugh": "face-laugh",
"laugh-beam": "face-laugh-beam", "laugh-beam": "face-laugh-beam",
"laugh-squint": "face-laugh-squint", "laugh-squint": "face-laugh-squint",
"laugh-wink": "face-laugh-wink", "laugh-wink": "face-laugh-wink",
"laugh": "face-laugh",
"legal": "gavel", "legal": "gavel",
"level-down-alt": "turn-down", "lemon-o": "lemon",
"level-down": "arrow-turn-down", "level-down": "arrow-turn-down",
"level-up-alt": "turn-up", "level-down-alt": "turn-down",
"level-up": "arrow-turn-up", "level-up": "arrow-turn-up",
"level-up-alt": "turn-up",
"lightbulb-o": "lightbulb",
"line-chart": "chart-line", "line-chart": "chart-line",
"list-1-2": "list-ol", "list-1-2": "list-ol",
"list-alt": "rectangle-list", "list-alt": "rectangle-list",
@ -289,64 +353,75 @@
"long-arrow-up": "arrow-up-long", "long-arrow-up": "arrow-up-long",
"low-vision": "eye-low-vision", "low-vision": "eye-low-vision",
"luggage-cart": "cart-flatbed-suitcase", "luggage-cart": "cart-flatbed-suitcase",
"magic-wand-sparkles": "wand-magic-sparkles",
"magic": "wand-magic", "magic": "wand-magic",
"magic-wand-sparkles": "wand-magic-sparkles",
"mail-bulk": "envelopes-bulk", "mail-bulk": "envelopes-bulk",
"mail-forward": "share", "mail-forward": "share",
"mail-reply-all": "reply-all",
"mail-reply": "reply", "mail-reply": "reply",
"mail-reply-all": "reply-all",
"male": "person", "male": "person",
"map-marked-alt": "map-location-dot",
"map-marked": "map-location", "map-marked": "map-location",
"map-marker-alt": "location-dot", "map-marked-alt": "map-location-dot",
"map-marker": "location-pin", "map-marker": "location-pin",
"map-marker-alt": "location-dot",
"map-o": "map",
"map-signs": "signs-post", "map-signs": "signs-post",
"mars-stroke-h": "mars-stroke-right", "mars-stroke-h": "mars-stroke-right",
"mars-stroke-v": "mars-stroke-up", "mars-stroke-v": "mars-stroke-up",
"medkit": "suitcase-medical", "medkit": "suitcase-medical",
"meh-blank": "face-meh-blank",
"meh-rolling-eyes": "face-rolling-eyes",
"meh": "face-meh", "meh": "face-meh",
"microphone-alt-slash": "microphone-lines-slash", "meh-blank": "face-meh-blank",
"meh-o": "face-meh",
"meh-rolling-eyes": "face-rolling-eyes",
"microphone-alt": "microphone-lines", "microphone-alt": "microphone-lines",
"microphone-alt-slash": "microphone-lines-slash",
"minus-circle": "circle-minus", "minus-circle": "circle-minus",
"minus-square": "square-minus", "minus-square": "square-minus",
"minus-square-o": "square-minus",
"mobile-alt": "mobile-screen-button", "mobile-alt": "mobile-screen-button",
"mobile-android-alt": "mobile-screen",
"mobile-android": "mobile", "mobile-android": "mobile",
"mobile-android-alt": "mobile-screen",
"mobile-phone": "mobile", "mobile-phone": "mobile",
"money-bill-alt": "money-bill-1", "money-bill-alt": "money-bill-1",
"money-bill-wave-alt": "money-bill-1-wave", "money-bill-wave-alt": "money-bill-1-wave",
"money-check-alt": "money-check-dollar", "money-check-alt": "money-check-dollar",
"moon-o": "moon",
"mortar-board": "graduation-cap", "mortar-board": "graduation-cap",
"mouse-pointer": "arrow-pointer",
"mouse": "computer-mouse", "mouse": "computer-mouse",
"mouse-pointer": "arrow-pointer",
"multiply": "xmark", "multiply": "xmark",
"museum": "building-columns", "museum": "building-columns",
"navicon": "bars", "navicon": "bars",
"newspaper-o": "newspaper",
"paint-brush": "paintbrush", "paint-brush": "paintbrush",
"paper-plane-o": "paper-plane",
"parking": "square-parking", "parking": "square-parking",
"pastafarianism": "spaghetti-monster-flying", "pastafarianism": "spaghetti-monster-flying",
"pause-circle": "circle-pause", "pause-circle": "circle-pause",
"pause-circle-o": "circle-pause",
"pen-alt": "pen-clip", "pen-alt": "pen-clip",
"pen-square": "square-pen", "pen-square": "square-pen",
"pencil-alt": "pencil", "pencil-alt": "pencil",
"pencil-ruler": "pen-ruler", "pencil-ruler": "pen-ruler",
"pencil-square": "square-pen", "pencil-square": "square-pen",
"pencil-square-o": "pen-to-square",
"people-arrows-left-right": "people-arrows", "people-arrows-left-right": "people-arrows",
"people-carry": "people-carry-box", "people-carry": "people-carry-box",
"percentage": "percent", "percentage": "percent",
"phone-alt": "phone-flip", "phone-alt": "phone-flip",
"phone-square-alt": "square-phone-flip",
"phone-square": "square-phone", "phone-square": "square-phone",
"phone-square-alt": "square-phone-flip",
"photo-video": "photo-film", "photo-video": "photo-film",
"picture-o": "image",
"pie-chart": "chart-pie", "pie-chart": "chart-pie",
"ping-pong-paddle-ball": "table-tennis-paddle-ball", "ping-pong-paddle-ball": "table-tennis-paddle-ball",
"play-circle": "circle-play", "play-circle": "circle-play",
"play-circle-o": "circle-play",
"plus-circle": "circle-plus", "plus-circle": "circle-plus",
"plus-square": "square-plus", "plus-square": "square-plus",
"poll-h": "square-poll-horizontal", "plus-square-o": "square-plus",
"poll": "square-poll-vertical", "poll": "square-poll-vertical",
"poll-h": "square-poll-horizontal",
"poo-bolt": "poo-storm", "poo-bolt": "poo-storm",
"portrait": "image-portrait", "portrait": "image-portrait",
"pound-sign": "sterling-sign", "pound-sign": "sterling-sign",
@ -356,19 +431,20 @@
"procedures": "bed-pulse", "procedures": "bed-pulse",
"project-diagram": "diagram-project", "project-diagram": "diagram-project",
"question-circle": "circle-question", "question-circle": "circle-question",
"quidditch-broom-ball": "broom-ball", "question-circle-o": "circle-question",
"quidditch": "broom-ball", "quidditch": "broom-ball",
"quidditch-broom-ball": "broom-ball",
"quote-left-alt": "quote-left", "quote-left-alt": "quote-left",
"quote-right-alt": "quote-right", "quote-right-alt": "quote-right",
"quran": "book-quran", "quran": "book-quran",
"radiation-alt": "circle-radiation", "radiation-alt": "circle-radiation",
"random": "shuffle", "random": "shuffle",
"rectangle-times": "rectangle-xmark", "rectangle-times": "rectangle-xmark",
"redo-alt": "rotate-right",
"redo": "arrow-rotate-right", "redo": "arrow-rotate-right",
"redo-alt": "rotate-right",
"refresh": "arrows-rotate", "refresh": "arrows-rotate",
"remove-format": "text-slash",
"remove": "xmark", "remove": "xmark",
"remove-format": "text-slash",
"reorder": "bars-staggered", "reorder": "bars-staggered",
"rmb": "yen-sign", "rmb": "yen-sign",
"rod-asclepius": "staff-snake", "rod-asclepius": "staff-snake",
@ -385,17 +461,19 @@
"sad-cry": "face-sad-cry", "sad-cry": "face-sad-cry",
"sad-tear": "face-sad-tear", "sad-tear": "face-sad-tear",
"save": "floppy-disk", "save": "floppy-disk",
"search": "magnifying-glass",
"search-dollar": "magnifying-glass-dollar", "search-dollar": "magnifying-glass-dollar",
"search-location": "magnifying-glass-location", "search-location": "magnifying-glass-location",
"search-minus": "magnifying-glass-minus", "search-minus": "magnifying-glass-minus",
"search-plus": "magnifying-glass-plus", "search-plus": "magnifying-glass-plus",
"search": "magnifying-glass", "send-o": "paper-plane",
"share-alt-square": "square-share-nodes",
"share-alt": "share-nodes", "share-alt": "share-nodes",
"share-alt-square": "square-share-nodes",
"share-square": "share-from-square", "share-square": "share-from-square",
"share-square-o": "share-from-square",
"shekel": "shekel-sign", "shekel": "shekel-sign",
"sheqel-sign": "shekel-sign",
"sheqel": "shekel-sign", "sheqel": "shekel-sign",
"sheqel-sign": "shekel-sign",
"shield-alt": "shield-halved", "shield-alt": "shield-halved",
"shield-blank": "shield", "shield-blank": "shield",
"shipping-fast": "truck-fast", "shipping-fast": "truck-fast",
@ -403,78 +481,87 @@
"shopping-basket": "basket-shopping", "shopping-basket": "basket-shopping",
"shopping-cart": "cart-shopping", "shopping-cart": "cart-shopping",
"shuttle-van": "van-shuttle", "shuttle-van": "van-shuttle",
"sign-in-alt": "right-to-bracket",
"sign-in": "arrow-right-to-bracket",
"sign-language": "hands",
"sign-out-alt": "right-from-bracket",
"sign-out": "arrow-right-from-bracket",
"sign": "sign-hanging", "sign": "sign-hanging",
"sign-in": "arrow-right-to-bracket",
"sign-in-alt": "right-to-bracket",
"sign-language": "hands",
"sign-out": "arrow-right-from-bracket",
"sign-out-alt": "right-from-bracket",
"signal-5": "signal", "signal-5": "signal",
"signal-perfect": "signal", "signal-perfect": "signal",
"signing": "hands", "signing": "hands",
"skating": "person-skating", "skating": "person-skating",
"skiing-nordic": "person-skiing-nordic",
"skiing": "person-skiing", "skiing": "person-skiing",
"skiing-nordic": "person-skiing-nordic",
"sliders-h": "sliders", "sliders-h": "sliders",
"smile-beam": "face-smile-beam",
"smile-wink": "face-smile-wink",
"smile": "face-smile", "smile": "face-smile",
"smile-beam": "face-smile-beam",
"smile-o": "face-smile",
"smile-wink": "face-smile-wink",
"smoking-ban": "ban-smoking", "smoking-ban": "ban-smoking",
"sms": "comment-sms", "sms": "comment-sms",
"snowboarding": "person-snowboarding", "snowboarding": "person-snowboarding",
"snowflake-o": "snowflake",
"soccer-ball": "futbol", "soccer-ball": "futbol",
"soccer-ball-o": "futbol",
"sort-alpha-asc": "arrow-down-a-z", "sort-alpha-asc": "arrow-down-a-z",
"sort-alpha-desc": "arrow-down-z-a", "sort-alpha-desc": "arrow-down-z-a",
"sort-alpha-down-alt": "arrow-down-z-a",
"sort-alpha-down": "arrow-down-a-z", "sort-alpha-down": "arrow-down-a-z",
"sort-alpha-up-alt": "arrow-up-z-a", "sort-alpha-down-alt": "arrow-down-z-a",
"sort-alpha-up": "arrow-up-a-z", "sort-alpha-up": "arrow-up-a-z",
"sort-alpha-up-alt": "arrow-up-z-a",
"sort-amount-asc": "arrow-down-wide-short", "sort-amount-asc": "arrow-down-wide-short",
"sort-amount-desc": "arrow-down-short-wide", "sort-amount-desc": "arrow-down-short-wide",
"sort-amount-down-alt": "arrow-down-short-wide",
"sort-amount-down": "arrow-down-wide-short", "sort-amount-down": "arrow-down-wide-short",
"sort-amount-up-alt": "arrow-up-short-wide", "sort-amount-down-alt": "arrow-down-short-wide",
"sort-amount-up": "arrow-up-wide-short", "sort-amount-up": "arrow-up-wide-short",
"sort-amount-up-alt": "arrow-up-short-wide",
"sort-asc": "sort-up", "sort-asc": "sort-up",
"sort-desc": "sort-down", "sort-desc": "sort-down",
"sort-numeric-asc": "arrow-down-1-9", "sort-numeric-asc": "arrow-down-1-9",
"sort-numeric-desc": "arrow-down-9-1", "sort-numeric-desc": "arrow-down-9-1",
"sort-numeric-down-alt": "arrow-down-9-1",
"sort-numeric-down": "arrow-down-1-9", "sort-numeric-down": "arrow-down-1-9",
"sort-numeric-up-alt": "arrow-up-9-1", "sort-numeric-down-alt": "arrow-down-9-1",
"sort-numeric-up": "arrow-up-1-9", "sort-numeric-up": "arrow-up-1-9",
"sort-numeric-up-alt": "arrow-up-9-1",
"space-shuttle": "shuttle-space", "space-shuttle": "shuttle-space",
"sprout": "seedling", "sprout": "seedling",
"square-o": "square",
"square-root-alt": "square-root-variable", "square-root-alt": "square-root-variable",
"staff-aesculapius": "staff-snake", "staff-aesculapius": "staff-snake",
"star-half-alt": "star-half-stroke", "star-half-alt": "star-half-stroke",
"star-half-o": "star-half-stroke",
"star-o": "star",
"step-backward": "backward-step", "step-backward": "backward-step",
"step-forward": "forward-step", "step-forward": "forward-step",
"sticky-note": "note-sticky", "sticky-note": "note-sticky",
"sticky-note-o": "note-sticky",
"stop-circle": "circle-stop", "stop-circle": "circle-stop",
"store-alt-slash": "shop-slash", "stop-circle-o": "circle-stop",
"store-alt": "shop", "store-alt": "shop",
"store-alt-slash": "shop-slash",
"stream": "bars-staggered", "stream": "bars-staggered",
"subtract": "minus", "subtract": "minus",
"subway": "train-subway", "subway": "train-subway",
"sun-o": "sun",
"surprise": "face-surprise", "surprise": "face-surprise",
"swimmer": "person-swimming", "swimmer": "person-swimming",
"swimming-pool": "water-ladder", "swimming-pool": "water-ladder",
"sync-alt": "rotate",
"sync": "arrows-rotate", "sync": "arrows-rotate",
"sync-alt": "rotate",
"t-shirt": "shirt", "t-shirt": "shirt",
"table-tennis": "table-tennis-paddle-ball", "table-tennis": "table-tennis-paddle-ball",
"tablet-alt": "tablet-screen-button", "tablet-alt": "tablet-screen-button",
"tablet-android": "tablet", "tablet-android": "tablet",
"tachometer": "gauge-simple-high",
"tachometer-alt": "gauge-high",
"tachometer-alt-average": "gauge", "tachometer-alt-average": "gauge",
"tachometer-alt-fast": "gauge-high", "tachometer-alt-fast": "gauge-high",
"tachometer-alt": "gauge-high",
"tachometer-average": "gauge-simple", "tachometer-average": "gauge-simple",
"tachometer-fast": "gauge-simple-high", "tachometer-fast": "gauge-simple-high",
"tachometer": "gauge-simple-high",
"tanakh": "book-tanakh", "tanakh": "book-tanakh",
"tasks-alt": "bars-progress",
"tasks": "list-check", "tasks": "list-check",
"tasks-alt": "bars-progress",
"teletype": "tty", "teletype": "tty",
"television": "tv", "television": "tv",
"temperature-0": "temperature-empty", "temperature-0": "temperature-empty",
@ -485,9 +572,9 @@
"temperature-down": "temperature-arrow-down", "temperature-down": "temperature-arrow-down",
"temperature-up": "temperature-arrow-up", "temperature-up": "temperature-arrow-up",
"tenge": "tenge-sign", "tenge": "tenge-sign",
"th": "table-cells",
"th-large": "table-cells-large", "th-large": "table-cells-large",
"th-list": "table-list", "th-list": "table-list",
"th": "table-cells",
"theater-masks": "masks-theater", "theater-masks": "masks-theater",
"thermometer-0": "temperature-empty", "thermometer-0": "temperature-empty",
"thermometer-1": "temperature-quarter", "thermometer-1": "temperature-quarter",
@ -502,44 +589,50 @@
"thumb-tack": "thumbtack", "thumb-tack": "thumbtack",
"thunderstorm": "cloud-bolt", "thunderstorm": "cloud-bolt",
"ticket-alt": "ticket-simple", "ticket-alt": "ticket-simple",
"times-circle": "circle-xmark",
"times-rectangle": "rectangle-xmark",
"times-square": "square-xmark",
"times": "xmark", "times": "xmark",
"tint-slash": "droplet-slash", "times-circle": "circle-xmark",
"times-circle-o": "circle-xmark",
"times-rectangle": "rectangle-xmark",
"times-rectangle-o": "rectangle-xmark",
"times-square": "square-xmark",
"tint": "droplet", "tint": "droplet",
"tint-slash": "droplet-slash",
"tired": "face-tired", "tired": "face-tired",
"tools": "screwdriver-wrench", "tools": "screwdriver-wrench",
"torah": "scroll-torah", "torah": "scroll-torah",
"tram": "cable-car", "tram": "cable-car",
"transgender-alt": "transgender", "transgender-alt": "transgender",
"trash-alt": "trash-can", "trash-alt": "trash-can",
"trash-restore-alt": "trash-can-arrow-up", "trash-o": "trash-can",
"trash-restore": "trash-arrow-up", "trash-restore": "trash-arrow-up",
"trash-restore-alt": "trash-can-arrow-up",
"triangle-circle-square": "shapes", "triangle-circle-square": "shapes",
"truck-loading": "truck-ramp-box", "truck-loading": "truck-ramp-box",
"try": "turkish-lira-sign", "try": "turkish-lira-sign",
"tshirt": "shirt", "tshirt": "shirt",
"turkish-lira": "turkish-lira-sign", "turkish-lira": "turkish-lira-sign",
"tv-alt": "tv", "tv-alt": "tv",
"undo-alt": "rotate-left",
"undo": "arrow-rotate-left", "undo": "arrow-rotate-left",
"undo-alt": "rotate-left",
"university": "building-columns", "university": "building-columns",
"unlink": "link-slash", "unlink": "link-slash",
"unlock-alt": "unlock-keyhole", "unlock-alt": "unlock-keyhole",
"unsorted": "sort", "unsorted": "sort",
"usd": "dollar-sign", "usd": "dollar-sign",
"user-alt-slash": "user-large-slash",
"user-alt": "user-large", "user-alt": "user-large",
"user-alt-slash": "user-large-slash",
"user-circle": "circle-user", "user-circle": "circle-user",
"user-circle-o": "circle-user",
"user-cog": "user-gear", "user-cog": "user-gear",
"user-edit": "user-pen", "user-edit": "user-pen",
"user-friends": "user-group", "user-friends": "user-group",
"user-md": "user-doctor", "user-md": "user-doctor",
"user-o": "user",
"user-times": "user-xmark", "user-times": "user-xmark",
"users-cog": "users-gear", "users-cog": "users-gear",
"utensil-spoon": "spoon", "utensil-spoon": "spoon",
"vcard": "address-card", "vcard": "address-card",
"vcard-o": "address-card",
"video-camera": "video", "video-camera": "video",
"volleyball-ball": "volleyball", "volleyball-ball": "volleyball",
"volume-control-phone": "phone-volume", "volume-control-phone": "phone-volume",
@ -556,6 +649,7 @@
"wifi-3": "wifi", "wifi-3": "wifi",
"wifi-strong": "wifi", "wifi-strong": "wifi",
"window-close": "rectangle-xmark", "window-close": "rectangle-xmark",
"window-close-o": "rectangle-xmark",
"wine-glass-alt": "wine-glass-empty", "wine-glass-alt": "wine-glass-empty",
"won": "won-sign", "won": "won-sign",
"xmark-circle": "circle-xmark", "xmark-circle": "circle-xmark",

View File

Before

Width:  |  Height:  |  Size: 777 B

After

Width:  |  Height:  |  Size: 776 B

View File

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 294 B

View File

@ -15,6 +15,7 @@
import { AfterViewInit, Directive, ElementRef, Input, OnChanges, SimpleChange } from '@angular/core'; import { AfterViewInit, Directive, ElementRef, Input, OnChanges, SimpleChange } from '@angular/core';
import { CoreLogger } from '@singletons/logger'; import { CoreLogger } from '@singletons/logger';
import { CoreIcons } from '@singletons/icons'; import { CoreIcons } from '@singletons/icons';
import { CoreConstants } from '../constants';
/** /**
* Directive to enable font-awesome 6.4 as ionicons. * Directive to enable font-awesome 6.4 as ionicons.
@ -41,65 +42,30 @@ export class CoreFaIconDirective implements AfterViewInit, OnChanges {
} }
/** /**
* Detect icon name and use svg. * Validate icon, e.g. checking if it's using a deprecated name.
*/ */
async setIcon(): Promise<void> { async validateIcon(): Promise<void> {
let library = ''; if (CoreConstants.BUILD.isDevelopment && !CoreIcons.isIconNamePrefixed(this.name)) {
let iconName = this.name; this.logger.warn(`Not prefixed icon ${this.name} detected, it could be an Ionic icon. Font-awesome is preferred.`);
let font = 'ionicons';
const parts = iconName.split('-', 2);
if (parts.length === 2) {
switch (parts[0]) {
case 'far':
library = 'regular';
font = 'font-awesome';
break;
case 'fa':
case 'fas':
library = 'solid';
font = 'font-awesome';
break;
case 'fab':
library = 'brands';
font = 'font-awesome';
break;
case 'moodle':
library = 'moodle';
font = 'moodle';
break;
case 'fam':
library = 'font-awesome';
font = 'moodle';
break;
default:
break;
}
} }
if (font === 'ionicons') { if (this.name.includes('_')) {
this.element.removeAttribute('src'); // Ionic icons cannot contain a '_' in the name, Ionic doesn't load them, replace it with '-'.
this.logger.warn(`Ionic icon ${this.name} detected`); this.logger.warn(`Icon ${this.name} contains '_' character and it's not allowed, replacing it with '-'.`);
this.updateName(this.name.replace(/_/g, '-'));
return;
} }
iconName = iconName.substring(parts[0].length + 1); if (this.name.match(/^fa[brs]?-/)) {
// It's a font-awesome icon, check if it's using a deprecated name.
const iconName = this.name.substring(this.name.indexOf('-') + 1);
const { fileName, newLibrary } = await CoreIcons.getFontAwesomeIconFileName(iconName);
// Set it here to avoid loading unexisting icon paths (svg/iconName) caused by the tick delay of the checkIconAlias promise. if (newLibrary) {
let src = CoreIcons.getIconSrc(font, library, iconName); this.updateName(CoreIcons.prefixIconName('font-awesome', newLibrary, fileName));
this.element.setAttribute('src', src); } else if (fileName !== iconName) {
this.updateName(this.name.replace(iconName, fileName));
if (font === 'font-awesome') {
const { fileName } = await CoreIcons.getFontAwesomeIconFileName(iconName);
if (fileName !== iconName) {
src = CoreIcons.getIconSrc(font, library, fileName);
this.element.setAttribute('src', src);
} }
} }
this.element.classList.add('faicon');
CoreIcons.validateIcon(this.name, src);
} }
/** /**
@ -123,7 +89,17 @@ export class CoreFaIconDirective implements AfterViewInit, OnChanges {
return; return;
} }
this.setIcon(); this.validateIcon();
}
/**
* Update the icon name.
*
* @param newName New name to use.
*/
protected updateName(newName: string): void {
this.name = newName;
this.element.setAttribute('name', newName);
} }
} }

View File

@ -160,8 +160,6 @@ import { CorePromisedValue } from '@classes/promised-value';
import { CorePlatform } from '@services/platform'; import { CorePlatform } from '@services/platform';
import { CoreAutoLogoutService } from '@features/autologout/services/autologout'; import { CoreAutoLogoutService } from '@features/autologout/services/autologout';
import '@angular/compiler';
/** /**
* Service to provide functionalities regarding compiling dynamic HTML and Javascript. * Service to provide functionalities regarding compiling dynamic HTML and Javascript.
*/ */
@ -206,6 +204,8 @@ export class CoreCompileProvider {
viewContainerRef: ViewContainerRef, viewContainerRef: ViewContainerRef,
extraImports: any[] = [], // eslint-disable-line @typescript-eslint/no-explicit-any extraImports: any[] = [], // eslint-disable-line @typescript-eslint/no-explicit-any
): Promise<ComponentRef<T> | undefined> { ): Promise<ComponentRef<T> | undefined> {
// Import the Angular compiler to be able to compile components in runtime.
await import('@angular/compiler');
// Create the component using the template and the class. // Create the component using the template and the class.
const component = Component({ template })(componentClass); const component = Component({ template })(componentClass);

View File

@ -540,11 +540,11 @@ export class CoreGradesHelperProvider {
text = text.replace('%2F', '/').replace('%2f', '/'); text = text.replace('%2F', '/').replace('%2f', '/');
if (text.indexOf('/agg_mean') > -1) { if (text.indexOf('/agg_mean') > -1) {
row.itemtype = 'agg_mean'; row.itemtype = 'agg_mean';
row.icon = 'moodle-agg_mean'; row.icon = 'moodle-agg-mean';
row.iconAlt = Translate.instant('core.grades.aggregatemean'); row.iconAlt = Translate.instant('core.grades.aggregatemean');
} else if (text.indexOf('/agg_sum') > -1) { } else if (text.indexOf('/agg_sum') > -1) {
row.itemtype = 'agg_sum'; row.itemtype = 'agg_sum';
row.icon = 'moodle-agg_sum'; row.icon = 'moodle-agg-sum';
row.iconAlt = Translate.instant('core.grades.aggregatesum'); row.iconAlt = Translate.instant('core.grades.aggregatesum');
} else if (text.indexOf('/outcomes') > -1 || text.indexOf('fa-tasks') > -1 || text.indexOf('fa-list-check') > -1) { } else if (text.indexOf('/outcomes') > -1 || text.indexOf('fa-tasks') > -1 || text.indexOf('fa-list-check') > -1) {
row.itemtype = 'outcome'; row.itemtype = 'outcome';

View File

@ -0,0 +1,22 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { CoreIcons } from '@singletons/icons';
/**
* Add custom icons to Ionicons.
*/
export default function(): void {
CoreIcons.addIconsToIonicons();
}

View File

@ -12,41 +12,43 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Http } from '@singletons';
import { CoreConstants } from '../constants'; import { CoreConstants } from '../constants';
import { CoreLogger } from './logger'; import { CoreLogger } from './logger';
import aliases from '@/assets/fonts/font-awesome/aliases.json'; import aliases from '@/assets/fonts/font-awesome/aliases.json';
import { firstValueFrom } from 'rxjs'; import { addIcons } from 'ionicons';
import icons from '@/assets/fonts/icons.json';
/** /**
* Singleton with helper functions for icon management. * Singleton with helper functions for icon management.
*/ */
export class CoreIcons { export class CoreIcons {
/**
* Object used to store whether icons exist or not during development.
*/
private static readonly DEV_ICONS_STATUS: Record<string, Promise<boolean>> = {};
private static readonly ALIASES = { ...aliases } as unknown as Record<string, string>; private static readonly ALIASES = { ...aliases } as unknown as Record<string, string>;
private static readonly CUSTOM_ICONS = { ...icons } as unknown as Record<string, string>;
protected static logger = CoreLogger.getInstance('CoreIcons'); protected static logger = CoreLogger.getInstance('CoreIcons');
/**
* Add custom icons to Ionicons.
*/
static addIconsToIonicons(): void {
addIcons(CoreIcons.CUSTOM_ICONS);
}
/** /**
* Check icon alias and returns the new icon name. * Check icon alias and returns the new icon name.
* *
* @param icon Icon name. * @param icon Icon name.
* @param isAppIcon Whether the icon is in the app's code, false if it's in some user generated content.
* @returns New icon name and new library if changed. * @returns New icon name and new library if changed.
*/ */
static async getFontAwesomeIconFileName(icon: string): Promise<{fileName: string; newLibrary?: string}> { static async getFontAwesomeIconFileName(icon: string, isAppIcon = true): Promise<{fileName: string; newLibrary?: string}> {
let newLibrary: string | undefined = undefined; const newLibrary = icon.endsWith('-o') ? 'regular' : undefined;
if (icon.endsWith('-o')) {
newLibrary = 'regular';
icon = icon.substring(0, icon.length - 2);
}
if (CoreIcons.ALIASES[icon]) { if (CoreIcons.ALIASES[icon]) {
if (isAppIcon) {
this.logger.error(`Icon ${icon} is an alias of ${CoreIcons.ALIASES[icon]}, please use the new name.`); this.logger.error(`Icon ${icon} is an alias of ${CoreIcons.ALIASES[icon]}, please use the new name.`);
}
return { newLibrary, fileName: CoreIcons.ALIASES[icon] }; return { newLibrary, fileName: CoreIcons.ALIASES[icon] };
} }
@ -55,30 +57,24 @@ export class CoreIcons {
} }
/** /**
* Validate that an icon exists, or show warning otherwise (only in development and testing environments). * Validate that an icon exists in the list of custom icons for the app, or show warning otherwise
* (only in development and testing environments).
* *
* @param name Icon name. * @param font Font Family.
* @param src Icon source url. * @param library Library to use.
* @param icon Icon Name.
*/ */
static validateIcon(name: string, src: string): void { static validateIcon(font: string, library: string, icon: string): void {
if (!CoreConstants.BUILD.isDevelopment && !CoreConstants.BUILD.isTesting) { if (!CoreConstants.BUILD.isDevelopment && !CoreConstants.BUILD.isTesting) {
return; return;
} }
if (!(src in CoreIcons.DEV_ICONS_STATUS)) { if (
CoreIcons.DEV_ICONS_STATUS[src] = firstValueFrom(Http.get(src, { responseType: 'text' })) CoreIcons.CUSTOM_ICONS[icon] === undefined &&
.then(() => true) CoreIcons.CUSTOM_ICONS[CoreIcons.prefixIconName(font, library, icon)] === undefined
.catch(() => false); ) {
this.logger.error(`Icon ${icon} not found`);
} }
// eslint-disable-next-line promise/catch-or-return
CoreIcons.DEV_ICONS_STATUS[src].then(exists => {
if (exists) {
return;
}
return this.logger.error(`Icon ${name} not found`);
});
} }
/** /**
@ -164,7 +160,7 @@ export class CoreIcons {
newIcon.setAttribute('aria-hidden', 'true'); newIcon.setAttribute('aria-hidden', 'true');
} }
const { fileName, newLibrary } = await CoreIcons.getFontAwesomeIconFileName(iconName); const { fileName, newLibrary } = await CoreIcons.getFontAwesomeIconFileName(iconName, false);
if (newLibrary) { if (newLibrary) {
library = newLibrary; library = newLibrary;
} }
@ -174,8 +170,7 @@ export class CoreIcons {
newIcon.setAttribute('src', src); newIcon.setAttribute('src', src);
newIcon.classList.add('faicon'); CoreIcons.validateIcon('font-awesome', library, iconName);
CoreIcons.validateIcon(iconName, src);
icon.parentElement?.insertBefore(newIcon, icon); icon.parentElement?.insertBefore(newIcon, icon);
icon.remove(); icon.remove();
@ -195,4 +190,34 @@ export class CoreIcons {
return `assets/fonts/${font}/${library}/${icon}.svg`; return `assets/fonts/${font}/${library}/${icon}.svg`;
} }
/**
* Prefix an icon name using the library prefix.
*
* @param font Font Family.
* @param library Library to use.
* @param icon Icon Name.
* @returns Prefixed icon name.
*/
static prefixIconName(font: string, library: string, icon: string): string {
const prefixes = CoreConstants.CONFIG.iconsPrefixes?.[font]?.[library];
if (!prefixes || !prefixes.length) {
return icon;
}
return `${prefixes[0]}-${icon}`;
}
/**
* Check if an icon name contains any of the prefixes configured for icons.
* If it doesn't then it probably is an Ionicon.
*
* @param icon Icon Name.
* @returns Whether icon is prefixed.
*/
static isIconNamePrefixed(icon: string): boolean {
return Object.values(CoreConstants.CONFIG.iconsPrefixes ?? {})
.some(library => Object.values(library)
.some(prefixes => prefixes.some(prefix => icon.startsWith(`${prefix}-`))));
}
} }

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { CoreConstants } from '@/core/constants';
import { CoreIcons } from '@singletons/icons'; import { CoreIcons } from '@singletons/icons';
describe('CoreIcons singleton', () => { describe('CoreIcons singleton', () => {
@ -81,4 +82,43 @@ describe('CoreIcons singleton', () => {
.toEqual('assets/fonts/font-awesome/solid/square-check.svg'); .toEqual('assets/fonts/font-awesome/solid/square-check.svg');
}); });
it('prefixes icons names', () => {
// Arrange.
CoreConstants.CONFIG.iconsPrefixes = {
foo: {
bar: ['fo', 'for'],
baz: ['foz'],
},
lorem: {
ipsum: ['lorip'],
},
};
// Act and assert.
expect(CoreIcons.prefixIconName('foo', 'bar', 'myicon')).toEqual('fo-myicon');
expect(CoreIcons.prefixIconName('foo', 'baz', 'myicon')).toEqual('foz-myicon');
expect(CoreIcons.prefixIconName('lorem', 'ipsum', 'myicon')).toEqual('lorip-myicon');
expect(CoreIcons.prefixIconName('invalid', 'invalid', 'myicon')).toEqual('myicon');
});
it('check if an icon is prefixed', () => {
// Arrange.
CoreConstants.CONFIG.iconsPrefixes = {
foo: {
bar: ['fo', 'for'],
baz: ['foz'],
},
lorem: {
ipsum: ['lorip'],
},
};
// Act and assert.
expect(CoreIcons.isIconNamePrefixed('fo-myicon')).toEqual(true);
expect(CoreIcons.isIconNamePrefixed('foz-myicon')).toEqual(true);
expect(CoreIcons.isIconNamePrefixed('lorip-myicon')).toEqual(true);
expect(CoreIcons.isIconNamePrefixed('myicon')).toEqual(false);
expect(CoreIcons.isIconNamePrefixed('fox-myicon')).toEqual(false);
});
}); });

View File

@ -75,4 +75,5 @@ export interface EnvironmentConfig {
disableTokenFile: boolean; // Disable the use of tokenpluginfile.php for downloading files (so it fallbacks to pluginfile.php) disableTokenFile: boolean; // Disable the use of tokenpluginfile.php for downloading files (so it fallbacks to pluginfile.php)
demoMode?: boolean; // Whether to run the app in "demo mode". demoMode?: boolean; // Whether to run the app in "demo mode".
hideInformativeLinks?: boolean; // Whether to hide informative links. hideInformativeLinks?: boolean; // Whether to hide informative links.
iconsPrefixes?: Record<string, Record<string, string[]>>; // Prefixes for custom font icons (located in src/assets/fonts).
} }