Release v2.4.0 — Erzwungene Einstellungen: about:config-Prefs bei jedem Start setzen
Neuer forcedPrefs-WebExtension-Experiment (Services.prefs) setzt beim Start globale Prefs (Startseite aus, Kartenansicht, nicht gruppiert, absteigend, 3-Spalten-Layout, nicht auto-gelesen) sowie pro Identität die Signatur-/ Antwort-Einstellungen. Pro Setting im Options-Dialog (⚙-Tab) abschaltbar. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015PZHd9vRsBiH8cKYdQo1Yv
This commit is contained in:
@@ -98,6 +98,55 @@ browser.storage.onChanged.addListener((changes, area) => {
|
|||||||
});
|
});
|
||||||
updateErledigtPopup();
|
updateErledigtPopup();
|
||||||
|
|
||||||
|
// ── about:config-Prefs bei jedem Start erzwingen ──
|
||||||
|
//
|
||||||
|
// Manche Thunderbird-Einstellungen (Startseite, Ansicht, Layout, pro Konto das
|
||||||
|
// Signatur-/Antwort-Verhalten) „springen zurück" und müssen sonst von Hand
|
||||||
|
// nachgezogen werden. Wir setzen sie bei JEDEM Start neu über den privilegierten
|
||||||
|
// forcedPrefs-Experiment (Services.prefs). Die Liste liegt in
|
||||||
|
// lib/forced-prefs-list.js (globale FORCED_PREFS / FORCED_IDENTITY_PREFS).
|
||||||
|
// Pro Eintrag kann der Client im Options-Dialog opt-outen (forced_prefs_optout).
|
||||||
|
const FORCED_PREFS_OPTOUT_KEY = 'forced_prefs_optout';
|
||||||
|
|
||||||
|
async function enforceForcedPrefs() {
|
||||||
|
const stored = await browser.storage.local.get(FORCED_PREFS_OPTOUT_KEY);
|
||||||
|
const disabled = new Set(stored[FORCED_PREFS_OPTOUT_KEY] || []);
|
||||||
|
|
||||||
|
// Globale Prefs
|
||||||
|
for (const p of FORCED_PREFS) {
|
||||||
|
if (disabled.has(p.id)) continue;
|
||||||
|
try {
|
||||||
|
await browser.forcedPrefs.setPref(p.name, p.type, p.value);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[forcedPrefs] konnte', p.name, 'nicht setzen:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pro-Identität-Prefs — über alle Konten/Identitäten iterieren
|
||||||
|
const accounts = await browser.accounts.list();
|
||||||
|
for (const acc of accounts) {
|
||||||
|
for (const ident of (acc.identities || [])) {
|
||||||
|
for (const p of FORCED_IDENTITY_PREFS) {
|
||||||
|
if (disabled.has(p.id)) continue;
|
||||||
|
const name = `mail.identity.${ident.id}.${p.suffix}`;
|
||||||
|
try {
|
||||||
|
await browser.forcedPrefs.setPref(name, p.type, p.value);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[forcedPrefs] konnte', name, 'nicht setzen:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enforceForcedPrefs(); // Top-Level = läuft einmal pro TB-Start
|
||||||
|
|
||||||
|
// Wird eine Pref im Options-Dialog wieder AKTIVIERT, sofort anwenden
|
||||||
|
// (statt bis zum nächsten Neustart zu warten).
|
||||||
|
browser.storage.onChanged.addListener((changes, area) => {
|
||||||
|
if (area === 'local' && changes[FORCED_PREFS_OPTOUT_KEY]) enforceForcedPrefs();
|
||||||
|
});
|
||||||
|
|
||||||
// ── Signatur in neuen Compose-Fenstern automatisch aktualisieren ──
|
// ── Signatur in neuen Compose-Fenstern automatisch aktualisieren ──
|
||||||
//
|
//
|
||||||
// Thunderbird merkt sich die Signatur einer Identität pro Sitzung. Öffnet der
|
// Thunderbird merkt sich die Signatur einer Identität pro Sitzung. Öffnet der
|
||||||
|
|||||||
61
experiments/prefs/api.js
Normal file
61
experiments/prefs/api.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Privilegierter Parent-Scope: schreibt/liest about:config-Prefs über Services.prefs.
|
||||||
|
// Der Modulpfad von ExtensionCommon wechselte mitten im TB-Lebenszyklus:
|
||||||
|
// TB 115+ -> ExtensionCommon.sys.mjs (ESM)
|
||||||
|
// TB 109-114 -> ExtensionCommon.jsm (JSM)
|
||||||
|
// Deshalb defensiv laden, damit dieselbe api.js über die Flotte funktioniert.
|
||||||
|
var { ExtensionCommon } = (() => {
|
||||||
|
try {
|
||||||
|
return ChromeUtils.importESModule(
|
||||||
|
"resource://gre/modules/ExtensionCommon.sys.mjs"
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
// `Services` ist im privilegierten ExtensionAPI-Sandbox in TB 109+ global
|
||||||
|
// (stellt Services.prefs = nsIPrefBranch bereit); kein Import nötig.
|
||||||
|
|
||||||
|
this.forcedPrefs = class extends ExtensionCommon.ExtensionAPI {
|
||||||
|
getAPI(context) {
|
||||||
|
return {
|
||||||
|
forcedPrefs: {
|
||||||
|
async setPref(name, type, value) {
|
||||||
|
switch (type) {
|
||||||
|
case "bool":
|
||||||
|
Services.prefs.setBoolPref(name, !!value);
|
||||||
|
break;
|
||||||
|
case "int":
|
||||||
|
Services.prefs.setIntPref(name, value | 0);
|
||||||
|
break;
|
||||||
|
case "string":
|
||||||
|
// setStringPref ist unicode-sicher (im Gegensatz zu setCharPref).
|
||||||
|
Services.prefs.setStringPref(name, String(value));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("forcedPrefs.setPref: unknown type " + type);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
async getPref(name, type) {
|
||||||
|
if (Services.prefs.getPrefType(name) === Services.prefs.PREF_INVALID) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case "bool":
|
||||||
|
return Services.prefs.getBoolPref(name, false);
|
||||||
|
case "int":
|
||||||
|
return Services.prefs.getIntPref(name, 0);
|
||||||
|
case "string":
|
||||||
|
return Services.prefs.getStringPref(name, "");
|
||||||
|
default:
|
||||||
|
throw new Error("forcedPrefs.getPref: unknown type " + type);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
29
experiments/prefs/schema.json
Normal file
29
experiments/prefs/schema.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"namespace": "forcedPrefs",
|
||||||
|
"description": "Read/write about:config preferences from a privileged parent context.",
|
||||||
|
"functions": [
|
||||||
|
{
|
||||||
|
"name": "setPref",
|
||||||
|
"type": "function",
|
||||||
|
"async": true,
|
||||||
|
"description": "Force an about:config pref to a fixed value. type is one of 'bool','int','string'.",
|
||||||
|
"parameters": [
|
||||||
|
{ "name": "name", "type": "string" },
|
||||||
|
{ "name": "type", "type": "string", "enum": ["bool", "int", "string"] },
|
||||||
|
{ "name": "value", "type": "any" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getPref",
|
||||||
|
"type": "function",
|
||||||
|
"async": true,
|
||||||
|
"description": "Read an about:config pref; returns null if unset.",
|
||||||
|
"parameters": [
|
||||||
|
{ "name": "name", "type": "string" },
|
||||||
|
{ "name": "type", "type": "string", "enum": ["bool", "int", "string"] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
52
lib/forced-prefs-list.js
Normal file
52
lib/forced-prefs-list.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Single Source of Truth für die about:config-Prefs, die bei jedem TB-Start
|
||||||
|
// erzwungen werden. Wird als klassisches Script in BEIDEN Kontexten geladen
|
||||||
|
// (Background + Options-Seite), definiert also nur globale Variablen — keine
|
||||||
|
// import/export-Mechanik.
|
||||||
|
//
|
||||||
|
// Jeder Eintrag:
|
||||||
|
// id - stabiler Opt-out-Key (NIE umbenennen/wiederverwenden; landet in storage)
|
||||||
|
// label - Text im Options-Dialog
|
||||||
|
// type - "bool" | "int" | "string"
|
||||||
|
// value - der bei jedem Start erzwungene Wert
|
||||||
|
// FORCED_PREFS zusätzlich: name - voller about:config-Pref-Name
|
||||||
|
// FORCED_IDENTITY_PREFS: suffix - voller Name = mail.identity.<id>.<suffix>
|
||||||
|
//
|
||||||
|
// WICHTIG: Werte an einem korrekt eingerichteten Referenz-Thunderbird
|
||||||
|
// (Doku-Seite „Thunderbird einrichten", Page 99) per about:config gegenprüfen —
|
||||||
|
// dieser Code läuft privilegiert auf allen Clients, ein Tippfehler trifft alle.
|
||||||
|
|
||||||
|
var FORCED_PREFS = [
|
||||||
|
{ id: "start_page_off", label: "Startseite beim Öffnen ausblenden",
|
||||||
|
name: "mailnews.start_page.enabled", type: "bool", value: false },
|
||||||
|
{ id: "listview_cards", label: "Kartenansicht (statt Tabelle)",
|
||||||
|
name: "mail.threadpane.listview", type: "int", value: 0 },
|
||||||
|
{ id: "no_grouping", label: "Nachrichten nicht gruppieren",
|
||||||
|
name: "mailnews.default_view_flags", type: "int", value: 0 },
|
||||||
|
{ id: "sort_descending", label: "Neue Nachrichten oben (absteigend)",
|
||||||
|
name: "mailnews.default_sort_order", type: "int", value: 2 },
|
||||||
|
{ id: "layout_3col", label: "3-Spalten-Ansicht (vertikales Layout)",
|
||||||
|
name: "mail.pane_config.dynamic", type: "int", value: 2 },
|
||||||
|
{ id: "no_auto_read", label: "Nachrichten nicht automatisch als gelesen markieren",
|
||||||
|
name: "mailnews.mark_message_read.auto", type: "bool", value: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Pro Identität angewandt: voller Name = mail.identity.<id>.<suffix>
|
||||||
|
var FORCED_IDENTITY_PREFS = [
|
||||||
|
{ id: "reply_on_top", label: "Antwort über dem Zitat",
|
||||||
|
suffix: "reply_on_top", type: "int", value: 1 },
|
||||||
|
// sig_bottom=false -> Signatur direkt unter der Antwort, VOR dem Zitat.
|
||||||
|
// (true wäre ganz unten nach dem Zitat.)
|
||||||
|
{ id: "sig_bottom", label: "Signatur unter Antwort / vor dem Zitat",
|
||||||
|
suffix: "sig_bottom", type: "bool", value: false },
|
||||||
|
{ id: "sig_on_reply", label: "Signatur bei Antworten beifügen",
|
||||||
|
suffix: "sig_on_reply", type: "bool", value: true },
|
||||||
|
{ id: "sig_on_fwd", label: "Signatur beim Weiterleiten beifügen",
|
||||||
|
suffix: "sig_on_fwd", type: "bool", value: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
if (typeof globalThis !== "undefined") {
|
||||||
|
globalThis.FORCED_PREFS = FORCED_PREFS;
|
||||||
|
globalThis.FORCED_IDENTITY_PREFS = FORCED_IDENTITY_PREFS;
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "HPS Vorlagen & Signaturen",
|
"name": "HPS Vorlagen & Signaturen",
|
||||||
"version": "2.3.6",
|
"version": "2.4.0",
|
||||||
"description": "Vorlagen- und Signaturverwaltung für Hotel Park Soltau mit Git-Sync",
|
"description": "Vorlagen- und Signaturverwaltung für Hotel Park Soltau mit Git-Sync",
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
@@ -29,11 +29,22 @@
|
|||||||
],
|
],
|
||||||
"background": {
|
"background": {
|
||||||
"scripts": [
|
"scripts": [
|
||||||
|
"lib/forced-prefs-list.js",
|
||||||
"lib/gitea-sync.js",
|
"lib/gitea-sync.js",
|
||||||
"background.js"
|
"background.js"
|
||||||
],
|
],
|
||||||
"persistent": true
|
"persistent": true
|
||||||
},
|
},
|
||||||
|
"experiment_apis": {
|
||||||
|
"forcedPrefs": {
|
||||||
|
"schema": "experiments/prefs/schema.json",
|
||||||
|
"parent": {
|
||||||
|
"scopes": ["addon_parent"],
|
||||||
|
"paths": [["forcedPrefs"]],
|
||||||
|
"script": "experiments/prefs/api.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"browser_action": {
|
"browser_action": {
|
||||||
"default_icon": {
|
"default_icon": {
|
||||||
"16": "icons/icon.png",
|
"16": "icons/icon.png",
|
||||||
|
|||||||
Binary file not shown.
@@ -1049,6 +1049,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Erzwungene Einstellungen -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title">Erzwungene Einstellungen</div>
|
||||||
|
<div class="card-desc">Diese Thunderbird-Einstellungen werden bei jedem Start automatisch gesetzt (sie „springen" sonst zurück). Zum Abschalten den jeweiligen Haken entfernen — der bisherige Wert wird dabei <strong>nicht</strong> wiederhergestellt, es wird nur nicht mehr erzwungen.</div>
|
||||||
|
<div id="forced-prefs-list"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="sync-log"></div>
|
<div id="sync-log"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1088,6 +1095,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- geteilte Pref-Liste muss VOR templates_options.js geladen werden -->
|
||||||
|
<script src="../lib/forced-prefs-list.js"></script>
|
||||||
<script src="templates_options.js"></script>
|
<script src="templates_options.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1978,3 +1978,51 @@ window.addEventListener('load', async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Opt-out-Schalter für erzwungene Prefs ──
|
||||||
|
// FORCED_PREFS / FORCED_IDENTITY_PREFS stammen aus ../lib/forced-prefs-list.js
|
||||||
|
// (im HTML vor diesem Script geladen). Gespeichert werden die ids der
|
||||||
|
// DEAKTIVIERTEN Prefs unter 'forced_prefs_optout'; Haken = wird erzwungen.
|
||||||
|
const FORCED_PREFS_OPTOUT_KEY = 'forced_prefs_optout';
|
||||||
|
|
||||||
|
async function renderForcedPrefsToggles() {
|
||||||
|
const container = document.getElementById('forced-prefs-list');
|
||||||
|
if (!container || typeof FORCED_PREFS === 'undefined') return;
|
||||||
|
|
||||||
|
const stored = await browser.storage.local.get(FORCED_PREFS_OPTOUT_KEY);
|
||||||
|
const disabled = new Set(stored[FORCED_PREFS_OPTOUT_KEY] || []);
|
||||||
|
|
||||||
|
container.textContent = '';
|
||||||
|
const entries = [...FORCED_PREFS, ...(typeof FORCED_IDENTITY_PREFS !== 'undefined' ? FORCED_IDENTITY_PREFS : [])];
|
||||||
|
for (const p of entries) {
|
||||||
|
const row = document.createElement('label');
|
||||||
|
row.className = 'forced-pref-row';
|
||||||
|
row.style.display = 'flex';
|
||||||
|
row.style.alignItems = 'center';
|
||||||
|
row.style.gap = '8px';
|
||||||
|
row.style.padding = '4px 0';
|
||||||
|
row.style.cursor = 'pointer';
|
||||||
|
|
||||||
|
const cb = document.createElement('input');
|
||||||
|
cb.type = 'checkbox';
|
||||||
|
cb.checked = !disabled.has(p.id); // Haken = wird erzwungen
|
||||||
|
cb.addEventListener('change', () => setForcedPrefEnabled(p.id, cb.checked));
|
||||||
|
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.textContent = p.label;
|
||||||
|
|
||||||
|
row.append(cb, span);
|
||||||
|
container.appendChild(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setForcedPrefEnabled(id, enabled) {
|
||||||
|
const stored = await browser.storage.local.get(FORCED_PREFS_OPTOUT_KEY);
|
||||||
|
const disabled = new Set(stored[FORCED_PREFS_OPTOUT_KEY] || []);
|
||||||
|
if (enabled) disabled.delete(id);
|
||||||
|
else disabled.add(id);
|
||||||
|
await browser.storage.local.set({ [FORCED_PREFS_OPTOUT_KEY]: [...disabled] });
|
||||||
|
// background.js lauscht auf storage.onChanged und wendet Aktivierungen sofort an.
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', renderForcedPrefsToggles);
|
||||||
|
|||||||
Reference in New Issue
Block a user