feature: social media share
This addresses Exam Simulator and Social Media Fixes #135
@@ -508,6 +508,12 @@
|
|||||||
function showSummary() {
|
function showSummary() {
|
||||||
reviewPartIndex.value = null;
|
reviewPartIndex.value = null;
|
||||||
view.value = "summary";
|
view.value = "summary";
|
||||||
|
if (completedResultUrl.value) {
|
||||||
|
var target = new URL(completedResultUrl.value);
|
||||||
|
if (window.location.pathname !== target.pathname || window.location.search !== target.search) {
|
||||||
|
window.location.assign(target.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,6 +554,9 @@
|
|||||||
var questionIds = session.value && session.value.homeworkIds;
|
var questionIds = session.value && session.value.homeworkIds;
|
||||||
if (!questionIds) return;
|
if (!questionIds) return;
|
||||||
clearStoredSession();
|
clearStoredSession();
|
||||||
|
var homeworkUrl = new URL("simulation.html", window.location.href);
|
||||||
|
homeworkUrl.search = "?homework=" + questionIds.join("+");
|
||||||
|
window.history.replaceState(null, "", homeworkUrl.toString());
|
||||||
startHomework(questionIds);
|
startHomework(questionIds);
|
||||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
}
|
}
|
||||||
@@ -598,9 +607,18 @@
|
|||||||
return "1." + result.choice.id + "." + result.scores.join(".");
|
return "1." + result.choice.id + "." + result.scores.join(".");
|
||||||
}
|
}
|
||||||
|
|
||||||
var resultForSharing = computed(function () {
|
function resultImageCode(result) {
|
||||||
if (sharedResult.value) return sharedResult.value;
|
if (!result) return "";
|
||||||
if (!session.value || view.value !== "summary") return null;
|
if (result.kind === "homework") return result.score === result.total ? "aenbv" : "";
|
||||||
|
if (!result.passed) return "";
|
||||||
|
var order = ["A", "E", "N", "B", "V"];
|
||||||
|
return order.filter(function (part) {
|
||||||
|
return result.choice.parts.indexOf(part) !== -1;
|
||||||
|
}).join("").toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function shareableSessionResult() {
|
||||||
|
if (!session.value) return null;
|
||||||
if (isHomework.value) {
|
if (isHomework.value) {
|
||||||
return { kind: "homework", score: session.value.parts[0].score, total: session.value.parts[0].questions.length };
|
return { kind: "homework", score: session.value.parts[0].score, total: session.value.parts[0].questions.length };
|
||||||
}
|
}
|
||||||
@@ -611,13 +629,33 @@
|
|||||||
scores: session.value.parts.map(function (part) { return part.score; }),
|
scores: session.value.parts.map(function (part) { return part.score; }),
|
||||||
passed: overallState.value === "passed"
|
passed: overallState.value === "passed"
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultForSharing = computed(function () {
|
||||||
|
if (sharedResult.value) return sharedResult.value;
|
||||||
|
if (view.value !== "summary") return null;
|
||||||
|
return shareableSessionResult();
|
||||||
|
});
|
||||||
|
|
||||||
|
function buildResultUrl(result) {
|
||||||
|
if (!result) return "";
|
||||||
|
var imageCode = resultImageCode(result);
|
||||||
|
var url = new URL(imageCode ? "result-" + imageCode + ".html" : "result.html", window.location.href);
|
||||||
|
url.search = "?result=" + encodedResult(result);
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
var completedResultUrl = computed(function () {
|
||||||
|
return buildResultUrl(shareableSessionResult());
|
||||||
});
|
});
|
||||||
|
|
||||||
var resultShareUrl = computed(function () {
|
var resultShareUrl = computed(function () {
|
||||||
if (!resultForSharing.value) return "";
|
return buildResultUrl(resultForSharing.value);
|
||||||
var url = new URL("simulation.html", window.location.href);
|
});
|
||||||
url.search = "?result=" + encodedResult(resultForSharing.value);
|
|
||||||
return url.toString();
|
var resultImageUrl = computed(function () {
|
||||||
|
var imageCode = resultImageCode(resultForSharing.value);
|
||||||
|
return imageCode ? "assets/images/exam-" + imageCode + ".jpeg" : "";
|
||||||
});
|
});
|
||||||
|
|
||||||
var shareText = computed(function () {
|
var shareText = computed(function () {
|
||||||
@@ -654,17 +692,7 @@
|
|||||||
navigator.share({ title: "50ohm.de-Ergebnis", text: shareText.value, url: resultShareUrl.value }).catch(function () {});
|
navigator.share({ title: "50ohm.de-Ergebnis", text: shareText.value, url: resultShareUrl.value }).catch(function () {});
|
||||||
}
|
}
|
||||||
|
|
||||||
function shareMatrix() {
|
function restoreSession(homeworkQuestionIds, allowHomeworkResult) {
|
||||||
if (navigator.share) {
|
|
||||||
navigator.share({ title: "50ohm.de-Ergebnis", text: shareText.value, url: resultShareUrl.value }).catch(function () {});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
copyText(shareText.value + " " + resultShareUrl.value).finally(function () {
|
|
||||||
window.open("https://chat.darc.de", "_blank", "noopener");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function restoreSession(homeworkQuestionIds) {
|
|
||||||
try {
|
try {
|
||||||
var raw = window.localStorage.getItem(STORAGE_KEY);
|
var raw = window.localStorage.getItem(STORAGE_KEY);
|
||||||
if (!raw) return;
|
if (!raw) return;
|
||||||
@@ -675,8 +703,8 @@
|
|||||||
}
|
}
|
||||||
var homeworkMode = stored.session.mode === "homework";
|
var homeworkMode = stored.session.mode === "homework";
|
||||||
var choice = EXAM_CHOICES.find(function (item) { return item.id === stored.session.choiceId; });
|
var choice = EXAM_CHOICES.find(function (item) { return item.id === stored.session.choiceId; });
|
||||||
var homeworkMatches = homeworkMode && homeworkQuestionIds.length > 0 &&
|
var homeworkMatches = homeworkMode && (allowHomeworkResult || (homeworkQuestionIds.length > 0 &&
|
||||||
JSON.stringify(stored.session.homeworkIds) === JSON.stringify(homeworkQuestionIds);
|
JSON.stringify(stored.session.homeworkIds) === JSON.stringify(homeworkQuestionIds)));
|
||||||
var valid = (homeworkMatches || choice) && stored.session.parts.every(function (part) {
|
var valid = (homeworkMatches || choice) && stored.session.parts.every(function (part) {
|
||||||
return Array.isArray(part.questions) && (homeworkMode || part.questions.length === QUESTION_COUNT) && part.questions.every(function (question) {
|
return Array.isArray(part.questions) && (homeworkMode || part.questions.length === QUESTION_COUNT) && part.questions.every(function (question) {
|
||||||
return Boolean(catalog.value[question.id]);
|
return Boolean(catalog.value[question.id]);
|
||||||
@@ -743,8 +771,16 @@
|
|||||||
var homeworkQuestionIds = parseHomeworkQuestionIds();
|
var homeworkQuestionIds = parseHomeworkQuestionIds();
|
||||||
if (queryValue("result")) {
|
if (queryValue("result")) {
|
||||||
if (!parsedResult) throw new Error("Der Ergebnislink ist ungültig.");
|
if (!parsedResult) throw new Error("Der Ergebnislink ist ungültig.");
|
||||||
|
restoreSession([], true);
|
||||||
|
var storedResult = shareableSessionResult();
|
||||||
|
if (storedResult && encodedResult(storedResult) === queryValue("result")) {
|
||||||
|
sharedResult.value = null;
|
||||||
|
view.value = "summary";
|
||||||
|
} else {
|
||||||
|
session.value = null;
|
||||||
sharedResult.value = parsedResult;
|
sharedResult.value = parsedResult;
|
||||||
view.value = "shared";
|
view.value = "shared";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
restoreSession(homeworkQuestionIds);
|
restoreSession(homeworkQuestionIds);
|
||||||
if (homeworkQuestionIds.length > 0 && !session.value) startHomework(homeworkQuestionIds);
|
if (homeworkQuestionIds.length > 0 && !session.value) startHomework(homeworkQuestionIds);
|
||||||
@@ -841,12 +877,13 @@
|
|||||||
resultIcon: resultIcon,
|
resultIcon: resultIcon,
|
||||||
resultDescription: resultDescription,
|
resultDescription: resultDescription,
|
||||||
resultForSharing: resultForSharing,
|
resultForSharing: resultForSharing,
|
||||||
|
completedResultUrl: completedResultUrl,
|
||||||
resultShareUrl: resultShareUrl,
|
resultShareUrl: resultShareUrl,
|
||||||
|
resultImageUrl: resultImageUrl,
|
||||||
shareText: shareText,
|
shareText: shareText,
|
||||||
socialShareLinks: socialShareLinks,
|
socialShareLinks: socialShareLinks,
|
||||||
copyResultLink: copyResultLink,
|
copyResultLink: copyResultLink,
|
||||||
shareNative: shareNative,
|
shareNative: shareNative
|
||||||
shareMatrix: shareMatrix
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
|
After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 325 KiB |
|
After Width: | Height: | Size: 325 KiB |
|
After Width: | Height: | Size: 326 KiB |
|
After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 325 KiB |
|
After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 325 KiB |
|
After Width: | Height: | Size: 324 KiB |
@@ -129,6 +129,7 @@ class Build:
|
|||||||
sidebar=None,
|
sidebar=None,
|
||||||
page_stylesheets=None,
|
page_stylesheets=None,
|
||||||
page_scripts=None,
|
page_scripts=None,
|
||||||
|
social_image=None,
|
||||||
):
|
):
|
||||||
page_template = self.env.get_template("html/page.html")
|
page_template = self.env.get_template("html/page.html")
|
||||||
return page_template.render(
|
return page_template.render(
|
||||||
@@ -137,6 +138,7 @@ class Build:
|
|||||||
sidebar=sidebar,
|
sidebar=sidebar,
|
||||||
page_stylesheets=page_stylesheets or [],
|
page_stylesheets=page_stylesheets or [],
|
||||||
page_scripts=page_scripts or [],
|
page_scripts=page_scripts or [],
|
||||||
|
social_image=social_image,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __build_exam_simulator(self):
|
def __build_exam_simulator(self):
|
||||||
@@ -163,11 +165,28 @@ class Build:
|
|||||||
page_scripts=[
|
page_scripts=[
|
||||||
"assets/vue.global.prod.js",
|
"assets/vue.global.prod.js",
|
||||||
"assets/question-catalog.js?ts=20260806",
|
"assets/question-catalog.js?ts=20260806",
|
||||||
"assets/exam-simulator.js?ts=20260806",
|
"assets/exam-simulator.js?ts=202608063",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
with (self.config.p_build / "simulation.html").open("w", encoding="utf-8") as file:
|
with (self.config.p_build / "simulation.html").open("w", encoding="utf-8") as file:
|
||||||
file.write(page)
|
file.write(page)
|
||||||
|
with (self.config.p_build / "result.html").open("w", encoding="utf-8") as file:
|
||||||
|
file.write(page)
|
||||||
|
|
||||||
|
for social_image in sorted((self.config.p_assets / "images").glob("exam-*.jpeg")):
|
||||||
|
result_page = self.__build_page(
|
||||||
|
content,
|
||||||
|
page_stylesheets=["assets/exam-simulator.css?ts=20260806"],
|
||||||
|
page_scripts=[
|
||||||
|
"assets/vue.global.prod.js",
|
||||||
|
"assets/question-catalog.js?ts=20260806",
|
||||||
|
"assets/exam-simulator.js?ts=202608063",
|
||||||
|
],
|
||||||
|
social_image=f"assets/images/{social_image.name}",
|
||||||
|
)
|
||||||
|
result_name = f"result-{social_image.stem.removeprefix('exam-')}.html"
|
||||||
|
with (self.config.p_build / result_name).open("w", encoding="utf-8") as file:
|
||||||
|
file.write(result_page)
|
||||||
|
|
||||||
def __build_homework_tool(self):
|
def __build_homework_tool(self):
|
||||||
toc_target = self.config.p_build_assets / "toc"
|
toc_target = self.config.p_build_assets / "toc"
|
||||||
@@ -182,7 +201,7 @@ class Build:
|
|||||||
page_scripts=[
|
page_scripts=[
|
||||||
"assets/vue.global.prod.js",
|
"assets/vue.global.prod.js",
|
||||||
"assets/question-catalog.js?ts=20260806",
|
"assets/question-catalog.js?ts=20260806",
|
||||||
"assets/homework.js?ts=20260806",
|
"assets/homework.js?ts=202608061",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
with (self.config.p_build / "hausaufgabe.html").open("w", encoding="utf-8") as file:
|
with (self.config.p_build / "hausaufgabe.html").open("w", encoding="utf-8") as file:
|
||||||
|
|||||||
@@ -36,16 +36,19 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<img v-if="resultImageUrl" class="img-fluid border mb-4" :src="resultImageUrl"
|
||||||
|
alt="50ohm.de-Prüfungssimulation">
|
||||||
<div v-if="resultForSharing" class="border-top pt-3 mt-3">
|
<div v-if="resultForSharing" class="border-top pt-3 mt-3">
|
||||||
<h2 class="h5">Ergebnis teilen</h2>
|
<h2 class="h5">Ergebnis teilen</h2>
|
||||||
<div class="d-flex flex-wrap gap-2">
|
<div class="d-flex flex-wrap gap-2">
|
||||||
<button type="button" class="btn btn-outline-secondary btn-sm" @click="shareNative">Teilen …</button>
|
<button type="button" class="btn btn-outline-secondary btn-sm d-inline-flex align-items-center" @click="shareNative">
|
||||||
|
<span class="material-icons fs-6 me-1" aria-hidden="true">share</span> Teilen …
|
||||||
|
</button>
|
||||||
<button type="button" class="btn btn-outline-secondary btn-sm" @click="copyResultLink">Link kopieren</button>
|
<button type="button" class="btn btn-outline-secondary btn-sm" @click="copyResultLink">Link kopieren</button>
|
||||||
<button type="button" class="btn btn-outline-secondary btn-sm" @click="shareMatrix" title="Über die Teilen-Funktion an Element senden">Matrix / Element</button>
|
|
||||||
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.mastodon" target="_blank" rel="noopener noreferrer">Mastodon</a>
|
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.mastodon" target="_blank" rel="noopener noreferrer">Mastodon</a>
|
||||||
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.bluesky" target="_blank" rel="noopener noreferrer">Bluesky</a>
|
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.bluesky" target="_blank" rel="noopener noreferrer">Bluesky</a>
|
||||||
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.facebook" target="_blank" rel="noopener noreferrer">Facebook</a>
|
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.facebook" target="_blank" rel="noopener noreferrer">Facebook</a>
|
||||||
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.x" target="_blank" rel="noopener noreferrer">X</a>
|
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.x" target="_blank" rel="noopener noreferrer">X (ehem. Twitter)</a>
|
||||||
</div>
|
</div>
|
||||||
<small v-if="shareMessage" class="d-block mt-2" role="status" v-text="shareMessage"></small>
|
<small v-if="shareMessage" class="d-block mt-2" role="status" v-text="shareMessage"></small>
|
||||||
</div>
|
</div>
|
||||||
@@ -145,16 +148,19 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<img v-if="resultImageUrl" class="img-fluid border mb-4" :src="resultImageUrl"
|
||||||
|
alt="50ohm.de-Prüfungssimulation">
|
||||||
<div v-if="resultForSharing" class="border-top pt-3 mt-3">
|
<div v-if="resultForSharing" class="border-top pt-3 mt-3">
|
||||||
<h2 class="h5">Ergebnis teilen</h2>
|
<h2 class="h5">Ergebnis teilen</h2>
|
||||||
<div class="d-flex flex-wrap gap-2">
|
<div class="d-flex flex-wrap gap-2">
|
||||||
<button type="button" class="btn btn-outline-secondary btn-sm" @click="shareNative">Teilen …</button>
|
<button type="button" class="btn btn-outline-secondary btn-sm d-inline-flex align-items-center" @click="shareNative">
|
||||||
|
<span class="material-icons fs-6 me-1" aria-hidden="true">share</span> Teilen …
|
||||||
|
</button>
|
||||||
<button type="button" class="btn btn-outline-secondary btn-sm" @click="copyResultLink">Link kopieren</button>
|
<button type="button" class="btn btn-outline-secondary btn-sm" @click="copyResultLink">Link kopieren</button>
|
||||||
<button type="button" class="btn btn-outline-secondary btn-sm" @click="shareMatrix" title="Über die Teilen-Funktion an Element senden">Matrix / Element</button>
|
|
||||||
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.mastodon" target="_blank" rel="noopener noreferrer">Mastodon</a>
|
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.mastodon" target="_blank" rel="noopener noreferrer">Mastodon</a>
|
||||||
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.bluesky" target="_blank" rel="noopener noreferrer">Bluesky</a>
|
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.bluesky" target="_blank" rel="noopener noreferrer">Bluesky</a>
|
||||||
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.facebook" target="_blank" rel="noopener noreferrer">Facebook</a>
|
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.facebook" target="_blank" rel="noopener noreferrer">Facebook</a>
|
||||||
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.x" target="_blank" rel="noopener noreferrer">X</a>
|
<a class="btn btn-outline-secondary btn-sm" :href="socialShareLinks.x" target="_blank" rel="noopener noreferrer">X (ehem. Twitter)</a>
|
||||||
</div>
|
</div>
|
||||||
<small v-if="shareMessage" class="d-block mt-2" role="status" v-text="shareMessage"></small>
|
<small v-if="shareMessage" class="d-block mt-2" role="status" v-text="shareMessage"></small>
|
||||||
</div>
|
</div>
|
||||||
@@ -191,8 +197,9 @@
|
|||||||
<span v-else-if="currentPart.status !== 'running'" class="btn btn-secondary btn-sm d-inline-flex align-items-center">Abgegeben</span>
|
<span v-else-if="currentPart.status !== 'running'" class="btn btn-secondary btn-sm d-inline-flex align-items-center">Abgegeben</span>
|
||||||
<button v-if="currentPart.status === 'evaluated' && !isReviewing && hasNextPart"
|
<button v-if="currentPart.status === 'evaluated' && !isReviewing && hasNextPart"
|
||||||
type="button" class="btn btn-primary btn-sm" @click="startNextPart">Nächsten Teil starten</button>
|
type="button" class="btn btn-primary btn-sm" @click="startNextPart">Nächsten Teil starten</button>
|
||||||
<button v-else-if="currentPart.status === 'evaluated' && !isReviewing"
|
<a v-else-if="currentPart.status === 'evaluated' && !isReviewing"
|
||||||
type="button" class="btn btn-primary btn-sm" @click="showSummary" v-text="isHomework ? 'Ergebnis' : 'Gesamtergebnis'"></button>
|
class="btn btn-primary btn-sm" :href="completedResultUrl"
|
||||||
|
v-text="isHomework ? 'Ergebnis' : 'Gesamtergebnis'"></a>
|
||||||
<button v-else-if="isReviewing" type="button" class="btn btn-primary btn-sm" @click="showSummary">Gesamtergebnis</button>
|
<button v-else-if="isReviewing" type="button" class="btn btn-primary btn-sm" @click="showSummary">Gesamtergebnis</button>
|
||||||
<button type="button" class="btn btn-secondary btn-sm d-inline-flex align-items-center" @click="abortExam"
|
<button type="button" class="btn btn-secondary btn-sm d-inline-flex align-items-center" @click="abortExam"
|
||||||
v-text="isHomework ? 'Hausaufgabe abbrechen' : 'Simulation abbrechen'"></button>
|
v-text="isHomework ? 'Hausaufgabe abbrechen' : 'Simulation abbrechen'"></button>
|
||||||
@@ -259,7 +266,8 @@
|
|||||||
<button type="button" class="btn btn-outline-secondary" @click="finishExamEarly">Simulation hier beenden</button>
|
<button type="button" class="btn btn-outline-secondary" @click="finishExamEarly">Simulation hier beenden</button>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<button type="button" class="btn btn-primary" @click="showSummary" v-text="isHomework ? 'Ergebnis anzeigen' : 'Gesamtergebnis anzeigen'"></button>
|
<a class="btn btn-primary" :href="completedResultUrl"
|
||||||
|
v-text="isHomework ? 'Ergebnis anzeigen' : 'Gesamtergebnis anzeigen'"></a>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -4,6 +4,21 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>50Ω Lernplattform für Amateurfunk</title>
|
<title>50Ω Lernplattform für Amateurfunk</title>
|
||||||
|
{% if social_image %}
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:site_name" content="50ohm.de">
|
||||||
|
<meta property="og:title" content="Ergebnis der 50ohm.de-Prüfungssimulation">
|
||||||
|
<meta property="og:description" content="Mein Ergebnis der Prüfungssimulation auf 50ohm.de">
|
||||||
|
<meta property="og:image" content="https://50ohm.de/{{ social_image }}">
|
||||||
|
<meta property="og:image:alt" content="50ohm.de-Prüfungssimulation">
|
||||||
|
<meta property="og:image:width" content="1448">
|
||||||
|
<meta property="og:image:height" content="1086">
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:title" content="Ergebnis der 50ohm.de-Prüfungssimulation">
|
||||||
|
<meta name="twitter:description" content="Mein Ergebnis der Prüfungssimulation auf 50ohm.de">
|
||||||
|
<meta name="twitter:image" content="https://50ohm.de/{{ social_image }}">
|
||||||
|
<meta name="twitter:image:alt" content="50ohm.de-Prüfungssimulation">
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<link rel="icon" href="assets/favicon/icon.png" type="image/png">
|
<link rel="icon" href="assets/favicon/icon.png" type="image/png">
|
||||||
<link rel="icon" href="assets/favicon/icon.svg" type="image/svg+xml">
|
<link rel="icon" href="assets/favicon/icon.svg" type="image/svg+xml">
|
||||||
|
|||||||